工学入門セミナー
テーマ
3:
画像処理入門∼顔検出のしくみ∼
1
準備
実験を始める準備
実験を始める前に食べ物や飲み物は片づけておく.
※実習室は飲食禁止です.
1. ディスプレイ裏側にある PC の電源ボタンを押してコンピュータを起動する.
2. Phantasys の起動イメージ選択画面になったら,
「全学共通 201209」を選ぶ (図 1).
図 1 PC の起動
図 2 Windows のログイン
3. Windows が起動したら,ID とパスワードを入力してログインする (図 2).
4. 画面左下方のタスクバーにあるボタンをクリックして Web ブラウザ (Firefox または Internet Explorer)
を起動する.
5. キーボードの左上の方の「漢字」キーを押すと,日本語入力と英語入力を切り替えられる(画面右下の
「あ」と「A」が入力モードを表している).何度か押して,英語入力にする.
6. ブラウザ上方のアドレス欄(http://……と表示されている欄)をクリックして,
http://www.cv.ics.saitama-u.ac.jp/yosinori/lecture/seminar/
と入力し,本テーマの Web ページを開く
1.
7. 配布した USB メモリを PC 前面の USB スロットに挿す (一番上).
8. 配布した Web カメラを PC 前面の USB スロットに挿す (上から二番目).
9. E:Yが USB メモリとなるので,この中にファイルを置いて作業する.
注意! 必ず自分の USB フラッシュメモリに保存! ……パソコン本体に保存していると再起動時に消えます
注意! 作業中はこまめに保存すること!
終了時の操作
1. 各アプリケーションで「ファイル
→ 終了」として,使用したアプリケーションをすべて終了させる.
2. タスクバー右下の USB コネクタのアイコンをクリックして,
「(USB メモリの名称)の取り出し」をク
リックして,安全に取り出せる事を示す表示が出てから,USB メモリを抜く.
3. Web カメラの USB コネクタを抜く.
4. 画面左下のスタートメニューから「シャットダウン」を選択する.
注意! USB メモリと Web カメラは必ず返却すること!
1もし「Not Found」と表示された場合は,おそらく入力が誤っている2
プログラムの作成
本演習では,Eclipse を使って,プログラムの作成を行います.
2.1
Eclipse の起動
1. タスクバー左下のスタートボタンをクリックして出てきたメニューから青いボールのアイコン「Eclipse」
をクリックする.
2. すると図 3 のように,起動中画面が表示され,ワークスペースを設定する画面 (図 4) が表示されるの
で,ワークスペースの欄に「E:Yworkspace」と入力して,OK をクリックする.
図 3 Eclipse の起動
図 4 ワークスペース設定画面
3. Eclipse の起動が完了すると,図 5 のような画面が表示される.
図 5 Eclipse 初期画面
2.2
新規プロジェクトの作成
1. 図 6 のように,Eclipse のウインドウのメニューから ファイル
→ 新規 → その他 を選ぶ.
2. すると,図 7 のような,プロジェクトの種類を選択する画面がでるので,
「C プロジェクト」を選択し
て「次へ」をクリックする.
図 6 新規プロジェクトの作成
図 7 プロジェクトの種類の選択
3. すると,図 8(a) のような,プロジェクトの設定画面がでるので,プロジェクト名に「test」と入力し,
プロジェクト・タイプに「実行可能」の中から,
「Hello World ANSI C プロジェクト」を選択,ツール
チェーンから「MinGW GCC」を選択して「完了」をクリックする.図 8(b) のような「関連付けられ
たパースペクティブを開きますか?」という確認ウィンドウが出るので,
「はい」をクリックする.
(a) プロジェクトの設定画面
(b) 確認ウィンドウ
4. すると,図 9 のような,初期画面が表示される.
図 9 プロジェクト作成後初期画面
2.3
プログラムの作成と実行
図 9 の真ん中の大きなウインドウの中身がプログラムである
2.最初にサンプルのプログラムが入力されて
いるので,まずばこれを実行してみよう.
1. Eclipse のウインドウのメニューから図 10 のように,プロジェクト
→ すべてビルド (または,ウィ
ンドウ左側の領域のプロジェクトエクスプローラーで test フォルダをクリックしてから,プロジェク
ト
→ プロジェクトのビルド)を選ぶ.
図 10 プロジェクトのビルド
2. すると,ウィンドウの下の方の領域(のコンソールタブ)に
Build complete for project test
Time consumed: 406 ms.
などと出力される.これで,プログラムの実行の準備ができたことになる.
3. ウィンドウの上の方の緑の矢印のアイコン「実行」をクリックして実行する.図 11 のように,ウィン
ドウの下の方の領域(のコンソールタブ)に
!!!Hello World!!!
と表示されれば成功である.
2表示されていなければ,ウィンドウ左側の領域のプロジェクトエクスプローラーから test→ src とフォルダを開いて,出てきた 「test.c」をダブルクリックする.図 11 プロジェクトの実行
注意! ウィンドウ左側の領域のプロジェクトエクスプローラーで選択したプロジェクトがビルドや実行の対
象となるので,実行したいプロジェクトを選択してからビルドや実行を行うこと.
次に,プログラムを書き換えて,自分のプログラムを実行をしてみよう.
1. 図 9 の真ん中の大きなウインドウの中身がプログラムであるので,これを,以下のようにすべて書き
換える
3.
Program 1 最も簡単なプログラムの例
1 # include < stdio .h > 23 int main ( void ) { 4
5 printf (" Hello World !\ n" ); 6 7 return 0; 8 }
2. 編集が終わったら,必ず,ウインドウ左上(メニューの「ファイル」のそば)のフロッピーディスクの
アイコン「保管」や「すべて保管」をクリックし,編集した内容を保存する.
3. 先の手順と同様に,ウインドウのメニューから図 10 のように,プロジェクト
→ すべてビルド を選択
してビルドし,緑の矢印のアイコン「実行」をクリックして実行する.
4. ウィンドウの下の方の領域(のコンソールタブ)に
Hello World!
と表示されれば成功である.これで,自分で改造したプログラムを実行できるようになった.
注意! Eclipse では,プログラム編集後に保存をしないと,古いプログラムをビルドや実行してしまうので,
編集後は必ずプログラムを保存すること.
2.4
プログラムの改造
先のプログラムは画面に”Hello World!”と表示するだけだった.これだけではつまらないので,少しだけ改
造してみよう.プログラムは 4 行目から,6 行目の間を書き換える
4.まず,以下のように書き換えてみる.
3文字の書かれている部分をマウスでクリックして,delete キーですべて消してから,入力しても良いし,マウスで文字をすべて選 択してから,delete キーを押して消去しても良い. 4前後を書き換えなければ,行数は増えても良い.Program 2 計算をするプログラムの例
1 # include < stdio .h >2
3 int main ( void ) { 4 5 int a, b , c; 6 7 a = 2; 8 b = 3; 9 10 c = a + b; 11 12 printf (" Total = %d\n" , c ); 13 14 return 0; 15 }
これは,5 行目で,a,b,c という名前の整数を入れる箱を作り,7 行目で a に 2 を入れ,8 行目で b に 3 を入
れている.10 行目では,a+b(a と b の和の計算) の結果を c に入れている.計算結果の c は 12 行目で表示し
ている (計算結果は%d のところに入る).
さらに,以下のように書き換えてみよう.
Program 3 繰り返して計算をするプログラムの例
1 # include < stdio .h > 23 int main ( void ) { 4 5 int c, i; 6 7 c = 0; 8 9 for (i =0;i <10; i ++){ 10 c = c + 2; 11 } 12 13 printf (" Total = %d\n" , c ); 14 15 return 0; 16 }
9 行目の for(i=0; i<10; i++) は,続くカッコ
{ } 内の処理を繰り返して行うための命令である.最初に
i に 0 を入れて,繰り返す度に i を 1 増やして,i が 10 未満の間繰り返す.という意味である
5.
5 行目で,c に 0 を入れ,for の中で 10 回 c = c + 2; を繰り返す.c = c + 2; はもともと c に入ってい
る数値に 2 を足して,新しい c の値として入れろという命令である.もともと c は 0 で,2 を足すことを 10
回行うので,このプログラムの計算結果は 20 となる.
2.5
やってみよう:その1
1 から 100 までの整数の和を計算するプログラムを作ってみよう.
520 回繰り返したければ for(i=0; i<20; i++) とすれば良い.ただし,最後の繰り返しの処理のときの i の値は,for(i=0; i<10;
i++) なら9,for(i=0; i<20; i++) なら 19 となっている.一方,for(i=1; i<=10; i++) と書くと,最初に i に 1 を入れて,繰り
3
画像処理プログラムの作成
本実験の Web ページからプログラムをダウンロードして,実行してみよう.
3.1
プログラムのダウンロードと実行
本実験の Web ページからリンクを右クリックして「対象をファイルに保存」などを選び,ファイルを保存
する.ここでは,デスクトップに保存したとして手順を説明する.
1. Eclipse のウインドウのメニューから ファイル
→ インポート を選ぶ.
2. すると,図 12 のような,インポート画面がでるので,
「一般」フォルダの「既存プロジェクトをワーク
スペースへ」を選択して「次へ」をクリックする.
図 12 インポート画面
3. すると,図 13 のような,インポートの設定画面がでるので,
「アーカイブファイルの選択」を選び,
「参
照」をクリックして,ダウンロードしたファイル(たとえば,main1.zip など)を指定する.その後,
「完
了」をクリックして,プロジェクトをインポートする.
図 13 インポートの設定画面
4. インポートが完了すると,図 14 のような画面が表示される.これで,プロジェクト・エクスプローラー
から,プログラムファイル(例えば main1.c など)を選択してプログラムを表示したり,前節の手順と
同様に,プロジェクトのビルドや実行を行うことができる.
3.2
写真表示プログラム
コンピュータの画面に写真を表示するプログラムを動かしてみよう.このプログラムは,実行すると写真
が表示され,何かキーを押すと終了する.
図 14 インポート後の画面
Program 4 写真表示プログラム
1 # include < stdio .h > 2 # include <cv .h > 3 # include < highgui .h > 45 int main ( void ) 6 {
7 // カ ラ ー 画 像 を 入 れ る 入 れ 物 c I m a g e R e s u l t を 作 る
8 IplImage * cImageResult = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 3); 9
10 // p i c t u r e と い う 名 前 の 入 れ 物 に " sample . jpg " と い う 画 像 フ ァ イ ル を 読 み 込 ん で 入 れ る 11 IplImage * picture = cvLoadImage (" sample . jpg " , CV_LOAD_IMAGE_COLOR );
12
13 // p i c t u r e の 中 身 を c I m a g e R e s u l t に 大 き さ を 調 整 し て 入 れ る 14 cvResize ( picture , cImageResult , CV_INTER_LINEAR ); 15
16 // " My Window1 " と い う 名 前 の ウ イ ン ド ウ を 作 る 17 cvNamedWindow (" My Window1 " , CV_WINDOW_AUTOSIZE ); 18
19 cvShowImage (" My Window1 " , cImageResult ); // cImageResultを " My Window1 " に 表 示 す る 20 21 cvWaitKey (0); // 何 か キ ー が 押 さ れ る ま で 待 つ 22 23 cvDestroyAllWindows (); // 作 っ た ウ イ ン ド ウ を 全 て 閉 じ る 24 25 return 0; 26 }
OpenCV では,プログラムリストの 8 行目の cImageResult や 11 行目の picture のように,まず,画像を入
れる入れ物を準備し,その中に画像を入れて,様々な処理を行う.具体的には,11 行目で,picture という名
前の入れ物に ”sample.jpg” という画像ファイルを読み込んで入れている.14 行目では,picture の中身を大
きさを調整して cImage の中に入れている.17 行目では,画像を表示するためのウインドウを作成している.
また,19 行目では,作成したウインドウに画像を表示させている.さらに 21 行目では,ユーザからのキー
入力を待っている.
3.3
カメラ画像モノクロ化プログラム
カメラからの入力をモノクロ化するプログラムを動かしてみよう.このプログラムは,実行するとカメラ
映像がカラーとモノクロで表示され, q を押すと終了する.
Program 5 カメラ画像モノクロ化プログラム
1 # include < stdio .h > 2 # include <cv .h > 3 # include < highgui .h > 45 int main ( void ) 6 {
7 // カ ラ ー 画 像 を 入 れ る 入 れ 物 c I m a g e を 作 る
8 IplImage * cImage = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 3); 9 // 白 黒 画 像 を 入 れ る 入 れ 物 g I m a g e を 作 る
10 IplImage * gImage = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 1); 11
12 // " My Window1 " と " My Window2 " と い う 名 前 の ウ イ ン ド ウ を 作 る 13 cvNamedWindow (" My Window1 " , CV_WINDOW_AUTOSIZE ); 14 cvNamedWindow (" My Window2 " , CV_WINDOW_AUTOSIZE ); 15
16 // カ メ ラ か ら 画 像 を 取 得 す る 準 備 を す る
17 CvCapture * capture = cvCreateCameraCapture (0);
18 // カ メ ラ の 画 像 サ イ ズ を 設 定 す る
19 cvSetCaptureProperty ( capture , CV_CAP_PROP_FRAME_WIDTH , 640);
20 cvSetCaptureProperty ( capture , CV_CAP_PROP_FRAME_HEIGHT ,480);
21
22 // 繰 り 返 し こ こ か ら
---23 for (;;){
24
25 // カ メ ラ か ら 画 像 を 一 枚 ず つ 取 得 し て c I m a g e に 入 れ る
26 cImage = cvQueryFrame ( capture );
27
28 // c I m a g e の 画 像 を 白 黒 に し て g I m a g e に 入 れ る
29 cvCvtColor ( cImage , gImage , CV_BGR2GRAY );
30
31 cvShowImage (" My Window1 " , cImage ); // c I m a g e を " My Window1 " に 表 示 す る 32 cvShowImage (" My Window2 " , gImage ); // g I m a g e を " My Window2 " に 表 示 す る 33 34 if ( cvWaitKey (10) == ’q ’) // q を 入 力 し た ら 終 わ る 35 break ; 36 } 37 // 繰 り 返 し こ こ ま で ---38 39 // カ メ ラ か ら 画 像 を 取 得 す る の を や め る
40 cvReleaseCapture (& capture ); 41 42 cvDestroyAllWindows (); // 作 っ た ウ イ ン ド ウ を 全 て 閉 じ る 43 44 return 0; 45 }
このプログラムでは,動画像を扱っているが,動画像の処理の場合でも,カメラから次々に送られてくる
各フレームの画像に対する画像処理を行うことが基本なので,難しく考える必要はない.
まず,8 行目と 10 行目で,カラー画像用の入れ物,白黒画像用の入れ物をそれぞれ作成している.13 行目
と 14 行目では,表示用のウインドウを 2 つ作成している.17 行目,19 行目,20 行目では,カメラからの画
像をサイズを指定して取得する準備を行っている.
22 行目の「繰り返し—ここから—」と 37 行目の「繰り返し—ここまで—」の間の処理が,各フレームの
画像に対して行われる処理である.
26 行目でカメラから現在の画像を取得し cImage という入れ物に入れ,29 行目でモノクロ化を行っている.
1 cvCvtColor ( cImage , gImage , CV_BGR2GRAY );という記述は,cvCvtColor という命令により,cImage に入っている画像を色変換して gImage に入れるとい
う処理を意味している.最後の CV BGR2GRAY は色変換がモノクロ化であることを意味している.
31 行目でウィンドウ”My Window1”に cImage を表示している.32 行目でウィンドウ”My Window2”に gImage
を表示している.
3.4
背景差分プログラム
ある時刻でのカメラからの入力画像を保存し,現在の入力画像との差分を表示するプログラムを動かし
てみよう.このプログラムは,実行するとカメラ映像がカラーで”My Window1”に表示される. s を押すと
その時のカメラ画像がモノクロ画像で保存され,”My Window2”に表示される.そして,差分の結果が”My
Window3”に表示される.また, p を押すと差分の結果がファイルに保存され, q を押すと終了する.
Program 6 背景差分プログラム
1 # include < stdio .h > 2 # include <cv .h > 3 # include < highgui .h > 46 {
7 // image と い う 名 前 の カ ラ ー 画 像 を 入 れ る 入 れ 物 を 作 る
8 IplImage * image = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 3); 9
10 // カ ラ ー 画 像 を 入 れ る 入 れ 物 を 幾 つ か 作 る
11 IplImage * cImageResult = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 3);
12 // 白 黒 画 像 を 入 れ る 入 れ 物 を 幾 つ か 作 る
13 IplImage * gImageFG = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 1); 14 IplImage * gImageBG = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 1); 15 IplImage * gImageMask = cvCreateImage ( cvSize (640 ,480) , IPL_DEPTH_8U , 1); 16
17 // " My Window1 " と " My Window2 " と " My Window3 " と い う 名 前 の ウ イ ン ド ウ を 作 る 18 cvNamedWindow (" My Window1 " , CV_WINDOW_AUTOSIZE );
19 cvNamedWindow (" My Window2 " , CV_WINDOW_AUTOSIZE ); 20 cvNamedWindow (" My Window3 " , CV_WINDOW_AUTOSIZE ); 21
22 // カ メ ラ か ら 画 像 を 取 得 す る 準 備 を す る
23 CvCapture * capture = cvCreateCameraCapture (0);
24 // カ メ ラ の 画 像 サ イ ズ を 設 定 す る
25 cvSetCaptureProperty ( capture , CV_CAP_PROP_FRAME_WIDTH , 640);
26 cvSetCaptureProperty ( capture , CV_CAP_PROP_FRAME_HEIGHT ,480);
27
28 char inputKey = ’a ’;
29 // 繰 り 返 し こ こ か ら
---30 for (;;){
31
32 // カ メ ラ か ら 画 像 を 一 枚 ず つ 取 得 し て i m a g e に 入 れ る
33 image = cvQueryFrame ( capture );
34
35 // image を " My Window1 " に 表 示 す る 36 cvShowImage (" My Window1 " , image ); 37
38 // s を 入 力 し た ら カ メ ラ の 画 像 を 一 枚 保 存 す る
39 if ( ’s ’ == inputKey ){
40 // i m a g e の 画 像 を 白 黒 に し て g I m a g e B G に 入 れ る
41 cvCvtColor ( image , gImageBG , CV_BGR2GRAY );
42 // cImageBG を " My Window2 " に 表 示 す る 43 cvShowImage (" My Window2 " , gImageBG );
44 inputKey = ’a ’; 45 } 46 47 // 保 存 し た 画 像 と 今 の 画 像 の 差 を 表 示 す る 48 49 // i m a g e の 画 像 を 白 黒 に し て g I m a g e F G に 入 れ る
50 cvCvtColor ( image , gImageFG , CV_BGR2GRAY );
51 // g I m a g e F G と g I m a g e B G の 差 を g I m a g e M a s k に 入 れ る
52 cvAbsDiff ( gImageFG , gImageBG , gImageMask );
53 // g I m a g e M a s k を 2 値 化 す る
54 cvThreshold ( gImageMask , gImageMask , 20 , 255 , CV_THRESH_BINARY ); 55 // c I m a g e R e s u l t の 画 像 を 真 っ 黒 に す る
56 cvSetZero ( cImageResult );
57
58 // g I m a g e M a s k を マ ス ク に し て i m a g e の 画 像 を c I m a g e R e s u l t に コ ピ ー す る
59 cvCopy ( image , cImageResult , gImageMask );
60 // cImageResult を " My Window3 " に 表 示 す る 61 cvShowImage (" My Window3 " , cImageResult ); 62
63 // p を 入 力 し た ら 処 理 結 果 画 像 を 保 存 す る
64 if ( ’p ’ == inputKey ){
65 // c I m a g e R e s u l t を フ ァ イ ル に 出 力 す る 66 cvSaveImage (" out . jpg " , cImageResult );
67 inputKey = ’a ’; 68 } 69 70 inputKey = cvWaitKey (10); 71 if ( ’q ’ == inputKey ) // q を 入 力 し た ら 終 わ る 72 break ; 73 } 74 // 繰 り 返 し こ こ ま で ---75 76 // カ メ ラ か ら 画 像 を 取 得 す る の を や め る
77 cvReleaseCapture (& capture ); 78 79 // 作 っ た ウ イ ン ド ウ を 全 て 閉 じ る 80 cvDestroyAllWindows (); 81 82 return 0; 83 }
まず,7 行目から 15 行目までで,カラー画像用の入れ物,白黒画像用の入れ物をそれぞれいくつか作成し
て,以降の様々な処理の準備をしている.また,18 行目から 20 行目でウインドウを 3 つ作成している.23
行目,25 行目,26 行目では,カメラからの画像をサイズを指定して取得する準備を行っている.
29 行目の「繰り返し—ここから—」と 74 行目の「繰り返し—ここまで—」の間の処理が,各フレームの
画像に対して行われる処理である.
33 行目でカメラから現在の画像を取得し image という入れ物に入れ,それを 36 行目で,ウインドウ”My
Window1”に表示している.
38 行目から 45 行目は,キーボードで’s’ が押された時の処理である.’s’ が押されたときに現在のカメラ画
像を背景画像 gImageBG として保存している.白黒に (モノクロ化) しているのは,差分の抽出をしやすくす
るためである.
49 行目からは背景差分を計算している.50 行目で現在のフレーム画像をモノクロ化して,前景 gImageFG
とする.そして,前景 gImageFG と背景画像 gImageBG の差分を 52 行目で以下のように計算している.
1 cvAbsDiff ( gImageFG , gImageBG , gImageMask );これは,cvAbsDiff という命令により,gImageFG と gImageBG の差分を計算し gImageMask に入れるという
処理を意味している.具体的には,2 つの画像の同じ位置の画素の値を引き算して,もう一つの画像のその
位置にその計算結果を入れるという処理をすべての画素に対して行う.ここでは引き算の結果が負にならな
いように絶対値を計算する命令 cvAbsDiff を使っている.
また,54 行目では,差分の計算結果を 2 値化してマスク画像を作っている.
1 cvThreshold ( gImageMask , gImageMask , 20 , 255 , CV_THRESH_BINARY );2 値化とは,何らかの条件で,画素値を 2 つに分けるもので,OpenCV では,0 と 255 の 2 つの画素値に分
けることで 2 値化画像を作成する.ここでは,前景と背景画像の各画素の値の差が 20 から 255 までのとき
に 255,それ以外の(20 より小さい)ときに 0 として 2 値化している.
59 行目では,現在のカメラからの入力画像をマスクして,差分のある部分だけの(言い換えれば,変化が
あった部分のみの)画像にしている.
1 cvCopy ( image , cImageResult , gImageMask );