C時代の enum CV_** を駆逐したい

# はじめに
これは、OpenCV Advent Calendar 2015 16日目の記事です。関連記事は[目次](http://qiita.com/advent-calendar/2015/opencv)にまとめられています。

OpenCV のC++サポートが入ったのは2.0からで、1.0系列およびその昔はC言語がメインなサポート対象でしたが、OpenCV 3.0 からはC++がサポートされ、C言語はサポート対象外へと変わっています。
OpenCV 2.0 はまさにその過渡期で、Cインタフェースしか無い関数とか、C++インタフェースはあるけれど実体はC版のラッパー（またはその逆）とか、かなり混在していました。今でもその名残は多々存在します。
その名残の一つとして、未だ使われている`CV_`で始まる定数シンボルがあります。

~~`CV_`で始まる定数シンボルを、一匹残らず駆逐してやる！~~

# C時代の名残とは
OpenCV では、関数内部の挙動を変化させるために、引数に定数シンボルを渡すことがあります。例えば、`cvtColor` がその代表例です。

```
void cvtColor( InputArray src, OutputArray dst, int code)
```

この色変換を行う関数のうち、`code`引数で適切なパラメータを渡すことで、望み通りの色変換を行うことができます。下記例だと、`CV_BGR2GRAY` がそれに該当します

```
    using namespace cv;
    Mat color = imread("color.png");
    Mat gray;
    cvtColor(color, gray, CV_BGR2GRAY); // CV_BGR2GRAY はRGB画像をグレースケール画像に変換する
```

利用できるパラメータは以下のように定義されています (一例を抜粋)

```c:types_c.h
 enum
{
    CV_BGR2BGRA    =0,
    CV_RGB2RGBA    =CV_BGR2BGRA,

    CV_BGRA2BGR    =1,
    CV_RGBA2RGB    =CV_BGRA2BGR,
    // 一部中略
    CV_BayerBG2RGB_EA = CV_BayerRG2BGR_EA,
    CV_BayerGB2RGB_EA = CV_BayerGR2BGR_EA,
    CV_BayerRG2RGB_EA = CV_BayerBG2BGR_EA,
    CV_BayerGR2RGB_EA = CV_BayerGB2BGR_EA,

    CV_COLORCVT_MAX  = 139
};
```

この`CV_`で始まる定数(enum)は、OpenCV のC時代の名残であったりします。
1.0系列時代のOpenCV では、OpenCV 由来の関数/定数/構造体を示すために、以下のような法則がありました。

- OpenCV 由来の関数は `cv` で始まる。（例：`cvCreateImage`、`cvCvtColor`）
- OpenCV 由来の構造体は `Cv` で始まる。（例：`CvMat`、`CvPoint2D`）
- OpenCV 由来の定数は `CV_` で始まる。（例：`CV_RGB2Gray`、`CV_INTER_NN`）


# 関数も構造体もcvが外されたのに、定数ときたら
一方でC++には、名前空間という仕組みがあります。基本的にOpenCV のすべての関数、クラス、構造体、定数などは `cv` 名前空間に属します。これによりもし他のライブラリで同じ名前の関数があったとしても、衝突を回避できるわけです。

OpenCV 3.0が出て早6ヶ月、OpenCV 2.2が登場して早5年が経過しました。Web上ではC言語の関数、IplImageやCvMatをはじめとするC言語の構造体の記述は、過去の遺物であり、新規の情報源としては上がってこなくなった気がします。しかし、`CV_`で始まる定数群はまだまだ現役で使われている感じがあります。

現に、OpenCV の内部実装でも、未だ`CV_`で始まる定数が大量に使われています。（例：`cvtColor`内部）

```c++:color.cpp
void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn )
{
    int stype = _src.type();
    int scn = CV_MAT_CN(stype), depth = CV_MAT_DEPTH(stype), bidx;

    // 一部略
    switch( code )
    {
        case CV_BGR2BGRA: case CV_RGB2BGRA: case CV_BGRA2BGR:
        case CV_RGBA2BGR: case CV_RGB2BGR: case CV_BGRA2RGBA:
    // 一部略
            break;
        case CV_BGR5652BGR: case CV_BGR5552BGR: case CV_BGR5652RGB: case CV_BGR5552RGB:
        case CV_BGR5652BGRA: case CV_BGR5552BGRA: case CV_BGR5652RGBA: case CV_BGR5552RGBA:
    // 一部略
            break;
        case CV_BGR2YCrCb: case CV_RGB2YCrCb:
        case CV_BGR2YUV: case CV_RGB2YUV:
    // 一部略
            break;
    // 一部略
        default:
            CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" );
    }
```

`CV_` で始まる定数だけが残っている現状がなんとなく気持ち悪いので、同値の `enum` であり、かつ `cv` 名前空間内で宣言されているものを紹介します。ほとんどが、enumでシリーズとして定義されているので、各シリーズのうち、1つずつを紹介しています。全部は網羅してないと思いますが、あくまで一部を紹介します。

# 同値のenum達
## calib3dモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_LMEDS|LMEDS|
|CV_ITERATIVE|SOLVEPNP_ITERATIVE|
|CV_CALIB_CB_ADAPTIVE_THRESH|CALIB_CB_ADAPTIVE_THRESH|
|CV_CALIB_USE_INTRINSIC_GUESS|CALIB_USE_INTRINSIC_GUESS|
|CV_FM_7POINT|FM_7POINT|

対応点関係やF行列を推定するための手法、また最適化手法などを選択します。RANSACはいいね。RANSACは心を潤してくれる。リリンの生み出した文化の極みだよ。

## coreモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_REDUCE_SUM|REDUCE_SUM|
|CV_L1|NORM_L1|
|CV_COVAR_SCRAMBLED|COVAR_SCRAMBLED|
|CV_LU|DECOMP_LU|
|CV_CMP_EQ|CMP_EQ|
|CV_DXT_INVERSE|DFT_INVERSE|
`CV_LU`のように、もともと短い定数は`DECOMP_LU`の様に、修飾する単語が増えたりします。
後述する`REDUCE_MAX`は、このモジュール内で定義されています。

## highguiモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_FONT_LIGHT|QT_FONT_LIGHT|
|CV_STYLE_NORMAL|QT_STYLE_NORMAL|
|CV_WND_PROP_AUTOSIZE|WND_PROP_AUTOSIZE|
|CV_WINDOW_AUTOSIZE|WINDOW_AUTOSIZE|
|CV_WINDOW_FULLSCREEN|WINDOW_FULLSCREEN|
|CV_EVENT_MOUSEMOVE|EVENT_MOUSEMOVE|
|CV_EVENT_FLAG_LBUTTON|EVENT_FLAG_LBUTTON|
単純にCV_を取り除くだけでほとんどの定数が置き換え可能です。highguiと言えば3.0より前は画像の読み書き、ウィンドウでの表示など、基本的なところでお世話になってますが、OpenCV 3.0からはあくまでGUI関連がhighgui、画像ファイルの読み書きはimgcodecsモジュールが担当となっています。

## imgcodecsモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_LOAD_IMAGE_UNCHANGED|IMREAD_UNCHANGED|
|CV_IMWRITE_JPEG_QUALITY|IMWRITE_JPEG_QUALITY|
imgcodecsは昔でいうhighguiのファイル読み書き担当です。OpenCV 3.0からは別モジュールに分けられました。
OpenCV ではファイルを保存する際に、ファイルごとにオプションを指定できます。
ここに出てる`IMWRITE_JPEG_QUALITY`はjpg形式で画像を保存する際にファイルサイズとノイズのトレードオフを選ぶためのパラメータです。

## imgprocモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_SHAPE_RECT|MORPH_RECT|
|CV_CLOCKWISE|true/falseに変更|
|CV_COMP_CORREL| HISTCMP_CORREL|
|CV_DIST_MASK_3|DIST_MASK_3|
|CV_DIST_LABEL_CCOMP|DIST_LABEL_CCOMP|
|CV_DIST_L1|DIST_L1|
|CV_THRESH_BINARY|THRESH_BINARY|
|CV_ADAPTIVE_THRESH_MEAN_C|ADAPTIVE_THRESH_MEAN_C|
|CV_FLOODFILL_FIXED_RANGE|FLOODFILL_FIXED_RANGE|
|CV_HOUGH_STANDARD|HOUGH_STANDARD|
|CV_BGR2BGRA|COLOR_BGR2BGRA|
|CV_INTER_NN|INTER_NEAREST|
|CV_WARP_FILL_OUTLIERS|WARP_FILL_OUTLIERS|
|CV_MOP_ERODE|MORPH_ERODE|
|CV_TM_SQDIFF|TM_SQDIFF|
|CV_RETR_EXTERNAL|RETR_EXTERNAL|
|CV_CHAIN_CODE| CHAIN_APPROX_NONE|
|CV_AA[^unanancyowen]|LINE_AA|
大量にある`COLOR_BGR2BGRA`の他にも、`INTER_NEAREST`(補間方法)を表すenumがたくさんあります。

## objdetectモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_HAAR_DO_CANNY_PRUNING|CASCADE_DO_CANNY_PRUNING|
Haar特徴量の内部で使うフラグです。詳細は未調査

## photoモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_INPAINT_NS|INPAINT_NS|
|CV_INPAINT_TELEA| INPAINT_TELEA|
OpenCV 3.0から登場したphotoモジュールです。詳細は未調査

## videoモジュール内
幾つかあるものの、対応関係が見つからず

## videoioモジュール内
|CV_で始まる定数|cv名前空間内|
|:--------------|:-----------|
|CV_CAP_ANY|CAP_ANY|
|CV_CAP_PROP_DC1394_OFF|CAP_PROP_POS_MSECPROP|
|CV_CAP_MODE_BGR|CAP_MODE_BGR|
|CV_CAP_OPENNI_DEPTH_MAP|CAP_OPENNI_DEPTH_MAP|
|CV_CAP_OPENNI_VGA_30HZ|CAP_OPENNI_VGA_30HZ|
|CV_CAP_INTELPERC_DEPTH_MAP|CAP_INTELPERC_DEPTH_MAP|
|CV_CAP_PROP_GPHOTO2_PREVIEW|CAP_PROP_GPHOTO2_PREVIEW|

`CAP_PROP_POS_MSECPROP`を始めとするプロパティ達は、キャプチャライブラリごとに細分化されています
もともと、全キャプチャライブラリ用の定数が全部数珠つなぎに同じenumになってましたが、
細分化されたことで、ヘッダを眺めた時に見やすくなるんじゃないか、と思ってます。


# 例外
調べた限り、すべてのCV_定数が置換可能なわけではなく、例外があります。そして、最も使われると言っても過言ではない定数がその例外だったりします。

- CV_8U
- CV_8S
- CV_16U
- CV_16S
- CV_32S
- CV_32F
- CV_64F

こいつらは、Matクラスの、各要素のbit深度を表す定数です。
OpenCV のソースコードを眺めてみましたが、こいつらだけは未だcvdef.h内で定義されたdefineしか存在せず、代替の定数はどこにも定義されていません。 (見落としてるだけだったらこっそり手島まで教えて下さい)

こいつらが、リプレースされないのは、~~面倒くさい~~ ~~誰も着手してない~~ ~~誰も不便を感じてない~~ 忙しくて後回しになっている可能性もありますが、思うに以下2点がネックになって居るのではないかと思います。

- CV_8Uなどの定数はチャンネル数とともに符号化される
- 先頭のCV_を外すと、数字が先頭になるので、文字列として利用できない

## CV_8Uなどの定数はチャンネル数とともに符号化される

`CV_8U`や`CV_32F`自体は0-6までの数字が振られているので、enumとして定義することは可能です。しかし、実際に定数をMat内に情報として保存する場合、bit深度以外にチャンネル数、疎行列か否かといった情報と共に、符号化して保存されます。なのでenumで定義するよりdefineで定義した方が何かと便利なのです。

```c:cvdefs.h
#define CV_8U   0
#define CV_8S   1
#define CV_16U  2
#define CV_16S  3
#define CV_32S  4
#define CV_32F  5
#define CV_64F  6
#define CV_USRTYPE1 7

#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))  // このdefineを使ってbit深度(depth)とチャンネル数(cn)を符号化する
#define CV_MAKE_TYPE CV_MAKETYPE

#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))
```

## 先頭のCV_を外すと、数字が先頭になるので、文字列として利用できない
`CV_8U`から、`8U`となってしまい、定数として定義できません。イマイチいいネーミングが思い浮かばずに、変更できずに居るのかも？でも、よく考えればDEPTH_8Uとか使えば、行けそうな気もします。おそらく符号化が原因でdefineの方が都合が良いので、enumにしようという方向に動かないのでしょう。

# サンプルコード
ただ定数を紹介しただけではワクワクがありませんね。[dandelion1124先生](https://twitter.com/dandelion1124)に煽られた[^dandelion1124]から、という訳では決してありませんが、サンプルコードを以下に書いておきます。
##cv::reduceを使った例
みんな大好きLenaさんの列ごとの分散を求めてみましょう。皆さんもこういうコード書きたいことってありませんか？私はよくあります．

```
    using namespace cv;
    cv::Mat rgb = imread("lena.png");
    
    // カラー画像をグレースケール画像に変換
    Mat gray;
    cvtColor(rgb, gray, COLOR_BGR2GRAY); // CV_BGR2GRAYじゃないよ!
    gray.clone().convertTo(gray, CV_32F);
    
    // 平均の計算
    Mat average;
    reduce(gray, average, 0, REDUCE_AVG); // CV_REDUCE_AVGじゃないよ!
    
    // 自乗の平均の計算
    Mat square;
    Mat variance;
    square = gray.mul(gray);
    average = average.mul(average);
    reduce(square, variance, 0, REDUCE_AVG); // CV_REDUCE_AVGじゃないよ!
    
    // 自乗の平均から平均の自乗を引くと分散になる
    variance -= average;
    
    // 表示用にスケーリングする
    double min, max;
    minMaxLoc(variance, &min, &max);
    variance /= max;
    resize(variance.clone(), variance, Size(variance.cols, outputImageHeight), INTER_NEAREST); // CV_INTER_NN じゃないよ!
    
    // 表示する
    namedWindow(outputWindowName, WINDOW_FREERATIO);  // CV_WINDOW_FREERATIOじゃないよ!
    imshow(outputWindowName, variance);
    
    // 保存用に再度スケーリングする
    variance *= whiteValue;
    imwrite("lena_statistics.png", variance);
```

みんな大好き`cv::reduce`で、画像を1行、もしくは1列に縮退することができます。ここでは単純にグレースケールの画像を平均値を求めることで縮退し、また画像自体を自乗することで、自乗の平均も計算します。最後に自乗の平均から平均の自乗を引くことで分散を求めます。

ちなみに求めた分散が以下の図です
![lena_statistics.png](https://qiita-image-store.s3.amazonaws.com/0/12162/687cc16e-39cc-3711-79e4-1113e40ac6f2.png)
右側から1/4ぐらいのところが分散が大きいことがわかります。Lenaさんの黒い髪の毛、白い肩が同じ列に存在するので、分散が大きくなってるわけですね。

## cv::imwrite/cv::imreadを使った例
もう1例、`imwrite`/`imread`を使ってfloatの値をそのままファイルに保存します。
OpenCV では、基本的にpngやjpegなど、画素それぞれは整数値を持ちます。単純にfloatを保存しようとすると、うまく行きません。
画像のfloatの値をそのままファイルに保存してみましょう。皆さんもこういうコード書きたいことってありませんか？私はよくあります．

```cpp
    using namespace cv;
    Mat floatImage;
    Mat lena = imread("lena.png", IMREAD_GRAYSCALE); // CV_LOAD_IMAGE_GRAYSCALE じゃないよ!
    
    // floatに無理やり変換
    // この時点で4byte / pixelのシングルチャンネル画像ができる
    lena.convertTo(floatImage, CV_32F); // く、駆逐できない
    
    // floatらしさのために、-1.0 -- 1.0 の間で正規化する
    double scaleMax, scaleMin;
    minMaxLoc(floatImage, &scaleMin, &scaleMax);
    floatImage /= (scaleMax - scaleMin);
    floatImage *= 2.0f;
    floatImage -= 1.0f;
    
    namedWindow(outputWindowName);
    imshow(outputWindowName, floatImage); // 途中経過を表示
    waitKey(0);
    
    // 無理やり4チャンネルの8bit画像に変換する
    // Matの第4引数にポインタを渡すことで、データを保持したまま、別の画像から参照することができる
    Mat writeImage = Mat(floatImage.rows, floatImage.cols, CV_8UC4, floatImage.data); // く、駆逐できない
    
    // png は4 channel 受け付ける
    // bmp も保存可能
    imwrite("float.png", writeImage);
    
    // 再度読み込む
    Mat reload = imread("float.png", IMREAD_UNCHANGED); // CV_LOAD_IMAGE_UNCHANGEDじゃないよ!
    // 上記の逆変換を行い、32bitシングルチャンネルに戻す
    Mat showImage = Mat(reload.rows, reload.cols, CV_32FC1, reload.data); // く、駆逐できない
    
    // 今度は 0.0 -- 1.0 の間で正規化する
    showImage += 1.0f;
    showImage /= 2.0f;
    
    imshow(outputWindowName, showImage);
    waitKey(0);
```

このように、floatの画像として保存するのでなく、8bit 4チャンネル(32bit)の画像として保存することで無理やり保存できる
途中経過時点の画像が以下のような図です。全体的に暗い画像になっているのは、OpenCV のウィンドウでfloatを表示する場合は0.0f-1.0fの間しか表示できず、それ以下は黒に、それ以上は白に丸め込まれます。
直前で-1.0f--1.0に正規化してるので、全体的に黒く見える訳です。

![lena_float.png](https://qiita-image-store.s3.amazonaws.com/0/12162/692c87c6-c622-48bc-1de2-b101816fb97f.png)

一方で、4チャンネル画像として保存したLenaさんは、なかなか芸術的な幾何学模様になっています。

![float.png](https://qiita-image-store.s3.amazonaws.com/0/12162/d668610a-b059-83c2-1e3b-a96d2435afe5.png)

これは単純な整数型でなく、float型として保存しているので、符号化されたbitが全体的に砂嵐の様に現れるわけです。
ただし、MSB (最上位bit) は符号bitであり、これはpng画像のうち、アルファチャンネルのMSBでもあります。なので、砂嵐と同時に全体的に白黒の2値画像の雰囲気も付いているのは、これが原因であります。

一件、壊れたように見えるfloat画像ですが、これを再度読み込んで0.0f--1.0fで正規化すると、以下のようにもとのLenaさんに戻せます。

![lena_reconstruct.png](https://qiita-image-store.s3.amazonaws.com/0/12162/a4f76cd1-5955-65ef-57da-f22b70ad1427.png)

# まとめ
未だ使われ続ける`CV_`で始まる~~定数群を駆逐したくて、~~定数群の代替を紹介しました。
どうしても`CV_8U`や`CV_32F`を駆逐できなかったのが心残りです。

# 備考
筆者は以下の環境で動作確認しました．

- OpenCV 3.0.0
- Windows 7 Professional (64bit) 
- Visual Studio 2012 Update4

# 補足事項
この記事はあくまで手島個人が `CV_` で始まる定数が好きではない、cv空間内のenumの方が好きだ！という主旨で書いている記事です。`CV_`で始まる定数を使ってるWeb上の記事を非難しているわけではありません。同値であるので、`cv::INTER_NEAREST` も `CV_INTER_NN` も、好きな方を使えば良いと思います。
 
[^dandelion1124]: [dandelionさんはTwitterを使っています: &quot;@tomoaki_teshima テクい記事楽しみにしてます！&quot;](https://twitter.com/dandelion1124/status/676380023138336768)

[^unanancyowen]: [UnaNancyOwen先生にご指摘頂きましたので、追加します。](https://twitter.com/UnaNancyOwen/status/676968304980967424)