2016-08-16 5 views
1

Я загружаю C++-библиотеку из моего кода на C# динамически. Я хочу найти маленькое изображение внутри большого, преобразование большого изображения в byte[] и небольшое изображение, читаемое с физического пути.
Когда я звоню imdecode, тогда large_img всегда возвращает 0cols и rows.MatchTemplate изображение с изображением, преобразованным в указатель BYTE в OpenCV

C#

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
private delegate ImageParams GetImageParams(IntPtr dataPtr, int size, string path); 

// ... 

byte[] largeImgByteArr = this.BitmapToByteArray(bmp); 
IntPtr dataPtr = Marshal.AllocHGlobal(largeImgByteArr.Length); 
Marshal.Copy(dataPtr, largeImgByteArr, 0, largeImgByteArr.Length); 

C++

ImageParams GetImageParams(BYTE* largeImgBuf, int bufLength, const char* smallImgPath) 
{ 
    Mat large_img_data(bufLength, 1, CV_32FC1, largeImgBuf); 

    Mat large_img = imdecode(large_img_data, IMREAD_COLOR); 
    Mat small_img = imread(smallImgPath, IMREAD_COLOR); 

    int result_cols = large_img.cols - small_img.cols + 1; 
    int result_rows = large_img.rows - small_img.rows + 1; 

    Mat result; 
    result.create(result_cols, result_rows, CV_32FC1); 

    matchTemplate(large_img, small_img, result, CV_TM_SQDIFF_NORMED); 
    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat()); 
} 

Что я делаю неправильно здесь?
Примечание. Я проверил, что путь изображения правильный, а массив байтов не пуст.

Edit 1

Я изменил код немного, предоставляя большое изображение width и height, также избавились от imdecode и что-то изменилось, как в this post.

ImageParams GetImageParams(BYTE* largeImgBuf, int height, int width, int bufLength, const char* smallImgPath) 
{ 
    // Mat large_img = imdecode(large_img_data, IMREAD_COLOR); 
    Mat large_img = Mat(height, width, CV_8UC3, largeImgBuf); 
    Mat small_img = imread(templPath, 1); 

    /// ... 
} 

Теперь он возвращает строки и столбцы, но когда вызов matchTemplate метод он бросает исключение:

enter image description here

+0

Зачем вам «imdecode» здесь? 'largeImgBuf', похоже, содержит правильные данные. Также 'CV_32FC1' выглядит неправильно. Просьба попробовать что-то вроде 'Mat large_img (bufLength, 1, CV_8UC3, largeImgBuf);' – Miki

+0

@Miki Спасибо за ответ, хотя то же самое происходит с 'CV_8UC3'. Я скоро обновляю свой вопрос с дополнительной информацией. – Edgar

ответ

0

Согласно документации

Функция считывает изображение из указанного буфер в памяти. Если буфер слишком короткий или содержит недопустимые данные, возвращается пустая матрица/изображение.

Попробуйте проверить код ошибки после imdecode части

#include <errno.h> 

cout << errno; 
+0

Спасибо за ваш ответ, см. Редактировать 1. – Edgar

2

Помните, что Растровая структура в C# использует заполнение данных (значение шага как кратность 4) whrereas OpenCV не может. Попробуйте создать объект Mat непосредственно из массива байтов, но скорректируйте значение шага (шаг), поскольку оно просто присваивает данные без какого-либо права собственности или перераспределения.

EDIT

Вот пример того, как создать объект OpenCV Mat из Bitmap. Данные из растрового изображения не копируются, а только назначаются. Тип макета PixelFormat и OpenCV должен иметь соответствующий размер одного элемента в байтах.

cv::Mat ImageBridge::cvimage(System::Drawing::Bitmap^ bitmap){ 
    if(bitmap != nullptr){ 
     switch(bitmap->PixelFormat){ 
     case System::Drawing::Imaging::PixelFormat::Format24bppRgb: 
      return bmp2mat(bitmap, CV_8UC3); 
     case System::Drawing::Imaging::PixelFormat::Format8bppIndexed: 
      return bmp2mat(bitmap, CV_8U); 
     default: return cv::Mat(); 
     } 
    } 
    else 
     return cv::Mat(); 
} 


cv::Mat ImageBridge::bmp2mat(System::Drawing::Bitmap^ bitmap, int image_type){ 
    auto bitmap_data = bitmap->LockBits(
     System::Drawing::Rectangle(0, 0, bitmap->Width, bitmap->Height), 
     System::Drawing::Imaging::ImageLockMode::ReadWrite, 
     bitmap->PixelFormat); 

    char* bmpptr = (char*)bitmap_data->Scan0.ToPointer(); 

    cv::Mat image(
     cv::Size(bitmap->Width, bitmap->Height), 
     image_type, 
     bmpptr, 
     bitmap_data->Stride); 

    bitmap->UnlockBits(bitmap_data); 
    return image; 
} 

РЕДАКТИРОВАТЬ 2

Преобразование в обратном направлении - в это время данные изображения Мат копируется как Растровые выделяет его собственной памяти.

System::Drawing::Bitmap^ ImageBridge::bitmap(cv::Mat& image){ 
    if(!image.empty() && image.type() == CV_8UC3) 
     return mat2bmp(image, System::Drawing::Imaging::PixelFormat::Format24bppRgb); 
    else if(!image.empty() && image.type() == CV_8U) 
     return mat2bmp(image, System::Drawing::Imaging::PixelFormat::Format8bppIndexed); 
    else 
     return nullptr; 
} 

System::Drawing::Bitmap^ ImageBridge::mat2bmp(cv::Mat& image, System::Drawing::Imaging::PixelFormat pixel_format){ 
    if(image.empty()) 
     return nullptr; 

    System::Drawing::Bitmap^ bitmap = gcnew System::Drawing::Bitmap(image.cols, image.rows, pixel_format); 

    auto bitmap_data = bitmap->LockBits(
     System::Drawing::Rectangle(0, 0, bitmap->Width, bitmap->Height), 
     System::Drawing::Imaging::ImageLockMode::ReadWrite, 
     pixel_format); 

    char* bmpptr = (char*)bitmap_data->Scan0.ToPointer(); 
    int line_length = (int)image.step; 
    int bmp_stride = bitmap_data->Stride; 

    assert(!image.isSubmatrix()); 
    assert(bmp_stride >= 0); 
    for(int l = 0; l < image.rows; l++){ 
     char* cvptr = (char*)image.ptr(l); 
     int bmp_line_index = l * bmp_stride; 

     for(int i = 0; i < line_length; ++i) 
      bmpptr[bmp_line_index + i] = cvptr[i]; 
    } 

    bitmap->UnlockBits(bitmap_data); 

    return bitmap; 
} 

Или если у вас есть изображение Мата с шагом, так как кратность 4, вы можете использовать версию без копирования.

System::Drawing::Bitmap^ ImageBridge::bitmap2(cv::Mat& image){ 
    System::Drawing::Bitmap^ bitmap; 
    assert(!image.isSubmatrix()); 
    if(!image.empty() && image.type() == CV_8UC3){ 
     bitmap = gcnew System::Drawing::Bitmap(
      image.cols, image.rows, 
      3 * image.cols, 
      System::Drawing::Imaging::PixelFormat::Format24bppRgb, 
      System::IntPtr(image.data)); 
    } 
    else if(!image.empty() && image.type() == CV_8U){ 
     bitmap = gcnew System::Drawing::Bitmap(
      image.cols, image.rows, 
      image.cols, 
      System::Drawing::Imaging::PixelFormat::Format8bppIndexed, 
      System::IntPtr(image.data)); 
    } 
    return bitmap; 
} 
+0

Я проверю это, как только смогу. Может быть, вы могли бы предоставить пример кода или какую-нибудь полезную ссылку, как это сделать? Это очень помогло бы мне. – Edgar

+0

Я отредактировал ответ с образцом кода. См. Выше. – willy

+0

Спасибо. Последнее, как мне преобразовать 'BYTE *' в 'System :: Drawing :: Bitmap'? – Edgar