drive a webcam with python

I bought a USB webcam off of eBay quite some time ago, and I decided to connect it to my telescope with a little bit of hardware hackery. I’ll have to see about posting a writeup on how I did that at a later time. Anyway, when I installed my camera software, I quickly found how horrible the program was. It gave a tiny preview of what the camera saw, and had no way of capturing images or video without waaaay too many clicks of the mouse. That’s when I decided to write my own in Python.

The main libraries that I ended up using were VideoCapture, PIL, and pygame. As you’ll see, it’s not really difficult at all to make a useful webcam application.

Here’s the code:

from VideoCapture import Device
import ImageDraw, sys, pygame, time
from pygame.locals import *
from PIL import ImageEnhance

res = (640,480)
pygame.init()
cam = Device()
cam.setResolution(res[0],res[1])
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption('Webcam')
pygame.font.init()
font = pygame.font.SysFont("Courier",11)

def disp(phrase,loc):
    s = font.render(phrase, True, (200,200,200))
    sh = font.render(phrase, True, (50,50,50))
    screen.blit(sh, (loc[0]+1,loc[1]+1))
    screen.blit(s, loc)

brightness = 1.0
contrast = 1.0
shots = 0

while 1:
    camshot = ImageEnhance.Brightness(cam.getImage()).enhance(brightness)
    camshot = ImageEnhance.Contrast(camshot).enhance(contrast)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()
    keyinput = pygame.key.get_pressed()
    if keyinput[K_1]: brightness -= .1
    if keyinput[K_2]: brightness += .1
    if keyinput[K_3]: contrast -= .1
    if keyinput[K_4]: contrast += .1
    if keyinput[K_q]: cam.displayCapturePinProperties()
    if keyinput[K_w]: cam.displayCaptureFilterProperties()
    if keyinput[K_s]:
        filename = str(time.time()) + ".jpg"
        cam.saveSnapshot(filename, quality=80, timestamp=0)
        shots += 1
    camshot = pygame.image.frombuffer(camshot.tostring(), res, "RGB")
    screen.blit(camshot, (0,0))
    disp("S:" + str(shots), (10,4))
    disp("B:" + str(brightness), (10,16))
    disp("C:" + str(contrast), (10,28))
    pygame.display.flip()

I decided to use pygame in order to build this because it can actually handle the fps that I need for video. I built something similar quite a while ago with wxPython, but it just wasn’t fast enough at image swapping to make it work well.

A couple of noteworthy points:

  • The function on line 15 is simply there to help automate displaying information on the screen. In my case, I display the number of screenshots I’ve saved to disk, and the current brightness and contrast levels. The function displays the text sent to it by writing it in dark gray, and then re-writing it in light gray over the top with a pixel offset. I found this helps a TON to make the text visible in different scenarios.
  • Line 26 is where I actually apply the contrast and brightness changes. Lines 30-33 set up the number keys 1-4 to adjust contrast/brightness on the fly. This is terribly useful, in my experience.
  • Lines 34 and 35 are setting up ‘q’ and ‘w’ to show the settings dialogs for the webcam… this is where you can set resolutions, exposure levels, etc.
  • On line 36, and those under the if statement, I save the current image and name it using the current time… this insures no overlapping image names.

If you’re trying to write a webcam app of your own, I hope this gets you pointed in the right direction. Let me know if you find this info useful!

25 Responses to “drive a webcam with python”

  1. sdf Says:

    amazing tut man. thanks a lot this helped me incredibly

  2. Mac [unlesbar] Says:

    Haven’t tried it yet but it looks mucho like what I want. Any ideas about making the beast portable to a Mac or Linux machine?

  3. pr Says:

    I’m glad it helps! As far as adapting it to a linux box… I’m running Linux now, I just haven’t taken a lot of time to port it over. I’ll see what I can do. :)

  4. Jo Vermeulen Says:

    A Linux version would be great! Any news on that?

    I think you need some way to interact with Video4Linux2 then …

  5. Anonymous Says:

  6. Diego Says:

    Hi
    It looks very nice
    I am trying to do the same in Ubuntu, but videocapture is for win32, do you know any Extension for Accessing Video Devices for python in linux?

    thanks

  7. John Says:

    This is really an amazing implementation. I’m using a cheap webcam and this does a fantastic job.

    I have written some improvements to this script though. I added controls to rotate the image, and use some of PIL’s imagefilters to have some fun with your mugshot.

    My goal is to build a Python app that duplicates (and may even work better) than Apple’s Photobooth. If anyone wants to see my improvements, just email me at webmaster@asporina.com

  8. R Says:

    Worked for me, great, but I had to make a couple of changes

    1. “from PIL import ImageDraw” instead of “import ImageDraw”, perhaps that’s due to a change in PIL.

    2. Added an infinite loop until the image was read, because “getImage()” does not guarantee to produce an image, and freezes the program. I used the following instead of calling getImage from inside Brightness()

    camshot = cam.getImage()
    while camshot==None:
    camshot = cam.getImage()

    Thanks a lot for this.

  9. Beau Gunderson Says:

    http://www.jperla.com/blog/2007/09/26/capturing-frames-from-a-webcam-on-linux/

    Here’s how to capture webcam images on Linux. :)

  10. BoJaN Says:

    Very nice, but I’m getting an error from the PIL Image.py script saying there is not enough image data. I can’t find my webcam right now so I’m using Fake Cam to use an avi file as a camera instead and AmCap is able to view it just fine. It’s 3am and people are sleeping so I don’t really want to go looking for my webcam ATM.

  11. robert small Says:

    thanks alot for posting this.
    I am learning python at the moment and this has taught me a few new tricks
    Rob

  12. Rand Batchelder Says:

    I have gone this far too.

    But I need to set the webcam to a higer resolution like 1600×1200 or higher and the setResolution function does not work in videoCapture.

    Any advice ?

    Rand

  13. pr Says:

    Rand, if setResolution doesn’t support it, my guess is that your driver doesn’t either. Does your webcam actually advertise that resolution?

    If it’s not supported, I think upscaling using PIL would be the only option.

  14. zoe Says:

    hello there, i’m trying to test this program but i’m not sure about the indentation.
    is this
    ” camshot = pygame.image.frombuffer(camshot.tostring(), res, “RGB”)
    screen.blit(camshot, (0,0)) ”
    indented under the “for” loop?
    btw, what is the function of the above 2 lines?
    i’ve tried many things but it just won’t work. my webcam light is on but the screen is black. i can save an image but the file is empty.
    the system hangs whenever i try to close the pygame window. :(
    please help me! thank you! :)

  15. pr Says:

    zoe, sorry about that, my old theme was causing issues with indenting. I’ve updated the plugin I was using, so hopefully you’ll be able to see the code properly now.

    Let me know if you have any other questions!

  16. Chad Says:

    Just getting into Python. Thank you for this. It worked great!

  17. Sanjeev Kumar Says:

    I would like to know its implementation with wxpython can u share ?

  18. Keith Gates Says:

    Firstly I want to say a huge thank you for sharing this. After a little bit of modification it will do exactly what I am needing.

    The only problem I was having is that when i took a photo it took between 1 and 5 photos at a time !!!

    I am very new to python.

    However I changed the keyinput = pygame.key.get_pressed() in the loop statement as follows:

    while 1:
    camshot = ImageEnhance.Brightness(cam.getImage()).enhance(brightness)
    camshot = ImageEnhance.Contrast(camshot).enhance(contrast)
    for event in pygame.event.get():
    if event.type == pygame.QUIT: sys.exit()
    if event.type == KEYDOWN:
    if event.key == K_ESCAPE: sys.exit()
    if event.key == K_1: brightness -= .1
    if event.key == K_2: brightness += .1
    if event.key == K_3: contrast -= .1
    if event.key == K_4: contrast += .1
    if event.key == K_5: cam.displayCapturePinProperties()
    if event.key == K_w: cam.displayCaptureFilterProperties()
    if event.key == K_s:
    filename = str(time.time()) + “.jpg”
    cam.saveSnapshot(filename, quality=80, timestamp=0)
    shots += 1
    camshot = pygame.image.frombuffer(camshot.tostring(), res, “RGB”)
    screen.blit(camshot, (0,0))
    disp(“S:” + str(shots), (10,4))
    disp(“B:” + str(brightness), (10,16))
    disp(“C:” + str(contrast), (10,28))
    pygame.display.flip()

    Now it works a treat.

  19. Bob Bond Says:

    Very cool. Worked first time. I plan to use the code as the basis for a home brew auto focuser for my solar telescope.

    Thanks!

  20. chucky Says:

    can i take videos with this code too?

  21. pr Says:

    @chucky – definitely not… it’s just taking lots of images and dropping them into a directory. Take a look here for some ideas on converting the images to video: http://superuser.com/questions/249101/how-can-i-combine-30-000-images-into-a-timelapse-movie

  22. chucky Says:

    @pr says – thx for the link.
    But is there another way.. a code like this which can take videos without use other software..

  23. pr Says:

    @chucky – well, turns out, yes. :)

    http://stackoverflow.com/questions/753190/programmatically-generate-video-or-animated-gif-in-python

    Specifically, comment 13. Looks like openCV could be used to do it. Actually, you can also use openCV for getting the images too. I might look into doing an update post for this. This code is so old, and Windows only. The structure could definitely be improved too.

    Let me know if you’d be interested in that, and I could play around with it.

  24. chuky Says:

    @pr Say

    I´m very interestet on a finished code which takes videos. especially for widows. It would be great if you could hepl me.

  25. userquestion Says:

    hello everybody. does someone know if the code works on windows 8?

Leave a Reply