Windows 10ユニバーサルアプリ(Universal Windows Application)でOpenCVを使う(その1)の続きです。
3. ARM用OpenCVライブラリをビルド
OpenCVを使ったユニバーサルアプリをビルドするためには、準備としてまずはアプリで使用するためのOpenCVライブラリとdllをビルドする必要があります。
GitHubのvs2015-samplesをコピーします。[Download ZIP]ボタンをクリックしてvs2015-samples.zipをダウンロードし、適当なフォルダに展開します。私の環境では、D:\Visual Studio Projects\opencv-vs2015-samplesフォルダに展開したので、以降ではこのフォルダを前提として説明します。みなさんの環境に読み替えて設定してください。
次に、ビルドするために必要な環境変数を新規作成します。OCV2015_ROOTという名前の環境変数を作成し、値としてさきほど展開したフォルダ「D:\Visual Studio Projects\opencv-vs2015-samples」を設定します。
次に、VisualStudio 2015でOpenCVプロジェクトを開き、ビルドします。opencv-vs2015-samples\vs2015\WS\10.0\ARM\OpenCV.slnを開きます。OpenCV.slnをダブルクリックすれば開くことが出来ます。 そして、VisualStudio 2015の[ビルド]-[ソリューションのビルド]でOpenCVプロジェクトをビルドします。ビルドエラーが出るときは、環境変数のパスがうまく通っていない可能性もあるので、VisualStudioを再起動してみてください。
ビルドが正常に終了すれば、opencv-vs2015-samples\vs2015\WS\10.0\ARMフォルダ以下のlibとbinフォルダにlibとdllが生成されているはずです。
4. OpenCVを使用するユニバーサルアプリのビルド
[ファイル]-[新規作成]-[プロジェクト]で「新しいプロジェクト」ウィンドウを開きます。
[テンプレート]-[Visual C++]で「空白のアプリ(ユニバーサル Windows) Visual C++」を選択し、名前として「RaspberryCV」と入力して[OK]をクリックしてRaspberryCVプロジェクトを作成します。
まず、ソリューションを選択します。ソリューション構成を「Debug」、ソリューションプラットフォームを「ARM」を選択します。
OpenCVのインクルードパスを設定します。[プロジェクト]-[RaspberryCVのプロパティ]で「RaspberryCVのプロパティ ページ」画面を開きます。[構成]コンボボックスで「すべての構成」を選択します。左側ウィンドウで[構成プロパティ]-[C/C++]-[全般]を選択し、右側ウィンドウの[追加のインクルードディレクトリ]に「$(OCV2015_ROOT)\include;$(OCV2015_ROOT)\vs2015\WS\10.0\include;」と入力して、OpenCVのインクルードパスを追加します。
次にライブラリパスの追加です。左側ウィンドウで[構成プロパティ]-[リンカー]-[全般]を選択し、右側ウィンドウの[追加のライブラリディレクトリ]に「$(OCV2015_ROOT)\vs2015\WS\10.0\ARM\lib\$(Configuration)」と入力して、OpenCVのライブラリパスを追加します。
次は依存ファイルの追加です。左側ウィンドウで[構成プロパティ]-[リンカー]-[入力]を選択し、右側ウィンドウの[追加の依存ファイル]に「opencv_core300d.lib;opencv_imgproc300d.lib;opencv_imgcodecs300d.lib;opencv_objdetect300d.lib」を入力して、OpenCVのライブラリファイルを追加します。
画像ファイルを追加します。opencv-vs2015-samples\samples\winrt_universal\RaspberryCV\RaspberryCV\Assetsフォルダにある下記3つのファイルを作成中のプロジェクトのRaspberryCV\RaspberryCV\Assetsフォルダにコピーします。
- grpPC1.jpg
- haarcascade_frontalface_alt.xml
- haarcascade_fullbody.xml
そして、ソリューション エクスプローラーでソリューション名「ソリューション'RaspberryCV'」の下の「RaspberryCV(Universal Windows)-Assets」上で右クリックして、コンテキストメニューから[追加]-[既存の項目]を選択し、先ほど追加した3つのファイルを選択してプロジェクトに追加します。
次に、さきほどビルドしたDllを追加します。ソリューション エクスプローラーでソリューション名「ソリューション'RaspberryCV'」の下の「RaspberryCV(Universal Windows)」上で右クリックして、コンテキストメニューから[追加]-[既存の項目]を選択します。
「既存のソリューション項目の追加」画面にて下記Dllを選択して追加します。
ARM\bin\Debug\opencv_core300d.dll
ARM\bin\Debug\opencv_imgcodecs300d.dll
ARM\bin\Debug\opencv_imgproc300d.dll
ARM\bin\Debug\opencv_ml300d.dll
ARM\bin\Debug\opencv_objdetect300d.dll
ARM\bin\Release\opencv_core300.dll
ARM\bin\Release\opencv_imgcodecs300.dll
ARM\bin\Release\opencv_imgproc300.dll
ARM\bin\Release\opencv_ml300.dll
ARM\bin\Release\opencv_objdetect300.dll
追加した項目をそれぞれマウスで選択、項目のプロパティ画面で「コンテンツ」を[True]に変更します。
実際にビルドして実行する際にopencv_core300d.dll関連のエラーが出たら、RaspberryCV.vcxprojを直接テキストエディタで開いて、下記のようにopencv_core300d.dllの定義部分を変更します。
None Include="$(OCV2015_ROOT)\vs2015\WS\10.0\ARM\bin\Debug\opencv_core300d.dll"
画面に部品を追加します。ソリューションエクスプローラーで「MainPage.xaml」をダブルクリックすると、VisualStudioに画面のデザインが表示されます。VisualStudio左側に[ツールボックス]という表示があるので、それを選択してツールボックスを表示します。無ければ、メニューで[表示]-[ツールボックス]を選択すると表示されます。
ツールボックスの[Button]をドラッグしてMainPage.xamlのデザイン上に持ってきます。さきほどドラッグしたButtonをクリックして選択状態にして、ソリューションエクスプローラーのプロパティで[名前]に「btn1」、[Content]に「Test Image」と入力します。同様に、順番に[Image]、[Button]、[Button]をドラッグして、さきほど設定したButtonも含め、最終的に4つの部品のプロパティに以下のように入力します。
部品 | 名前 | Content |
Button | btn1 | Test Image |
Image | img1 | |
Button | btn2 | Canny |
Button | btn3 | Detect |
ボタンのイベントハンドラーを追加します。btn1を選択した状態で、プロパティで稲妻のマークを選択し、[Click]横の箱をダブルクリックすると「btn1_Click」というイベントハンドラーが追加されます。
同様にbtn2とbtn3のイベントハンドラーを追加します。
それでは、ソースを書いていきます。ソリューションエクスプローラーで[MainPage.xaml]の左側の三角形をクリックしてツリーを展開し、[MainPage.xaml.h]を選択し、以下のインクルードを追加します。
#include
MainPageクラスに以下の定義を追加します。
private:
cv::Mat Lena;
void UpdateImage(const cv::Mat& image);
次に[MainPage.xaml.cpp]を選択し、以下のインクルードを追加します。
#include #include #include #include #include #include using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::Storage::Streams; using namespace Microsoft::WRL;
cv:Matをイメージコントロールに(img1)表示さるためのUpdateImage関数を実装します。
void RaspberryCV::MainPage::UpdateImage(const cv::Mat& image) { // Create the WriteableBitmap WriteableBitmap^ bitmap = ref new WriteableBitmap(image.cols, image.rows); // Get access to the pixels IBuffer^ buffer = bitmap->PixelBuffer; unsigned char* dstPixels; // Obtain IBufferByteAccess ComPtr pBufferByteAccess; ComPtr pBuffer((IInspectable*)buffer); pBuffer.As(&pBufferByteAccess); // Get pointer to pixel bytes pBufferByteAccess->Buffer(&dstPixels); memcpy(dstPixels, image.data, image.step.buf[1] * image.cols*image.rows); // Set the bitmap to the Image element img1->Source = bitmap; }
btn1のイベントハンドラーにimg1にイメージをロードするための以下のような実装をします。
void RaspberryCV::MainPage::btn1_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
cv::Mat image = cv::imread("Assets/grpPC1.jpg");
Lena = cv::Mat(image.rows, image.cols, CV_8UC4);
cv::cvtColor(image, Lena, CV_BGR2BGRA);
UpdateImage(Lena);
}
btn2のイベントハンドラーにCanny filterでエッジ検出を実験するための以下のような実装をします。
void RaspberryCV::MainPage::btn2_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { //Canny filter cv::Mat result; cv::Mat intermediateMat; cv::Canny(Lena, intermediateMat, 80, 90); cv::cvtColor(intermediateMat, result, CV_GRAY2BGRA); UpdateImage(result); }
btn3用のイベントハンドラーと関連関数に顔検出ためのコードとして以下を入力します。
cv::String face_cascade_name = "Assets/haarcascade_frontalface_alt.xml"; cv::CascadeClassifier face_cascade; cv::String body_cascade_name = "Assets/haarcascade_fullbody.xml"; cv::CascadeClassifier body_cascade; void internalDetectObjects(cv::Mat& inputImg, std::vector & objectVector, std::vector & objectVectorBodies) { cv::Mat frame_gray; cvtColor(inputImg, frame_gray, CV_BGR2GRAY); cv::equalizeHist(frame_gray, frame_gray); // Detect faces face_cascade.detectMultiScale(frame_gray, objectVector, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 30)); //detect bodies body_cascade.detectMultiScale(frame_gray, objectVectorBodies, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 300)); } void RaspberryCV::MainPage::btn3_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { if (!face_cascade.load(face_cascade_name)) { printf("Couldnt load Face detector '%s'\n", face_cascade_name); exit(1); } if (!body_cascade.load(body_cascade_name)) { printf("Couldnt load Body detector '%s'\n", body_cascade_name); exit(1); } cv::Mat frame = cv::imread("Assets/grpPC1.jpg"); if (frame.empty()) return; std::vector faces; std::vector bodies; internalDetectObjects(frame, faces, bodies); for (unsigned int i = 0; i < faces.size(); i++) { auto face = faces[i]; cv::rectangle(Lena, face, cv::Scalar(0, 0, 255, 255), 5); } for (unsigned int i = 0; i < bodies.size(); i++) { auto body = bodies[i]; cv::rectangle(Lena, body, cv::Scalar(0, 0, 0, 255), 5); } UpdateImage(Lena); }
ビルドする環境とターゲットデバイスを設定します。VisualStudioのメニューから[ビルド]-[構成マネージャ]を実行して構成マネージャ画面を開きます。[アクティブソリューション構成]で[Debug]を選択、[アクティブプラットフォーム]で[ARM]を選択します。[配置]にチェックが入っていることを確認して、プロパティページを閉じて、ビルドをしてください。
今回はここまでで、次回は実際にRaspberry Pi 2に対して接続して実行してみます。