Control an SLR Camera from Python (Nikon, Canon, Sony, etc…)

Want to see how easy it is to control an SLR camera from python?  LOOK!

from boost_camera import CameraManager
camera_manager = CameraManager()
success = camera_manager.initialize()
for frame_number in xrange(10):
    # this byte array can easily be integrating into OpenCV or the Python
    # Imaging Library
    numpy_array_jpeg_bytes = camera_manager.grab_frame()

# lastly, take a picture that saves directly to the camera
camera_manager.take_picture()

Super easy! Sort of.  This is the end results of hours worth of frustration getting Boost to work properly on a Mac OS X.  The rest of the code required needed to make this work can be found on my Github repository. You’ll also need to ensure you have the following dependencies:

  • Boost.Python
  • gPhoto2 (dev)
  • NumPy (Not necessarily required, but I wanted a C++ function to return a Numpy Array)
  • SciPy (I’m not entirely sure if this was required. It was something I installed as part of a troubleshooting process in conjunction with NumPy)
  • A positive attitude

I tried tackling this problem for a variety of reasons.  First and foremost, I’m still dedicating a lot of work to getting my blimp working, and one of the requirements is to interface with an SLR camera. I had previously done this in Windows using Canon’s Software Development Kit, but now I needed this done in Linux and I needed to interface via Python.

The second and more immediate reason for choosing to tackle this aspect of the blimp project is that I’ve been playing around with OpenCV and facial recognition lately. I wrote a recent post about reading a jpeg stream from an IP camera, but I found that the IP camera was insufficient for what I was trying to do. Basically, I had been working on a facial recognition system in the office, but I found that the IP camera was impractical because of its low picture quality and because it had no optical zoom. For facial recognition in particular, this meant that it was very hard to capture an in-focus face of a moving person as they walked by.  For that reason, I decided that I would use an SLR camera and attach a telephoto lens to it, and fix the camera to point at and focus on a chokepoint within the office entrance.  In that way, I can get clear, quality images of faces, and by using the focus of an SLR, I can isolate a single plane to see in-focus faces.  This will help to reduce extraneous non-faces from creeping into my analysis.

Getting Boost Working

Managing packages in Mac OS X proved to be the major stumbling block in getting this code working.  While the code for this project seems very straightforward, I was consistently ran into segmentation faults when trying to run the code.  Eventually I discovered that the version of python on my PATH did not match the python version of what I was compiling the code against.  As a result, this created some low level problems that I didn’t have immediate visibility on.

I also learned a decent bit about the guts of Python as well.  While I knew that all python variables are essentially references to objects in memory, I discovered through this project that all python variables are actually “smart pointers” wherein a pointer to an object exists along with a hefty amount of metadata about the object it points to.  When using Boost and interfacing with C++ code from Python, to return any type of custom created Class directly to python would require an in-depth conversion of the reference to the object to a smart pointer as described above.  Rather than bothering to learn how to go about doing that, I instead opted to create a “Manager” class, if you will (and I will), that can be easily referenced from using Boost ever so gently like so:

class CameraManager{
    private:
        int attribute;
	CameraManager(){}
    public:
	void some_function();
};

void CameraManager::some_function(){
	// do something
}

using namespace boost::python;
BOOST_PYTHON_MODULE(boost_camera) // this parameter needs to match filename
{
    class_<CameraManager>("CameraManager")
        .def("some_function", &CameraManager::some_function)
}

We can access this code in Python like so:

import boost_camera  # this loads from boost_camera.so

camera_manager = boost_camera.CameraManager()
camera_manager.some_function()

From here, I wrote some C++ functions to interface with the gPhoto API. Quick example of some code to take a picture:

int take_picture_to_camera(Camera *camera, GPContext *context){
    int error;
    CameraFilePath camera_file_path;
    error = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
    if (error) {
        return 0;
    }
    return 1;
}

You’ll notice that the function parameters here are a Camera and a GPContext, both of which are gPhoto objects.  This was the reason to create a manager class; without doing so, I would have had to try and manage these objects in python which is not necessarily a trivial task.  By creating a manager class that abstracts away some of the private data members from us, we can minimize the points to interface between Python and C++.

Why Python is Awesome

In PyCon 2010, Peter Wang gave a presentation in which he argued that Python’s power as a language is directly proportional and scales easily to the skill of the programmer. In other words, it’s very effective for both beginning and experienced developers. For this particular case, we want to use C++ so that we can interface with hardware, and if we can surface the highest level operations to python, we can now easily create a myriad of different programs. In my case, I’m going to use it for facial recognition (and later aerial photography), and I’d also like to do a small time lapse in the office (which is now extremely easy to do). If anyone has some other interesting ideas for how to leverage some programming logic with a camera, I’m definitely all ears.

  • Tamir lance

    This is great – is there a way to make this work in Windows 7?

    • Scott Benedict Lobdell

      I’m not sure about gPhoto on Windows, but I know Canon has its own SDK for windows. I’ve tried both and found gPhoto to be a lot more usable.

  • deathbullet

    Hey,
    This is great, thanks. Can you tell me how you connected the SLR to the PC ? USB ?

    • Scott Benedict Lobdell

      Yup, it’s just done over USB. I don’t recall exactly, but you might need to install some additional drivers. I think it works right away though.

  • Reginadellamensa Di Med

    Very nice
    Did you try a version for Ubuntu?
    I am trying to work on it, but quite struggling to create the working python module

    • Scott Benedict Lobdell

      it should work on Ubuntu out of the box if you clone the repo and compile the C++ code