Archive for 3月, 2011

10分で学ぶOpenCV超入門

日曜日, 3月 27th, 2011
このエントリーを含むはてなブックマークはてなブックマーク - 10分で学ぶOpenCV超入門


 
 
C言語を勉強した人がOpenCVを始める取っ掛かりとして使ってもらえれば嬉しいです。配列とか関数は分かるけど、ポインタはちょっと・・・というくらいの人から読めるように書いてみました。
 

OpenCVとは

 
OpenCVとは画像処理で使える関数がたくさん用意されている関数群(フレームワーク)です。例えばC言語でファイルを読み込むのにfgets()という関数が用意されているように、OpenCVでは画像を読み込むcvLoadImage()という関数が用意されています。勿論、用意されている関数はこれだけではなく、画像をグレースケール化したり画像中から四角形を検出したり、顔を検出したりする関数まで用意されています。

 

OpenCVでの処理の流れ

OpenCVを使ったプログラムの基本的な流れは


 
といった流れになっています。以下では、まず始めにstep1とstep3だけを行うプログラムを作成したあとに徐々に複雑なプログラムを作成していきます。どんなに複雑なプログラムでも、基本の流れは上のようになっているので、これを見失わないようにしてください。
 
 

OpenCVを使ったプログラム

 
1. 画像を読み込み表示する

単純に画像ファイルから画像データを読み込み、それを表示するプログラムです。最初ですのでプログラムについて細かく説明していきます。

まずはじめに、OpenCVにはIplImage型という画像を格納する型が用意されています。C言語では整数をいれるint型や小数をいれるdouble型が用意されていましたが、これと同様に画像を入れるIplImage型があるわけです。
 
3行目ではIplImage*型のimgという変数を宣言しています。ポインタ型で宣言されていますが、とりあえずは無視して普通の変数と同じように考えても大丈夫です(^^;;)このimg変数にcvLoadImage関数を使って画像を読み込んでいます。

4行目から7行目まではエラー処理です。画像が読み込めなかった場合には変数の中身がNULLになることを利用してエラー検出をしています。

9行目と10行目が画像の表示処理です。9行目でImageという名前のウインドウを作成し、10行目で作成したImageウインドウに画像を表示しています。

13行目と14行目は後始末です。ポインタ型を使った場合には必ず確保したメモリを開放する必要があるのでプログラムの最後で開放しています。iPhoneなどの環境ではメモリ制限が厳しいのでこの処理は忘れないようにしましょう。

int main()
{
	IplImage *img = cvLoadImage("test.jpg", CV_LOAD_IMAGE_ANYCOLOR);
	if( img == NULL ){
		fprintf(stderr, "no such file or directory\n");
		exit(-1);
	}
	
	cvNamedWindow("Image",CV_WINDOW_AUTOSIZE);
	cvShowImage("Image",img);
	cvWaitKey(0); 
	
	cvReleaseImage(&img); 
	cvDestroyWindow("Image");
	
	return 0;
}

 
 
 
2. 画像のサイズを変更する
 
 
 
 
読み込んだ画像のサイズを縦横1/2にして表示するプログラムです。プログラムとしては上記のものと大半が同じですので、違うところだけ説明していきたいと思います。

1行目ではimg変数に画像を読み込んでいます。2行目ではリサイズ後の画像を格納する変数resizedを定義してから、直後にcvCreateImage()関数で画像の大きさや種類などを指定して、どれくらいのメモリが必要かを記述しています。

このように、IplImage*型を使う場合には、cvCreateImage関数を使って画像の大きさ、1pxのビットデプス、レイヤ数の3つを指定する必要があります。この3つについて以下に説明します。

画像の大きさはcvSize(幅,高さ)、またはcvGetSize(画像)を使って指定します。今回は縦横1/2の大きさにするので、先ほどimgに読み込んだ画像の高さと幅の半分を指定します。img画像の幅と高さはそれぞれimg->width, img->heighで取得できます(C言語のポインタ型で宣言された構造体のメンバにアクセスする際の記述です)

次にビットデプスですが、1pxの明度は大抵の場合0から255で表すのでビットデプスは8bitになります。今回もIPL_DEPTH_8Uを指定しています。

最後のレイヤ数ですが下図に示すように、グレースケール画像の場合には1レイヤ、RGB画像(カラー画像)の場合には3レイヤ、RGBA画像(透明部分を含んだ画像)の場合には4レイヤが必要になります。今回は単純なカラー画像なので3レイヤを指定しています。

11行目では処理の本体であるリサイズを行っています。といっても、複雑な処理を記述する必要はなくcvResize関数に元画像imgと結果を格納する画像resizedと補間方法を渡しているだけです。CV_INTER_CUBICはバイキュービック法を用いて画像を補間します。このアルゴリズムは結果画像が綺麗な代わりに、処理時間が遅くなります。一方、CV_INTER_LINIERを指定すると結果画像は雑になるけれども高速な処理が可能なアルゴリズムが適応されます。

このように、引数によってアルゴリズムを差し替えられるのがOpenCVの強みの1つではないでしょうか?

int main()
{
	IplImage *img = cvLoadImage( "test.jpg", CV_LOAD_IMAGE_ANYCOLOR);
	IplImage *resized = cvCreateImage(cvSize(img->width/2, img->height/2), IPL_DEPTH_8U, 3);
	
	if( img == NULL ){
		fprintf(stderr, "no such file or directory\n");
		exit(-1);
	}
	
	cvResize(img, resized, CV_INTER_CUBIC);
	
	cvNamedWindow("Image",CV_WINDOW_AUTOSIZE);
	cvShowImage("Image",resized);
	cvWaitKey(0); 
	
	cvReleaseImage(&img); 
	cvReleaseImage(&resized); 	
	cvDestroyWindow("Image");
	
	return 0;
}

 
 
 
3. 画像をグレースケール化する
 

 
次のプログラムは画像をカラー画像からグレースケールに変換するものです。4行目でグレースケール化した画像を格納する変数grayを宣言し、直後でcvCreateImageを用いて大きさ・ビットデプス・レイヤ数を指定しています。

変数grayはグレースケール画像を格納するものであり、画像のサイズはimgと同じものなのでcvGetSizeを使ってimgと同じサイズを指定しています。また、グレースケールのレイヤ数は1枚なのでcvCreateImageの3つめの引数には1を指定しました。

グレースケール変換の本体は11行目に記述しています。こちらもリサイズと同じく、cvCvtColorという関数だけ処理することが可能です。関数の引数に元画像img、変換画像gray、変換方法CV_RGB2GRAYを渡しています。

3つめの引数にCV_RGB2GRAYを指定することでRGB画像からGRAY画像に変換することが可能です。(RGB2GRAYの2はtoの略記です)他にもHSV画像やRGBA画像など、かなり自由に変換することができるので非常に重宝する関数です。

自分でグレースケール変換処理を記述する場合、RGBからグレースケールに変換する場合にはRGB各レイヤの明度に対して、以下の式で変換を行う必要があります。
 

 
このような変換を知らなくても記述できてしまうのがOpenCVの強みでもあります。(まぁ、問題でもあるんですけどね・・・)

int main()
{
	IplImage *img = cvLoadImage( "test.jpg", CV_LOAD_IMAGE_ANYCOLOR);
	IplImage *gray = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	
	if( img == NULL ){
		fprintf(stderr, "no such file or directory\n");
		exit(-1);
	}
	
	cvCvtColor(img, gray, CV_RGB2GRAY);
	
	cvNamedWindow("Image",CV_WINDOW_AUTOSIZE);
	cvShowImage("Image",gray);
	cvWaitKey(0); 
	
	cvReleaseImage(&img); 
	cvReleaseImage(&gray);	 
	cvDestroyWindow("Image");
	
	return 0;
}

 
 
 
4. 画像を2値化する
 
 
 
超入門編の最後として、画像を白と黒の2値化するプログラムを紹介します。まずは、読み込んだ画像をグレースケール化し、その後グレースケール値を閾値に基づいて白か黒に振り分けます。具体的には各ピクセルのグレースケール値が閾値以上のなら白色(明度:255)に、閾値以下なら黒色(明度:0)に変換します。


 
まずは4, 5行目でグレースケール画像を格納するgrayと2値化画像を格納するbinを宣言しています。サイズは読み込んだ画像と同じものなので、どちらもcvGetSizeで指定、レイヤ数は1レイヤを指定しています。

また、12行目でグレースケール化、13行目で2値化を行っています。cvThreshold関数の引数には元画像、結果画像、閾値、最大値、変換方法を指定しています。3つめの引数である閾値の値を変化させることで画像の白部分と黒部分の割合が変化します。
 

int main()
{
	IplImage *img = cvLoadImage( "test.jpg", CV_LOAD_IMAGE_ANYCOLOR);
	IplImage *gray = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);	
	IplImage *bin = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	
	if( img == NULL ){
		fprintf(stderr, "no such file or directory\n");
		exit(-1);
	}
	
	cvCvtColor(img, gray, CV_RGB2GRAY);
	cvThreshold(gray, bin, 128, 255, CV_THRESH_BINARY);
	
	cvNamedWindow("Image",CV_WINDOW_AUTOSIZE);
	cvShowImage("Image",bin);
	cvWaitKey(0); 
	
	cvReleaseImage(&img); 
	cvReleaseImage(&bin);	 
	cvDestroyWindow("Image");
	
	return 0;
}

 
このように、OpenCVを使えば非常に簡単に画像処理が行えます。用意されている関数を上手く使うことで、QRコードの読み取りや顔の特徴点検出など、さらに高度な処理が可能となります。次回はlplimageのピクセルにアクセスする方法を中心に解説していきたいと思います。
 
 

参考書籍

 
「OpenCVプログラミングブック」
OpenCVを1から始めるのなら、一番わかり易い参考書は本書でしょう。画像処理の基礎的なアルゴリズムをOpenCVで実装している例が多数掲載されているため、画像処理の勉強にも最適です。

OpenCV プログラミングブック 第2版 OpenCV 1.1対応
奈良先端科学技術大学院大学 OpenCVプログラミングブック制作チーム
毎日コミュニケーションズ
売り上げランキング: 66050

 
「詳解OpenCV」
ただ、どの大概の入門書がそうであるように、画像処理の踏み込んだ話までは書かれていません。(例えばフーリエ領域での計算とか計算コストの話、などなど)そこで役に立つのがオライリーの「詳解OpenCV」です。OpenCVの参考書というよりも、画像処理の中級書といった方が正しいかもしれません。高度な内容まで網羅されているので、画像処理をやるなら、手元に一冊置いておいて絶対損はありませんよ!

詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識
Gary Bradski Adrian Kaehler
オライリージャパン
売り上げランキング: 29059

 
 

コラム
 
「plImage*型を使う場合には、必ず画像の大きさ・デプス・レイヤ数を必ず指定」といっておきながら、変数imgを宣言したときにはcvLoadImageを使っているぢゃん!!と思われる方もいたかと思います。

はい、仰るとおりです(笑)実はファイルを読み込んだ際に、ファイルに埋め込まれた情報からcvLoadImage関数の中で内部的に指定してくれています。なので、例外的にcvCreateImageでメモリ確保を明示的に行う必要がないのです。(勿論、cvReleaseImageでメモリの開放は必要です)

cvLoadImageの他には、cvCloneImageという関数もありまして、こちらも明示的に宣言する必要はありません。まぁ、例外的なのはこの2つくらいなので、入門編ではちょこっと説明を端折ってしまいました。