Как правильно передать несколько cv :: Mat из c ++ dll в opencvsharp Mat c #? - PullRequest
0 голосов
/ 07 октября 2019

Я работаю над проектом, который требует использования файла dll для другой программы, написанной на c # (я не очень знаком с использованием C ++ / C #). Для самого последнего шага, чтобы завершить мою работу, у меня есть проблема с передачей «множественного» cv :: Mat из dll в C #.

В Интернете я нашел несколько примеров о C #, использующих OpenCvSharp для полученияcv :: Mat из dll, и в моем коде это работает хорошо (это упрощено):

//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, как мне нужно, , но этот код не работает ...

//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: попытка чтения или записи защищенной памяти )


Затем я нашел другой способ, передает вектор 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. pass множественное cv :: Mat * с параметрами функции

  2. pass авектор кратного cv :: Mat * в нем с параметрами функции

    ( Не несколько векторов только одного cv :: Mat * в каждом )

Но я действительно не знаю, как правильно пересмотреть код, поэтому любые советы или помощь действительно приветствуются ...

(Заранее благодарен за то, что дочитал моё грязное описание вопроса! )

1 Ответ

1 голос
/ 07 октября 2019

Поскольку вы уже возвращаете указатель на 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 объекта. Это необходимо для правильного увеличения значения 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();
        }            
    }
}
...