【问题标题】:Transferring an image from Matlab to an OpenCV IplImage将图像从 Matlab 传输到 OpenCV IplImage
【发布时间】:2011-04-24 18:18:47
【问题描述】:

我在 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 负责从文件中加载图像可能更容易?
  • 是的,让 OpenCV 负责加载图像会更容易。然而,我想要实现的是创建一个 matlab MEX 文件,该文件将使用此库在 GPU 上进行 SURF 关键点检测:mis.tu-darmstadt.de/surf 该库需要一个 IplImage 作为输入。使用场景是有人在 Matlab 中进行图像处理,然后通过 SURF 库运行结果,并在 Matlab 中愉快地使用结果。
  • @karlphillip:感谢代码!

标签: matlab opencv


【解决方案1】:

根据答案和How the image matrix is stored in the memory on 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);

}

【讨论】:

    【解决方案2】:

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

    注意:如果使用库,请确保从库中编译您的 mex 函数 with MxArray.cpp 文件;你可以在 MATLAB 命令行中这样做:

        mex yourmexfile.cpp MxArray.cpp
    

    【讨论】:

      【解决方案3】:

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

      我也想这样做,但我认为使用 matlab dll 和 calllib 会更好。我不会用 opencv 格式而不是 matlab 来转换图像,因为它会很慢。这是 matlab openCV 的最大问题之一。我认为 opencv2.2 对这个问题有一些很好的解决方案。看起来 opencv 社区为 octave 提供了一些类似的解决方案,但我仍然不明白它们。他们以某种方式使用了 opencv 的 c++ 功能。

      【讨论】:

      • 也许您也可以观看该实现:code.google.com/p/j-ml-contrib/source/browse (cvlib_mex.zip) 我不太喜欢它,因为 mex 开头的字符串解析以及我认为的所有深度版本的实现有点脑力,用c++可以做得更好。
      【解决方案4】:

      花了一天时间进行有趣的图像格式转换&lt;/sarcasm&gt; 我现在可以回答我自己的问题了。

      Matlab 将图像存储为 3 维数组:高 × 宽 × 颜色
      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);
      }
      

      【讨论】:

      • 如果这是您问题的答案,您应该将其标记为是!
      猜你喜欢
      • 1970-01-01
      • 2018-06-09
      • 1970-01-01
      • 1970-01-01
      • 2011-08-05
      • 2011-07-08
      • 2012-07-01
      • 2017-09-21
      • 1970-01-01
      相关资源
      最近更新 更多