Python OpenCV wrapper using ctypes

| tags: programming

Starting from the work of Michael Otto, I have hacked together the beginnings of a ctypes-based wrapper for OpenCV. You may download the code and examples here code and examples from UNC Python Tools on SourceForge. If there is enough interest I'll move it to sourceforge.

I have heard rumors of other efforts to wrap OpenCV with ctypes but couldn't find any code other than Michael's. Wanting to get on with my work, I spent a couple of days bashing this together. If someone else comes out with a better wrapper, I'll gladly switch. In the meantime this one gets the job done.

You'll, of course, need to download OpenCV and get the dll's on your path. Then you can use this module like this:

from CVtypes import cv
win = 'Show Cam'
cv.NamedWindow(win)
cap = cv.CreateCameraCapture(0)
while cv.WaitKey(1) != 27:
    img = cv.QueryFrame(cap)
    cv.ShowImage(win, img)

I prefer wxPython to the old-school highgui included with OpenCV so I included a couple of functions to make that easy. Here is an example using wx:

import wx
from CVtypes import cv

class myFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Try CV')
        self.SetClientSize((320,240))

        self.cap = cv.CreateCameraCapture(0)
        self.Bind(wx.EVT_IDLE, self.onIdle)

    def onIdle(self, event):
        img = cv.QueryFrame(self.cap)
        self.displayImage(img)
        event.RequestMore()

    def displayImage(self, img, offset=(0,0)):
        bitmap = cv.ImageAsBitmap(img)
        dc = wx.ClientDC(self)
        dc.DrawBitmap(bitmap, offset[0], offset[1], False)

class myApp(wx.App):
    def OnInit(self):
        self.frame = myFrame()
        self.frame.Show(True)
        return True
    
app = myApp(0)
app.MainLoop()

Most of the functions have exactly the same prototype in Python as in C. Using the amazing power of the prototypes in ctypes I've got it type checking most of the arguments and it allows defaults where they are defined in the C prototypes. OpenCV uses lots of type puns, I worry that this is going to bite me sooner or later.

Ctypes allows for output arguments that are returned rather than passing in a pointer. So for functions like GetMinMaxHistValue instead of allocating and passing in pointers you instead call it like:

min_val, max_val, min_idx, max_idx = cv.GetMinMaxHistValue(hist)

Many of the structured values have __repr__ methods so they can be printed.

Another amazing ctypes feature is the ability to specify types with class objects that have a from_param method. I used this to allow calling functions that expect a C array with a Python list or tuple. So, for example, this bit creates a 2D histogram

hist = cv.CreateHist(2, [18,16], cv.HIST_ARRAY, [[0,180][0,255]])
cv.CalcHist([hue,sat], hist)

Check out the code around cv.HaarDetectObjects for another great ctypes feature. I'm using the errcheck function to hide the ugly casting necessary to extract the list of cv.Rect from the cv.Seq that gets returned. This should serve as a model for other functions with ugly results.

Also, check out the cv.ImageAsBuffer function to see how to make a Python buffer object from an image. I haven't tried it yet but I'm guessing this is a route to making numpy arrays from images.

For documentation you'll have to use the source, Luke. The code needs lots more work and is likely rife with bugs. I have only used a tiny fraction of the functions. But ctypes is so easy to use, I expect you'll be able to figure out what is going wrong and fix it.

I'm using this daily to experiment with web-cams as replacements for mechanical switches for kids with movement disorders. As I find bugs I'll add them to the archive. If you find a bug, fix it, and let me know.