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

  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.rhkb.cn/news/288639.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云实时计算Flink的产品化思考与实践【上】

摘要:本文整理自阿里云高级产品专家黄鹏程和阿里云技术专家陈婧敏在 FFA 2023 平台建设专场中的分享。内容主要为以下五部分: 阿里云实时计算 Flink 简介产品化思考产品化实践SQL 产品化思考及实践展望 该主题由黄鹏程和陈婧敏共同完成,前半程…

BaseDao封装JavaWeb的增删改查

目录 什么是BaseDao? 为什么需要BaseDao? BaseDao的实现逻辑 什么是BaseDao? Basedao 是一种基于数据访问对象(Data Access Object)模式的设计方法。它是一个用于处理数据库操作的基础类,负责封装数据库…

Adobe推出20多个,企业版生成式AI定制、微调服务

3月27日,全球多媒体领导者Adobe在拉斯维加斯召开“Summit 2024”大会,重磅推出了Firefly Services。 Firefly Services提供了20 多个生成式AI和创意API服务,支持企业自有数据对模型进行定制、微调,同时可以与PS、Illustrator、Ex…

高级DBA带你处理MySQL客户端程序频繁访问MYSQL数据库并错误链接不释放导致连接数爆满事故实战

高级DBA带你处理MySQL客户端程序频繁访问MYSQL数据库并错误链接不释放导致连接数爆满事故实战 一、生产事故描述 Mysql生产数据库最大连接数爆满,其余客户端也同样拿不到数据库连接,生产异常,数据传输失败! 报错如下&#xff1a…

架构师之路--Docker的技术学习路径

Docker 的技术学习路径 一、引言 Docker 是一个开源的应用容器引擎,它可以让开发者将应用程序及其依赖包打包成一个可移植的容器,然后在任何支持 Docker 的操作系统上运行。Docker 具有轻量级、快速部署、可移植性强等优点,因此在现代软件开…

解决kubesphere流水线docker登陆错误http: server gave HTTP response to HTTPS client

kubesphere DevOps流水线中,在登录私有的harbor仓库时,报以下错误 docker login 111.230.19.120:80 -u admin -p test123. WARNING! Using --password via the CLI is insecure. Use --password-stdin. Error response from daemon: Get "https://…

使用vue构建一个简单实用的春节红包插件!

摘要:本文将介绍如何使用Vue.js构建一个简单实用的春节红包插件。该插件通过模拟红包的打开和关闭过程,以及金额的随机分配,为春节红包活动提供了一个有趣且互动的体验。 一、引言 在春节这个充满欢乐和祝福的时刻,红包成为了传递…

pt-archiver的实践分享,及为何要用 ob-archiver 归档数据的探讨

作者简介:肖杨,软件开发工程师 在数据密集型业务场景中,数据管理策略是否有效至关重要,它直接关系到系统性能与存储效率的提升。数据归档作为该策略的关键环节,不仅有助于优化数据库性能,还能有效降低存储成…

11.2024

插入排序 代码&#xff1a; public class 第十一题 {public static void main(String[] args) {int a[]{2,2,1,6,4,9,7,6,8};for (int k1;k<a.length;k){int sk-1;//排好序的最后一位int sssa[k];//记录哨兵的值while (s>0&&sss<a[s]){a[s1]a[s];s--;}a[s1]…

Python中的变量与常量

变量&#xff1a;在程序运行过程中&#xff0c;值会发生变化的量&#xff0c; 常量&#xff1a;在程序运行过程中&#xff0c;值不会发生变化的量。 无论是变量还是常量&#xff0c;在创建时都会在内存中开辟一块空间&#xff0c;用于保存它的值。 Python 中的变量不需要声明…

数仓建设实践——58用户画像数仓建设

目录 一、数据仓库&用户画像简介 1.1 数据仓库简介 1.2 数据仓库的价值 1.3 用户画像简介 1.4 用户画像—标签体系 二、用户画像数仓建设过程 2.1 画像数仓—背景&现状 2.2 画像数仓—整体架构 2.3 画像数仓—研发流程 2.4 画像数仓—指标定义 2.5 画像数仓…

基于大语言模型的云故障根因分析|顶会EuroSys24论文

*马明华 微软主管研究员 2021年CCF国际AIOps挑战赛程序委员会主席&#xff08;第四届&#xff09; 2021年博士毕业于清华大学&#xff0c;2020年在佐治亚理工学院做访问学者。主要研究方向是智能运维&#xff08;AIOps&#xff09;、软件可靠性。近年来在ICSE、FSE、ATC、EuroS…

【b站李炎恢】Vue.js Element UI | 十天技能课堂 | 更新中... | 李炎恢

课程地址&#xff1a;【Vue.js Element UI | 十天技能课堂 | 更新中... | 李炎恢】 https://www.bilibili.com/video/BV1U54y127GB/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 备注&#xff1a;虽然标题声明还在更新中&#xff0c;但是看一些常用…

一键换脸的facefusion

FaceFusion 一个开源换脸软件&#xff0c;提供UI界面&#xff0c;启动后可直接在浏览器上面上传图片进行换脸操作。 电脑环境win10&#xff0c;软件pycharm&#xff0c;需要提前安装好python环境&#xff0c;推荐使用Anaconda3。关注文章下方公共号发送 “ 软件安装包 ”可以获…

Docker搭建LNMP环境实战(06):Docker及Docker-compose常用命令

Docker搭建LNMP环境实战&#xff08;06&#xff09;&#xff1a;Docker及Docker-compose常用命令 此处列举了docker及docker-compose的常用命令&#xff0c;一方面可以做个了解&#xff0c;另一方面可以在需要的时候进行查阅。不一定要强行记忆&#xff0c;用多了就熟悉了。 1、…

sheng的学习笔记-AI-YOLO算法,目标检测

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 目标定位&#xff08;Object localization&#xff09; 定义 原理图 具体做法&#xff1a; 输出向量 图片中没有检测对象的样例 损失函数 ​编辑 特征点检测&#xff08;Landmark detection&#xff09; 定义&a…

Day23:事务管理、显示评论、添加评论

事务管理 事务的定义 什么是事务 事务是由N步数据库操作序列组成的逻辑执行单元&#xff0c;这系列操作要么全执行&#xff0c;要么全放弃执行。 事务的特性(ACID) 原子性(Atomicity):事务是应用中不可再分的最小执行体&#xff08;事务中部分执行失败就会回滚 。一致性(C…

R语言 for循环问题

今天偶然发现在R的for循环中&#xff0c;作为循环计次的i&#xff0c; 并不会因为在循环体中的赋值变化而变化。 记录一下&#xff0c;还没有找到相关的解释。

设计模式——行为型——策略模式Strategy

Q&#xff1a;策略模式的特点 A&#xff1a; 具体算法从具体的业务方法中独立出来策略模式是同行为的不同实现 Q&#xff1a;什么时候使用策略模式 A&#xff1a;多个if-else使用策略模式 收费对象类 public class CashContext {private CashStrategy cashStrategy;public…

R: 网状Meta分析进行模型构建及图形绘制

网状meta分析的制作步骤主要包括&#xff1a; 1. 绘制网状证据图 2. 普通Meta分析&#xff08;两两之间的直接比较&#xff09; 3. 网状Meta分析&#xff08;整合直接比较和间接比较的结果&#xff0c;绘制相关图形&#xff09; 4. 绘制累积概率排序图 5. 三个假设的检验…