![](/img/trans.png)
[英]how to convert Mat.at<cv::Vec3b>() from C++ opencv to Mat for C# opencvsharp?
[英]How to pass multiple cv::Mat from c++ dll to opencvsharp Mat c# properly?
我正在开发一个项目,该项目需要 dll 文件供另一个用 c# 编写的程序使用(我不太熟悉 C++/C# 的用法)。 对于完成我的工作的最后一步,我在将“多个”cv::Mat 从 dll 传递到 C# 时遇到问题。
我在互联网上找到了一些关于 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);
}
由于它工作成功,我计划使用相同的语法并通过参数传递结果 function,这样我就可以根据需要返回多个 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* 的向量与函数的参数一起传递,代码如下:
//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 个项目,但结果只有一个项目。
(当我遍历 IntPtr[ ] 时,它只得到 1 个项目,然后停止并出现“索引超出范围”之类的错误)
我知道我的代码中一定有一些语法错误,但我不知道它们在哪里以及如何纠正它们......
(而且使用指针似乎是一些非常基本的语法问题...... )
由于上面的方法仍然可以得到向量的“第一个”项,我目前可以通过这种方式传递“多个” 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# 的正确方法。
在我看来,它应该是这样的:
使用 function 的参数传递多个 cv::Mat*
使用 function 的参数在其中传递多个 cv::Mat*的向量
(不是每个只有一个 cv::Mat* 的多个向量)
但我真的不知道如何正确修改代码,所以任何建议或帮助都非常感谢......
(提前感谢您阅读完我凌乱的问题描述!)
由于您已经返回指向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
object 有多少字节。 这是正确递增 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();
}
}
}
您可以只使用一个 function 在两种语言之间传递 Mat object。
C++ Function
//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);
}
how can I pass an image cv::Mat OpenCV of this function in the form of a mat through the C++ DLL to C# and receive it in the form of a PictureBox in C# by opencvsharp? 我想将 Mat object 从 C++ opencv 转换为 ZD7EFA19FBE7D3972FD5ADB60242Idon't3D7 转换为如何打开。
C++/opencv--------Mat------------>DLL------->C#/opencvsharp------------>Mat.
起初,我为 C++ 写了一个 DLL 如下:
//C++ DLL -----非托管
c++:
extern "C" __declspec(dllexport) cv::Mat draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects)
{ static const char* class_names[] = { “人”、“自行车”、“汽车”、“摩托车”、“飞机”、“公共汽车”、“火车”、“卡车”、“船”、“红绿灯” , “消防栓”, “停车标志”, “停车计时器”, “长凳”, “鸟”, “猫”, “狗”, “马”, “羊”, “牛”, “大象”, “熊” ”、“斑马”、“长颈鹿”、“背包”、“雨伞”、“手提包”、“领带”、“手提箱”、“飞盘”、“滑雪板”、“滑雪板”、“运动球”、“风筝” , “棒球棒”, “棒球手套”, “滑板”, “冲浪板”, “网球拍”, “瓶子”, “酒杯”, “杯子”, “叉子”, “刀”, “勺子”, “碗”、“香蕉”、“苹果”、“三明治”、“橙子”、“西兰花”、“胡萝卜”、“热狗”、“披萨”、“甜甜圈”、“蛋糕”、“椅子”、“沙发” ”、“盆栽”、“床”、“餐桌”、“马桶”、“电视”、“笔记本电脑”、“鼠标”、“遥控器”、“键盘”、“手机”、“微波炉”、“烤箱”、“烤面包机”、“水槽”、“冰箱”、“书”、“时钟”、“花瓶”、“剪刀”、“泰迪熊”、“吹风机”、“牙刷”};
static const unsigned char colors[81][3] = {
{56, 0, 255},
{226, 255, 0},
{0, 94, 255},
{0, 37, 255},
{0, 255, 94},
{255, 226, 0},
{0, 18, 255},
{255, 151, 0},
{170, 0, 255},
{0, 255, 56},
{255, 0, 75},
{0, 75, 255},
{0, 255, 169},
{255, 0, 207},
{75, 255, 0},
{207, 0, 255},
{37, 0, 255},
{0, 207, 255},
{94, 0, 255},
{0, 255, 113},
{255, 18, 0},
{255, 0, 56},
{18, 0, 255},
{0, 255, 226},
{170, 255, 0},
{255, 0, 245},
{151, 255, 0},
{132, 255, 0},
{75, 0, 255},
{151, 0, 255},
{0, 151, 255},
{132, 0, 255},
{0, 255, 245},
{255, 132, 0},
{226, 0, 255},
{255, 37, 0},
{207, 255, 0},
{0, 255, 207},
{94, 255, 0},
{0, 226, 255},
{56, 255, 0},
{255, 94, 0},
{255, 113, 0},
{0, 132, 255},
{255, 0, 132},
{255, 170, 0},
{255, 0, 188},
{113, 255, 0},
{245, 0, 255},
{113, 0, 255},
{255, 188, 0},
{0, 113, 255},
{255, 0, 0},
{0, 56, 255},
{255, 0, 113},
{0, 255, 188},
{255, 0, 94},
{255, 0, 18},
{18, 255, 0},
{0, 255, 132},
{0, 188, 255},
{0, 245, 255},
{0, 169, 255},
{37, 255, 0},
{255, 0, 151},
{188, 0, 255},
{0, 255, 37},
{0, 255, 0},
{255, 0, 170},
{255, 0, 37},
{255, 75, 0},
{0, 0, 255},
{255, 207, 0},
{255, 0, 226},
{255, 245, 0},
{188, 255, 0},
{0, 255, 18},
{0, 255, 75},
{0, 255, 151},
{255, 56, 0},
{245, 255, 0}
};
int color_index = 0;
cv::Mat image = bgr.clone();
for (int i = 0; i < objects.size(); i++) {
const Object& obj = objects[i];
const unsigned char* color = colors[color_index % 80];
color_index++;
cv::Scalar cc(color[0], color[1], color[2]);
fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);
for(int y = 0; y < image.rows; y++){
uchar* image_ptr = image.ptr(y);
const float* mask_ptr = obj.cv_mask.ptr<float>(y);
for(int x = 0; x < image.cols; x++){
if(mask_ptr[x] >= 0.5)
{
image_ptr[0] = cv::saturate_cast<uchar>(image_ptr[0] * 0.5 + color[2] * 0.5);
image_ptr[1] = cv::saturate_cast<uchar>(image_ptr[1] * 0.5 + color[1] * 0.5);
image_ptr[2] = cv::saturate_cast<uchar>(image_ptr[2] * 0.5 + color[0] * 0.5);
}
image_ptr += 3;
}
}
cv::rectangle(image, obj.rect, cc, 2);
char text[256];
sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
int baseLine = 0;
cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
int x = obj.rect.x;
int y = obj.rect.y - label_size.height - baseLine;
if (y < 0)
y = 0;
if (x + label_size.width > image.cols)
x = image.cols - label_size.width;
cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
cc, -1);
cv::putText(image, text, cv::Point(x, y + label_size.height),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 255));
}
return image;
}
c#:
[DllImport(path_to_my_dll, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern opencvsharp.Mat draw_objects(opencvsharp.Mat bgr, array[] objects)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.