この記事はOpenCV Advent Calendar 2022の4日目の記事です。
1. はじめに
この記事ではOpenCVのcoreモジュールにある以下の機能および内部実装を紹介します。
cv::Formatter
cv::Formatted
cv::format
cv::print
2. cv::Formatter
2.1 cv::Formatterとは
cv::Formatter
は、プリセットのフォーマットで整形する機能です。
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);
std::cout << "\nI = \n" << cv::format(I, cv::Formatter::FMT_PYTHON) << std::endl;
例えば上記のMatクラスのインスタンスをcv::Formatterを使って表示すると以下のようになります(この例ではPythonフォーマットで出力しています)。ソースコードでテーブルを定義するときなんかに便利ですね。
I =
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]
この結果だけ見ると、cv::Formatterがcv::Mat
にだけ対応しているように見えるかもしれません。それではstd::vector<cv::Point>
を使った例も見ていきましょう。
std::vector<cv::Point> pts;
for(int i = 0; i < 10; i++)
{
pts.push_back(cv::Point(i, 2 * i));
}
std::cout << "pts = " << cv::format(pts, cv::Formatter::FMT_PYTHON) << std::endl << std::endl;
実行結果は以下の通りです。点列のデータを表示するときもわざわざ自前でfor文を使って処理を書く必要がないので簡単ですね。
pts = [[[0, 0], [1, 2], [2, 4], [3, 6], [4, 8], [5, 10], [6, 12], [7, 14], [8, 16], [9, 18]]
2.2 使い方
cv::Formatter
で使用できるフォーマットを下表に示します。
cv::Formatter::FormatType | 意味 |
---|---|
cv::Formatter::FMT_DEFAULT |
デフォルト |
cv::Formatter::FMT_MATLAB |
MATLABフォーマット |
cv::Formatter::FMT_CSV |
CSVフォーマット |
cv::Formatter::FMT_PYTHON |
Pythonフォーマット |
cv::Formatter::FMT_NUMPY |
NumPyフォーマット |
cv::Formatter::FMT_C |
Cフォーマット |
上記フォーマットを指定したサンプルコードを示します。
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);
std::cout << "I (default) = \n" << cv::format(I, cv::Formatter::FMT_DEFAULT) << ";" << std::endl;
std::cout << "\nI (matlab) = \n" << cv::format(I, cv::Formatter::FMT_MATLAB) << ";" << std::endl;
std::cout << "\nI (csv) = \n" << cv::format(I, cv::Formatter::FMT_CSV) << std::endl;
std::cout << "\nI (python) = \n" << cv::format(I, cv::Formatter::FMT_PYTHON) << std::endl;
std::cout << "\nI (numpy) = \n" << cv::format(I, cv::Formatter::FMT_NUMPY) << std::endl;
std::cout << "\nI (c) = \n" << cv::format(I, cv::Formatter::FMT_C) << ";" << std::endl;
出力結果は以下の通りです。
I (default) =
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1];
I (matlab) =
(:, :, 1) =
1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1;
I (csv) =
1, 0, 0, 0
0, 1, 0, 0
0, 0, 1, 0
0, 0, 0, 1
I (python) =
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]
I (numpy) =
array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]], dtype='float64')
I (c) =
{1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1};
2.3 cv::Formatterの使用パターン
cv::Formatter
を使用するときの典型的なパターンは以下の3つです。
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);
// (1) cv::formatを使う
std::cout << cv::format(I, cv::Formatter::FMT_PYTHON) << std::endl;
// (2) cv::Formatter::getを使う パターン1
std::cout << cv::Formatter::get(cv::Formatter::FMT_PYTHON)->format(I) << std::endl;
// (3) cv::Formatter::getを使う パターン2
cv::Ptr<cv::Formatter> formatter = cv::Formatter::get(cv::Formatter::FMT_PYTHON);
std::cout << formatter->format(I) << std::endl;
上記のいずれも以下の結果が得られます。
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]
2.4 cv::Formatterのメソッド紹介
cv::Formatter
にはいくつかの便利なメソッドが用意されているので紹介します。
メソッド | 概要 |
---|---|
set16fPrecision | FP16で表示するときの精度設定 |
set32fPrecision | FP32で表示するときの精度設定 |
set64fPrecision | FP64で表示するときの精度設定 |
setMultiline | 複数行で表示するかを設定 |
以下にset32fPrecision
メソッドを使ったサンプルコードを示します。
cv::Mat r = cv::Mat(3, 3, CV_32F);
cv::randu(r, cv::Scalar::all(0.0f), cv::Scalar::all(1.0f));
// フォーマッター作成
cv::Ptr<cv::Formatter> formatter = cv::Formatter::get(cv::Formatter::FMT_PYTHON);
// FP32で表示するときの精度を小数点以下8桁にする(デフォルト)
std::cout << "[set32fPrecision=8]" << std::endl;
std::cout << formatter->format(r) << std::endl << std::endl;
// FP32で表示するときの精度を小数点以下4桁にする
std::cout << "[set32fPrecision=4]" << std::endl;
formatter->set32fPrecision(4);
std::cout << formatter->format(r) << std::endl;
出力結果は以下の通りです。表示される値の精度が変化していることがわかります。
[set32fPrecision=8]
[[0.5302828, 0.19925919, 0.40105945],
[0.81438506, 0.43713298, 0.2487897],
[0.77310503, 0.76209372, 0.30779448]]
[set32fPrecision=4]
[[0.5303, 0.1993, 0.4011],
[0.8144, 0.4371, 0.2488],
[0.7731, 0.7621, 0.3078]]
続いてsetMultiline
メソッドを使ったサンプルコードを示します。
cv::Mat r = cv::Mat(3, 3, CV_32F);
cv::randu(r, cv::Scalar::all(0.0f), cv::Scalar::all(1.0f));
// フォーマッター作成
cv::Ptr<cv::Formatter> formatter = cv::Formatter::get(cv::Formatter::FMT_PYTHON);
// 複数行表示を有効にする(デフォルト)
std::cout << "[setMultiline=true]" << std::endl;
formatter->setMultiline(true);
std::cout << formatter->format(r) << std::endl << std::endl;
// 複数行表示を無効にする
std::cout << "[setMultiline=false]" << std::endl;
formatter->setMultiline(false);
std::cout << formatter->format(r) << std::endl;
出力結果は以下の通りです。複数行表示を有効、無効が切り替わっていることがわかります。
[setMultiline=true]
[[0.5302828, 0.19925919, 0.40105945],
[0.81438506, 0.43713298, 0.2487897],
[0.77310503, 0.76209372, 0.30779448]]
[setMultiline=false]
[[0.5302828, 0.19925919, 0.40105945], [0.81438506, 0.43713298, 0.2487897], [0.77310503, 0.76209372, 0.30779448]]
2.5 巨大なデータを表示したときの挙動
cv::Formatter
で巨大なデータを表示したときの挙動が気になるので確認してみましょう。まずは比較対象としてNumPyで表示してみます。
import numpy as np
A = np.zeros((50, 50))
print(A)
このスクリプトを実行すると以下のように途中が省略された表示になります。
[[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
...
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]]
cv::Formatter
だとどのような出力になるのでしょうか?同じように表示するコードを書いてみましょう。
cv::Mat A = cv::Mat::zeros(50, 50, CV_32F);
std::cout << "\nA = \n" << cv::format(A, cv::Formatter::FMT_NUMPY) << std::endl;
出力結果は以下の通りです。cv::Formatter
で巨大なデータを表示すると途中省略されないようです。
cv::Formatterの出力結果
A =
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype='float32')
3. cv::Formatted
cv::Formatted
はcv::Formatter
で整形された結果を保持するデータ構造です。
3.1 使い方
サンプルコードを見てもらったほうがわかりやすいので例を示します。
cv::Mat r = cv::Mat(3, 3, CV_32F);
cv::randu(r, cv::Scalar::all(0.0f), cv::Scalar::all(1.0f));
// フォーマッターによる整形
cv::Ptr<cv::Formatted> formatted = cv::Formatter::get()->format(r);
// 整形済みの文字列を表示
for(const char* str = formatted->next(); str; str = formatted->next())
{
std::cout << str;
}
// 上記forループを抜けた時点で終端に到達
// イテレータを先頭に戻す
formatted->reset();
for(const char* str = formatted->next(); str; str = formatted->next())
{
// どの単位で区切られているかわかるように文字列を挿入
std::cout << "#" << str << "@";
}
最初のforループではフォーマッターで整形した文字列が表示されます。ただし、このforループを抜けた時点で終端に到達しているため、そのままだとフォーマッターで整形した文字列を再度参照することができません。
[0.5302828, 0.19925919, 0.40105945;
0.81438506, 0.43713298, 0.2487897;
0.77310503, 0.76209372, 0.30779448]
前述のコードではresetメソッドでイテレータを先頭に戻しているので2つ目のforループでも同じ結果が得られます。ここでは区切り単位がわかるように文字列(#
、@
)を挿入しています。
#[@#0.5302828@#, @#0.19925919@#, @#0.40105945@#;@#
@# @#0.81438506@#, @#0.43713298@#, @#0.2487897@#;@#
@# @#0.77310503@#, @#0.76209372@#, @#0.30779448@#]@
3.2 内部実装
cv::Ptr<cv::Formatted>
のデータをstd::cout
で表示できる仕組みを理解するために内部実装を読んでみましょう。
https://github.com/opencv/opencv/blob/4.6.0/modules/core/include/opencv2/core/cvstd.inl.hpp#L77-L84
static inline
std::ostream& operator << (std::ostream& out, Ptr<Formatted> fmtd)
{
fmtd->reset();
for(const char* str = fmtd->next(); str; str = fmtd->next())
out << str;
return out;
}
modules/core/include/opencv2/core/cvstd.inl.hpp
で演算子<<
がオーバーロードされていて、前述のイテレータを使って出力していることがわかります。
4. cv::format
cv::format
は以下のような使い方ができます。
-
cv::Formatter
で整形 - 書式指定で整形
以降、それぞれの使い方について紹介します。
4.1 cv::Formatterで整形
「2.3 cv::Formatterの使用パターン」で紹介したようにcv::Formatter
を使って出力を整形することができます。
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);
std::cout << cv::format(I, cv::Formatter::FMT_PYTHON) << std::endl;
出力結果は以下の通りです。
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]
この挙動について理解するためにcv::format
の実装を読んでみましょう。
https://github.com/opencv/opencv/blob/4.6.0/modules/core/include/opencv2/core/operations.hpp#L450-L454
static inline
Ptr<Formatted> format(InputArray mtx, Formatter::FormatType fmt)
{
return Formatter::get(fmt)->format(mtx.getMat());
}
cv::format
は、入力データ、フォーマット指定をもとに整形済みのcv::Ptr<cv::Formatted>
を返していることがわかります。そのため、「3.2 内部実装」で紹介したようにstd::coutで出力すると整形済みの結果が表示されることがわかります。
4.2 書式指定で整形
端的に言うとprintfと同じような書式付きで表示できます。
Type | Specifier |
---|---|
const char* |
%s |
char |
%c |
float / double
|
%f ,%g
|
int , long , long long
|
%d , %ld , %lld
|
unsigned , unsigned long , unsigned long long
|
%u , %lu , %llu
|
uint64 -> uintmax_t , int64 -> intmax_t
|
%ju , %jd
|
size_t |
%zu |
サンプルコードを見てもらったほうがわかりやすいので例を示します。
int i = 0;
std::string str = cv::format("i = %d", i);
std::cout << str << std::endl;
実行結果は以下の通りです。
i = 0
5. cv::print
cv::print
は、以下のデータ型を表示することができます。
cv::Mat
cv::UMat
std::vector<Point_<_Tp>
std::vector<Point3_<_Tp>
Matx<_Tp, m, n>
5.1 使い方
サンプルコードを以下に示します。
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);
// 標準出力(デフォルト)
cv::print(I);
第2引数にstderr
を指定して標準エラー出力として表示することもできます。
// 標準エラー出力
cv::print(I, stderr);
出力結果は以下の通りです。
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1]
5.2 内部実装
「5.1 使い方」で以下のような結果が得られていました。この出力結果を見るとcv::Formatter::FMT_DEFAULT
で整形されています。
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1]
この挙動について理解するためにcv::print
の実装を読んでみましょう。
https://github.com/opencv/opencv/blob/4.6.0/modules/core/include/opencv2/core/operations.hpp#L467-L471
static inline
int print(const Mat& mtx, FILE* stream = stdout)
{
return print(Formatter::get()->format(mtx), stream);
}
上記実装でデフォルト指定(=cv::Formatter::FMT_DEFAULT
)となっていることがわかります。また、この実装からもわかるようにcv::print
の現実装ではcv::Formatter::FMT_DEFAULT
以外を指定することができない点に注意が必要です。
6. 動作確認環境
- Ubuntu 22.04
- OpenCV 4.6.0
7. おわりに
この記事ではOpenCVのcoreモジュールにある以下の機能および内部実装を紹介しました。
cv::Formatter
cv::Formatted
cv::format
cv::print
明日のOpenCV Advent Calendar 2022はhon_no_mushiさんの「OpenCVにおける画像ファイル入出力の仕組み(imwrite編)」です。
宣伝
今年、「OpenCVではじめよう ディープラーニングによる画像認識」(技術評論社)という書籍を出版しました。OpenCVの基礎的な解説から、dnnモジュールを用いたディープラーニングによる画像認識についても解説しているので興味あればご一読ください。