Ask Your Question
0

Having trouble with using Mat image in pictureBox of Visual C++ CLR project

asked 2013-05-29 01:35:32 -0600

avinash gravatar image

updated 2013-05-29 01:36:20 -0600

Mat imgs = imread("d:/s.jpg");

cvtColor(imgs, imgs, CV_BGR2RGB, 1);

this->pictureBox1->Image=(gcnew System::Drawing::Bitmap(imgs.cols,imgs.rows,imgs.step.p[0], System::Drawing::Imaging::PixelFormat::Format24bppRgb, System::IntPtr)imgs.data));

it is not working, it only show some white and gray lines Please Help Me thanks

edit retag flag offensive close merge delete

3 answers

Sort by » oldest newest most voted
0

answered 2013-05-29 02:00:47 -0600

Basically, the standard pixtureboxes do not support the Mat datatype and thus do not let you visualize the images directly. There are two things you could do:

  1. Save the image locally in a temporary folder then read it into your pixtureBox.
  2. Create a bitmap, loop over every pixel value in the matrix element using the at operator and then assigning that value to the same location in your bitmap object. And then visualizing this.

If you do not want a workaround, call a namedWindow when you want to visualize everything, and perform an imshow() function on that namedWindow.

edit flag offensive delete link more

Comments

1

Thanks Steven, could u explain specifically about your option about creating a bitmap, some sort of code will be helpful

avinash gravatar imageavinash ( 2013-05-29 02:11:36 -0600 )edit
StevenPuttemans gravatar imageStevenPuttemans ( 2013-05-29 07:18:29 -0600 )edit
4

answered 2013-05-29 09:45:22 -0600

Ben gravatar image

updated 2013-05-29 09:58:39 -0600

Here's a method I'm using to copy data from a cv::Mat to a System::Drawing::Bitmap. If I remember right, it is inspired by EmguCV's way to deal with this problem, but I'm not sure. I just slightly edited this code to simplify it, so no guarantee it will work.

System::Drawing::Bitmap^ MatToBitmap(const cv::Mat& img)
{
    if (img.type() != CV_8UC3)
    {
        throw gcnew NotSupportedException("Only images of type CV_8UC3 are supported for conversion to Bitmap");
    }

    //create the bitmap and get the pointer to the data
    PixelFormat fmt(PixelFormat::Format24bppRgb);
    Bitmap ^bmpimg = gcnew Bitmap(img.cols, img.rows, fmt);

    BitmapData ^data = bmpimg->LockBits(System::Drawing::Rectangle(0, 0, img.cols, img.rows), ImageLockMode::WriteOnly, fmt);

    byte *dstData = reinterpret_cast<byte*>(data->Scan0.ToPointer());

    unsigned char *srcData = img.data;

    for (int row = 0; row < data->Height; ++row)
    {
        memcpy(reinterpret_cast<void*>(&dstData[row*data->Stride]), reinterpret_cast<void*>(&srcData[row*img.step]), img.cols*img.channels());
    }

    bmpimg->UnlockBits(data);

    return bmpimg;
}
edit flag offensive delete link more

Comments

ah, so basically the problem in avinash's original approach is, that the Mat goes out of scope, thus the pixel data gets invalid ?

berak gravatar imageberak ( 2013-05-29 11:00:24 -0600 )edit

Oh my! I've been struggling to convert a byte array to bitmap to show in PictureBox for many days (C++, Windows Forms). I am receiving sensor data from serial and wanted to show it. It seems to take a bit of resources, but works very well. I used it for one byte grey data.

img = cv::Mat(19, 19, CV_8UC1, pchCpy);

PixelFormat fmt(PixelFormat::Format8bppIndexed);

The other parts are same to yours. Thank you @Ben!!! I signed up for this ;)

helloMiki gravatar imagehelloMiki ( 2015-04-14 10:34:48 -0600 )edit

great,this work!

jsxyhelu gravatar imagejsxyhelu ( 2018-08-20 10:02:34 -0600 )edit
0

answered 2013-05-29 09:05:16 -0600

wuling gravatar image

updated 2018-09-27 07:52:51 -0600

Hi, here is the example code, but web is japan language http://imagingsolution.blog107.fc2.co... 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() + ")" ;

}

edit flag offensive delete link more

Comments

Sorry I delete some comment!

wuling gravatar imagewuling ( 2018-09-27 07:53:14 -0600 )edit

Question Tools

Stats

Asked: 2013-05-29 01:35:32 -0600

Seen: 11,580 times

Last updated: Sep 27 '18