从Matlab传输图像到OpenCV IplImage

5

我在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不同的约定来存储图像(例如,它们交错颜色通道)。

有人能解释一下这些约定的差异,并提供一些指针以正确显示图像吗?


最好的方法可能是将文件名传递给MEX,让OpenCV负责从文件中加载图像。 - Paul R
1
http://mirone.googlecode.com/svn/tags/1.3.0/mex/cvcolor_mex.c - karlphillip
是的,让OpenCV负责加载图像会更容易。然而,我想要实现的是创建一个matlab MEX文件,使用这个库在GPU上进行SURF关键点检测: http://www.mis.tu-darmstadt.de/surf 该库需要一个IplImage作为输入。使用场景将是有人在Matlab中进行图像操作,然后通过SURF库运行结果,并在Matlab中愉快地使用结果。 - Marijn van Vliet
4个回答

12
在做了有趣的图像格式转换()一整天后,我现在可以回答自己的问题了。
Matlab将图像存储为三维数组:高度×宽度×颜色 OpenCV将图像存储为二维数组:(颜色×宽度)×高度
此外,为了获得最佳性能,OpenCV使用零填充对图像进行补齐,以使行始终对齐于32位块。
我已经在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);
}

如果这是你问题的答案,你应该将其标记为解决方案! - karlphillip

1

尝试使用由Kota Yamaguchi开发的库: http://github.com/kyamagu/mexopencv 它定义了一个名为'MxArray'的类,可以执行从MATLAB mxArray变量到OpenCV对象(以及从OpenCV到MATLAB)的所有类型转换。例如,此库可以在mxArray和cv::Mat数据类型之间进行转换。顺便说一句,如果您使用OpenCV的C++ API,则IplImage不再相关,最好使用cv::Mat。

注意:如果使用该库,请确保使用库中的MxArray.cpp文件编译您的mex函数;您可以在MATLAB命令行中使用以下命令完成:

    mex yourmexfile.cpp MxArray.cpp

1

您可以使用mxGetDimensions和mxGetNumberOfDimensions来优化程序,以获取图像的大小,并使用mxGetClassID更准确地确定深度。

我想做同样的事情,但我认为最好使用matlab dll和calllib来完成。我不会在opencv格式或matlab中进行图像转换,因为这会很慢。这是matlab openCV的最大问题之一。我认为opencv2.2有一些很好的解决方案。看起来opencv社区已经为Octave完成了一些类似的解决方案,但我仍然不理解它们。他们在某种程度上使用了opencv的c++功能。


1
也许您可以观看该实现:http://code.google.com/p/j-ml-contrib/source/browse/ (cvlib_mex.zip)。我不是很喜欢它,因为mex开头的字符串解析和所有深度版本的实现。我认为用一点点脑力,用C++可以做得更好。 - Alessandro

1
根据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);

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接