Передача изображения из Matlab в OpenCV IplImage - PullRequest
5 голосов
/ 13 октября 2010

У меня есть изображение в Matlab:

img = imopen('image.jpg')

, которое возвращает высоту массива uint8 x ширину x каналов (3 канала: RGB).

Теперь яЯ хочу использовать openCV для каких-то манипуляций с ним, поэтому я записываю MEX-файл, который берет изображение в качестве параметра и создает из него IplImage:

#include "mex.h"
#include "cv.h"

void mexFunction(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) {
    char *matlabImage = (char *)mxGetData(prhs[0]);
    const mwSize *dim = mxGetDimensions(prhs[0]);

    CvSize size;
    size.height = dim[0];
    size.width = dim[1];

    IplImage *iplImage = cvCreateImageHeader(size, IPL_DEPTH_8U, dim[2]);
    iplImage->imageData = matlabImage;
    iplImage->imageDataOrigin = iplImage->imageData;

    /* Show the openCV image */
    cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
    cvShowImage("mainWin", iplImage);
}

Этот результат выглядит совершенно неверным, поскольку openCV используетдругие соглашения, чем matlab для хранения изображения (например, они чередуют цветовые каналы).

Может кто-нибудь объяснить, в чем различия между соглашениями, и дать некоторые советы о том, как правильно отображать изображение?

Ответы [ 4 ]

12 голосов
/ 13 октября 2010

Проведя целый день с забавными преобразованиями форматов изображений </sarcasm> Теперь я могу ответить на свой вопрос.

Matlab сохраняет изображения в виде 3-х мерных массивов: высота × ширина × цветOpenCV сохраняет изображения в виде двухмерных массивов: (цвет × ширина) × высота

Кроме того, для лучшей производительности OpenCV дополняет изображения нулями, чтобы строки всегда выравнивались по 32-битным блокам.

IВы сделали преобразование в Matlab:

function [cv_img, dim, depth, width_step] = convert_to_cv(img)

% Exchange rows and columns (handles 3D cases as well)
img2 = permute( img(:,end:-1:1,:), [2 1 3] );

dim = [size(img2,1), size(img2,2)];

% Convert double precision to single precision if necessary
if( isa(img2, 'double') )
    img2 = single(img2);
end

% Determine image depth
if( ndims(img2) == 3 && size(img2,3) == 3 )
    depth = 3;
else
    depth = 1;
end

% Handle color images
if(depth == 3 )
    % Switch from RGB to BGR
    img2(:,:,[3 2 1]) = img2;

    % Interleave the colors
    img2 = reshape( permute(img2, [3 1 2]), [size(img2,1)*size(img2,3) size(img2,2)] );
end

% Pad the image
width_step = size(img2,1) + mod( size(img2,1), 4 );
img3 = uint8(zeros(width_step, size(img2,2)));
img3(1:size(img2,1), 1:size(img2,2)) = img2;

cv_img = img3;

% Output to openCV
cv_display(cv_img, dim, depth, width_step);

Код для преобразования этого в IplImage находится в файле MEX:

#include "mex.h"
#include "cv.h"
#include "highgui.h"

#define IN_IMAGE prhs[0]
#define IN_DIMENSIONS prhs[1]
#define IN_DEPTH prhs[2]
#define IN_WIDTH_STEP prhs[3]

void mexFunction(int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs) {
    bool intInput = true;

    if(nrhs != 4)
        mexErrMsgTxt("Usage: cv_disp(image, dimensions, depth, width_step)");

    if( mxIsUint8(IN_IMAGE) )
        intInput = true;
    else if( mxIsSingle(IN_IMAGE) )
        intInput = false;
    else 
        mexErrMsgTxt("Input should be a matrix of uint8 or single precision floats.");

    if( mxGetNumberOfElements(IN_DIMENSIONS) != 2 )
        mexErrMsgTxt("Dimension vector should contain two elements: [width, height].");

    char *matlabImage = (char *)mxGetData(IN_IMAGE);

    double *imgSize = mxGetPr(IN_DIMENSIONS);
    size_t width = (size_t) imgSize[0];
    size_t height = (size_t) imgSize[1];

    size_t depth = (size_t) *mxGetPr(IN_DEPTH);
    size_t widthStep = (size_t) *mxGetPr(IN_WIDTH_STEP) * (intInput ? sizeof(unsigned char):sizeof(float));

    CvSize size;
    size.height = height;
    size.width = width;

    IplImage *iplImage = cvCreateImageHeader(size, intInput ? IPL_DEPTH_8U:IPL_DEPTH_32F, depth);
    iplImage->imageData = matlabImage;
    iplImage->widthStep = widthStep;
    iplImage->imageDataOrigin = iplImage->imageData;

    /* Show the openCV image */
    cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
    cvShowImage("mainWin", iplImage);
}
1 голос
/ 10 декабря 2014

На основании ответа и Как матрица изображений хранится в памяти на OpenCV , мы можем сделать это только с помощью операции Opencv Mat!

C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
C++: void merge(const Mat* mv, size_t count, OutputArray dst)

Тогда код mex C / C ++:

#include "mex.h"
#include <opencv2/opencv.hpp>
#define uint8 unsigned char

 void mexFunction(int nlhs, mxArray *out[], int nrhs, const mxArray *input[])
{

    // assume the type of image is  uint8
    if(!mxIsClass(input[0], "uint8"))
    {
        mexErrMsgTxt("Only image arrays of the UINT8 class are allowed.");
        return;
    }

    uint8* rgb = (uint8*) mxGetPr(input[0]);
    int* dims = (int*) mxGetDimensions(input[0]);

    int height = dims[0];
    int width = dims[1];
    int imsize = height * width;


    cv::Mat imR(1, imsize, cv::DataType<uint8>::type, rgb);
    cv::Mat imG(1, imsize, cv::DataType<uint8>::type, rgb+imsize);
    cv::Mat imB(1, imsize, cv::DataType<uint8>::type, rgb+imsize + imsize);

    // opencv is BGR and matlab is column-major order
    cv::Mat imA[3];
    imA[2] = imR.reshape(1,width).t();
    imA[1] = imG.reshape(1,width).t();
    imA[0] = imB.reshape(1,width).t();

    // done! imf is what we want!
    cv::Mat imf; 
    merge(imA,3,imf);

}

1 голос
/ 13 января 2012

Попробуйте использовать библиотеку, разработанную Kota Yamaguchi: http://github.com/kyamagu/mexopencv Он определяет класс с именем MxArray, который может выполнять все типы преобразований из переменных MATLAB mxArray в объекты OpenCV (и из OpenCV в MATLAB).Например, эта библиотека может конвертировать между типами данных mxArray и cv :: Mat.Кстати, IplImage больше не актуален, если вы используете C ++ API OpenCV, лучше вместо этого использовать cv :: Mat.

Примечание: при использовании библиотеки обязательно скомпилируйте свою функцию mex с файлом MxArray.cpp из библиотеки;Вы можете сделать это в командной строке MATLAB с помощью:

    mex yourmexfile.cpp MxArray.cpp
1 голос
/ 31 мая 2011

Вы можете оптимизировать свою программу с помощью mxGetDimensions и mxGetNumberOfDimensions, чтобы получить размер изображения и использовать mxGetClassID для более точного определения глубины.

Я хотел сделать то же самое, но я думаю, что было бы лучше сделать это, используя matlab dll и calllib.Я бы не делал преобразование изображения в формате opencv, а не в matlab, потому что это было бы медленно.Это одна из самых больших проблем с Matlab OpenCV.Я думаю, что opencv2.2 имеет несколько хороших решений для этой проблемы.Похоже, что есть некоторые решения, которые были сделаны сообществом opencv для октавы, но я до сих пор не понимаю их.Они как-то используют функциональность c ++ opencv.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...