12

I follow the code in OpenCV cookbook for python interface to transform cvMat to numpy array:

mat = cv.CreateMat(3,5,cv.CV_32FC1)
cv.Set(mat,7)
a = np.asarray(mat)

but with OpenCV 2.1 on my PC, it does not work. The result a here is a object array, using "print a" does not print all element in a, only print <cvmat(type=42424005 rows=3 cols=5 step=20 )>. so how to fully transform a OpenCV Mat object to original numpy.ndarray object.

PinkyJie
  • 817
  • 3
  • 8
  • 28

3 Answers3

9

Try using appending [:,:] to the matrix (ie. use mat[:,:] instead of mat) in your call to np.asarray - doing this will also allows asarray to work on images.

Your example:

>>> import cv
>>> import numpy as np
>>> mat = cv.CreateMat( 3 , 5 , cv.CV_32FC1 )
>>> cv.Set( mat , 7 )
>>> a = np.asarray( mat[:,:] )
>>> a
array([[ 7.,  7.,  7.,  7.,  7.],
       [ 7.,  7.,  7.,  7.,  7.],
       [ 7.,  7.,  7.,  7.,  7.]], dtype=float32)

And for an image:

>>> im = cv.CreateImage( ( 5 , 5 ) , 8 , 1 )
>>> cv.Set( im , 100 )
>>> im_array = np.asarray( im )
>>> im_array
array(<iplimage(nChannels=1 width=5 height=5 widthStep=8 )>, dtype=object)
>>> im_array = np.asarray( im[:,:] )
>>> im_array
array([[100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100]], dtype=uint8)
rroowwllaanndd
  • 3,858
  • 1
  • 22
  • 20
  • i test your answer on my PC with opencv 2.1, it did not work. Appending **[:,:]** to the matrix still gave the wrong output like object array,is your opencv version same with mine? – PinkyJie May 05 '11 at 02:07
  • @PinkyJie, sorry to hear it still didn’t work. I’m using OpenCV 2.1 on Ubuntu. The difference could be to do with the version of Numpy you’re using (use `np.version.version` to find out - I’m using version 1.3). – rroowwllaanndd May 05 '11 at 13:10
  • really so strange, i'm using numpy 1.5.1 – PinkyJie May 06 '11 at 02:52
2

You are right, the cookbook example is not working either for me and I get the same output as yours (win xp, python 2.6.6, opencv 2.1., numpy 1.5.1).

Maybe you can use something similar to:

>>> mat = cv.CreateMat(3,5,cv.CV_32FC1)
>>> cv.Set(mat,7)
>>> mylist = [[mat[i,j] for i in range(3)] for j in range(5)]
>>> ar = np.array(mylist)
>>> ar
array([[ 7.,  7.,  7.],
       [ 7.,  7.,  7.],
       [ 7.,  7.,  7.],
       [ 7.,  7.,  7.],
       [ 7.,  7.,  7.]])
joaquin
  • 82,968
  • 29
  • 138
  • 152
  • Thank you! That is a workable solution, but when the dimension of the Mat is large, i.e. much bigger than 3 and 5, i think the calculation of "mylist" will waste a lot of time, right? – PinkyJie Apr 23 '11 at 09:47
  • @PinkyJie: use generators. This is untested, but it might work: mylist = ((mat[i,j] for i in range(3)) for j in range(5)) – plaes Apr 23 '11 at 09:50
  • @plaes: your solution is the same with @joaquin 's. – PinkyJie Apr 23 '11 at 10:47
  • @PinkyJie: That depends of how bigger the dimensions are and what do you consider a too long running time... test it... – joaquin Apr 23 '11 at 10:48
  • @PinkyJie, plaes recommend using a generator (parenthesis) instead of a list (brackets) in order to save memory and gain speed when managing high amounts of data. – joaquin Apr 23 '11 at 10:51
  • @plaes,@joaquin, not familiar with generator, but using "mylist = ((mat[i,j] for i in range(3)) for j in range(5))", "mylist" is a generator object, not a list, so np.array(mylist) return a generator object array, which violates my mind. – PinkyJie Apr 23 '11 at 11:12
  • @PinkyJie, yes, I know. PinkyJie already said 'is untested'. The list versions works. what about upvoting? – joaquin Apr 23 '11 at 11:36
2

For the 2.1 version of OpenCV, if you need to share the memory and if you don't care about a bit of C programming and SWIG wrapping, you could try this solution that I used for some time:

CvMat * npymat_as_cvmat_32f(float * npymat_float, int rows, int cols)
{
  CvMat * cvmat;

  cvmat = cvCreateMatHeader(rows, cols, CV_32FC1);
  cvSetData(cvmat, npymat_float, cols * sizeof(float));

  return cvmat;
}

Create a header, for example, mat_conversion.h:

/* npymat_as_cvmat_32f
 * 
 * Create an OpenCV CvMat that shared its data with the input NumPy double array
 */
CvMat * npymat_as_cvmat_32f(float * npymat_float, int rows, int cols);

and a interface file (numpy_meets_opencv.i):

/* numpy_meets_opencv */
%module numpy_meets_opencv

%{
#define SWIG_FILE_WITH_INIT
#include <cv.h>
#include "mat_conversion.h"
%}

%include "numpy.i"

%init %{
import_array();
%}

%apply (float* INPLACE_ARRAY2, int DIM1, int DIM2) {(float* npymat_float, int rows, int cols)};

%include "mat_conversion.h"

Compile:

numpy_meets_opencv: numpy_meets_opencv.i mat_conversion.c
        swig -python -classic numpy_meets_opencv.i
        $(CC) $(NPY_CFLAGS) -fPIC -fno-stack-protector -c mat_conversion.c `pkg-config --cflags $(PKGS)`        
        $(CC) $(NPY_CFLAGS) -fPIC -fno-stack-protector -c numpy_meets_opencv_wrap.c `pkg-config --cflags $(PKGS)`
        ld -shared mat_conversion.o numpy_meets_opencv_wrap.o `pkg-config --libs $(PKGS)` -o _numpy_meets_opencv.so

Finally, you can do your stuff:

In [1]: import numpy_meets_opencv as npyocv
In [2]: import opencv as cv
In [4]: import numpy as npy
In [12]: Inpy = npy.zeros((5,5), dtype=npy.float32)
In [13]: Iocv = npyocv.npymat_as_cvmat_32f(Inpy)
In [14]: Inpy
Out[14]: 
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]], dtype=float32)
In [15]: Iocv
Out[15]: <opencv.cv.CvMat; proxy of <Swig Object of type 'CvMat *' at 0x30e6ed0> >
In [17]: cv.cvSetReal2D(Iocv, 3,3, 255)
In [18]: Inpy
Out[18]: 
array([[   0.,    0.,    0.,    0.,    0.],
       [   0.,    0.,    0.,    0.,    0.],
       [   0.,    0.,    0.,    0.,    0.],
       [   0.,    0.,    0.,  255.,    0.],
       [   0.,    0.,    0.,    0.,    0.]], dtype=float32)
TH.
  • 1,738
  • 1
  • 12
  • 15
  • I think you did not understand my question, your answer converted numpy.ndarray to cvMat, but what i asked is how to convert cvMat to numpy.ndarray. If you want to simply convert numpy array to cvMat, i think there is a easy way. Here is a example: data = np.array(range(1,9),np.float32) a = cv.CreateMat(2,4,cv.CV_32FC1) cv.SetData(a,data,cv.CV_AUTOSTEP) – PinkyJie Apr 25 '11 at 11:55
  • @PinkyJie, I considered the recurrent problem to work on an image, sometimes using Numpy tools, other times using OpenCV tools, but always working on the same data in memory. The solution you presented above raised a TypeError in my system: "in method 'cvSetData', argument 2 of type 'void *". That's because OpenCV Python wrappers are not performing casting properly. – TH. Apr 25 '11 at 16:27
  • I think the interfaces we used are not the same, I used the new python interface starting from opencv 2.0, not the swig-based python interface. – PinkyJie Apr 26 '11 at 01:34
  • @PinkyJie, I *guess* that's because I'm using the pre-compiled packages for Ubuntu (python-opencv). Maybe the pre-compiled packages are using the SWIG interface. Thanks for remember me. – TH. Apr 26 '11 at 11:11