c言語演習(2) - opencv
DESCRIPTION
OpenCVのイントロダクションとC言語の基礎(関数と構造体)について. 以前研究室の講義で使用してもので, 2008年当時の内容です.TRANSCRIPT
C言語演習(2)
-OpenCVで画像処理 ‒2008-04-03
アジェンダ関数構造体 OpenCV• OpenCVとは•画像を読み込んで表示させる•四角形を描く•エッジ検出•テンプレートマッチング
関数
関数とは入力されたデータを加工し、出力するもの
printfやmainも関数
関数
入力 加工 出力
関数のメリット同じ処理を何度も書かなくてよい一度関数を作れば、何度も再利用できる
関数の中身の処理を気にしなくてもよい
例 二乗を返す関数”square”
関数名は”square” 整数nをうけとっている。 “return”で値を返す 返される値はint型
int square ( int n ){
return n*n;}
関数の書き方戻り値の型 関数名 ( 引数の宣言, 引数の宣言, … ){
処理}
引数は入力されるデータ(複数可)戻り値は出力する結果引数、戻り値がない場合もある•そのときは”void”
使い方
()の中に渡すデータ(引数)戻り値は代入(無視してもよい)
int main(void){int result;
result = square(5);
printf(“result is %d\n”, result);}
宣言
関数は使う前に宣言する
#include <stdio.h>
int square(int n); //宣言
int main(void){…
}
int square(int n) //関数本体{…
}
プロトタイプ宣言
関数の宣言のことを、プロトタイプ宣言という。
戻り値の型 関数名 (引数の宣言);
printfの宣言や本体はどこにある?
プロトタイプ宣言はヘッダファイル(stdio.h)に書かれている
本体はライブラリという別ファイルに書かれている
#include <stdio.h>
…
Int main(void){…
…
int printf(...); …
…
int printf(...){
…} …
stdio.h自作ファイル ライブラリ
構造体
構造体とはいろいろな変数を、一つにパッケージして、まとめて扱えるようにしたもの
例 座標をあつかう構造体struct point{int x; //x座標int y; //y座標
};
struct point pt1;
pt1.x = 5;pt1.y = pt1.x + 10;
printf(“%d, %d\n”, pt1.x, pt1.y);
定義の仕方struct 構造体名{変数の宣言};
構造体の中の変数のことを、メンバーという
メンバーはどのような型でもOK配列、構造体もメンバーにできる
使い方
struct 構造体の型名 名前;
“struct”を付けて、変数のように宣言する
名前.メンバー
メンバーにアクセスするときは、”.”を使う
構造体の入れ子struct st1{int n;int m;
};
struct st2{int l;struct st1 str;}
構造体の入れ子も可能今st2はメンバーにst1を持っている
構造体の入れ子struct st1 a;struct st2 b;
b.str = a;
b.str.n = 1;b.str.m = 2;
中の構造体のメンバーへアクセスするには、ドット演算子をつなげて書けばよい
OpenCV
OpenCVとは画像処理に便利な関数がたくさん入っているライブラリ•無料•インテルが作った•いろんな大学で使われている
画像処理人は、全ての情報の8割を視覚から得ている
よって、ロボット研究で画像をあつかうことは多い
画像を読み込んで表示させる
ヘッダファイル
OpenCVの関数を使うために読み込む
ほかには、”cxcore.h”や”cvaux.h”がある
#include “opencv/cv.h”#include “opencv/hightgui.h”
基本構造体
OpenCVでは、全ての画像を、この構造体であつかう
画像の幅、高さ、容量、色深度、チャンネル数、データ本体へのポインタなど、たくさんのメンバーを持つ
IplImage *img;
IplImageの中身typedef struct _IplImage{
int nSize; /* sizeof(IplImage) */int ID; /* バージョン (=0)*/int nChannels; /* OpenCV のほとんどの関数が,1,2,3および4チャンネルをサポートする */int alphaChannel; /* OpenCV では無視される */int depth; /* ピクセルの色深度のビット数 */char colorModel[4]; /* OpenCV では無視される */char channelSeq[4]; /* 同上 */int dataOrder; /* 0 - インタリーブカラーチャンネル.1 - 分離カラーチャンネル*/int origin; /* 0 - 左上原点, 1 - 左下原点(Windowsのビットマップ形式) */int align; /* 画像の行のアライメント(4 あるいは 8).OpenCV はこれを無視して,代わりにwidthStep を使用する. */int width; /* 画像のピクセル幅 */int height; /* 画像のピクセル高さ */struct _IplROI *roi;/* 画像 ROI.これが NULL でない場合,この特定の領域が処理の対象となる */struct _IplImage *maskROI; /* OpenCV では必ずNULL */void *imageId; /* 同上 */struct _IplTileInfo *tileInfo; /* 同上 */int imageSize; /* 画像データのバイトサイズ*/char *imageData; /* アライメントが調整された画像データへのポインタ */int widthStep; /* アライメントが調整された画像の行のバイトサイズ */int BorderMode[4]; /* 画像境界の設定.OpenCV では無視される */int BorderConst[4]; /* 同上 */char *imageDataOrigin; /* オリジナル画像データへのポインタ */
}IplImage;
画像の取り扱い グレイスケール• 色深度8ビット• 1チャンネル
赤:255緑:0青:0
赤:255緑:255青:0
赤:0緑:0青:0
明るさ:255
明るさ:235
明るさ:100
明るさ:0
カラー• 色深度8ビット
• 3チャンネル
画像の読み込み
画像を読み込み、基本構造体”img”に入れる。
CV_LOAD_IMAGE_UNCHANGEDは元画像の色を変更しないという意味。
img = cvLoadImage(“lena.jpg”,CV_LOAD_IMAGE_UNCHANGED);
ウィンドウの作成
読み込んだ画像を表示させるためのウィンドウを作る
名前は”result”
cvNamedWindow("result", 1);
読みこんだ画像の表示
”result”に読み込んだ画像を表示させる
cvShowImage("result", img);
キー入力待機、メモリ解放
何かキーが押されるまで待機する IplImageとウィンドウを使った場合は、必ず解放する
cvWaitKey(0);
cvReleaseImage(&img);cvDestroyWindow("result");
結果
四角形を描く
CvSize
画像の大きさをあつかうための構造体
typedef struct CvSize{int width; /* 矩形の幅 */int height; /* 矩形の高さ */}CvSize;
CvSize img_size = {640, 480};
CvPoint
座標をあつかうための構造体
typedef struct CvPoint{int x; /* x 座標 */int y; /* y 座標 */}CvPoint;
CvPoint pt1 = {50, 50};
原点 x
y
画像領域の確保
画像をあつかうためのメモリを確保する
IplImageを使うときは、このcvCreateImageやcvLoadImageなどで、必ず初期化しなければならない
img = cvCreateImage(img_size, IPL_DEPTH_8U,3);
真っ黒な画像をつくる
imgの全てのピクセルの値を、ゼロにする。
真っ黒な画像になる
cvSetZero(img);
四角形を描く
img上の座標”pt1”から、座標”pt2”まで、赤色で(CV_RGB)、太さ4ピクセルの四角形を描く
cvRectangle(img, pt1, pt2, CV_RGB(255, 0, 0),4, CV_AA, 0);
結果
エッジ検出
エッジ検出とは 画像の明るさが急激に変化した部分を検出する方法
画像の1次微分とも言う
Sobelオペレータある点とその周辺9ピクセルに、右の数を掛けて、その和を求める
明るさの変化がない点では、和は0になる
変化が大きい点では、和も大きな値になる
-1 0 1
-2 0 2
-1 0 1
計算例周りの明るさが一定だと、計算結果は0に近づく
この場合、50*1+50*2+50*1-50*1+50*2+50*1=0
50 50 50
50 50 50
50 50 50
計算例周りの明るさの変化が激しいと、計算結果は大きくなる
この場合、255*1+255*2+255*1-0*1+0*2+0*1=1020
255500
255500
255500
エッジ色の変わり目でのみ、値が極端に大きくなる
cvSobel
Sobelオペレータを用いて、一次微分画像を計算する
cvSobel(src_img, tmp_img, 1, 0, 3);
cvConvertScaleAbs
計算結果の値の範囲を、8ビット(0~255)に変換する
cvConvertScaleAbs(tmp_img, dst_img, 1, 0);
結果
テンプレートマッチング
テンプレートマッチングとは 画像の中から、テンプレートと同じ場所を探す処理
元画像 テンプレート 結果
結果保存用領域の準備
結果を保存するための領域を準備する
size = cvSize( src_img->width - tmp_img->width + 1, src_img->height - tmp_img->height + 1);
dst_img = cvCreateImage(size, IPL_DEPTH_32F, 1);
マッチングの方法画像の全領域を、テンプレートをスライドさせながら、順番に計算する
テンプレート
結果の個数は、
(元画像の横幅-テンプレートの横幅+1)*(元画像の縦幅-テンプレートの縦幅+1)
マッチングを計算
マッチング計算のアルゴリズムにはいくつか種類がある
今回はCV_TM_CCOEFF_NORMEDを使用
cvMatchTemplate (src_img, tmp_img, dst_img,CV_TM_CCOEFF_NORMED);
結果の取り出し
min_val, min_locには、最小値の値と座標、max_val, max_locには最大値の値と座標が入る
今回のアルゴリズムは、最もマッチングしている点が最大値をとるので、max_locを使う
cvMinMaxLoc (dst_img, &min_val, &max_val, &min_loc, &max_loc,NULL);
結果の描画
max_valには、最もマッチングした点の座標が入っている。この点はテンプレート画像の左上にあたる
よって、max_locから、max_locにテンプレート画像の幅と高さを足した点までの大きさの四角を描けばよい
cvRectangle(src_img, max_loc, cvPoint(max_loc.x + tmp_img->width, max_loc.y + tmp_img->height), CV_RGB(255, 0, 0), 3,CV_AA, 0);
結果
テンプレート画像
課題について
リファレンス 日本語リファレンスのサイトを参考にしながら、課題を行ってください
http://opencv.jp/opencv/document/
便利なので、必ずブックマークしておくこと
OpenCV プログラミングブック
初心者向け 生協にも売っている(¥3,570)
先のサイトと同じ作者
ssh
lunaの部分にマシン名を入れるパスワードは自分のパスワードでOK
$ ssh ‒X luna
username@luna’s passward: パスワードを入力
makefileコンパイルはメイクファイルを使うと便利
Makefileの作成
“TARGET=“には、作ったプログラムのファイル名(**.cの”**”の部分)をスペースで区切って列挙
“Makefile”という名前で、プログラムと同じディレクトリに保存する
CXXFLAGS+=`pkg-config opencv --cflags`LDFLAGS+=`pkg-config opencv --libs`
TARGET= filename1 filename2
all: $(TARGET)
clean:rm -f *.o *̃ $(TARGET)
Makefileの作成
あとはターミナルに”make”と入れればコンパイルできる
時間のエラー(クロックのひずみを検出…)は無視してもよい
$ make