Tạo ứng Dụng OpenCV Bằng C# - THỊ GIÁC MÁY TÍNH

  • Bên project c++ viết hàm để trả về 1 Mat (VD: load ảnh và chuyển thành ảnh xám)
  • Bên project clr viết hàm convert Mat và trả về bitmap
  • Bên project C# tạo 1 Picture box và hiển thị bitmap nhận được
  • Để tránh code rườm rà khó hiểu mình đưa toàn bộ code vào project mẫu. Các bạn down về chạy thử, nếu xuất ra hình ảnh là ok.

    Tham khảo: Convert Bitmap sang Mat và ngược lại

    C# sử dụng định dạng hình ảnh bitmap, còn OpenCV là Mat, do đó cần có hàm để convert Mat thành Bitmap

    Show More Bitmap^ TGMTbridge::MatToBitmap(cv::Mat img) { if (img.data == nullptr) return nullptr; 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 Bitmap ^bmpimg = gcnew Bitmap(img.cols, img.rows, PixelFormat::Format24bppRgb); BitmapData ^data = bmpimg->LockBits(System::Drawing::Rectangle(0, 0, img.cols, img.rows), ImageLockMode::WriteOnly, PixelFormat::Format24bppRgb); 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); delete(data); img.release(); return bmpimg; }
    12345678910111213141516171819202122232425262728 Bitmap^TGMTbridge::MatToBitmap(cv::Mat img){if(img.data==nullptr)returnnullptr;if(img.type()!=CV_8UC3){throwgcnew NotSupportedException("Only images of type CV_8UC3 are supported for conversion to Bitmap");} //create the bitmap and get the pointer to the dataBitmap^bmpimg=gcnew Bitmap(img.cols,img.rows,PixelFormat::Format24bppRgb); BitmapData^data=bmpimg->LockBits(System::Drawing::Rectangle(0,0,img.cols,img.rows),ImageLockMode::WriteOnly,PixelFormat::Format24bppRgb); byte*dstData=reinterpret_cast<byte*>(data->Scan0.ToPointer()); unsignedchar*srcData=img.data; for(introw=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);delete(data);img.release();returnbmpimg;}

    Còn đây là hàm convert Bitmap thành Mat

    Show More cv::Mat BitmapToMat(Bitmap^ bitmap) { Drawing::Rectangle rect = Drawing::Rectangle(0, 0, bitmap->Width, bitmap->Height); BitmapData^ bmpData = bitmap->LockBits(rect, ImageLockMode::ReadWrite, bitmap->PixelFormat); // data = scan0 is a pointer to our memory block. IntPtr data = bmpData->Scan0; // step = stride = amount of bytes for a single line of the image size_t step = bmpData->Stride; // So you can try to get you Mat instance like this: cv::Mat mat; if (bitmap->PixelFormat == PixelFormat::Format8bppIndexed) { mat = cv::Mat(bitmap->Height, bitmap->Width, CV_8UC1, data.ToPointer(), step); } else if (bitmap->PixelFormat == PixelFormat::Format24bppRgb) { mat = cv::Mat(bitmap->Height, bitmap->Width, CV_8UC3, data.ToPointer(), step); } else { TGMTbridgeUtil::ShowErrorBox("IPSS error", "Does not support input image because don't know format"); } // Unlock the bits. bitmap->UnlockBits(bmpData); delete(bmpData); return mat; }
    12345678910111213141516171819202122232425262728293031323334 cv::Mat BitmapToMat(Bitmap^bitmap){Drawing::Rectangle rect=Drawing::Rectangle(0,0,bitmap->Width,bitmap->Height); BitmapData^bmpData=bitmap->LockBits(rect,ImageLockMode::ReadWrite,bitmap->PixelFormat); // data = scan0 is a pointer to our memory block.IntPtr data=bmpData->Scan0; // step = stride = amount of bytes for a single line of the imagesize_t step=bmpData->Stride;// So you can try to get you Mat instance like this:cv::Mat mat; if(bitmap->PixelFormat==PixelFormat::Format8bppIndexed){mat=cv::Mat(bitmap->Height,bitmap->Width,CV_8UC1,data.ToPointer(),step);}elseif(bitmap->PixelFormat==PixelFormat::Format24bppRgb){mat=cv::Mat(bitmap->Height,bitmap->Width,CV_8UC3,data.ToPointer(),step);}else{TGMTbridgeUtil::ShowErrorBox("IPSS error","Does not support input image because don't know format");} // Unlock the bits.bitmap->UnlockBits(bmpData);delete(bmpData);returnmat;}

    Download

    Nếu các bước trên phức tạp đối với bạn hoặc gặp lỗi khó giải quyết thì sử dụng source có sẵn. Mình có viết 1 sample đơn giản để minh họa việc gọi C++ từ C#. Trong repo Github có nhiều project sample, các bạn chạy riêng CsCallCpp.sln để hiểu cách C# gọi C++;

    Solution CsCallCpp.sln load 1 ảnh rồi làm mờ bằng thuật toán blur(). Nhớ là clone submodule mới build được chương trình.

    Trong Solution có nhiều project:

    • libjpeg-turbo: dùng để xử lý ảnh jpg
    • libpng: dùng để xử lý ảnh png
    • zlib: nén/giải nén data, dùng cho 2 project trên
    • opencv_core: project chính của opencv xử lý các phép tính toán mat (matrix – ma trận)
    • opencv_highgui: tạo form hiển thị Mat, các control như trackbar, button,…
    • opencv_imgcodecs: load ảnh bằng cách gọi các lib libjpeg-turbo, >libpng
    • opencv_imgproc: các phép biến đổi hình ảnh bằng phép tính toán ma trận
    • Sample: project xử lý ảnh viết bằng C++ build ra file lib
    • UI: project C# để làm giao diện
    • TGMTbridge: cầu nối giữa UI và Sample

    Lưu ý: khi Visual Studio 2017 hỏi có retarget các project không thì các bạn cứ chọn cancel

    https://github.com/thigiacmaytinh/TGMTcpp

    Từ khóa » Sử Dụng Opencv Trong C#