I'm trying to simply get an image from either PiCamera or Pygame.Camera, convert it and parse it with Zbar to get any QRCode data in it. I can fetch the images just fine, but it's interpreting them with zbar where I'm having issues, but I don't know how to get it working correctly. I've looked at every post I could find online about it, but most seem relatively outdated.

The images are being taken successfully with either method (tested) and I can save them as image files to disk.

How do I take the image and parse it in zbar? From my code below, you'll see that I've tried a few approaches, but I'm hoping to just be able to pull the image from pygame.camera to keep things consistent.

I probably don't understand IO and the image formats in Python enough.

Note: Zbar, PiCamera and Pygame are all installed correctly and functioning.

#!/usr/bin/env python

import sys
import pygame
from pygame.locals import *
from pygame.color import *
import numpy as np
import time
import os
import subprocess
from PIL import Image
import zbar
import numpy as np
from picamera.array import PiRGBArray
from picamera import PiCamera
import io
import pygame.camera
from StringIO import StringIO

def p(slug):
    return os.getcwd() + "/" + slug


# Set Display Variables
info = pygame.display.Info()
sizing = (info.current_w, info.current_h)
screen = pygame.display.set_mode(sizing, pygame.FULLSCREEN)
cam_size = (330, 460)
cam = pygame.camera.Camera("/dev/video0", cam_size)

# Set QR Scanner
scanner = zbar.ImageScanner()
#scanner.set_config(zbar.Symbol.NONE, zbar.Config.ENABLE, 0)
#scanner.set_config(zbar.Symbol.QRCODE, zbar.Config.ENABLE, 1)

# Create the background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))

# Add the logo
charRect = pygame.Rect((info.current_w - 200, info.current_h - 110), (150, 70))
charImage = pygame.image.load(p("images/logo.png")).convert_alpha()
charImage = pygame.transform.scale(charImage, charRect.size)
charImage = charImage.convert()

background.blit(charImage, charRect)

screen.blit(background, (0,0))

# Start the camera
#camera = PiCamera()
#camera.resolution = (330, 460)
#camera.framerate = 32
#camera.exposure_mode = 'auto'
#camera.sharpness = 40
#camera.contrast = 5
#camera.hFlip = True

#stream = PiRGBArray(camera)
stream = io.BytesIO()

# Warm up the camera

# Camera Surface
snapshot = pygame.surface.Surface(cam_size, 0, screen)

while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN:

    #raw = cam.get_raw()
    img = cam.get_image(snapshot)

    snapshot = pygame.transform.flip(img, 1, 0)
    screen.blit(snapshot, (10, 10))

    #data = pygame.image.tostring(img, 'RGBA')
    #img = Image.fromstring('RGBA', (330, 460), data)
    #zdata = StringIO()
    #img.save(zdata, 'JPEG')

    #image = zbar.Image(330, 460, 'Y800', data)

        #for symbol in image:
        #   print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data

        #camera.capture(stream, format='rgb')

    #pil = Image.open(stream).convert('LA')
    #image = zbar.Image(330, 460, 'Y800', raw)
    #for symbol in image:
    #   print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data



Note: If you have v4l installed correctly, you can add bcm2835-v4l2 to the end of your /etc/modules.conf file to have the drivers loaded automatically at boot time. That's how I'm currently accessing the PiCamera though /dev/video0.


The following should work, at least as far as capturing the image and converting it to a format that zbar can use:

#!/usr/bin/env python

# Python 3 compat
from __future__ import print_function
    input = raw_input
except NameError:

import time
import picamera
import picamera.array
import numpy as np
import zbar

with picamera.PiCamera() as camera:
    camera.resolution = (1280, 720)
    camera.framerate = 24
    # Wait for the user to press Enter before capturing something
    with picamera.array.PiYUVArray(camera) as stream:
        camera.capture(stream, format='yuv')
        # Now stream.array is a numpy array containing YUV image data.
        # zbar's Image class wants stuff in "Y800" format which just
        # means the Y plane (0) of this data.
        image = zbar.Image(1280, 720, 'Y800', stream.array[..., 0].tostring())
        # Finally, get zbar to scan it...
        scanner = zbar.ImageScanner()
        for symbol in image:
            print(symbol.type, symbol.data)

However, from playing around with this it's extremely hard to get a stock Pi camera module to scan a barcode. zbar is fairly particular about the barcode being in focus, large, and with decent contrast in the images that it scans (unsurprisingly). The lens on the stock Pi camera module simply isn't set up for close shots so unless you've got a module with an adapted lens you're going to have real difficulty getting anything to be recognized by this. In practice, I found resting a 10x close-up lens on top gave reasonable results.

  • Nb. People who need to do close up stuff mechanically change the focal depth of the camera. I tried this a little and could not break the glue, but I did not try very hard due to worries about slipping and scratching the lens. I didn't try the pencil eraser trick either (just found that). – goldilocks Sep 27 '15 at 13:43
  • Yup, that's probably the best way to go about it for long term use. For the answer above though, I just practised with a completely stock camera module and rested a lens from a kit like this on top of it which turned out to be good enough to recognize the barcode on a DVD box several times in a row – Dave Jones Sep 27 '15 at 13:48
  • @goldilocks I adjusted the lens focus with the eraser trick enough for it to take a clear shot of my fingerprint – SeanNieuwoudt Sep 27 '15 at 14:24
  • Excellent - in that case you should be good to go :) – Dave Jones Sep 27 '15 at 14:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.