1

I create a Matlab engine to covert OpenCV Mat file to Matlab matrix. However, I got wrong results. I attached my code so that you can directly test it.

#pragma comment (lib, "libmat.lib")
#pragma comment (lib, "libmx.lib")
#pragma comment (lib, "libmex.lib")
#pragma comment (lib, "libeng.lib")

    void cvLoadMatrixToMatlab(const Mat& m, const string name, Engine *m_pEngine)
    {
        int rows=m.rows;
        int cols=m.cols;  
        string text;
        mxArray *T=mxCreateDoubleMatrix(cols, rows, mxREAL);

        memcpy((char*)mxGetPr(T), (char*)m.data, rows*cols*sizeof(char));
        engPutVariable(m_pEngine, name.c_str(), T);
        text = name + "=" + name + "'";                    // Column major to row major
        engEvalString(m_pEngine, text.c_str());

        mxDestroyArray(T);
    }

    int main(int argc, char **argv)
    {
        /*Open Matlab Engine*/
            Engine *m_pEngine;
            m_pEngine = engOpen("null");

            cv::Mat img = imread("panda.jpg",0);

            cvLoadMatrixToMatlab(img,"imgMatlab", m_pEngine);
            engEvalString(m_pEngine, "imshow(imgMatlab)");
        cv::waitKey(0);

    }

Original Image Result shows in Matlab

I suspect the code below cause this problem but I don't know how to fix it.

memcpy((char*)mxGetPr(T), (char*)m.data, rows*cols*sizeof(char));
herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
SimaGuanxing
  • 673
  • 2
  • 10
  • 29
  • If I'm not mistaken, `memcpy` copies out data in row-major format. What you should probably do is **transpose** the matrix first before doing `memcpy`. That way even though the copying is done in row-major, the data is arranged in column-major format when performing row-major readouts. – rayryeng Apr 07 '15 at 02:13
  • Hey @rayryeng. Thanks for your reply. I tried put transpose in front of memcpy function but it's still not working. Will you be able to modify my code so that I can test it in my end. Thanks! – SimaGuanxing Apr 07 '15 at 04:52

2 Answers2

4

When copying data from cv::Mat to Matlab's matrix, you should pay attention to:

  1. In OpenCV, all matrix are row-major, while col-major for Matlab. You should transpose before copying.

  2. For copying color images, you should transfer data channel by channel.


As you're going to copying a color image cv::Mat to Matlab, you should do as follows:

mwSize dims[] = {rows, cols, 3};
mxArray *T = mxCreateNumericArray(3, dims, mxUINT8_CLASS, mxREAL);
UINT8 *ptr = (UINT8 *) mxGetData(T);

std::vector<cv::Mat> channels; // B, G, R channels
cv::split(m, channels);

// remember to transpose first because MATLAB is col-major!!!
cv::transpose(channels[0], channels[0]);
cv::transpose(channels[1], channels[1]);
cv::transpose(channels[2], channels[2]);

memcpy(ptr, channels[2].ptr(), rows*cols*sizeof(UINT8));
memcpy(ptr+rows*cols, channels[1].ptr(), rows*cols*sizeof(UINT8));
memcpy(ptr+2*rows*cols, channels[0].ptr(), rows*cols*sizeof(UINT8));

engPutVariable(m_pEngine, name.c_str(), T); // put into matlab

Updated: If you're copying a gray-scale image, it should be like

mwSize dims[] = {rows, cols};
mxArray *T = mxCreateNumericArray(2, dims, mxUINT8_CLASS, mxREAL);
UINT8 *ptr = (UINT8 *) mxGetData(T);

cv::transpose(m, m); // remember to tranpose first because MATLAB is col-major!!!
memcpy(ptr, m.ptr(), rows*cols*sizeof(UINT8));

engPutVariable(m_pEngine, name.c_str(), T); // put into matlab

Note that, UINT8 here is to be consistent with Matlab's uint8 type used for images.

Shmil The Cat
  • 4,548
  • 2
  • 28
  • 37
herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • Thanks! I am actually copying grayscale data hence my imread function's second parameter is 0. Also, what is your UINT8 means? Could you please point out which place in my code cause problem? I tried to put transpose in front of memcpy function but it's still not working. – SimaGuanxing Apr 07 '15 at 04:40
  • Sorry, I still can't run the code correctly by replacing UINT8 to char. The program cause crushing when doing transpose. I suppose your rows and cols are m.rows and m.cols, right? – SimaGuanxing Apr 07 '15 at 16:42
  • @JoeWang How about you replace `memcpy(ptr, m.ptr(), rows*cols*sizeof(UINT8));` by `memcpy(ptr, (UINT8 *)m.datastart, rows*cols*sizeof(UINT8));` ? – herohuyongtao Apr 08 '15 at 01:55
  • Hi, UINT8 is not a valid type in OpenCV – SimaGuanxing Apr 08 '15 at 20:50
  • @JoeWang You can simply add `typedef unsigned char UINT8;` to make it work, as described [here](https://msdn.microsoft.com/en-us/library/cc230388.aspx). – herohuyongtao Apr 09 '15 at 00:47
0

Okay. I use for loop to copy data and finally it works.

int rows=m.rows;
        int cols=m.cols;  

        mwSize dims[] = {rows, cols};
        mxArray *T = mxCreateNumericArray(2, dims, mxUINT8_CLASS, mxREAL);
        char *ptr = (char *) mxGetData(T);

         for (int i = 0; i < rows; i++)  
        {  
            for (int j = 0; j < cols; j++)  
            {    
            ptr[j*rows + i] =  (* m.row(i).col(j).data);  
             }  
        }  

        engPutVariable(m_pEngine, name.c_str(), T); // put into matlab

For the answer posted above, I am still working on it. Thanks the help from @herohuyongtao to mention transpose issue.

SimaGuanxing
  • 673
  • 2
  • 10
  • 29