Calling OpenCV functions via Cython from Python 3.X.
I have managed to run OpenCV function from Python3 via Cython today, so I want to write about it.
Before you start something like this, you should know you have to read at least header files of C library
to write right definition in Cython,because Cython does not do that for you.
I am using virtualenv, python from pythonz, and home-brewed OpenCV.
Almost everything of C++ works just fine with Cython, but still, you should do some Voodoo things like for integer
template argument.
---------
update: Mar 3 '14 <unsinged int> was missing. Thank you, Andrey.
ctypedef void* int_parameter ctypedef int_parameter two "2" ctypedef Point_[float, two] Point2fNow, main sample Cython code is following.
#===== testopencv.pyx ========
import numpy as np
cimport numpy as np # for np.ndarray
from libcpp.string cimport string
from libc.string cimport memcpy
cdef extern from "opencv2/core/core.hpp":
cdef int CV_WINDOW_AUTOSIZE
cdef int CV_8UC3
cdef extern from "opencv2/core/core.hpp" namespace "cv":
cdef cppclass Mat:
Mat() except +
void create(int, int, int)
void* data
cdef extern from "opencv2/highgui/highgui.hpp" namespace "cv":
void namedWindow(const string, int flag)
void imshow(const string, Mat)
int waitKey(int delay)
cdef void ary2cvMat(np.ndarray ary, Mat& out):
assert(ary.ndim==3 and ary.shape[2]==3, "ASSERT::3channel RGB only!!")
ary = np.dstack((ary[...,2], ary[...,1], ary[...,0])) #RGB -> BGR
cdef np.ndarray[np.uint8_t, ndim=3, mode = 'c'] np_buff = np.ascontiguousarray(ary, dtype = np.uint8)
cdef unsigned int* im_buff = <unsigned int*> np_buff.data
cdef int r = ary.shape[0]
cdef int c = ary.shape[1]
out.create(r, c, CV_8UC3)
memcpy(out.data, im_buff, r*c*3)
cdef showMat(Mat m):
namedWindow( "WIN", CV_WINDOW_AUTOSIZE )
imshow( "WIN", m )
waitKey(0)
def openImage(pil_img):
cdef Mat m
ary2cvMat(np.array(pil_img), m)
showMat(m)
The last function openImage(pil_image) will open pil_image in different window.
(I know you can do this without OpenCV. Just for a sample.)
This sample changes pil_image to numpy array and then convert it to OpenCV's cv::Mat.
To compile this, you need a setup file like this.
#======== setup.py ===========
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
import numpy
import subprocess
proc_libs = subprocess.check_output("pkg-config --libs opencv".split())
proc_incs = subprocess.check_output("pkg-config --cflags opencv".split())
libs = [lib for lib in str(proc_libs, "utf-8").split()]
#print("OPENCV LIBS=", libs)
setup(
cmdclass = {'build_ext': build_ext},
ext_modules = cythonize(Extension("opencvsample",
sources = ["opencvsample.pyx"],
language = "c++",
include_dirs=[numpy.get_include(), "/usr/local/Cellar/opencv/2.4.5/include/"],
extra_link_args=libs
)
)
)
To compile and execute, you should do
python setup.py build_ext --inplace
python -c "import testopencv as to; from PIL import Image; to.openImage(Image.open('test.jpg'))"
I spent several days to work this all through, so I hope this might be helpful to somebody.
Feedbacks are always appreciated.---------
update: Mar 3 '14 <unsinged int> was missing. Thank you, Andrey.
Comments
testopencv.pyx:28:38: Cannot assign type 'char *' to 'unsigned int *'
at:
cdef unsigned int* im_buff = np_buff.data
Do you know how I can fix this error?
The correct code should be
cdef unsigned int* im_buff = <unsigned int*> np_buff.data
<...> was omitted by tag-thing. I'll fix it.
Thanks for the comment!