Wednesday 1 February 2012

WebCam capture with Python on Mac OS X

I typically program in Python, and in the absence of any open source astrophotography software for webcam image capture that will run under the current Mac OS X, perhaps I can write something myself. So - how can you capture web camera photos from Python? One way is using OpenCV (originally Intel’s Open Source Computer Vision library). Another popular library is VideoCapture, but it is Windows only.

OpenCV and Python

The next best bet looked to be the open source library OpenCV (originally Intel’s Open Source Computer Vision library), which has a choice of Python interfaces. At the time of writing, the current release is OpenCV 2.3.1 (August 2011), prior to that v2.3.0 (July 2011), and v2.2.0 (December 2010). That seems pretty current - so how can I call it from Python?

It turns out there are several (defunct) interfaces for OpenCV from Python. One of the older ones was ctype-opencv which used Python's ctypes library to give access to (some of) OpenCV. Looking at their source repository, the last commit was in 2009, so the library seems dead.

Later project PyOpenCV tried to address weaknesses in wrapping OpenCV's C++ seen in swig or ctypes-based approaches, and instead uses Boost.Python for interoperability between C++ and Python. However, the latest release is from September 2008, PyOpenCV v1.2.0 for OpenCV v2.1.0 (which is now quite out of date). There is an open issue for updating PyOpenCV to work with OpenCV v2.2.x, where a year ago (Jan 2011) developer Minh Tri talks about the alternative Python bindings being provided with OpenCV itself, and waiting to see how things pan out. It looks like PyOpenCV is also dead.

All is not lost though. Looking over the OpenCV release notes, it seems they have their own Python interfaces - an older one called cv, and a more recent C++ style one called cv2. Since this is part of OpenCV, it seems like my best bet. Specifically their VideoCapture class.

Installing OpenCV on Mac OS X

First the source code OpenCV-2.3.1a.tar.bz2 doesn't come with an INSTALL text file, they point you at the website - I found the OpenCV install instructions here which linked to another page with Mac specific guidelines (confusingly full of historical information for out dated OpenCV releases).

The first hurdle was installing CMake 2.6 or later, which thankfully comes with Mac binaries - so that was easy. Now unzip the tar ball and change to that directory, then:
$ cd OpenCV-2.3.1
$ mkdir build
$ cd build
$ cmake -G "Unix Makefiles" ..
$ make
$ sudo make install
That worked smoothly on Mac OS X 10.7 "Lion", and the Apple provided Python 2.6.

Using OpenCV in Python

If everything went smoothly with the OpenCV install, and you have a camera installed, then this should work in Python:
>>> import cv2
>>> vidcap = cv2.VideoCapture()
>>> vidcap.open(0)
True
>>> retval, image = vidcap.retrieve()
>>> vidcap.release()
Cleaned up camera.
>>> cv2.imwrite("test.png", image)
True
So far so good... that turned on my built in FaceTime camera (green LED on), grabbed a single image, and then turned it off (LED off), and saved the image to disk. When I have a USB camera plugged in as well, using device 0 or 1 seems to work, and I've captured test images from both.

As a bonus, the images are the right way round - using Apple's Photo Booth application gives me mirror images with left/right swapped.

I haven't worked out how the device numbers get assigned yet - device zero may depend on what was last used? I think there should be away to get the device name associated with this index. From experiment using 0 to 99 seemed to work differently from 100 or more. Reading the source code file modules/highgui/src/cap.cpp there is a comment that you can add multiples of 100 to the camera index to select an API (if the camera supports more than one). Looking deeper, on the Mac it looks like they use the QuickTime API via modules/highgui/src/cap_qt.cpp and this has some debug code which looks at the device name - but does not seem to provide the device name to the calling code.

The API style of the cv2 library isn't very Pythonic. Calling function methods and getting return codes rather than a Python exception is much more C or C++ style. Also the library seems to print lots of messages to screen (probably to stderr,  I haven't checked) where using Python's warning library would be more elegant. But it works, and ought to be cross platform too (Mac OS X, Linux and Windows).

Conclusion

For this to be useful for astrophotography, I'm going to need to work out how to control things like the automatic exposure control and the gain, and how to capture save images at a good frame rate.

The bad news is that under Mac OS X, OpenCV doesn't seem to support accessing or setting things like brightness, contrast, saturation, hue, gain, or exposure. There is a comment to this effect in modules/highgui/src/cap_qt.cppcapture properties currently unimplemented for QuickTime camera interface, which clinches it. This harks back to the QTKit capture API limitations I mentioned on my last post. It looks like for a Mac solution with full control over the camera settings, we either have to wait for Apple to fix their QTKit API, or do it via the UVC interface (which isn't so straightforward).

7 comments:

  1. Thanks for taking the time to put up the note! Next step: Learn how to capture images at intervals from a streaming video.

    ReplyDelete
  2. Thanks for your helpful note. However, it should be added that webcam selection on the mac is *not* possible unless the usb-cam attached is a USV (USB Video Class) standard camera. I've spend a whole day until it became clear that opencv just cannot recognize my old spc900nc cam. A rather affordable logitech c310 did the job then, selected as cam 1 after the builtin cam 0. IMac 10.6/Python 2.6/OpenCV2.4.2 from MacPorts.

    ReplyDelete
  3. Another tool to look at is SimpleCV http://simplecv.org/

    ReplyDelete
    Replies
    1. From a quick look at their website, SimpleCV is basically a bundling of OpenCV with Python, NumPy and SciPy, right?

      Delete
  4. The installation and sample code for an OpenCV interface in Python at

    http://www.calebmadrigal.com/intro-opencv-python/

    worked perfectly for me. The author there explains how to install MacPorts and use that system to load an appropriate library:

    # Install numpy as a prerequisite
    sudo port install numpy

    # Install OpenCV with the Python API
    sudo port install opencv +python27

    I don't know which, if any, of the Python interfaces mentioned in this post might match the one I loaded using these instructions. All I know right now is that I can see video on my screen taken directly from the inexpensive little pen-type camera, described as a microscope, which I bought on Amazon:

    http://www.amazon.com/Portable-Microscope-Endoscope-Magnification-Recording/dp/B007ROP2VO/ref=sr_1_26?ie=UTF8&qid=1358014561&sr=8-26&keywords=usb+microscope

    ReplyDelete
    Replies
    1. Hey, Loved your way of getting things done. I am working on a project where I need to capture an image using a webcam connected to a raspberry PI and email it. Can you help me write a python script to automate this process? I need the image-capture to happen when a signal is received at the GPIO pins of RPI. - (arushidoshi@gmail.com)

      Delete
    2. That sounds very possible, but I know very little about the GPIO programming. I have previously followed some examples for LED control.

      Delete