【4opencv】爲基於OpenCV的圖像處理程序編寫界面—關於QT\MFC\CSharp的選擇以及GOCW的介紹

        基於OpenCV編寫圖像處理項目,除了算法之外,比較重要一個問題就是界面設計問題。對於c++語系的程序員來講,通常來講有QT/MFC兩種考慮。QT的確功能強大,特別是QML編寫android界面頗有一套( http://www.javashuo.com/article/p-rjrdnbeg-z.html),在樹莓派上進行設計也很方便( http://www.javashuo.com/article/p-grwhouvx-ba.html);可是使用QT的一個現實問題就是和現有平臺的結合,好比客戶須要將結果導出到excel中,使用QT就比較彆扭(固然不是說不能夠)。因此如今我通常這樣來作:對於Android和PI,或者須要在Linux上運行的項目,使用QT編寫界面,調用Opencv函數;對於須要在windows上運行的項目,使用MFC編寫界面,直接就能夠引用OpenCV。
        有人會吐槽MFC使用起來很是麻煩,這點我很是贊成。但MFC通過這麼多年的發展,今日仍有活力,而且短期內不會消失。由於相比較其餘一些所見即所得的語言和環境來講(QT/Csharp),mfc的消息映射機制和座標體系等,的確有它的優點,對於圖像處理程序來講尤爲如此;加以積累,可以快速作出不少專業的東西;近期出現的ribbon界面也爲mfc加分很多( http://www.javashuo.com/article/p-btchctlf-bv.html
       選擇了MFC這個方向,思考圖像處理程序問題,通常來講分爲「處理圖像」和"處理視頻"兩類:對於圖像處理來講,我提供的GOPaint框架( http://www.javashuo.com/article/p-uhraverl-x.html)可以提供一個基本的靜態圖像處理框架;而GOMFCTemplate2( https://www.cnblogs.com/jsxyhelu/p/GOMFCTemplate2.html)則適合用來處理視頻。這兩種都分別 成功運用於多種視頻處理項目中。
       可是這裏我想更進一步:但願可以用Csharp編寫界面,由於它更好用;可是又不想引入EmguCV相似的庫,由於裏面不少東西不是我須要的。那麼最直接的方法就是使用Csharp調用基於Opencv編寫的類庫文件(Dll)的,我取名叫作 GreenOpenCsharpWarper(GOCW)
       通過比較長時間的探索研究,目前的GOCW已經能夠直接以函數的形式在內存中傳遞bitmap和Mat對象,達到了函數級別的應用。由於這裏涉及到託管代碼編寫,也就是CLR程序編寫,因此有比較複雜的地方;爲了展示GOCW的優良特性,我編寫實現GOGPY項目,也就是一個"Csharp編寫界面,OpenCV實現算法的實時視頻處理程序」,相關細節都包含其中。之因此叫「GPY」,是採集硬件這塊,我採用了 成像質量較好的 高拍儀設備(GaoPaiYi)。
       這裏簡單將最核心內容進行講解。 GOCW的核心問題, 無非就是基於CLR之上的兩個方向的數據流轉換。核心函數爲
Bitmap ^  GOClrClass : :testMethod(cli : :array < unsigned  char > ^ pCBuf1)
{
    pin_ptr <System : :Byte > p1  =  &pCBuf1[ 0];
     unsigned  char * pby1  = p1;
    cv : :Mat img_data1(pCBuf1 - >Length, 1,CV_8U,pby1);
    cv : :Mat img_object  = cv : :imdecode(img_data1,IMREAD_UNCHANGED); //得到數據到img_object中去
     //////////////////////////////////處理過程///////////////////////////////////////
    cvtColor(img_object,img_object, 40);
    
     /////////////////////////////////////////////////////////////////////////////////
    Bitmap ^ bb  = MatToBitmap(img_object);
     if ( !img_object.data)
         return nullptr;
    std : :vector <uchar > buf;
    cv : :imencode( ".jpg", img_object, buf);
     return bb;
}
以及
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( 00, img.cols, img.rows), ImageLockMode : :WriteOnly, fmt);
     //byte *dstData = reinterpret_cast<byte*>(data->Scan0.ToPointer());
    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;
}

而在chsarp中,直接
Bitmap b = new Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
// If the image is upsidedown
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
srcImage = b;
if (picPreview.Image != null)
    picPreview.Image.Dispose();
//調用clr+opencv圖像處理模塊
MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bytes = ms.GetBuffer();
Bitmap bitmap = client.testMethod(bytes);
就能夠調用,而且得到結果。

如下內容爲2017年更新的內容,適當參考:
1、CLR編寫的DLL部分
一、按照正常方法引入Opencv;
二、提供接口函數,進行圖像處理(這裏只是實現了cvtColor,實際過程當中能夠用本身編寫的複雜函數)
String ^  Class1 : :Method(cli : :array < unsigned  char > ^ pCBuf1)
{
     pin_ptr <System : :Byte > p1  =  &pCBuf1[ 0];
      unsigned  char * pby1  = p1;
     cv : :Mat img_data1(pCBuf1 - >Length, 1,CV_8U,pby1);
     cv : :Mat img_object  = cv : :imdecode(img_data1,IMREAD_UNCHANGED);
      //////////////////////////////////處理過程/////////
     cvtColor(img_object,img_object, 40);
      /////////////////////////////////////////////////////////////////////////////////
      if ( !img_object.data)
         return nullptr;
      //得到目錄,保存文件
     cv : :imwrite( "c:/Method.jpg",img_object);
      return  "c:/Method.jpg";
}
 
String ^  Class1 : :Method2(cli : :array < unsigned  char > ^ pCBuf1)
{
    pin_ptr <System : :Byte > p1  =  &pCBuf1[ 0];
     unsigned  char * pby1  = p1;
    cv : :Mat img_data1(pCBuf1 - >Length, 1,CV_8U,pby1);
    cv : :Mat img_object  = cv : :imdecode(img_data1,IMREAD_UNCHANGED);
     //////////////////////////////////處理過程///////////////////////
    cvtColor(img_object,img_object, 6);
  /////////////////////////////////////////////////////////////////////////////////
     if ( !img_object.data)
         return nullptr;
     //得到目錄,保存文件
    cv : :imwrite( "c:/Method2.jpg",img_object);
     return  "c:/Method2.jpg";
}
2、Winform調用接口部分(TIP:不只能夠用Winform調用,asp.net/webservice都是能夠調用的)
一、直接引用clr dll
二、編寫helper文件(應該也能夠叫作 warpper),經過外部IO的方法獲取clr dll的文件
  class GOCsharpHelper
    {
        Class1 client  =  new Class1();
        string strResult1  = null;
        string strResult2  = null;
         //輸入參數是string或bitmap
         public Bitmap ImageProcess(string ImagePath){
            Image  ImageTemp  = Bitmap.FromFile(ImagePath);
             return ImageProcess(ImageTemp);
        }
         //輸出結果是bitmap
         public Bitmap ImageProcess(Image image)
        {
            MemoryStream ms  =  new MemoryStream();
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            byte[] bytes  = ms.GetBuffer();
            strResult1  = client.Method(bytes);
            Image ImageResult  = Bitmap.FromFile(strResult1);
             return (Bitmap)ImageResult;
        }
         public Bitmap ImageProcess2(string ImagePath)
        {
            Image ImageTemp  = Bitmap.FromFile(ImagePath);
             return ImageProcess2(ImageTemp);
        }
         //輸出結果是bitmap
         public Bitmap ImageProcess2(Image image)
        {
            MemoryStream ms  =  new MemoryStream();
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            byte[] bytes  = ms.GetBuffer();
            strResult2  = client.Method2(bytes);
            Image ImageResult  = Bitmap.FromFile(strResult2);
             return (Bitmap)ImageResult;
        }
         public  void Clear()
        {
             if (File.Exists(strResult1))
                File.Delete(strResult1);
             if (File.Exists(strResult2))
                File.Delete(strResult2);
        }
    }
三、使用例子(注意控件的dispose):

    private  void button2_Click(object sender, EventArgs e)
        {
             if (pictureBox1.Image  != null)
                pictureBox1.Image.Dispose();
             if (pictureBox2.Image  != null)
                pictureBox2.Image.Dispose();
           Image image1  = gocsharphelper.ImageProcess( " E:/sandbox/logo.jpg");
           pictureBox1.Image  = image1;
           Image image2  = gocsharphelper.ImageProcess2( "E:/sandbox/lena.jpg");
           pictureBox2.Image  = image2;
         
        }

3、解釋說明 
使用外部I/O不只僅是權宜之計,實際上Opencv的Decode使用的就是外部I/O。就目前研究的水平來講,這是最穩定的。
目前搭建成功的框架已經可以完成「csharp調用opencv的」目標,而且在調試、參數傳遞方面都很強。
若是是處理靜態圖片,已經夠用。
4、殺手程序
GOImageResearch:
使用這種方法編寫的圖像處理預分析程序。
、程序調試
在使用這個框架的過程當中,有網友反饋
clr怎麼調試啊,在clr工程中相關的數據全是無效的,怎麼看呢,不能保證寫的圖像算法徹底正確啊……
那麼clr確定是能夠調試,出現這個問題的緣由是沒有掌握相關調試技巧。這裏是相關解決方法:





2019年8月30日22:50:18 更新
主要是添加了OpenCVDNN模塊,將代碼升級到2017版本,並解決細節問題,如今應該說處理靜態圖片,那是至關好的了。
d





附件列表

相關文章
相關標籤/搜索