【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





附件列表

相关文章
相关标签/搜索