OpenCV とは、画像処理ライブラリである。
ソースコードからコンパイルする方法もあるのだろうが、 私は結局 apt get install でインストールした。 Ubuntu 20.04 (Windows Subsystem for Linux 2 , WSL2) である。
$ sudo apt-get install libopencv-dev python3-opencv
$ dpkg -l | grep libopencv
私の場合は 4.2.0+dfsg-5 だった。
私は C++ でプログラムを動かそうと思っている。使うコンパイラは g++ 9.3.0 である。 さて、OpenCV のサンプルを探そうとして、OpenCV サンプルプログラム、サンプルプログラム、 などでで検索すると、 OpenCV のバージョンが 1 のものから、最新のものまでけっこうみつかった。
この先どう進めてよいか迷ったが、OpenCV 本家のサンプルを使うのがよいだろうと判断した。 本家の C++ のサンプルプログラムは下記にある: https://github.com/opencv/opencv/tree/master/samples/cpp
このページ直下にあるプログラムだけで 88 もある。どれが一番簡単かわからない。 ファイル名をたよりに探したところ、opencv_version.cpp が簡単そうなので、コンパイルしてみた。
$ g++ opencv_version.cpp -I/usr/include/opencv4 -lopencv_core -o opencv_version $ ./opencv_version Welcome to OpenCV 4.2.0
次に簡単なプログラムはどれだろうか。demhist.cpp というのが、 ヒストグラムのデモンストレーションではないかと推測し、コンパイルしてみた。
$ g++ demhist.cpp -I/usr/include/opencv4 -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui -o demhist
/usr/bin/ld: /tmp/ccZ19vcW.o: undefined reference to symbol 'tempfilename'
/usr/bin/ld: /lib/x86_64-linux-gnu/libopencv_imgproc.so.4.2: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
ああ、これはライブラリが足りなかったのだな。
$ g++ demhist.cpp -I/usr/include/opencv4 -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc -o demhist
/usr/bin/ld: /tmp/ccZ19vcW.o: undefined reference to symbol 'tempfilename'
コンパイルは通った。実行してみよう。
$ ./demhist
[ WARN:0] global ../modules/core/src/utils/samples.cpp (59) findFile cv::samples::findFile('baboon.jpg') => ''
terminate called after throwing an instance of 'cv::Exception'
what(): OpenCV(4.2.0) ../modules/core/src/utils/samples.cpp:62: error: (-2:Unspecified error) OpenCV samples: Can't find required data file: baboon.jpg in function 'findFile'
カレントディレクトリに baboon.jpg を用意すればよさそうだ。
https://github.com/opencv/opencv/blob/master/samples/data/baboon.jpg
からダウンロードした。
再度実行して、無事ヒストグラムが表示された。
なお、OpenCV のライブラリ libopencv_***.so は、私の環境(Ubuntu 20.04)では、
/lib/x86_64-linux-gnu/
にある。C++ でコンパイル・リンクして実行ファイルを作るとき、
g++ や Makefile で -L オプションを指定せずとも上記ディレクトリを見るようだ。
もう少し C++ による OpenCV のサンプルプログラムがないか探していたら、 OpenCV超入門 サンプルプログラム集 (nn-hokuson.hatenablog.com)というページがあった。非常にありがたい。
上記ページのプログラムを WSL2 の Ubuntu 20.04, C++ (g++ 9.30), OpenCV 4.2 でコンパイルするにあたって考慮すべき点を以下述べる。
では、画像ファイルを読み込んで表示をするだけのプログラム display.cpp を掲げる。 起動したファイルと同じディレクトリ(フォルダ)に img.png があるという決め打ちのプログラムである。
/* display.cpp */
#include <opencv2/core.hpp> // cvcore
#include <opencv2/opencv.hpp> // line, rectangle, circle, ...
#include <opencv2/imgcodecs.hpp> // imread, ...
#include <opencv2/highgui.hpp> // imshow, ...
#include <opencv2/imgproc/imgproc.hpp> // blur, canny, ...
#include <iostream> // cout, ...
int main(void)
{
Mat img = imread("img.png");
if (!img.data)
{
std::cout << "Image not loaded" << "\n";
return -1;
}
imshow("image",img);
waitKey(0);
return 0;
}
Makefile は次のとおりとなる。
# Makefile all : display INCDIR = -I /usr/include/opencv4 CXXFLAGS = -Wall $(INCDIR) LDLIBS = -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc
本家の C++ のサンプルプログラム:
https://github.com/opencv/opencv/tree/master/samples/cpp
の一覧の一部をまとめてみた。
| ファイル名 | 内容 | 備考 |
|---|---|---|
| 3calibration.cpp | 較正 | |
| application_trace.cpp | ||
| asift.cpp | アフィン特徴検出器・抽出器:AffineFeature detector/extractor | |
| bgfg_segm.cpp | 背景前景分離 | |
| calibration.cpp | 較正 | |
| camshiftdemo.cpp | 移動平均に基づくトラッキング:mean-shift based tracking | |
| cloning_demo.cpp | GUI を使わないクローニングのデモンストレーション | |
| cloning_gui.cpp | GUI を使うクローニングのデモンストレーション | |
| connected_components.cpp | 連結成分とトラックバー使用のデモンストレーション | |
| contours2.cpp | 等高線 | |
| convexhull.cpp | 凸包 | |
| cout_mat.cpp | 直列出力:demonstrates the serial out capabilities of cv::Mat | |
| create_mask.cpp | マスク像の作成:demonstrates how to make mask image (black and white) | |
| dbt_face_detection.cpp | 顔検出(dbt : DetectionBasedTracker) | |
| delaunay2.cpp | ドロネー図 | |
| demhist.cpp | ヒストグラムのデモ | |
| detect_blob.cpp | 領域検出・フィルターに BLOB を使う方法 | |
| detect_mser.cpp | 領域検出に MSER (Maximally Stable Extremal Regions)を使う方法 | |
| dft.cpp | 離散フーリエ変換 | |
| digits_lenet.cpp | LeNet-5と連結成分解析に基づくディジタル認識 | |
| digits_svm.cpp | サポートベクトルマシン(SVM)とk-近傍法に基づくディジタル認識 | |
| dis_opticalflow.cpp | DIS (Dense Inverse Search) オプティカルフロー | |
| distrans.cpp | エッジ画像間距離変換関数の使い方 | |
| drawing.cpp | ドローイング関数とテキスト関数 | |
| edge.cpp | Canny フィルタによるエッジ検出 | |
| ela.cpp | 誤差レベル解析(Error Level Analysis)を用いた画像内領域同定 | |
| em.cpp | Expectation-Maximization アルゴリズムによるクラスタリング | |
| epipolar_lines.cpp | エピポーラ線 | |
| essential_mat_reconstr.cpp | 基本行列(Essential Matrix)再構成 | |
| facedetect.cpp | 顔検出 | |
| facial_features.cpp | 顔特徴 | |
| falsecolor.cpp | applyColorMap 関数の使い方 | |
| fback.cpp | Gunnar Farneback による密オプティカルフローアルゴリズム | |
| ffilldemo.cpp | floodFill() 関数 | |
| filestorage.cpp | シリアライズ機能 | |
| fitellipse.cpp | 楕円フィッティング | |
| flann_search_dataset.cpp | FLANN を使った画像の探索 | |
| grabcut.cpp | GrabCut セグメンテーションのデモ | |
| image_alignment.cpp | 画像のアラインメントに使う ECC アルゴリズムを実装した findTransformECC 関数のデモ | |
| imagelist_creator.cpp | コマンドラインの引数からファイルの一覧を YAML か XML で作る | |
| imagelist_reader.cpp | imagelist_creator.cpp で作った一覧をアップロードする | |
| inpaint.cpp | ||
| intelligent_scissors.cpp | 知的なハサミ | |
| intersectExample.cpp | 凸図形どうしの交わり | |
| kalman.cpp | カルマンフィルター | |
| kmeans.cpp | K-平均クラスタリング | |
| laplace.cpp | ラプラシアンフィルタによる点・エッジ検出 | |
| letter_recog.cpp | 文字認識 | |
| lkdemo.cpp | Lukas-Kanade 法によるオプティカルフロー | |
| logistic_regression.cpp | ロジスティック回帰 | |
| mask_tmpl.cpp | マスクを用いたテンプレートマッチング | |
| matchmethod_orb_akaze_brisk.cpp | ||
| minarea.cpp | 輪郭外接(長方形|三角形|円) | |
| morphology2.cpp | ||
| neural_network.cpp | 神経回路網 | |
| npr_demo.cpp | 非写実的レンダリング(Non-Photorealistic Rendering) | |
| opencv_version.cpp | OpenCV のバージョン表示 | |
| pca.cpp | 主成分分析(Principal Component Analysis) | |
| peopledetect.cpp | ||
| phase_corr.cpp | 位相限定相関法 | |
| points_classifier.cpp | 点のクラス分け | |
| polar_transforms.cpp | ||
| qrcode.cpp | QR コード | |
| segment_objects.cpp | ||
| select3dobj.cpp | ||
| simd_basic.cpp | SIMDアクセラレーション組み込み関数の基本 | |
| smiledetect.cpp | ||
| squares.cpp | ピラミッドスケーリング、Canny フィルタ、等高線を用いた四角形検出 | |
| stereo_calib.cpp | ||
| stereo_match.cpp | ||
| stitching.cpp | パノラマ写真画像合成 | |
| stitching_detailed.cpp | ||
| text_skewness_correction.cpp | テキストの傾き補正 | |
| train_HOG.cpp | HOG 特徴量の訓練 | |
| train_svmsgd.cpp | サポートベクトルマシン(Support Vector Machine)・確率的勾配降下法(Stochastic Gradient Method) | |
| travelsalesman.cpp | ||
| tree_engine.cpp | 決定木の取り扱い方法 | |
| videocapture_basic.cpp | ビデオキャプチャー基本 | |
| videocapture_camera.cpp | ||
| videocapture_gphoto2_autofocus.cpp | ||
| videocapture_gstreamer_pipeline.cpp | ||
| videocapture_image_sequence.cpp | ||
| videocapture_intelperc.cpp | ||
| videocapture_openni.cpp | ||
| videocapture_starter.cpp | ||
| videowriter_basic.cpp | ||
| warpPerspective_demo.cpp | 透視変換のデモ | |
| watershed.cpp | 有名な watershed アルゴリズムセグメンテーション |
上記のサンプルプログラムのうち、intersectExample.cpp をコンパイルし、 実行すると、エラーになる。なお、このエラーになるプログラムを、 intersectExampleErr.cpp とリネームする。
$ ./intersectExampleErr terminate called after throwing an instance of 'cv::Exception' what(): OpenCV(4.2.0) ../modules/core/src/matrix_wrap.cpp:50: error: (-215:Assertion failed) i < 0 in function 'getMat_'
一方、docs.opencv.org には似たプログラムがある。
https://docs.opencv.org/4.2.0/df/da5/samples_2cpp_2intersectExample_8cpp-example.html
こちらをコンパイルして実行した結果は正常である。どこが違うのか。diff -c をかけて調べてみた。 なお、違いを最小限にするため、intersectExampleErr.cpp の空行は省いている。
$ diff -c intersectExampleErr.cpp intersectExample.cpp
*** intersectExampleErr.cpp 2021-02-02 15:52:50.020160700 +0900
--- intersectExample.cpp 2021-02-02 14:39:17.165149100 +0900
***************
*** 42,48 ****
{
fillColor = Scalar(0, 0, 255);
}
! fillPoly(image, intersectionPolygon, fillColor);
}
polylines(image, polygons, true, Scalar(0, 0, 0));
return intersectArea;
--- 42,50 ----
{
fillColor = Scalar(0, 0, 255);
}
! vector<vector<Point> > pp;
! pp.push_back(intersectionPolygon);
! fillPoly(image, pp, fillColor);
}
polylines(image, polygons, true, Scalar(0, 0, 0));
return intersectArea;
実行で失敗するプログラムは、fillPoly の第2引数が intersectionPolygon であったが、 成功するプログラムは pp である。両者の違いは、 変数 intersectionPolygon の型がvector <Point> であるのに対し、 変数 pp の型は vector<vector<Point> > であることだ。 成功するプログラムでは、事前に intersectionPolygon を push_back メソッドによって型を変換している。
fillPoly のプロトタイプは次のいずれかである:
今回の fillPoly は後者のプロトタイプを使っていることは明らかだ。 そのため第2引数は配列の配列 InputArrayOfArrays にする必要があり、成功するプログラムは型を合わせる変換をしていた、 ということがわかった(2021-02-02)。
https://docs.opencv.org/4.2.0/index.html
にあることができればいい、と思っている。