(4opencv)CLR基本原理和如何运用于GOCW

简介: GOCW的重点和难点就在于Csharp调用OpenCV,其中的桥梁就是CLR,当然我们也有其他方法,但是CLR是一个比较新的、比较可靠的、关键是能用的桥梁。这里关于CLR的基本原理知识、如何用于GOCW项目的相关内容加以整理思考,以图深入:一、什么是CLR;1、什么是CLRCLR(Common Language Runtime)是“公共语言运行时”的缩写,简单来说它是和Java虚拟机一样的一个运行时环境。
GOCW的重点和难点就在于Csharp调用OpenCV,其中的桥梁就是CLR,当然我们也有其他方法,但是CLR是一个比较新的、比较可靠的、关键是能用的桥梁。这里关于CLR的基本原理知识、如何用于GOCW项目的相关内容加以整理思考,以图深入:
一、什么是CLR;

1、什么是CLR

CLR(Common Language Runtime)是“公共语言运行时”的缩写,简单来说它是和Java虚拟机一样的一个运行时环境。它负责资源管理(内存分配和垃圾收集),并保证应用和底层操作系统之间必要的分离。

通用语言运行时是.NET 框架应用程序的执行引挚。它提供了许多服务,其中包括:代码管理(装入和执行)、类型安全性验证、元数据(高级类型信息)访问、为管理对象管理内存、管理代码,COM对象和预生成的DLLs(非管理代码和数据)的交互操作性、对开发人员服务的支持等等。

我们GOCW项目中为了能够使用Csharp调用OpenCV,采用了托管C++;

2、什么是托管C++?

 托管是.NET的一个专门概念,它是融于通用语言运行时(CLR)中的一种新的编程理念,使用托管C++意味着,我们的代码可以被CLR所管理,并能开发出具有最新特性如垃圾自动收集、程序间相互访问等的.NET框架应用程序。

  由托管概念所引发的C++应用程序包括托管代码、托管数据和托管类三个组成部分。  

  (1) 托管代码:. Net环境提供了许多核心的运行(RUNTIME)服务,比如异常处理和安全策略。为了能使用这些服务,必须要给运行环境提供一些信息代码(元数据),这种代码就是托管代码。所有的C#、VB.NET、JScript.NET默认时都是托管的,但Visual C++默认时不是托管的,必须在编译器中使用命令行选项(/CLR)才能产生托管代码。

  (2) 托管数据:与托管代码密切相关的是托管数据。托管数据是由公共语言运行的垃圾回收器进行分配和释放的数据。默认情况下,C#、Visual Basic 和 JScript.NET 数据是托管数据。不过,通过使用特殊的关键字,C# 数据可以被标记为非托管数据。Visual C++数据在默认情况下是非托管数据,即使在使用 /CLR 开关时也不是托管的。

  (3) 托管类: 尽管Visual C++数据在默认情况下是非托管数据,但是在使用C++的托管扩展时,可以使用"__gc"关键字将类标记为托管类。就像该名称所显示的那样,它表示类实例的内存由垃圾回收器管理。另外,一个托管类也完全可以成为 .NET 框架的成员,由此可以带来的好处是,它可以与其他语言编写的类正确地进行相互操作,如托管的C++类可以从Visual Basic类继承等。但同时也有一些限制,如托管类只能从一个基类继承等。需要说明的是,在托管C++应用程序中既可使用托管类也可以使用非托管类。这里的非托管类不是指标准C++类,而是使用托管C++语言中的__nogc关键字的类。

3、托管C++与标准C++的主要区别

 尽管托管C++是从标准C++建立而来的,但它与标准C++有着本质上的区别,这主要体现在以下几个方面:
  (1) 广泛采用"名称空间"(namespace)
  名称空间是类型的一种逻辑命名方案,.NET使用该命名方案用于将类型按相关功能的逻辑类别进行分组,利用名称空间可以使开发人员更容易在代码中浏览和引用类型。当然,我们也可将名称空间理解成是一个"类库名"。

       (2) 基本数据类型的变化

  我们知道,标准C++语言的数据类型是非常丰富的。而托管C++的数据类型更加丰富,不仅包含了标准C++中的数据类型,而且新增了__int64 (64位整型)、Decimal(96位十进制数)、String*(字符串类型)和Object*(对象类型)等类型,表1-1列出它们各自数据类型。

      (3) 新增三个托管C++类型:__gc class、__value class和__gc interface

  一个__gc类或结构意味着该类或结构的生命周期是由.NET开发平台自动管理及垃圾自动收集,用户不必自已去调用delete来删除。定义一个__gc类或结构和标准C++基本相似,所不同的是在class或struct前加上__gc。


二、CLR为什么能用于Csharp和C++互相调用

      基本的思路是将C++代码封装成为托管代码,而CSharp代码本来就可以翻译成CLR语句。在这种情况下,C++实现的效果,能够直接被CSharp调用,从而达到联合的目的。
      其中的难点,其实并不是引用,而是参数的传递:如何将“图像”这种本质上较为巨大的数据在两种系统里面传递,所以必然就需要有内存的操作;此外CLR语言的编码方法和普通CSharp差距较大,也是需要注意。
      一般来说:
      有C#及C++背景的人使用C++/CLI的必备知识:
  1、C++/CLI里的new等于C++里的new, gcnew等于C#里的new
  2、原生指针用*表示,托管引用使用^表示
  如: Stream^ stream = gcnew Stream();
  3、array<unsigned char>^ 等于 System.Byte[]
  4、pin_ptr关键字能把托管引用转换为原生指针:
  如: pin_ptr<BYTE> pBytes = & byteArray[0];
  然后pBytes就可以当作原生的BYTE* 使用了. 
  等代码执行完pBytes的有效范围,byteArray就会恢复可被GC处理的状态

三、CLR如何具体用于GOCW项目:

这里应该算是核心代码的解析,完整代码可以自己看,主要讲接口
Csharp(BitMap)->Mat->Csharp(BitMap) ,几乎全部的内容都在CLR形式的C++代码中,其它地方只是实现接口。
    ////////////////////////////////将输入cli::array<unsigned char>转换为cv::Mat/////////////////////////
    pin_ptr<System::Bytep1 = &pCBuf1[0];
    unsigned charpby1 = p1;
    cv::Mat img_data1(pCBuf1->Length,1,CV_8U,pby1);
    cv::Mat img_object = cv::imdecode(img_data1,IMREAD_UNCHANGED);
    if (!img_object.data)
        return nullptr;
这里注意,内存操作其实是在imdecode中实现的,如果进一步研究,会发现imdecode还有磁盘I/O操作,这个地方应该报一个ISSUE\BUGREPORT(TODO)

System::Drawing::BitmapMatToBitmap(const cv::Matimg)
{
    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.colsimg.rowsfmt);
    BitmapData ^data = bmpimg->LockBits(System::Drawing::Rectangle(0, 0, img.colsimg.rows), ImageLockMode::WriteOnlyfmt);
    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;
}

bmp是有LocKBits操作的,就是在这里将Bitmap处理的结果固定在内存中的。


Bitmap类使用LockBits和UnLockBits方法来将位图的数据矩阵保存在内存中、直接对它进行操作,最后用修改后的数据代替位图中的原始数据。 LockBits返回以各BitmapData的类用已描述数据在已锁定的矩阵中的位置和分布。
      BitmapData类包括以下几个重要的属性:
  • Scan0:数据矩阵在内存中的地址。
  • Stride:数据矩阵中的行宽,以byte为单位。可能会扩展几个Byte,后面会介绍。
  • PixelFormat:像素格式,这对矩阵中字节的定位很重要。
  • Width:位图的宽度。
  • Height:位图的高度。

  具体关系见下图:


 
   如上图所示,stride属性表示位图数据矩阵的行宽,以byte为单位。出于效率考虑,矩阵的行宽并非刚好是每行像素数的整数倍,系统往往会将其封装成4的整数倍。举例来说,对于一幅24位深17像素宽的图像,其stride属性为52;每行的数据量为17*3=51,系统将其自动封装一个字节,所以它的stride为52byte(或13*4byte)。对于一幅17像素宽的4位索引图,其stride为12,其中9byte(准确地说是8.5个byte)用来记录数据信息,每行再自动添加3(3.5)个byte保证其为4的整数倍。
   具体数据的分布因其pixelformat而异。24位深的图像每隔3个byte包含一组RGB信息;32位深的图像每隔4个byte包含一组RGBA信息。那些每个字节包含多个像素的pixelformat,比如4位索引图像或1位索引图像,必须经过仔细处理,从而保证同一字节中的相邻byte不会混淆。
指针的准确定位
  • 32位RGB:假设X、Y为位图中像素的坐标,则其在内存中的地址为scan0+Y*stride+X*4。此时指针指向蓝色,其后分别是绿色、红色,alpha分量。
  • 24位RGB:scan0+Y*stride+X*3。此时指针指向蓝色,其后分别是绿色和红色。
  • 8位索引:scan0+Y*stride+X。当前指针指向图像的调色盘。
  • 4位索引:scan0+Y*stride+(X/2)。当前指针所指的字节包括两个像素,通过高位和低位索引16色调色盘,其中高位表示左边的像素,低位表示右边的像素。
  • 1位索引:scan0+Y*stride+X/8。当前指针所指的字节中的每一位都表示一个像素的索引颜色,调色盘为两色,最左边的像素为8,最右边的像素为0 (TODO EMGUCV ISSUE)
     
     







目前方向:图像拼接融合、图像识别 联系方式:jsxyhelu@foxmail.com
目录
相关文章
|
4月前
|
计算机视觉 C++
基于VS2019和Opencv4,对hsv颜色空间的图像分割原理以及实现
这篇文章介绍了基于HSV颜色空间的图像分割原理,包括HSV模型的基本概念和如何在OpenCV中通过设置HSV的色彩范围来实现图像中特定颜色的物体分割,并通过示例代码展示了在静态图像和视频流中进行颜色分割的方法。
基于VS2019和Opencv4,对hsv颜色空间的图像分割原理以及实现
|
7月前
|
算法 计算机视觉
【OpenCV】仿射变换中cv2.estimateAffine2D 的原理
【OpenCV】仿射变换中cv2.estimateAffine2D 的原理
295 0
|
API 计算机视觉
OpenCV 图像模糊原理
OpenCV 图像模糊原理
133 0
OpenCV 图像模糊原理
|
计算机视觉
OpenCV的findContours源码与原理
OpenCV的findContours源码与原理
1306 0
OpenCV的findContours源码与原理
|
计算机视觉 算法 定位技术
OpenCV 单应矩阵应用:全景图像融合原理
之前的写了好几篇文,什么特征点检测,匹配,RANSAC之类的乱七八糟的,就是为了做这个应用。了解原理之后用NI Vision实现,数图的课程设计算是交差了~~全景图像融合使用到SIFT算子(特征点检测和匹配)、单应矩阵(立体几何)和RANSAC(随机抽样一致性)之类的内容,了解其中的领域和原理还是需要花点时间的。
2120 0
|
计算机视觉 编解码 算法
(4opencv)如何基于GOCW,创建一个实时视频程序
直接使用提供的代码框架进行修改,是最快得到效果的方法;但是这样的灵活性较差,而且真正的程序员从来都不会停滞在这一步:我们需要的是“将框架解析到最小化、理清楚每个构建之间的关系”,只有这样才能灵活运用。 一、准备工作1、高拍仪已经接通,如果需要的话,还要安装驱动; 2、vs2012编程环境,能够编写Csharp和OpenCV程序(具体不清楚可以回过头来看配置); 3、是DirectShow.net(http://directshownet.sourceforge.net/docs.html)的可使用类 它本身包含文档,有时间可以看一下。
1210 0
|
.NET C# 计算机视觉
为基于OpenCV的图像处理程序编写界面—关于QT\MFC\CSharp的选择以及GOCW的介绍
基于OpenCV编写图像处理项目,除了算法以外,比较重要一个问题就是界面设计问题。对于c++语系的程序员来说,一般来说有QT/MFC两种考虑。QT的确功能强大,特别是QML编写android界面很有一套(https://www.cnblogs.com/jsxyhelu/p/8286476.html),在树莓派上进行设计也很方便(https://www.cnblogs.com/jsxyhelu/p/7839062.html);但是使用QT的一个现实问题就是和现有平台的结合,比如客户需要将结果导出到excel中,使用QT就比较别扭(当然不是说不可以)。
2333 0
|
2月前
|
计算机视觉
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
这篇文章详细介绍了OpenCV库中的图像二值化函数`cv2.threshold`,包括二值化的概念、常见的阈值类型、函数的参数说明以及通过代码实例展示了如何应用该函数进行图像二值化处理,并展示了运行结果。
377 0
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
|
3月前
|
算法 计算机视觉
opencv图像形态学
图像形态学是一种基于数学形态学的图像处理技术,它主要用于分析和修改图像的形状和结构。
51 4
|
3月前
|
存储 计算机视觉
Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制
本文介绍了使用OpenCV进行图像读取、显示和存储的基本操作,以及如何绘制直线、圆形、矩形和文本等几何图形的方法。
Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制