2010-10-13 9 views
5

I tienen una imagen en Matlab:Transferencia de una imagen de Matlab a un OpenCV IplImage

img = imopen('image.jpg')

que devuelve una altura array uint8 x x canales de anchura (3 canales: RGB).

Ahora quiero utilizar OpenCV hacer algunas manipulaciones en él, así que escribir un archivo MEX que toma la imagen como parámetro y construye una IplImage de ella:

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

Este resultado se ve completamente equivocado , porque openCV usa otras convenciones que matlab para almacenar una imagen (por ejemplo, intercalan los canales de color).

¿Alguien puede explicar cuáles son las diferencias en las convenciones y dar algunos consejos sobre cómo mostrar la imagen correctamente?

+0

es probable que sea más fácil simplemente pasar el nombre de archivo para el MEX y dejar OpenCV cuidar de cargar la imagen desde el archivo ? –

+1

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

+0

Sí, sería más fácil dejar que OpenCV se encargue de cargar la imagen. Sin embargo, lo que quiero lograr es crear un archivo MEX de matlab que realice la detección de punto clave SURF en la GPU usando esta lib: http://www.mis.tu-darmstadt.de/surf La lib requiere una IplImage como entrada . Escenarios de uso será alguien haciendo manipulaciones de imagen en Matlab, luego ejecutando el resultado a través de la lib de SURF, y felizmente utilizando el resultado en Matlab. –

Respuesta

12

Después de pasar el día haciendo conversiones de formato de imagen divertidas </sarcasm> ahora puedo responder mi propia pregunta.

Matlab almacena las imágenes como 3 arrays dimensionales: altura × anchura × de color
almacena OpenCV imágenes como 2 arrays dimensionales: (color × anchura) × altura

Además, para un mejor rendimiento, OpenCV almohadillas de las imágenes con ceros así que las filas siempre están alineadas en bloques de 32 bits.

que he hecho la conversión en 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); 

El código para transformar esto en una IplImage está en el archivo 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

¡Si esta es la respuesta a su pregunta, debería marcarla como tal! – karlphillip

+0

tiene que esperar 2 días para aceptar su propia respuesta – Marc

1

Se puede optimizar su programa con mxGetDimensions y mxGetNumberOfDimensions para obtener el tamaño de la imagen y usar el mxGetClassID para determinar la profundidad con mayor precisión.

Yo quería hacer lo mismo, pero creo que sería mejor hacerlo con matlab dll y calllib. No haría la transformación de la imagen en formato opencv no en matlab porque sería lenta. Este es uno de los mayores problemas con matlab openCV. Creo que opencv2.2 tiene algunas buenas soluciones para ese problema. Parece que hay algunas soluciones como la de opencv community para octava, pero todavía no las entiendo. De alguna manera están usando la funcionalidad C++ de opencv.

+1

y quizás también pueda ver esa implementación: http://code.google.com/p/j-ml-contrib/source/browse/ (cvlib_mex.zip) I realmente no me gusta por el análisis sintáctico de cadena al comienzo de Mex y la implementación de todas las versiones de profundidad, creo que con un poco de capacidad mental se podría hacer mejor con C++. – Alessandro

1

Trate de usar la biblioteca desarrollado por Kota Yamaguchi: http://github.com/kyamagu/mexopencv Se define una clase llamada 'MxArray' que puede realizar todo tipo de conversiones de variables de MATLAB mxArray a objetos OpenCV (y de OpenCV a MATLAB). Por ejemplo, esta biblioteca puede convertir entre tipos de datos mxArray y cv :: Mat. Por cierto, IplImage ya no es relevante si usas la API C++ de OpenCV, es mejor usar cv :: Mat en su lugar.

Nota: si utiliza la biblioteca, asegúrese de compilar su función mex con MxArray.archivo cpp de la biblioteca; puede hacerlo en la línea de comandos de MATLAB con:

mex yourmexfile.cpp MxArray.cpp 
1

Sobre la base de la respuesta y How the image matrix is stored in the memory on OpenCV, podemos hacerlo con la operación OPENCV Mat solamente!

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) 

continuación, el código mex C/C++ es:

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

}

Cuestiones relacionadas