2010-10-13 43 views
5

我在Matlab圖像:從Matlab的一個圖像轉印到的OpenCV的IplImage

img = imopen('image.jpg')

它返回一個UINT8陣列高度×寬度×信道(信道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以外的其他約定來存儲圖像(例如,它們交錯顏色通道)。

任何人都可以解釋約定的差異是什麼,並給出一些關於如何正確顯示圖像的指針?

+0

這可能是更容易的文件名只傳遞給MEX,讓OpenCV的採取從文件加載圖像的護理? – 2010-10-13 10:21:58

+1

http://mirone.googlecode.com/svn/tags/1.3.0/mex/cvcolor_mex.c – karlphillip 2010-10-13 14:00:29

+0

是的,讓OpenCV負責加載圖像會更容易。然而,我想要實現的是創建一個matlab MEX文件,該文件將使用此lib在GPU上執行SURF關鍵點檢測: http://www.mis.tu-darmstadt.de/surf 該庫需要一個IplImage作爲輸入。 使用場景將有人在Matlab中進行圖像處理,然後通過SURF庫運行結果,並在Matlab中高興地使用結果。 – 2010-10-13 17:05:24

回答

12

花了一天時間做有趣的圖像格式轉換</sarcasm>我現在可以回答我自己的問題。

Matlab的存儲圖像作爲3個維數組:高度×寬度×顏色
OpenCV的存儲圖像作爲2個維陣列:(顏色×寬度)×高度

此外,爲獲得最佳性能,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); 
} 
+0

如果這是你的問題的答案,你應該把它標記爲這樣! – karlphillip 2010-10-13 20:02:12

+0

你必須等待2天才能接受你自己的回答 – Marc 2010-10-14 17:25:23

1

您可以用mxGetDimensions和mxGetNumberOfDimensions優化您的計劃獲取圖像的大小並使用mxGetClassID更精確地確定深度。

我想要做同樣的事情,但我認爲使用matlab dll和calllib來做到這一點會更好。我不會在matlab中進行opencv格式的圖像轉換,因爲它會很慢。這是matlab openCV最大的問題之一。我認爲opencv2.2爲這個問題提供了一些很好的解決方案。它看起來像是從opencv社區爲八度完成的一些解決方案,但我仍然不明白它們。他們以某種方式使用opencv的C++功能。

+1

也許你也可以觀察一下這個實現:http://code.google.com/p/j-ml-contrib/source/browse/(cvlib_mex.zip)I真的不喜歡它,因爲在mex開始時的字符串解析和所有深度版本的實現我認爲用一點智慧它可以用C++更好地完成。 – Alessandro 2011-05-31 18:20:20

1

嘗試使用哥打山口開發庫: http://github.com/kyamagu/mexopencv 它定義了一個名爲「MxArray」類,可以執行從MATLAB mxArray變量OpenCV的對象(從OpenCV的MATLAB的)所有類型的轉化。例如,該庫可以在mxArray和cv :: Mat數據類型之間進行轉換。順便說一句,IplImage是不相關的,如果你使用OpenCV的C++ API,最好使用cv :: Mat代替。

注意:如果使用庫,請確保編譯您的mex函數 MxArray。來自庫的cpp文件;你可以在MATLAB命令行中這樣寫:

mex yourmexfile.cpp MxArray.cpp 
1

基礎上的答案,How the image matrix is stored in the memory on OpenCV,我們只能與opencv的墊操作做到!

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); 

}