Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
OpenCV4.0 簡易マニュアル パターン
認識演習
OpenCV について
OpenCV(Open source Computer Vision library)はオープンソースのコンピュータビジョン 向けライブラリであり,画像の読み取りや表⽰以外に,⾏列演算などもサポートしている. 今回の演習では OpenCV4.0 を使⽤する.ネットで調べる際には 1.x 系列や 2.x 系列および 3.0 系列と混同してしまわないように注意すること. 基本的には公式のリファレンス(https://docs.opencv.org/4.0.0/)を参考にしま しょう.
演習を始める前に
演 習 に ⽤ い る 計 算 機 ccparallel-1.sys.wakayama-u.ac.jp は デ フ ォ ル ト でOpenCV4 を⽤いるように設定されています.
ヘッダファイルのインクルード
OpenCV3 系のヘッダファイルをインクルードする場合は以下のサンプルの通りにインクルードしてください.ネット上に転がっているサンプルコードは 2 系のものが多いので,間違っ てインクルードするとエラーの原因になる可能性があります.
OpenCV を利用した,ソースコードのコンパイル
通常のプログラムのコンパイルとは少し異なります,今回の演習では C++でプログラムを書 きますのでコンパイラは”cc”や”gcc”ではなく”g++”になります.注意してください. 以下にコンパイルの例を⽰します.オプションが多く覚えづら
#include <opencv2/opencv.hpp>
いかもしれませんが,原理がわかれば簡単です.
<例> OpenCV を利⽤したプログラム sample2.cpp をコンパイルして 実⾏ファイル sample2 を 作成する場合
まずは,配布したサンプルプログラムをコンパイルするところから始めてみましょう.
cv::Mat
ここからは,OpenCV で最も使⽤頻度が多く,基本的なクラスである Mat について解説し ていきます.Mat にはかなりの種類のメンバ関数,メンバ変数が存在するので,全てを説明 することはできませんが,基本的な使い⽅について,ここで抑えてもらえたらと思います. Mat についてわからないことがあるならば,ここで調べてください. https://docs.opencv.org/4.0.0/d3/d63/classcv_1_1Mat.html
Mat の宣言 Mat の宣言については色々な方法があります.
例えば 2x2 の整数型の Mat ”matrix” を宣⾔するだけでも,いくつかの⽅法が存在します.
(1)は始めに matrix の宣⾔のみを⾏い,後から Mat のサイズとタイプを決めてい
$ g++ -std=c++11 `pkg-config opencv4 --cflags --libs` sample2.cpp -o sample2
cv::Mat matrix; // (1)
matrix = cv::Mat(2,2,CV_32SC1); // (1)
cv::Mat matrix(3,3,CV_32SC1); // (2)
cv::Mat matrix = (cv::Mat_<int>(3,3) << 1,2,3,4); // (3)
る. (2)では 宣⾔と同時にサイズとタイプを決めている. (3)ではテンプレートクラスを⽤いて,宣⾔し ながら値を代⼊している. (1)(2)ではコンストラクタへ渡す第三引数に”CV_32SC1”と記述 しているが,これはこの Mat が,32bit,符号付整数,チャンネル数は 1 ということを⽰して いる. 詳細は次の節で.
Mat のタイプ
前節でも軽く触れましたが,Mat のタイプをどのように宣⾔するかによって,表現できる 値の範囲が変わります.タイプの種類と記法に関しては以下を参考にしてください. <要素型の種類と記法> CV_<bit_depth>(S|U|F)C<number_of_channels>
bit_depth:ビット数=8,16,32,64 S:符号付き整数
U:符号なし整数
F:浮動小数 number_of_channels:チャンネル数=1〜4
⾏列演算を⾏うときや画像を扱うときによって適切なタイプを使い分ける必要がある. 主なタイプと使⽤例は以下の通り. 以下の図も参考にしてみてください.
● CV_8UC1 グレースケール画像を表現するときに使う
● CV_8UC3 カラー画像を表現するときに使う ● CV_32FC1 float 型の値を格納できる,⾏列演算に使う ● CV_64FC1 double 型の値を格納できる,float より厳密な計算を⾏いたい場合に
.type()で出力される値と(左)と各タイプの mat が表現できる値の範囲(右)
http://ninghang.blogspot.jp/2012/11/list-of-mat-type-in-opencv.html より引用
OpenCV 使い始めのころは,Mat のタイプを特に意識せずコーディンクしてしまいがちです.予想外のバグに出くわしたら,.type()関数を⽤いて Mat のタイプを出⼒することで確認 を⾏ってみても良いかもしれません.
Mat による行列演算と行列の操作
C++で書かれた OpenCV ではクラスを⽤いているので,⾏列の演算をシンプルに⾏えます .以下に例を⽰します. <例> 3x3 の⾏列 A,B,3x1 の⾏列 C,D に対して
cv::Mat A,B,C,D; /* 値の代⼊は省略 */ A + B; // ⾏列の和 A - B; // ⾏列の差 A * B; // ⾏列の積 2 * A; // ⾏列のスカラ倍 A.t() // ⾏列の転置 C.dot(D) // 内積の計算 C.cross(D) // 外積の計算
Mat の出力
Mat の出力は非常に簡単です,配列のようにわざわざ要素を走査して出力する必要はな
く ,出力ストリームに Mat を渡せば出力してくれます.
<例> 3x3 の行列 A を出力
OpenCV で画像を扱う
ここからは,本格的に画像を取り扱っていきます.OpenCV では画像も Mat で保持します,
画像の読み込み
まズ,基本となる画像の読み込みからです.画像の読み込みには cv::imread()関数を使いま す.この関数は指定したファイルから画像を読み込み,その画像の Matを返り値とします.第 ⼀引数にファイル名を与え,第⼆引数には,整数でオプションを与えます.オプションの値 によって画像の読み込み⽅が変わってきます.具体的には
l >0 3 チャンネルカラー画像として画像を読み込む
l =0 1 チャンネルグレースケール画像として画像を読み込む
l <0 そのままの画像として読み込む
となります. <例> 実⾏ファイルと同じディレクトリにある”lena.jpg”を読み込む場合
cv::Mat A;
/* 値の代入は省略 */
std::cout << A << std::endl;
cv::Mat image;
image = cv::imread(“lena.jpg”,1);// lena.jpg をカラー画像として読み込む
image = cv::imread(“lena.jpg”,0);// lena.jpg をクレースケール画像として読み込む
画像の表示
次に,Mat に読み込んだ画像の画⾯へ出⼒する⽅法について説明していきます.⼤まかな流 れは,cv::namedWindow()関数でウインドウを作成した後に,cv::imshow()関数で画像を出 ⼒するウインドウの名前と表⽰したい画像の Matを引数として渡します.以下の例を⾒てみましょう.
この例で最後に cv::waitKey()という関数が使われていますが,これは引数で指定された時間 だけ待機するという関数になってます.ただし,引数が 0 の場合はキーボードからの⼊⼒が 来るまで待ち続けるという挙動になります.この関数を使わないと,画像を表⽰した直後に プログラムが終了してしまい,画像が出⼒されているか確認できません.
画像の保存
Mat で保持している画像をファイルとして保存する⽅法について説明します.これは⾮常に 簡単で,cv::imwrite()関数で⾏うことができます.第⼀引数に保存するファイル名を与えて, 第⼆引数には保存したい画像の Mat を与えます.ファイル名を与えるとき,拡張⼦もセットに して与えると,その拡張⼦の形で保存してくれます. <例>lena.jpg:jpeg 画像として保存,lena.png:png として保存
cv::Mat img = cv::imread(“lena.jpg”) //画像読み込む
cv::namedWindow(“lena”); //”lena”という名前のウイントウの作成
cv::imshow(“lena”,img); //”lena”というウイントウに img を表⽰する
cv::waitKey(0); // キーホートからの⼊⼒を待つ
cv::Mat img = cv::imread(“lena.jpg”,0); // 画像の読み込み
cv::imread(“lena_gray.jpg”,img); // jpeg で保存
cv::imread(“lena_gray.png”,img); // png で保存
当然ですが,同名のファイルが存在する場合は,確認などは⼀切⾏わずに上書き保存されるので注意しましょう.
画素へのアクセス
1.at メソッドを使う⽅法 難易度:易,実⾏速度:遅
はしめに cv::Mat::at<>を使⽤した⽅法について解説します.at メソッドを使うではMat 上 の値を直感的にしてすることができることです. at メソッドを使う場合まズテンプレート引数に Mat の型と同様のものを選択します,ここ で型の指定を誤ると,意図しないメモリ番地にアクセスすることになってしまいます.次に アクセスする場所を引数で与えます.Mat の i ⾏ j 列⽬にアクセスしたい場合は,引数を(i,j) といったように与えることで各要素へのアクセスが可能になります. ですが画像の場合だと少し勝⼿が変わってきます ここで,グレースケール画像のある画素(x,y)にアクセスする場合の例を⽰します. 画素へのアクセスを⾏う場合は,画像の x 座標が⾏列の列,y 座標が⾏列の⾏に対応している ため,引数は逆になるということに注意してください.
行列上での位置と画像上での位置の関係についてのイメージ
ここまで,グレースケール画像での例を⽰しましたが,ここからはカラー画像
cv::Mat image; // クレースケール画像
image.at<unsigned char>(y,x); // 画像上の画素(x,y)にアクセス
の例も⽰します. カラー画像は基本的に 3 チャンネルの Mat で表現されます.まズ,ここで注⽬
して欲しい のは,テンプレート引数が”cv::Vec3b”となっています,これで 3 つのチャンネルの値を表現 しています.次に,位置を指定した後にチャンネルを指定しています.この,チャンネルを 指定する際に注意して欲しいのが,OpenCVでは各チャンネルことのでータが B GR の順番で 格納されているということです.⼀般的な感覚で RGB の順で格納されていると勘違いして 意図せぬ値が取り出されることがあるので,気をつけるようにしてください.
2.data を直接参照する⽅法 難易度:やや難,実⾏速度:速
次に Mat の値が保存されている data に直接アクセスすることで画素値を得る⽅法について 解説します.Mat では⾏列の値を⼀次元配列で格納しています.この値を格納している data に直接アクセスする⽅法になります.この⽅法は at を⽤いた⽅法より⾼速であるというメ リットがありますが,直感的に操作できない分少し,取り扱いが難しいかもしれません. 以下にクレースケール画像上のある画素(x,y)にアクセスする例を⽰します
image.cols は”image”の列数,つまり画像の横幅を表しています.つまり data の添え字のな かの image.cols*y は⾏列の y ⾏⽬の先頭の番地に相当します.さらにそこから x 列ズれた場所 を参照することで,⾏列の y ⾏ x 列⽬を参照しているということになります
cv::Mat image; //カラー画像
image.at<cv::Vec3b>(y,x)[0]; // 画素(x,y)の B チャンネルにアクセス
image.at<cv::Vec3b>(y,x)[1]; // 画素(x,y)の G チャンネルにアクセス
image.at<cv::Vec3b>(y,x)[2]; // 画素(x,y)の R チャンネルにアクセス
cv::Mat image; //グレースケール画像
image.data[image.cols*y + x]; // 画素(x,y)にアクセス
data と画素の関係のイメージ(グレースケール画像の場合)
次にカラー画像での例も⽰します. カラー画像の場合,data の配列の中にデータは BGRBGRBGRBGRBGRBGR・・・と いったように格納されています.そのため,クレースケールの時のように各⾏の先頭番地を 指定するのに,メンバ変数”cols”を使⽤するのではなくチャンネル数も考慮したメンバ変数 ”step”を⽤いています.これで,画像上の任意の画素のアクセスすることができます.あと は BGR の順に格納されているでータのうち,必要なものにアクセスすれバ良いということ です. cv::Mat image; //カラー画像
image.data[image.step*y + x + 0]; // 画素(x,y)の B チャンネルにアクセス
image.data[image.step*y + x + 1]; // 画素(x,y)の G チャンネルにアクセス
image.data[image.step*y + x + 2]; // 画素(x,y)の R チャンネルにアクセス