我正在开发一个项目,该项目需要一个 dll 文件供另一个用 c# 编写的程序使用(我不太熟悉 C++/C# 的用法)。对于完成我的工作的最后一步,我在将“多个”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);
}
由于它成功运行,我计划使用相同的语法并使用函数通过参数传递结果,以便我可以根据需要返回多个 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的部分,但是这段代码并没有要么工作...
// 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: Attempted to read or write protected memory)
然后我想出了另一种方法,传递一个带有函数参数的cv::Mat* vector ,代码如下:
//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);
}
}
问题是,我预计返回的 vector 中应该有 2 项,但结果只有一项。
(当我循环遍历 IntPtr[ ] 时,它只得到 1 个项目,然后因“索引超出范围”之类的错误而停止)
我知道我的代码中一定有一些语法错误,但我不知道它们在哪里以及如何更正它们...
(这似乎是一些使用指针的非常基本的语法问题...)
由于上面的方法仍然可以获得 vector 的“第一个”项,我目前可以通过这种方式传递“多个”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*的 vector
(不是每个只有一个 cv::Mat* 的多个 vector )
但我真的不知道如何正确修改代码,所以非常感谢任何建议或帮助......
(提前感谢您阅读我凌乱的问题描述!)
最佳答案
因为您已经返回了一个指向 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
也是一个引用,因此您可以将分配给 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();
}
}
}
关于c# - 如何正确地将多个 cv::Mat 从 c++ dll 传递到 opencvsharp Mat c#?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58271603/