【OpenCV】 OpenCV (C++) 与 OpenCvSharp (C#) 之间数据通信

news2024/10/1 21:38:13

  OpenCV是一个基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。 它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
  OpenCvSharp 是一个OpenCV的.Net wrapper,应用最新的OpenCV库开发,使用习惯比EmguCV更接近原始的OpenCV,有详细的使用样例供参考。该库采用LGPL发行,对商业应用友好。使用OpenCvSharp,可用C#,VB.NET等语言实现多种流行的图像处理(image processing)与计算机视觉(computer vision)算法。

  但是在实际使用中,由于涉及到不同编程语言之间互相调用,导致C++ 中的OpenCV与C#中的OpenCvSharp 图像数据在不同编程语言之间难以有效传递。在本文中我们将结合OpenCvSharp源码实现原理,探究两种数据之间的通信方式。

1. 问题分析

  在日常开发中,由于一些库不支持C#接口,因此在使用时我们需要借助动态链接库的方式,在C#中调用C++封装的应用。由于C++与C#底层编译方式不同,因此动态链接库只可以传递基础的数据类型,无法传递像Class这种高级的数据格式。

  在日常开发中,我们在C#中使用OpenCvSharp进行图像处理,但是我们调用的算法是通过C++封装的动态链接库,且需要将图片数据传递到C++封装的动态链接库中进行处理,因此实现高效的实现图片数据传递是十分有必要的。常见的方式有两种:

在这里插入图片描述

  (1)第一种方式是在C#中将图片数据转为基本数据类型byte[]数组,然后将该数据传递到C++动态链接库中,在接收到该数据后,由C++再将该数据重新转为图片数据进行处理。目前该方式经过测试,是可以实现的,但是这样有一个弊端,图片数据需要进行两次的转换,这样会导致严重浪费时间和消耗大量内存。

  (2)第二种方式是在C#中将数据保存到本地,然后再C++动态链接库中读取。与上一种方式一样,这样会导致严重浪费时间和消耗大量内存。

2. 解决办法

  为了解决这个问题,我们探究了一下OpenCvSharp 实现方式,通过其源码可知,OpenCvSharp 在实现时,是通过对C++中的OpenCV进行了进一步封装,将Mat数据定义成指针类型,然后以指针的方式在C++与C#中进行传递;而在C#中,重新定义了Mat数据类型,将C++传递来的Mat指针作为成员变量进行初始化,而后续基于Mat的所有操作,其低层都是通过传递这个指针进行操作的。

在这里插入图片描述

  知道了Mat的这个数据类型的实现原理后,我们可以模仿这种方式,以指针的方式实现将OpenCvSharp的数据传递到OpenCV C++中,这样就可以快速实现数据类型传递。实现方式如下图所示。

在这里插入图片描述

  在C#中使用OpenCvSharp获取一个图片数据,数据类型为Mat,我们可以先进行处理等操作;接下来我们可以获取OpenCvSharp的地址CvPtr,然后在C++中使用*Mat指针进行获取,然后通过*Mat我们便可以获取到OpenCV C++中的Mat数据。接下来,用户就可以根据自己的需求进行处理即可。在处理完成后,在将获得新的用Mat数据转为用*Mat指针,然后再C#中,使用IntPtr数据类型进行接收,然后使用OpenCvSharp的Mat以获取的指针数据为初始值初始化Mat数据类型即获得新的Mat数据。

  通过上述方式,我们便可以很轻松的实现C#中的OpenCvSharp与C++中的OpenCv数据转换。

3. 项目创建

为方便演示,下述所有程序设计与编译皆是在Windows11环境下,使用Visual Studio 2022编辑器实现。

  • OpenCV: 4.8.0
  • OpenCvSharp: 4.9.0

大家可以根据上述版本进行配置,也可以使用其他版本配置,但要保证OpenCV与OpenCvSharp都是同一个基础版本的,且版本差别不要太大。

3.1 创建C++项目

  使用Visual Studio 2022创建一个空的C++项目,然后添加两个文件,分别为:mat_conv.hmat_conv.cpp

  接下来配置项目属性,首先配置项目输出类型,如下图所示,设置图片输出类型为动态库(.dll)

请添加图片描述

  然后配置OpenCV C++项目依赖,主要是配置C++项目的包含目录、库目录以及附加依赖项三个地方,如下图所示:

  以下是我的项目设置信息,大家可以根据自己安装的OpenCV情况进行设置:

包含目录: C:\3rdpartylib\opencv\build\include
库目录: C:\3rdpartylib\opencv\build\x64\vc16\lib
附加依赖项: opencv_world480.lib

3.2 创建C#项目

  使用Visual Studio 2022创建一个新的C#控制台项目,然后使用NuGet安装所需的程序集即可,此处只需要安装OpenCvSharp即可,如下图所示:

请添加图片描述

4. 接口测试

  此处主要测试四个接口:

  • 第一个接口测试在OpenCvSharp中读取一张图片,然后将图片数据传入到OpenCV中,测试传入是否成功。
  • 第二个测试接口在OpenCV创建一个图片,绘制一个矩形,然后将创建好的图片传出到OpenCvSharp,测试传出数据是否成功。
  • 第三个测试接口是在OpenCvSharp中读取一张图片,然后将图片数据传入到OpenCV中,并进行一步处理,该处理结果会将数据保存到另一个新的图片数据中,将该新的图片数据传出,然后在OpenCvSharp查看是否处理成功,测试该过程是否成功。
  • 第四个测试接口是在OpenCvSharp中读取一张图片,然后将图片数据传入到OpenCV中,并进行一步处理,该处理结果会直接在原有数据上进行修改,然后在OpenCvSharp查看是否处理成功,测试该过程是否成功。

4.1 接口一测试

  在以下文件中分别添加以下代码:

mat_conv.h

#include "opencv2/opencv.hpp"
extern "C"  __declspec(dllexport) void __stdcall mat_conv1(cv::Mat * mat);

mat_conv.cpp

#include "mat_conv.h"

void mat_conv1(cv::Mat *mat)
{
	cv::imshow("image", *mat);
	cv::waitKey(0);
}

Program.cs

using OpenCvSharp;
using OpenCvSharp.Internal;
using System.Runtime.InteropServices;
namespace opencv_csharp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
            string image_path = "image.jpg";
            Mat mat1 = Cv2.ImRead(image_path);
            Methord.mat_conv1(mat1.CvPtr);
        }
    }

    class Methord 
    {
        private const string dll_path = "C:\\Users\\lenovo\\Desktop\\test_opencv\\x64\\Release\\opencv_cpp.dll";
        [DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern void mat_conv1(IntPtr mat);
    }
}

  其中,mat_conv.hmat_conv.cpp为C++项目文件,Program.cs文件为C#项目文件。

  在C++项目文件中,extern "C" __declspec(dllexport)表示使用C语言的编译方式进行编译,并导出到dll中。 mat_conv1(cv::Mat * mat)方法主要是接受传入的Mat指针,并使用cv::imshow("image", *mat)将图片数据展示出来。

  在C#项目中,使用[DllImport]属性将动态链接库中的mat_conv1读取出来,同时因为在C#中指针都是被封装为IntPtr类型的,因此使用IntPtr表示此处传入的参数为指针类型。在使用该接口时,直接调用该方法,并且传入指针参数,该指针参数可以通过Mat.CvPtr直接获得。

  如下图所示,程序在运行后,成功将传入的图片数据绘制出来,如下图所示,说明该接口测试成功,也证明了该方法是可行的。

请添加图片描述

4.2 接口二测试

  在以下文件中分别添加以下代码:

mat_conv.h

#include "opencv2/opencv.hpp"
extern "C"  __declspec(dllexport) void __stdcall mat_conv2(cv::Mat **returnValue);

mat_conv.cpp

#include "mat_conv.h"

void  mat_conv2(cv::Mat** returnValue)
{
    // 创建一个空白图像
    cv::Mat image = cv::Mat::zeros(400, 400, CV_8UC3);
    // 矩形的左上角和右下角坐标
    cv::Point2f rect_start(50, 50);
    cv::Point2f rect_end(350, 350);
    // 矩形颜色 (B, G, R)
    cv::Scalar color(255, 0, 0); // 红色
    // 矩形线条粗细
    int thickness = 2;
    // 绘制矩形
    cv::rectangle(image, rect_start, rect_end, color, thickness);
    *returnValue = new cv::Mat(image);
}

Program.cs

using OpenCvSharp;
using OpenCvSharp.Internal;
using System.Runtime.InteropServices;
namespace opencv_csharp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
            IntPtr ptr2 = IntPtr.Zero;
            Methord.mat_conv2(out ptr2);
            Mat mat2 = new Mat(ptr2);
            Cv2.ImShow("image2", mat2);
            Cv2.WaitKey(0);
        }
    }

    class Methord 
    {
        private const string dll_path = "C:\\Users\\lenovo\\Desktop\\test_opencv\\x64\\Release\\opencv_cpp.dll";
        [DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern void mat_conv2(out IntPtr returnValue);
    }
}

  其中,mat_conv.hmat_conv.cpp为C++项目文件,Program.cs文件为C#项目文件。

  在C++项目文件中,mat_conv2(cv::Mat** returnValue)主要是创建一个画布,并绘制一个矩形,然后将创建好的图片数据以指针的方式传递到C#中。

  在C#项目中,使用[DllImport]属性将动态链接库中的mat_conv2读取出来,传出数据此处使用的是双重指针,因此使用out IntPtr进行接收。在获取到该方法后,我们调用new Mat(IntPtr ptr)构造方法初始化为新的Mat数据。。

  如下图所示,程序在运行后,成功将传出的图片数据绘制出来,如下图所示,说明该接口测试成功,也证明了该方法是可行的。

请添加图片描述

4.3 接口三测试

  在以下文件中分别添加以下代码:

mat_conv.h

#include "opencv2/opencv.hpp"
extern "C"  __declspec(dllexport) void __stdcall mat_conv3(cv::Mat * mat, cv::Mat **returnValue);

mat_conv.cpp

#include "mat_conv.h"

void  mat_conv3(cv::Mat * mat, cv::Mat **returnValue)
{
	cv::Mat m;
	cv::cvtColor(*mat, m, cv::COLOR_BGR2GRAY);
	*returnValue = new cv::Mat(m);
}

Program.cs

using OpenCvSharp;
using OpenCvSharp.Internal;
using System.Runtime.InteropServices;
namespace opencv_csharp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
            string image_path = "image.jpg";
            Mat mat3 = Cv2.ImRead(image_path);
            IntPtr ptr3 = IntPtr.Zero;
            Methord.mat_conv3(mat1.CvPtr, out ptr3);
            Mat mat3 = new Mat(ptr3);
            Cv2.ImShow("image1", mat3);
            Cv2.WaitKey(0);
        }
    }

    class Methord 
    {
        private const string dll_path = "C:\\Users\\lenovo\\Desktop\\test_opencv\\x64\\Release\\opencv_cpp.dll";
        [DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern void mat_conv3(IntPtr mat, out IntPtr return_value);
    }
}

  其中,mat_conv.hmat_conv.cpp为C++项目文件,Program.cs文件为C#项目文件。

  在C++项目文件中, mat_conv2(cv::Mat * mat, cv::Mat **returnValue)方法主要是接受传入的Mat指针,并将传入的图片数据转为灰度图,同时将转换好的图片数据以指针的方式传出到C#中。

  在C#项目中,使用[DllImport]属性将动态链接库中的mat_conv3读取出来,其中传入数据为指针数据,所以直接使用IntPtr即可;而对于传出数据此处使用的是双重指针,因此使用out IntPtr进行接收。在获取到该方法后,我们调用new Mat(IntPtr ptr)构造方法初始化为新的Mat数据。

  如下图所示,程序在运行后,成功将传入的图片数据进行灰度转换,并将转换后的图片数据成功传递出来,说明该接口测试成功,也证明了该方法是可行的。同时我们测试了该过程所需时间,仅使用了3.69毫秒。

请添加图片描述

4.4 接口四测试

  在以下文件中分别添加以下代码:

mat_conv.h

#include "opencv2/opencv.hpp"
extern "C"  __declspec(dllexport) void __stdcall mat_conv4(cv::Mat * mat);

mat_conv.cpp

#include "mat_conv.h"

void  mat_conv4(cv::Mat* mat)
{
    // 矩形的左上角和右下角坐标
    cv::Point2f rect_start(50, 50);
    cv::Point2f rect_end(350, 350);
    // 矩形颜色 (B, G, R)
    cv::Scalar color(255, 0, 0); // 红色
    // 矩形线条粗细
    int thickness = 2;
    // 绘制矩形
    cv::rectangle(*mat, rect_start, rect_end, color, thickness);
}

Program.cs

using OpenCvSharp;
using OpenCvSharp.Internal;
using System.Runtime.InteropServices;
namespace opencv_csharp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
            string image_path = "image.jpg";
            Mat mat4 = Cv2.ImRead(image_path);
            Methord.mat_conv4(mat1.CvPtr);
            Cv2.ImShow("image2", mat4);
            Cv2.WaitKey(0);
        }
    }

    class Methord 
    {
        private const string dll_path = "C:\\Users\\lenovo\\Desktop\\test_opencv\\x64\\Release\\opencv_cpp.dll";
        [DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern void mat_conv4(IntPtr mat);
    }
}

  其中,mat_conv.hmat_conv.cpp为C++项目文件,Program.cs文件为C#项目文件。

  在C++项目文件中, mat_conv4(IntPtr mat)方法主要是接受传入的Mat指针,并在传入的图片数据中绘制一个矩形,因为该操作是在原始数据上进行的操作,没有产生新的图像数据,所以不需要传出。

  在C#项目中,使用[DllImport]属性将动态链接库中的mat_conv4读取出来,其中传入数据为指针数据,所以直接使用IntPtr即可。然后该方法运行完后,我们直接查看该图像数据信息,查看是否已经被修改。

  如下图所示,程序在运行后,结果如下图所示,说明该接口测试成功,也证明了该方法是可行的。

请添加图片描述

5. 总结

  在项目中,我们结合OpenCvSharp源码,使用OpenCvSharp数据指针实现了在C#与C++之间传递图像数据。与传统的数据传递方式相比,该方式通过传递指针,通过指针的方式实现对同一块图像数据进行操作,避免了图像数据的来回转换,极大的节省了程序运行时间以及内存消耗。

  最后如果各位开发者在使用中有任何问题,欢迎大家与我联系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1549749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL中使用distinct单、多字段去重方法

目录 一、distinct 1.1 只对一个字段查重 1.2多个字段去重 1.3针对null处理 1.4与distinctrow同义 二、聚合函数中使用distinct 三、CONCAT_WS函数 多个字段拼接去重是指将多个字段的值按照一定的规则进行拼接,并去除重复的拼接结果。这样可以生成唯一标识符…

蓝桥杯刷题day10——猜灯谜【算法赛】

一、问题描述 在元宵节的活动现场,有一串环形排列的灯笼,共计 n 个。每个灯笼上伴随着一个谜底以及一个数字,这些数字分别为 a1,a2 ,…,an。 根据元宵节的传统,每个灯笼的谜底都是由相邻两个灯笼上的数字之和得出的。需要注意的…

勾八头歌之分类回归聚类

一、机器学习概述 第1关机器学习概述 B AD B BC 第2关常见分类算法 #编码方式encodingutf8from sklearn.neighbors import KNeighborsClassifierdef knn(train_data,train_label,test_data):input:train_data用来训练的数据train_label用来训练的标签test_data用来测试的数据…

R 生存分析3:Cox等比例风险回归及等比例风险检验

虽然Kaplan-Meier分析方法目前应用很广,但是该方法存在一下局限: 对于一些连续型变量,必须分类下可以进行生存率对比 是一种单变量分析,无法同时对多组变量进行分析 是一种非参数分析方法,必须有患者个体数据才能进行分析 英国…

春秋云境CVE-2023-27179

简介 GDidees CMS v3.9.1及更低版本被发现存在本地文件泄露漏洞,漏洞通过位于 /_admin/imgdownload.php 的 filename 参数进行利用。 正文 进入靶场发现没有什么可以利用的地方,那么就按照靶场提示来,直接访问/_admin/imgdownload.php 打开…

鸿蒙雄起!风口就在当下,你如何抉择?

近年来,华为自主研发的鸿蒙操作系统(HarmonyOS)引起了广泛的关注和讨论。鸿蒙系统不仅标志着华为在软件领域的一次重大突破,也预示着全球智能设备市场格局的潜在变化。本文将深入探讨鸿蒙系统的兴起、其在市场上的表现以及对程序员…

【SQL】1633. 各赛事的用户注册率(COUNT函数 表达式用法)

题目描述 leetcode题目:1633. 各赛事的用户注册率 Code select contest_id, round(count(*)/(select count(*) from Users)*100, 2) as percentage from Register group by contest_id order by percentage desc, contest_id ascCOUNT()函数 COUNT函数用法&#…

k8s入门到实战(十一)—— DaemonSet详细介绍及使用

DaemonSet 说明 是个 Pod 控制器能够确保 k8s 的所有节点都运行一个相同的 pod 副本,假设这个 pod 名称为 pa 当增加 node 节点时,这个节点会自动创建一个 pa副本当删除 node 节点时,pa 副本会自动删除 删除 daemonset 会删除它们创建的 pod 使用场景 需…

PyQt:实现自定义绘制pyqtgraph折线图中的symbol图标

一、写在前面 1.关于symbol的官方文档说明:symbol 2.我自己绘制的自定义symbol符号,如下图:一个在坐标轴上移动的“小货车” 2.默认可以选择的有以下多种symbol符号 但最后一项也说明了:支持QPainterPath绘制的自定义符号形状。…

案例分析-IEEE 754浮点标准

案例一: 请分析IEEE 754双精度浮点数规格化数的表示范围。 案例二: 规格化浮点数的Bias为什么采用2k-1-1而不是2k-1​?非规范数的指数E1-Bias而不是0-Bias? (1) ① bias 127时 E e - 127 (00…

0.96寸OLED屏调试 ----(一)

所需设备: 1、USB 转 SPI I2C 适配器;内附链接 2、0.96寸OLED显示模块; 备注:专业版、升级版都适用; 首先介绍一下OLED显示模块,SSD1306是一款OLED驱动芯片,拥有最大128*64像素支持&#xff…

卷积变体-----分组卷积、深度可分离卷积、膨胀卷积

文章目录 一、分组卷积1.1 概述1.2 参数量变换 二、深度可分离卷积2.1 概述2.2 计算 三、膨胀卷积 一、分组卷积 1.1 概述 1. 分组卷积(Group convolution )最早在AlexNet中出现,由于当时的硬件资源有限,训练AlexNet时卷积操作不…

Axure中后台系统原型模板,B端页面设计实例,高保真高交互54页

作品概况 页面数量:共 50 页(长期更新) 兼容版本:Axure RP 9/10,不支持低版本 应用领域:网页模板、网站后台、中台系统、B端系统 作品特色 本品为「web中后台系统页面设计实例模板」,默林原创…

聊聊CSS

css 的介绍 学习目标 能够知道css的作用 1. css 的定义 css(Cascading Style Sheet)层叠样式表,它是用来美化页面的一种语言。 没有使用css的效果图 使用css的效果图 2. css 的作用 美化界面, 比如: 设置标签文字大小、颜色、字体加粗等样式。 控制页面布局, 比如…

逐步学习Go-协程goroutine

参考:逐步学习Go-协程goroutine – FOF编程网 什么是线程? 简单来说线程就是现代操作系统使用CPU的基本单元。线程基本包括了线程ID,程序计数器,寄存器和线程栈。线程共享进程的代码区,数据区和操作系统的资源。 线…

jMeter学习

一. JMeter介绍 1. 什么是JMeter? Apache JMeter™ 应用程序是开源软件,一个 100% 纯 Java 应用程序,旨在加载测试功能行为和测量性能 。它最初是为测试 Web 应用程序而设计的,但后来扩展到其他测试功能。 2. JMeter能做啥&#x…

RabbitMQ(简单模式)

2种远程服务调用 1openFeign: 优点:能拿到被调用的微服务返回的数据,系统系耦度高,系统稳定。 缺点:同步调用,如果有很多服务需要被调用,耗时长。 MQ,消息队列,RabbitMQ是消息we…

数据可视化-ECharts Html项目实战(8)

在之前的文章中,我们学习了如何设置散点图涟漪效果与仪表盘动态指针效果。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢 今天的文章,会…

标定系列——预备知识-OpenCV中实现Rodrigues变换的函数(二)

标定系列——预备知识-OpenCV中实现Rodrigues变换的函数(二) 说明记录 说明 简单介绍罗德里格斯变换以及OpenCV中的实现函数 记录

Machine Learning机器学习之向量机(Support Vector Machine,SVM)

目录 前言 算法提出背景: 核心思想: 原理: 应用领域: 一、支持向量机分类(主要变体) 二、构建常见的支持向量机模型 基于Python 中的 Scikit-learn 库构建线性支持向量机(SVM) 三、向…