如何正确地从c ++ dll传递多个cv :: Mat到opencvsharp Mat c#?
我正在开发一个项目,该项目需要dll文件才能使用c#编写的另一个程序使用(我对C ++/C#的用法不是很熟悉).对于完成我的工作的最后一步,我在将多个" cv :: Mat从dll传递到C#时遇到了问题.
I'm working on a project which required a dll file for another program written in c# to use (I'm not very familiar with the usage of C++/C#). For the very last step to complete my work, I have a problem with passing "multiple" cv::Mat from dll to C#.
我在Internet上找到了一些有关C#的示例,这些示例使用OpenCvSharp从dll接收cv :: Mat,并且在我的代码中,它工作得很好(简化):
I've found some examples on the Internet about C# using OpenCvSharp to receive a cv::Mat from dll, and it worked well like this in my code (it's simplified):
//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);
}
由于成功运行,我计划使用相同的语法并将结果通过带有函数的参数传递,以便我可以根据需要返回多个cv :: Mat,但是此代码不起作用.
Since it worked successfully, I planned to use the same syntax and pass the result through parameters with function, so that I can return multiple cv::Mat as I need, but this code doesn't work...
//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的部分,,但是这段代码没有要么工作,要么...
I thought it was because I did the wrong assignment to the cv::Mat*, so it didn't get the correct address, then I revise the part of rv1.cpp, but this code doesn't work either...
// 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 *向量,其代码如下:
Then I came up with another way, passing a vector of cv::Mat* with the function's parameters, and the code is like:
//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);
}
}
问题是,我希望返回的向量中应该有2个项目,但事实证明只有一个项目.
The thing is, I expected the returned vector should have 2 items in it, but it turned out to have only one item.
(当我循环遍历IntPtr []时,它只会得到1个项目,然后以索引超出范围"之类的错误停止)
(When I loop through the IntPtr[ ], it only get 1 item, and then stop with the error like "index out of range")
我知道我的代码中必须存在语法错误,但是我不知道它们在哪里以及如何纠正它们...
I know there must be some syntax error in my code, but I have no idea where are they and how to correct them...
(而且,使用指针似乎是一些非常基本的语法问题... )
(And it seems to be some very basic syntax problems with the usage of pointer...)
由于上述方法仍然可以获取向量的第一"项,因此我目前可以通过这种方式传递多个" cv :: Mat *:
Since the method above can still get the "first" item of the vector, I currently can pass "multiple" cv::Mat* in this way:
(这真的很愚蠢,不适合这样的代码...)
(Which is really stupid and improper to code like this...)
//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#的正确方法.
As I said above, I don't think this is a proper way to pass multiple cv::Mat* from dll to C#.
我认为应该是这样的:
-
向多个cv :: Mat * 传递函数的参数
向其中传递带有功能参数的包含多个cv :: Mat *的向量
pass a vector of multiple cv::Mat* in it with the parameters of the function
(在每个向量中不存在多个仅一个cv :: Mat *的向量)
但是我真的不知道如何正确地修改代码,因此任何建议或帮助都非常感谢...
But I really have no idea how to revise the code correctly, so any advise or help is really appreciated...
(感谢您阅读完我凌乱的问题描述!)
(Thanks in advance for finish reading my messy question description!)
由于您已经返回了指向cv::Mat
的指针,因此也可以使其成为动态数组.
Since you already return a pointer to a cv::Mat
you can also make it a dynamic array.
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
也是参考,因此您可以将分配给C#的实际图像数返回. -
int& mat_type_size
只是说出cv::Mat
对象有多少字节.为了正确地递增C#IntPtr
指向数组中的下一个图像,这是必需的.
-
cv::Mat*& res
is now of a reference to a pointer. Just passing a pointer doesn't work since you just reassign the pointer to a new address. -
int& img_count
is also a reference so you can return the actual number of images that you have allocated back to C#. -
int& mat_type_size
simply says how many bytes acv::Mat
object is. This is necessary to correctly increment the C#IntPtr
to point to the next image in the array.
在您的C#代码中,您应该能够像这样导入它(我对编组的知识是有限的):
In your C# code you should be able to import it like this (my knowledge about marshalling is limited):
[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)));
// ...
}
我已经测试了代码,并且可以正常工作.这就是我的使用方式:
I've tested the code and it works. This is how I am using it:
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();
}
}
}