LoginSignup
14
4

More than 1 year has passed since last update.

cv::Formatter、cv::printを使いこなす

Last updated at Posted at 2022-12-03

この記事は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::Formattedcv::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 2022hon_no_mushiさんの「OpenCVにおける画像ファイル入出力の仕組み(imwrite編)」です。

宣伝

今年、「OpenCVではじめよう ディープラーニングによる画像認識」(技術評論社)という書籍を出版しました。OpenCVの基礎的な解説から、dnnモジュールを用いたディープラーニングによる画像認識についても解説しているので興味あればご一読ください。

image.png

参考URL

14
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
4