如何正确地将多个 cv::Mat 从 c++ dll 传递到 opencvsharp Mat c#?
How to pass multiple cv::Mat from c++ dll to opencvsharp Mat c# properly?
我正在做一个项目,该项目需要一个 dll 文件供另一个用 c# 编写的程序使用(我不是很熟悉 C++/C# 的用法)。对于完成我的工作的最后一步,我在将 "multiple" cv::Mat 从 dll 传递到 C# 时遇到问题。
我在 Internet 上找到了一些关于 C# 使用 OpenCvSharp 从 dll 接收 cv::Mat 的示例,它在我的代码中运行良好(它是简化):
//original.hpp
extern "C" LIB_API cv::Mat* inference(unsigned char* img_pointer, long data_len);
//original.cpp
LIB_API cv::Mat* inference(unsigned char* img_pointer, long data_len)
{
cv::Mat A;
..... // process that update A
return new cv::Mat(A);
}
//original.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern IntPtr inference(byte[] img, long data_len);
static void Main()
{
Intptr res = inference(X, Y);
Mat A1 = new Mat(res);
Cv2.ImShow("test1", A1);
Cv2.WaitKey(2000);
}
既然成功了,我打算使用相同的语法并通过函数传递参数,这样我就可以return多个cv::Mat,但是这段代码不起作用...
//rv1.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat* res);
//rv1.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat *res)
{
cv::Mat A;
..... // process that update A
res = new cv::Mat(A);
}
//rv1.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr res);
static void Main()
{
Intptr res;
inference(X, Y, out res);
Mat A1 = new Mat(res);
Cv2.ImShow("test1", A1);
Cv2.WaitKey(2000);
}
我以为是因为我给cv::Mat*赋值错误,所以没有得到正确的地址,于是修改了rv1.cpp,[=55=的部分]但是这段代码也不起作用...
// rv1_1.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat *res)
{
cv::Mat A;
..... // process that update A
res = &A;
}
(错误是System.AccessViolationException:试图读取或写入受保护的内存)
然后我想到了另一种方法,将cv::Mat*的向量与函数的参数一起传递,代码如下:
//rv2.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat ***data_1, long* len);
// rv2.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat ***data_1, long* len)
{
std::vector<cv::Mat*> vec_mat;
cv::Mat A;
cv::Mat B;
..... // process that update A, B
vec_mat.push_back(new cv::Mat(A));
vec_mat.push_back(new cv::Mat(B));
*len = vec_mat.size();
auto size = (*len) * sizeof(cv::Mat*);
*data_1 = static_cast<cv::Mat**>(CoTaskMemAlloc(size));
memcpy(*data_1, vec_mat.data(), size);
}
//rv2.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr[] data, out int len);
static void Main()
{
IntPtr[] sss1;
int itemsCount;
inference(image_byte_array, ms.Length, out sss1, out itemsCount);
for (int i = 0; i < itemsCount; i++) // index out of range (the length of ss1 is "1")
{
Mat A3 = new Mat(sss1[i]);
Cv2.ImShow("test3", A3);
Cv2.WaitKey(2000);
}
}
问题是,我预计 returned 向量应该有 2 个项目,但结果只有一个项目。
(当我循环遍历 IntPtr[ ] 时,它只得到 1 个项目,然后停止并出现类似 "index out of range" 的错误)
我知道我的代码中一定有一些语法错误,但我不知道它们在哪里以及如何更正它们...
(这似乎是一些 非常基本的语法问题与指针的使用...)
由于上面的方法还是可以得到vector的"first"项,我目前可以这样传"multiple" cv::Mat*:
(像这样写代码真的很愚蠢也不合适...)
//rv3.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*** data_1, cv::Mat ***data_2);
// rv3.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat ***data_1, cv::Mat ***data_2)
{
std::vector<cv::Mat*> vec_mat1;
std::vector<cv::Mat*> vec_mat2;
cv::Mat A;
cv::Mat B;
..... // process that update A, B
vec_mat1.push_back(new cv::Mat(A));
vec_mat2.push_back(new cv::Mat(B));
auto size = (*len) * sizeof(cv::Mat*);
*data_1 = static_cast<cv::Mat**>(CoTaskMemAlloc(size));
*data_2 = static_cast<cv::Mat**>(CoTaskMemAlloc(size));
memcpy(*data_1, vec_mat1.data(), size);
memcpy(*data_2, vec_mat2.data(), size);
}
//rv3.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr[] data_1, out IntPtr[] data_2);
static void Main()
{
IntPtr[] sss1, sss2;
int itemsCount;
inference(image_byte_array, ms.Length, out sss1, out sss2);
Mat A3 = new Mat(sss1[0]);
Cv2.ImShow("test3", A3);
Cv2.WaitKey(2000);
Mat A4 = new Mat(sss2[0]);
Cv2.ImShow("test4", A4);
Cv2.WaitKey(2000);
}
正如我上面所说,我不认为这是将多个 cv::Mat* 从 dll 传递到 C# 的正确方法。
在我看来应该是这样的:
用函数的参数
传递多个cv::Mat*
将多个cv::Mat*的向量传进去,函数的参数是
(不是只有一个cv::Mat*的多个向量)
但我真的不知道如何正确修改代码,所以非常感谢任何建议或帮助...
(提前感谢您看完我乱七八糟的问题描述!)
因为你已经 return 一个指向 cv::Mat
的指针,你也可以使它成为一个动态数组。
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size)
{
img_count = 10;
mat_type_size = sizeof(cv::Mat);
res = new cv::Mat[img_count]
for (int i = 0; i < img_count; i++)
{
// process each mat
cv::Mat& A = res[i];
}
}
注意
cv::Mat*& res
现在是对指针的引用。仅传递指针是行不通的,因为您只是将指针重新分配给新地址。
int& img_count
也是一个参考,因此您可以 return 您分配回 C# 的实际图像数量。
int& mat_type_size
只是说一个 cv::Mat
对象有多少字节。这是正确递增 C# IntPtr
以指向数组中的下一个图像所必需的。
在您的 C# 代码中,您应该能够像这样导入它(我对编组的了解有限):
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr images, ref int img_count, out int mat_type_size);
并像这样使用它:
static void Main()
{
int imgCount = 5;
inference(new byte[10], 10, out var imgPtrs, ref imgCount, out var matTypeSize);
List<Mat> images = new List<Mat>();
for (int i = 0; i < imgCount; i++)
images.Add(new Mat(IntPtr.Add(imgPtrs, i * matTypeSize)));
// ...
}
我已经测试了代码并且它有效。这就是我的使用方式:
例子
C++
// hpp
extern "C" __declspec(dllexport) void inference(unsigned char* img_pointer, long data_len, cv::Mat * &res, int& img_count, int& mat_type_size);
// cpp
void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size)
{
mat_type_size = sizeof(cv::Mat);
res = new cv::Mat[img_count];
for (int i = 0; i < img_count; i++)
{
// process each mat
cv::Mat& A = res[i];
A.create(100, 100, CV_8UC1);
cv::circle(A, {50, 50}, 10 * i, 255, -1);
}
}
C#
static class Program
{
[DllImport(@"Cpp.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr images, ref int img_count, out int mat_type_size);
static void Main(string[] args)
{
int imgCount = 5;
inference(new byte[10], 10, out var imgPtrs, ref imgCount, out var matTypeSize);
List<Mat> images = new List<Mat>();
for (int i = 0; i < imgCount; i++)
images.Add(new Mat(IntPtr.Add(imgPtrs, i * matTypeSize)));
foreach (var img in images)
{
Cv2.ImShow("Test", img);
Cv2.WaitKey();
}
}
}
您可以只使用一个函数在两种语言之间传递 Mat 对象。
C++ 函数
//for conversion from c++ to cs
//this variable must be delete after using
std::vector<uchar>* vec = new std::vector<uchar>;
void convertMat2CS(cv::Mat income_mat, uchar** ptr, int* length)
{
cv::imencode(".png", income_mat, *vec);
*ptr = &vec[0][0];
*length = static_cast<int>(vec->size());
}
C# 端
[DllImport(dll)]
public static extern void convertMat2CS(out IntPtr ptr, out int len);
void Main(){
convertMat2CS(out IntPtr ptr, out int length);
byte[] pngImageBytes = new byte[length];
Marshal.Copy(ptr, pngImageBytes, 0, length);
Mat mat = new Mat();
CvInvoke.Imdecode(pngImageBytes, LoadImageType.AnyColor, mat);
CvInvoke.Imshow("Test_" + showCount, mat);
CvInvoke.WaitKey(1);
}
我正在做一个项目,该项目需要一个 dll 文件供另一个用 c# 编写的程序使用(我不是很熟悉 C++/C# 的用法)。对于完成我的工作的最后一步,我在将 "multiple" cv::Mat 从 dll 传递到 C# 时遇到问题。
我在 Internet 上找到了一些关于 C# 使用 OpenCvSharp 从 dll 接收 cv::Mat 的示例,它在我的代码中运行良好(它是简化):
//original.hpp
extern "C" LIB_API cv::Mat* inference(unsigned char* img_pointer, long data_len);
//original.cpp
LIB_API cv::Mat* inference(unsigned char* img_pointer, long data_len)
{
cv::Mat A;
..... // process that update A
return new cv::Mat(A);
}
//original.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern IntPtr inference(byte[] img, long data_len);
static void Main()
{
Intptr res = inference(X, Y);
Mat A1 = new Mat(res);
Cv2.ImShow("test1", A1);
Cv2.WaitKey(2000);
}
既然成功了,我打算使用相同的语法并通过函数传递参数,这样我就可以return多个cv::Mat,但是这段代码不起作用...
//rv1.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat* res);
//rv1.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat *res)
{
cv::Mat A;
..... // process that update A
res = new cv::Mat(A);
}
//rv1.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr res);
static void Main()
{
Intptr res;
inference(X, Y, out res);
Mat A1 = new Mat(res);
Cv2.ImShow("test1", A1);
Cv2.WaitKey(2000);
}
我以为是因为我给cv::Mat*赋值错误,所以没有得到正确的地址,于是修改了rv1.cpp,[=55=的部分]但是这段代码也不起作用...
// rv1_1.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat *res)
{
cv::Mat A;
..... // process that update A
res = &A;
}
(错误是System.AccessViolationException:试图读取或写入受保护的内存)
然后我想到了另一种方法,将cv::Mat*的向量与函数的参数一起传递,代码如下:
//rv2.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat ***data_1, long* len);
// rv2.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat ***data_1, long* len)
{
std::vector<cv::Mat*> vec_mat;
cv::Mat A;
cv::Mat B;
..... // process that update A, B
vec_mat.push_back(new cv::Mat(A));
vec_mat.push_back(new cv::Mat(B));
*len = vec_mat.size();
auto size = (*len) * sizeof(cv::Mat*);
*data_1 = static_cast<cv::Mat**>(CoTaskMemAlloc(size));
memcpy(*data_1, vec_mat.data(), size);
}
//rv2.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr[] data, out int len);
static void Main()
{
IntPtr[] sss1;
int itemsCount;
inference(image_byte_array, ms.Length, out sss1, out itemsCount);
for (int i = 0; i < itemsCount; i++) // index out of range (the length of ss1 is "1")
{
Mat A3 = new Mat(sss1[i]);
Cv2.ImShow("test3", A3);
Cv2.WaitKey(2000);
}
}
问题是,我预计 returned 向量应该有 2 个项目,但结果只有一个项目。
(当我循环遍历 IntPtr[ ] 时,它只得到 1 个项目,然后停止并出现类似 "index out of range" 的错误)
我知道我的代码中一定有一些语法错误,但我不知道它们在哪里以及如何更正它们...
(这似乎是一些 非常基本的语法问题与指针的使用...)
由于上面的方法还是可以得到vector的"first"项,我目前可以这样传"multiple" cv::Mat*:
(像这样写代码真的很愚蠢也不合适...)
//rv3.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*** data_1, cv::Mat ***data_2);
// rv3.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat ***data_1, cv::Mat ***data_2)
{
std::vector<cv::Mat*> vec_mat1;
std::vector<cv::Mat*> vec_mat2;
cv::Mat A;
cv::Mat B;
..... // process that update A, B
vec_mat1.push_back(new cv::Mat(A));
vec_mat2.push_back(new cv::Mat(B));
auto size = (*len) * sizeof(cv::Mat*);
*data_1 = static_cast<cv::Mat**>(CoTaskMemAlloc(size));
*data_2 = static_cast<cv::Mat**>(CoTaskMemAlloc(size));
memcpy(*data_1, vec_mat1.data(), size);
memcpy(*data_2, vec_mat2.data(), size);
}
//rv3.cs
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr[] data_1, out IntPtr[] data_2);
static void Main()
{
IntPtr[] sss1, sss2;
int itemsCount;
inference(image_byte_array, ms.Length, out sss1, out sss2);
Mat A3 = new Mat(sss1[0]);
Cv2.ImShow("test3", A3);
Cv2.WaitKey(2000);
Mat A4 = new Mat(sss2[0]);
Cv2.ImShow("test4", A4);
Cv2.WaitKey(2000);
}
正如我上面所说,我不认为这是将多个 cv::Mat* 从 dll 传递到 C# 的正确方法。
在我看来应该是这样的:
用函数的参数
传递多个cv::Mat*
将多个cv::Mat*的向量传进去,函数的参数是
(不是只有一个cv::Mat*的多个向量)
但我真的不知道如何正确修改代码,所以非常感谢任何建议或帮助...
(提前感谢您看完我乱七八糟的问题描述!)
因为你已经 return 一个指向 cv::Mat
的指针,你也可以使它成为一个动态数组。
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size)
{
img_count = 10;
mat_type_size = sizeof(cv::Mat);
res = new cv::Mat[img_count]
for (int i = 0; i < img_count; i++)
{
// process each mat
cv::Mat& A = res[i];
}
}
注意
cv::Mat*& res
现在是对指针的引用。仅传递指针是行不通的,因为您只是将指针重新分配给新地址。int& img_count
也是一个参考,因此您可以 return 您分配回 C# 的实际图像数量。int& mat_type_size
只是说一个cv::Mat
对象有多少字节。这是正确递增 C#IntPtr
以指向数组中的下一个图像所必需的。
在您的 C# 代码中,您应该能够像这样导入它(我对编组的了解有限):
[DllImport(@"D:\Coco\Code\C_plus_plus\cpp_dll\x64\Release\cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr images, ref int img_count, out int mat_type_size);
并像这样使用它:
static void Main()
{
int imgCount = 5;
inference(new byte[10], 10, out var imgPtrs, ref imgCount, out var matTypeSize);
List<Mat> images = new List<Mat>();
for (int i = 0; i < imgCount; i++)
images.Add(new Mat(IntPtr.Add(imgPtrs, i * matTypeSize)));
// ...
}
我已经测试了代码并且它有效。这就是我的使用方式:
例子
C++
// hpp
extern "C" __declspec(dllexport) void inference(unsigned char* img_pointer, long data_len, cv::Mat * &res, int& img_count, int& mat_type_size);
// cpp
void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size)
{
mat_type_size = sizeof(cv::Mat);
res = new cv::Mat[img_count];
for (int i = 0; i < img_count; i++)
{
// process each mat
cv::Mat& A = res[i];
A.create(100, 100, CV_8UC1);
cv::circle(A, {50, 50}, 10 * i, 255, -1);
}
}
C#
static class Program
{
[DllImport(@"Cpp.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr images, ref int img_count, out int mat_type_size);
static void Main(string[] args)
{
int imgCount = 5;
inference(new byte[10], 10, out var imgPtrs, ref imgCount, out var matTypeSize);
List<Mat> images = new List<Mat>();
for (int i = 0; i < imgCount; i++)
images.Add(new Mat(IntPtr.Add(imgPtrs, i * matTypeSize)));
foreach (var img in images)
{
Cv2.ImShow("Test", img);
Cv2.WaitKey();
}
}
}
您可以只使用一个函数在两种语言之间传递 Mat 对象。
C++ 函数
//for conversion from c++ to cs
//this variable must be delete after using
std::vector<uchar>* vec = new std::vector<uchar>;
void convertMat2CS(cv::Mat income_mat, uchar** ptr, int* length)
{
cv::imencode(".png", income_mat, *vec);
*ptr = &vec[0][0];
*length = static_cast<int>(vec->size());
}
C# 端
[DllImport(dll)]
public static extern void convertMat2CS(out IntPtr ptr, out int len);
void Main(){
convertMat2CS(out IntPtr ptr, out int length);
byte[] pngImageBytes = new byte[length];
Marshal.Copy(ptr, pngImageBytes, 0, length);
Mat mat = new Mat();
CvInvoke.Imdecode(pngImageBytes, LoadImageType.AnyColor, mat);
CvInvoke.Imshow("Test_" + showCount, mat);
CvInvoke.WaitKey(1);
}