2010-10-13 67 views
5

Tôi có một hình ảnh trong Matlab:Chuyển hình ảnh từ Matlab để một OpenCV IplImage

img = imopen('image.jpg')

mà trả về một chiều cao mảng uint8 x x kênh chiều rộng (3 kênh: RGB).

Bây giờ tôi muốn sử dụng OpenCV để làm một số thao tác trên nó, vì vậy tôi viết lên một tập tin MEX mà mất hình ảnh như một tham số và xây dựng một IplImage từ nó:

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

Kết quả này trông hoàn toàn sai , vì openCV sử dụng các quy ước khác so với MATLAB để lưu trữ một hình ảnh (ví dụ, chúng xen kẽ các kênh màu).

Bất cứ ai có thể giải thích sự khác biệt trong quy ước là gì và cung cấp một số gợi ý về cách hiển thị hình ảnh chính xác?

+0

Đây có thể là dễ dàng hơn để chỉ cần vượt qua tên tập tin đến MEX và để OpenCV chăm sóc tải hình ảnh từ tập tin ? –

+1

http://mirone.googlecode.com/svn/tags/1.3.0/mex/cvcolor_mex.c – karlphillip

+0

Có, sẽ dễ dàng hơn khi để OpenCV chăm sóc tải hình ảnh. Tuy nhiên, những gì tôi muốn đạt được là tạo một tệp MEX matlab sẽ thực hiện phát hiện điểm khóa SURF trên GPU bằng cách sử dụng lib này: http://www.mis.tu-darmstadt.de/surf lib yêu cầu IplImage làm đầu vào . Các tình huống sử dụng sẽ là ai đó thực hiện thao tác hình ảnh trong Matlab, sau đó chạy kết quả thông qua lib SURF, và vui vẻ sử dụng kết quả trong Matlab. –

Trả lời

12

Sau khi dành cả ngày để chuyển đổi định dạng hình ảnh vui nhộn </sarcasm> Tôi hiện có thể trả lời câu hỏi của riêng mình.

Matlab lưu trữ hình ảnh như 3 mảng chiều: chiều cao × rộng × màu
cửa hàng OpenCV hình ảnh như 2 mảng chiều: (màu × chiều rộng) × chiều cao

Bên cạnh đó, for best performance, OpenCV miếng hình ảnh với số không do đó các hàng luôn được căn chỉnh trên các khối 32 bit.

tôi đã thực hiện việc chuyển đổi trong 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); 

Mã chuyển đổi này vào một IplImage là trong file 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

Nếu đây là câu trả lời cho câu hỏi của bạn, bạn nên đánh dấu nó như vậy! – karlphillip

+0

bạn phải chờ 2 ngày để chấp nhận câu trả lời của riêng bạn – Marc

1

Bạn có thể tối ưu hóa chương trình của bạn với mxGetDimensions và mxGetNumberOfDimensions để có được kích thước của hình ảnh và sử dụng mxGetClassID để xác định độ sâu chính xác hơn.

Tôi muốn thực hiện tương tự nhưng tôi nghĩ sẽ tốt hơn nếu bạn sử dụng dll MATLAB và calllib. Tôi sẽ không làm việc chuyển đổi của hình ảnh trong định dạng opencv không trong MATLAB bởi vì nó sẽ được làm chậm. Đây là một trong những vấn đề lớn nhất với openCV MATLAB. Tôi nghĩ opencv2.2 có một số giải pháp tốt cho vấn đề đó. Dường như có một số giải pháp như vậy được thực hiện từ cộng đồng opencv cho quãng tám nhưng tôi vẫn không hiểu chúng. Chúng bằng cách nào đó sử dụng chức năng C++ của opencv.

+1

và có thể bạn cũng có thể xem triển khai đó: http://code.google.com/p/j-ml-contrib/source/browse/ (cvlib_mex.zip) I không thực sự thích nó vì việc phân tích chuỗi ở đầu mex và implementatin của tất cả các phiên bản chiều sâu mà tôi nghĩ với một chút trí tuệ nó có thể được thực hiện tốt hơn với C++. – Alessandro

1

Hãy thử sử dụng các thư viện phát triển bởi Kota Yamaguchi: http://github.com/kyamagu/mexopencv Nó định nghĩa một lớp được gọi là 'MxArray' có thể thực hiện tất cả các loại chuyển đổi từ biến MATLAB mxArray các đối tượng OpenCV (và từ OpenCV để MATLAB). Ví dụ, thư viện này có thể chuyển đổi giữa các kiểu dữ liệu mxArray và cv :: Mat. Btw, IplImage không còn liên quan nữa nếu bạn sử dụng C++ API của OpenCV, tốt hơn nên sử dụng cv :: Mat thay vào đó.

Lưu ý: nếu sử dụng thư viện, hãy đảm bảo biên dịch hàm mex của bạn với MxArray.tệp cpp từ thư viện; bạn có thể làm như vậy trong dòng lệnh MATLAB với:

mex yourmexfile.cpp MxArray.cpp 
1

Dựa trên câu trả lời và How the image matrix is stored in the memory on OpenCV, chúng ta có thể làm cho nó chỉ hoạt động 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) 

Sau đó, mex C/C++ là:

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

}

Các vấn đề liên quan