简体   繁体   English

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

[英]How to pass multiple cv::Mat from c++ dll to opencvsharp Mat c# properly?

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#).我正在开发一个项目,该项目需要 dll 文件供另一个用 c# 编写的程序使用(我不太熟悉 C++/C# 的用法)。 For the very last step to complete my work, I have a problem with passing "multiple" cv::Mat from dll to C#.对于完成我的工作的最后一步,我在将“多个”cv::Mat 从 dll 传递到 C# 时遇到问题。

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):我在互联网上找到了一些关于 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);
}

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...由于它工作成功,我计划使用相同的语法并通过参数传递结果 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);
}

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...我以为是因为我给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;
}

(The error is System.AccessViolationException: Attempted to read or write protected memory ) (错误是System.AccessViolationException: Attempted to read or write protected memory


Then I came up with another way, passing a vector of cv::Mat* with the function's parameters , and the code is like:然后我想出了另一种方法,将 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);
    }
}

The thing is, I expected the returned vector should have 2 items in it, but it turned out to have only one item.问题是,我预计返回的向量中应该有 2 个项目,但结果只有一个项目。

(When I loop through the IntPtr[ ], it only get 1 item, and then stop with the error like "index out of range") (当我遍历 IntPtr[ ] 时,它只得到 1 个项目,然后停止并出现“索引超出范围”之类的错误)

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... ) (而且使用指针似乎是一些非常基本的语法问题......


Since the method above can still get the "first" item of the vector, I currently can pass "multiple" cv::Mat* in this way:由于上面的方法仍然可以得到向量的“第一个”项,我目前可以通过这种方式传递“多个” cv::Mat*:

(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);
}

As I said above, I don't think this is a proper way to pass multiple cv::Mat* from dll to C#.正如我上面所说,我认为这不是将多个 cv::Mat* 从 dll 传递到 C# 的正确方法。

In my opinion, it should be like:在我看来,它应该是这样的:

  1. pass multiple cv::Mat* with the parameters of the function使用 function 的参数传递多个 cv::Mat*

  2. pass a vector of multiple cv::Mat* in it with the parameters of the function使用 function 的参数在其中传递多个 cv::Mat*的向量

    ( Not multiple vectors of only one cv::Mat* in each ) 不是每个只有一个 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!) (提前感谢您阅读完我凌乱的问题描述!)

Since you already return a pointer to a cv::Mat you can also make it a dynamic array.由于您已经返回指向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];
    }
}

Note that注意

  • cv::Mat*& res is now of a reference to a pointer. cv::Mat*& res现在是对指针的引用。 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& img_count也是一个参考,因此您可以将已分配的实际图像数量返回给 C#。
  • int& mat_type_size simply says how many bytes a cv::Mat object is. int& mat_type_size简单地说cv::Mat object 有多少字节。 This is necessary to correctly increment the C# IntPtr to point to the next image in the array.这是正确递增 C# IntPtr以指向数组中的下一个图像所必需的。

In your C# code you should be able to import it like this (my knowledge about marshalling is limited):在您的 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);

and use it like this:并像这样使用它:

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:这就是我使用它的方式:

Example例子

C++ 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# 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();
        }            
    }
}

You can use just one function for passing Mat object between two languages.您可以只使用一个 function 在两种语言之间传递 Mat object。

C++ Function 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# Side 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? 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? I want to Convert Mat object from C++ opencv to C# opencvsharp.but I don't how to do.我想将 Mat object 从 C++ opencv 转换为 ZD7EFA19FBE7D3972FD5ADB60242Idon't3D7 转换为如何打开。

C++/opencv-------Mat------------>DLL------->C#/opencvsharp---------->Mat. C++/opencv--------Mat------------>DLL------->C#/opencvsharp------------>Mat.

At first,I Wrote one DLL for C++ as following:起初,我为 C++ 写了一个 DLL 如下:

//C++ DLL -----unmanaged //C++ DLL -----非托管

c++: c++:

 extern "C" __declspec(dllexport) cv::Mat draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects)

{ static const char* class_names[] = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" }; { 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#: 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