如何正确地从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#.

我认为应该是这样的:

  1. 多个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 a cv::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();
        }            
    }
}