繁体   English   中英

如何正确地将 c++ dll 中的多个 cv::Mat 传递给 opencvsharp Mat c#?

[英]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# 的正确方法。

在我看来,它应该是这样的:

  1. 使用 function 的参数传递多个 cv::Mat*

  2. 使用 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM