OpenCVをWin32ベースで利用するの3回目です。前回は画像ファイルを入力にしてその画像に対してOpenCVで画像処理を行うというプログラムでした。今回は大きくは変わらないですが、カメラからの画像を入力にして画像処理を行います。今回のプログラムで重要になってくるのはWin32APIでのスレッド処理です。しかしながら、スレッド処理はOpenCVとは関係ないのでポイントだけということで書いていきたいと思います。
(※2017年2月8日 いまさらですが、ソースが中途半端で動作しないというご指摘を頂きましたので修正しました。読み込みボタンでカメラの起動処理を実行して、キャプチャやグレースケールスケール変換が出来ると思います。何かありましたら、フィードバックページ等をご利用下さい。)
今回のソース一式は以下からダウンロード出来ます。(※必要な場合は用途に限らずご利用頂いて問題ありませんが、一切無保証です。弊社は一切の責任を負いません。)
今回のソース一式
(※OpenCVを取り込んでいるため58M程度のサイズになっています。)
それではまずは今回のプログラムの動作からです。以下のような流れを想定しました。当然ですがカメラが接続(または内蔵)されていて利用出来ることが前提です。前回から変わっているのは入力がカメラになったということです。
1. 読み込みボタンでカメラからの画像をメインダイアログに表示する。(読み込み方は必ず3チャンネルカラー画像)
↓
2. ボタンを押した際に何かしらの画像処理を行う。(ボタンは5個あり各ボタンクリック時の関数を用意)
↓
3. 画像処理の結果を表示する(結果の表示はOpenCVのGUIを利用)
↓
4. 画像処理の結果を保存用の変数にセットする(必要な場合のみ)
↓
5. 画像処理の結果を保存する(必要な場合のみ)
ここで大事なのは前回と同様に、2.と3.です。ここでやりたいOpenCVの処理を書きます。ここではまずはビデオ画像をキャプチャして表示するという処理と、キャプチャした画像をグレースケールに変換して表示するというプログラムを作りました。(※ページ先頭のソース一式です。)
グレースケールボタンでキャプチャーしてグレースケールに変換後に別画面で表示
すいません、夜の時間帯に室内から窓の外を撮ったものであまり違いがはっきりしないですがご了承ください。
あと、保存処理は前回と同様です。保存用の画像変数に設定した画像が保存出来ます。
次に今回のプログラムの要点のスレッド処理についてです。(理解されている方は当然理解されていると思いますが)スレッドとはプログラム内(Windowsの場合はプロセスという表現が出来ると思いますが)で別々の独立した処理の流れのことです。C言語の場合は独立した処理というのは関数単位です。最新の様々なプログラミング言語仕様では非同期という処理が可能なので今回のような処理も非同期を使えば比較的に簡単に出来るかと思いますがやはりスレッドは重要なプログラミングの要素です。
しかしながらですが、最初に書いたように、スレッド処理は、OpenCVとは関係ないのでポイントだけ書きます。以下がポイントになるかと思います。
・カメラ入力の処理はリアルタイムに動いているのでこれを別スレッドとして実行する。
(メインダイアログでの処理とは別にしてメインダイアログは常に処理を受け付けている状態とする。)
・Win32APIのスレッド起動は、_beginthreadex()を使う。
(最も安全に利用できる関数(API)である。)
・カメラ入力の別スレッドではGUIを操作しない。
(C言語(Win32API)の場合は、おそらく問題なく動作する場合もあると思うが基本的には別スレッドからはGUIは操作しない。)
以下がスレッドの関数です。スレッドの形式は、ここではこういった決まった形式で実装するということにしておきます。OpenCVの処理としては、最初に見つかったビデオ入力(カメラと想定しています)をオープンして終了のフラグがセットされるまで入力用の画像に取り込んでWin32APIでメインダイアログに表示し続けています。
/*------------------------------------------------ カメラキャプチャー -------------------------------------------------*/ unsigned int __stdcall CameraCaptureThread(PVOID pv) { cv::VideoCapture cap(0); if (!cap.isOpened()){ return -1; } while (1){ if (endFlag){ break; } cap >> imgMatRead; img = imgMatRead; imgFromOpenCV = &img; //メモリを確保 if (bmpData != NULL){ HeapFree(GetProcessHeap(), 0, bmpData); } bmpData = (LPDWORD)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, imgFromOpenCV->width * imgFromOpenCV->height * 4); //iplImageからDIBへ変換 iplTobmp(imgFromOpenCV, bmpInfo, bmpData); //ここで描画 StretchDIBits(hDC, 0, 0, imgFromOpenCV->width, imgFromOpenCV->height, 0, 0, imgFromOpenCV->width, imgFromOpenCV->height, bmpData, &bmpInfo, DIB_RGB_COLORS, SRCCOPY); } return 0; }
このスレッドを起動する処理が以下です。該当部分のみです。
case ID_READ: //カメラキャプチャー開始 if (threadHandle != NULL) { break; } //スレッド起動 threadHandle = (HANDLE)_beginthreadex( NULL, 0, CameraCaptureThread, NULL, 0, (unsigned int*)&dwThread); if (!threadHandle) { MessageBox(hDlg, "スレッド起動エラー", "debug", MB_OK); } break;
プログラム全体はページ先頭のリンクからダウンロード出来ます。全体が必要な場合はお手数ですがダウンロードをして下さい。
今回はここまでです。次回はカメラ入力は同じですがリアルタイムに画像処理を行って画像処理を動画として保存出来るようなプログラムに変更してみます。
関連書籍
(※APIで学ぶWindows徹底理解は中古でしか手に入らないようですが私も持っています。)
(※OpenCV 2 プログラミングブックは私も持っています。)
(※ここからはKindle版で英語ですが最新の情報もあるようです。また価格的にはいいかもしれないです。)