如何正确地从c ++ dll传递多个cv :: Mat到opencvsharp Mat c#? [英] How to pass multiple cv::Mat from c++ dll to opencvsharp Mat c# properly?

查看:167
本文介绍了如何正确地从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();
        }            
    }
}

这篇关于如何正确地从c ++ dll传递多个cv :: Mat到opencvsharp Mat c#?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆