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

この記事は[OpenCV Advent Calendar 2022](https://qiita.com/advent-calendar/2022/opencv)の4日目の記事です。

## 1. はじめに

この記事ではOpenCVのcoreモジュールにある以下の機能および内部実装を紹介します。

- `cv::Formatter`
- `cv::Formatted`
- `cv::format`
- `cv::print`

## 2. cv::Formatter

### 2.1 cv::Formatterとは

`cv::Formatter`は、プリセットのフォーマットで整形する機能です。

```cpp
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フォーマットで出力しています）。ソースコードでテーブルを定義するときなんかに便利ですね。

```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>`を使った例も見ていきましょう。

```cpp
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文を使って処理を書く必要がないので簡単ですね。

```python
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フォーマット|

上記フォーマットを指定したサンプルコードを示します。

```cpp
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;
```

出力結果は以下の通りです。

```python
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つです。

```cpp
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;
```

上記のいずれも以下の結果が得られます。

```python
[[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`メソッドを使ったサンプルコードを示します。

```cpp
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;
```

出力結果は以下の通りです。表示される値の精度が変化していることがわかります。

```python
[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`メソッドを使ったサンプルコードを示します。

```cpp
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;
```

出力結果は以下の通りです。複数行表示を有効、無効が切り替わっていることがわかります。

```python
[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で表示してみます。

```python
import numpy as np

A = np.zeros((50, 50))
print(A)
```

このスクリプトを実行すると以下のように途中が省略された表示になります。

```python
[[0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [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`だとどのような出力になるのでしょうか？同じように表示するコードを書いてみましょう。

```c++
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`で巨大なデータを表示すると途中省略されないようです。

<details><summary>cv::Formatterの出力結果</summary><div>

```
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')
```
</div></details>

## 3. cv::Formatted

`cv::Formatted`は`cv::Formatter`で整形された結果を保持するデータ構造です。

### 3.1 使い方

サンプルコードを見てもらったほうがわかりやすいので例を示します。

```cpp
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ループを抜けた時点で終端に到達しているため、そのままだとフォーマッターで整形した文字列を再度参照することができません。

```python
[0.5302828, 0.19925919, 0.40105945;
 0.81438506, 0.43713298, 0.2487897;
 0.77310503, 0.76209372, 0.30779448]
```

前述のコードではresetメソッドでイテレータを先頭に戻しているので2つ目のforループでも同じ結果が得られます。ここでは区切り単位がわかるように文字列（`#`、`@`）を挿入しています。

```text
#[@#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>

```cpp
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`を使って出力を整形することができます。

```cpp
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);
std::cout << cv::format(I, cv::Formatter::FMT_PYTHON) << std::endl;
```

出力結果は以下の通りです。

```python
[[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>

```cpp
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`|

サンプルコードを見てもらったほうがわかりやすいので例を示します。

```cpp
int i = 0;
std::string str = cv::format("i = %d", i);
std::cout << str << std::endl;
```

実行結果は以下の通りです。

```text
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 使い方

サンプルコードを以下に示します。

```cpp
cv::Mat I = cv::Mat::eye(4, 4, CV_64F);

// 標準出力（デフォルト）
cv::print(I);
```

第2引数に`stderr`を指定して標準エラー出力として表示することもできます。

```cpp
// 標準エラー出力
cv::print(I, stderr);
```

出力結果は以下の通りです。

```python
[1, 0, 0, 0;
 0, 1, 0, 0;
 0, 0, 1, 0;
 0, 0, 0, 1]
```

### 5.2 内部実装

「5.1 使い方」で以下のような結果が得られていました。この出力結果を見ると`cv::Formatter::FMT_DEFAULT`で整形されています。

```python
[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>

```cpp
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](https://qiita.com/advent-calendar/2022/opencv)は[hon_no_mushi](https://qiita.com/hon_no_mushi)さんの「OpenCVにおける画像ファイル入出力の仕組み(imwrite編)」です。

## 宣伝

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/105529c1-ee4b-1bf5-4d35-1cc43918e80e.png)


## 参考URL

- <https://docs.opencv.org/4.6.0/d3/da1/classcv_1_1Formatter.html>
- <http://opencv.jp/cookbook/opencv_mat.html>
- <https://qiita.com/wakaba130/items/ae7cdd4b9a9c38fbc971>