1 | initial version |
Hi, here is the example code, but web is japan language http://imagingsolution.blog107.fc2.com/blog-entry-91.html if you use CLI you should remember your code is mamaged or unmamaged. When I use CLI, I always convert to bmp and then give the picture box.
2 | No.2 Revision |
Hi, here is the example code, but web is japan language http://imagingsolution.blog107.fc2.com/blog-entry-91.html if you use CLI you should remember your code is mamaged or unmamaged. When I use CLI, I always convert to bmp and then give the picture box.
注!!! OpenCVのVer.1.1prea1版をインストールすると、ファイルを開くとき、
'System.Threading.ThreadStateException' のハンドルされていない例外が System.Windows.Forms.dll で発生しました。
追加情報: OLE が呼び出される前に、現在のスレッドが Single Thread Apartment (STA) モードに設定されていなければなりません。Main 関数に STAThreadAttribute が設定されていることを確認してください。 この例外はデバッガがプロセスにアタッチされている場合にのみ発生します。
というメッセージが出て、ファイルを開く事ができません。 その場合は、現状ではVer1.0版のOpenCVをインストールして下さい。
以下、簡単なソースの解説です。
画像ファイルの読込 ファイルメニューのファイル→開くでファイルを開くダイアログボックスを使って、画像ファイルを選択し、cvLoadImage関数で画像の読込を行っています。
private: System::Void mnuFileOpen_Click(System::Object^ sender, System::EventArgs^ e) { //ファイルメニュー⇒開く
///////////////////////////////////
// ビットマップファイルの取得
//
//ファイルを開くダイアログの作成
OpenFileDialog^ dlg = gcnew OpenFileDialog;
//ファイルフィルタ
dlg->Filter = "画像ファイル(*.bmp,*.jpg,*.png,*.tif,jp2)|*.bmp;*.jpg;*.png;*.tif;*.jp2";
//ダイアログの表示
if (dlg->ShowDialog() == System::Windows::Forms::DialogResult::Cancel) return;
//System::String^型のファイル名
System::String^ strFilename = dlg->FileName;
///////////////////////////////////
// src_img の解放
//
if (src_img != NULL){
IplImage *temp_img = src_img;
cvReleaseImage(&temp_img);
src_img = NULL;
}
///////////////////////////////////
// IplImageの確保
//
//System::String^からchar*へ変換
char* pStr = (char*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(strFilename).ToPointer();
//アンマネージ関数へchar*を渡す
src_img = cvLoadImage(pStr, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
//メモリの解放
System::Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(pStr));
///////////////////////////////////
//IplImageをピクチャボックスへ描画
//
DrawCvImage(src_img);
}
ここでのポイントはStringToHGlobalAnsiメソッドを使ってString^からchar*に変換している部分でしょうか。
IplImageのPictureBoxへの描画
OpenCVの画像データのポインタ(imageData)をBitmapへ渡し、Bitmapを確保して、DrawImageを使ってピクチャボックスへ描画しています。
private: System::Void DrawCvImage(IplImage *CvImage) { //IplImageをピクチャボックスへ描画
///////////////////////////////////
// Graphicsの確保
//
if ((pictureBox1->Image == nullptr)
|| (pictureBox1->Width != CvImage->width)
|| (pictureBox1->Height != CvImage->height)){
//ピクチャボックスをビットマップ画像サイズに合わせる
pictureBox1->Width = CvImage->width;
pictureBox1->Height = CvImage->height;
//PictureBoxと同じ大きさのBitmapクラスを作成する。
Bitmap^ bmpPicBox = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
//空のBitmapをPictureBoxのImageに指定する。
pictureBox1->Image = bmpPicBox;
}
//Graphicsクラスの作成(空のピクチャボックスからGraphicsを作成する)
Graphics^g = Graphics::FromImage(pictureBox1->Image);
///////////////////////////////////
// IplImageからBitmapの確保
//
//IplImageの画像データのポインタ(imageData)をBitmapへ渡し、Bitmapを作成
Bitmap^ bmp = gcnew Bitmap(CvImage->width, CvImage->height, CvImage->widthStep,
System::Drawing::Imaging::PixelFormat::Format24bppRgb, IntPtr(CvImage->imageData));
///////////////////////////////////
// 画像の描画
//
//ピクチャボックスのImageへ
g->DrawImage(bmp, 0, 0, CvImage->width, CvImage->height);
pictureBox1->Refresh();
delete g;
}
OpenCVの処理
サンプルプログラムのメニューのOpenCV以下のフィルタ処理のイベント処理は、それぞれのイベントごとに処理を書くのが面倒だったので、全て同じイベント処理を通すようにしてみました。
private: System::Void mnuOpenCV_Click(System::Object^ sender, System::EventArgs^ e) { //OpenCVメニュー
//処理後の画像データ
IplImage* dst_img;
//描画時間計測用
Diagnostics::Stopwatch^ sw = gcnew Diagnostics::Stopwatch;
//メニューアイテム(どのメニューがクリックされたのか?)
ToolStripMenuItem^ mnu = (ToolStripMenuItem^)sender;
//画像データが無い場合
if (src_img == NULL) return;
//処理時間計測開始
sw->Start();
//src_imgと同じサイズのIplImageの確保
dst_img = cvCreateImage(cvSize(src_img->width, src_img->height),
src_img->depth, src_img->nChannels);
//クリックされたメニューごとにOpenCVの処理
if (mnu->Name == "mnuThreshold"){
//二値化(しきい値127)
cvThreshold(src_img, dst_img, 127, 255, CV_THRESH_BINARY);
}else if (mnu->Name == "mnuSmooth3_3"){
//平滑化(3×3)
cvSmooth(src_img, dst_img, CV_BLUR, 3, 3);
}else if (mnu->Name == "mnuGaussian21_21"){
//ガウシアン(21×21)
cvSmooth(src_img, dst_img, CV_GAUSSIAN , 21, 21);
}else if (mnu->Name == "mnuDilate"){
//膨張
cvDilate(src_img, dst_img, NULL , 1);
}else if (mnu->Name == "mnuErode"){
//収縮
cvErode(src_img, dst_img, NULL , 1);
}else{
return;
}
//処理後の画像を元の画像データにコピーする。
cvCopy(dst_img, src_img);
//解放
cvReleaseImage(&dst_img);
//処理時間計測停止
sw->Stop();
//処理時間の表示
toolStripStatusLabel2->Text =
mnu->Text + "処理時間 = " + sw->ElapsedMilliseconds.ToString() + "msec";
//画像の描画
DrawCvImage(src_img);
}
輝度値の取得
画像の輝度値はピクチャボックスのMouseMoveイベントでマウスポインタの位置の輝度値をステータスバーに表示するようにしました。 OpenCVにおける輝度値はIplImageのimageDataのポインタを参照することで拾うことができるのですが、imageDataのポインタはなぜか符号付char型になっているので、符合なしのucharで受けています。 って、opencv.jpの内容をそのままマネしただけですけど...
private: System::Void pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { //画像の輝度値の表示
//画像データが無い場合
if (src_img == NULL) return;
//輝度値の取得
uchar p[3];
p[0] = src_img->imageData[src_img->widthStep * e->Y + e->X * 3]; // B
p[1] = src_img->imageData[src_img->widthStep * e->Y + e->X * 3 + 1]; // G
p[2] = src_img->imageData[src_img->widthStep * e->Y + e->X * 3 + 2]; // R
//輝度値表示
//輝度値(X座標, Y座標) = (R, G, B)
toolStripStatusLabel1->Text =
"輝度値(" + e->X.ToString() + "," + e->Y.ToString() + ") = (" +
p[2].ToString() + "," + p[1].ToString() + "," + p[0].ToString() + ")" ;
}