通过zbar和Raspicam模块扫描二维码

4 投票
7 回答
26997 浏览
提问于 2025-04-18 05:48

我想用我的树莓派相机模块来扫描二维码。为了检测和解码二维码,我想使用zbar这个工具。以下是我现在的代码:

import io
import time
import picamera
import zbar
import Image

if len(argv) < 2: exit(1)

# Create an in-memory stream
my_stream = io.BytesIO()
with picamera.PiCamera() as camera:
    camera.start_preview()
    # Camera warm-up time
    time.sleep(2)
    camera.capture(my_stream, 'jpeg')

scanner = zbar.ImageScanner()
scanner.parse_config('enable')   

pil = Image.open(argv[1]).convert('L')
width, height = pil.size
raw = pil.tostring()

my_stream = zbar.Image(width, height, 'Y800', raw) 

scanner.scan(image)

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

如你所见,我想创建一个图片流,然后把这个流发送给zbar,看看图片里是否包含二维码。不过,我无法运行这段代码,出现了这个错误:

段错误

------------------(程序以代码:139 退出)按回车继续

我找不到解决这个问题的方法,有什么想法吗?

祝好;

7 个回答

0

对于还在寻找解决方案的人来说……

这段代码虽然看起来不太好,但用普通的网络摄像头效果不错,我还没试过树莓派的摄像头。我刚开始学Python,所以这是我能想到的在Python2和Python3中都能用的最好方法。

首先,创建一个叫做kill.sh的bash脚本,并让它可以执行……(chmod -x)

  #kill all running zbar tasks ... call from python 
ps -face | grep zbar | awk '{print $2}' | xargs kill -s KILL

然后在Python中这样调用系统命令……

import sys
import os

def start_cam():
    while True:
        #Initializes an instance of Zbar to the commandline to detect barcode data-strings.
        p=os.popen('/usr/bin/zbarcam --prescale=300x200','r')
        #Barcode variable read by Python from the commandline.
        print("Please Scan a QRcode to begin...")
        barcode = p.readline()
        barcodedata = str(barcode)[8:]

        if barcodedata:
            print("{0}".format(barcodedata))
            #Kills the webcam window by executing the bash file 
            os.system("/home/pi/Desktop/kill.sh")

start_cam()

希望这能帮助将来有同样问题的人!

0

在阅读了这篇文章后,我想出了一个用Python和OpenCV的解决方案。

首先,你需要按照这些步骤在树莓派上安装OpenCV。这可能需要几个小时才能完成。

安装完成后,重启树莓派,然后使用下面的脚本(假设你已经安装了python-zbar)来获取二维码或条形码的数据:

import cv2
import cv2.cv as cv
import numpy
import zbar

class test():
    def __init__(self):
        cv.NamedWindow("w1", cv.CV_WINDOW_NORMAL)

#        self.capture = cv.CaptureFromCAM(camera_index) #for some reason, this doesn't work
        self.capture = cv.CreateCameraCapture(-1)
        self.vid_contour_selection()



    def vid_contour_selection(self):


      while True:

          self.frame = cv.QueryFrame(self.capture)


          aframe = numpy.asarray(self.frame[:,:])
          g = cv.fromarray(aframe)


          g = numpy.asarray(g)

          imgray = cv2.cvtColor(g,cv2.COLOR_BGR2GRAY)

          raw = str(imgray.data)
          scanner = zbar.ImageScanner()


          scanner.parse_config('enable')          

          imageZbar = zbar.Image( self.frame.width, self.frame.height,'Y800', raw)
          scanner.scan(imageZbar)

          for symbol in imageZbar:

              print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data          


          cv2.imshow("w1", aframe)

          c = cv.WaitKey(5)

      if c == 110: #pressing the 'n' key will cause the program to exit
        exit()
#        
p = test()

注意:我需要把树莓派摄像头的镜头逆时针转动大约1/4到1/3圈,才能让zbar检测到二维码或条形码。

使用上面的代码,每当zbar检测到二维码或条形码时,解码后的数据会在控制台上打印出来。这个程序会一直运行,只有在按下n键时才会停止。

0

我在我的项目中使用树莓派进行二维码解码。我是通过使用subprocess模块来解决这个问题的。这里是我用来解码二维码的函数:

import subprocess

def detect():
    """Detects qr code from camera and returns string that represents that code.

    return -- qr code from image as string
    """
    subprocess.call(["raspistill -n -t 1 -w 120 -h 120 -o cam.png"],shell=True)
    process = subprocess.Popen(["zbarimg -D cam.png"], stdout=subprocess.PIPE, shell=True)
    (out, err) = process.communicate()

    qr_code = None

    # out looks like "QR-code: Xuz213asdY" so you need
    # to remove first 8 characters plus whitespaces
    if len(out) > 8:
        qr_code = out[8:].strip()

    return qr_code

你可以很简单地给这个函数添加参数,比如图片的宽度和高度,然后修改这部分代码

"raspistill -n -t 1 -w 120 -h 120 -o cam.png"

"raspistill -n -t 1 -w %d -h %d -o cam.png" % (img_width, img_height)

如果你想要解码不同大小的图片的话。

1

在这一行

scanner.scan(image)

你使用了一个在之前的代码中没有出现过的变量。因为zbar是用C语言写的,所以它不会检查这个变量是否定义过,结果这个库就试图把一些无效的数据当成图片来读取。这样就会导致程序崩溃。我猜你是想用my_stream而不是image。

4

其他答案的一个主要问题是它们存在很大的延迟——比如说,它们扫描并显示到屏幕上的内容,其实是几秒钟前拍摄的画面。

这主要是因为树莓派的CPU比较慢。所以,帧率(也就是每秒显示的画面数量)远远超过了我们的软件能够读取和扫描的速度。

经过很多努力,我终于写出了这段代码,它的延迟很小。所以当你给它一个二维码或条形码时,它能在不到一秒的时间内给你结果。

我使用的技巧在代码中有解释。

import cv2
import cv2.cv as cv
import numpy
import zbar
import time
import threading

'''
LITTLE-DELAY BarCodeScanner
Author: Chen Jingyi (From FZYZ Junior High School, China)
PS. If your pi's V4L is not available, the cv-Window may have some error sometimes, but other parts of this code works fine.
'''
class BarCodeScanner(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

        self.WINDOW_NAME = 'Camera'
        self.CV_SYSTEM_CACHE_CNT = 5 # Cv has 5-frame cache
        self.LOOP_INTERVAL_TIME = 0.2

        cv.NamedWindow(self.WINDOW_NAME, cv.CV_WINDOW_NORMAL)
        self.cam = cv2.VideoCapture(-1)

    def scan(self, aframe):
        imgray = cv2.cvtColor(aframe, cv2.COLOR_BGR2GRAY)
        raw = str(imgray.data)

        scanner = zbar.ImageScanner()
        scanner.parse_config('enable')          

        #print 'ScanZbar', time.time()
        width = int(self.cam.get(cv.CV_CAP_PROP_FRAME_WIDTH))
        height = int(self.cam.get(cv.CV_CAP_PROP_FRAME_HEIGHT))
        imageZbar = zbar.Image(width, height,'Y800', raw)
        scanner.scan(imageZbar)
        #print 'ScanEnd', time.time()

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

    def run(self):
        #print 'BarCodeScanner run', time.time()
        while True:
            #print time.time()
            ''' Why reading several times and throw the data away: I guess OpenCV has a `cache-queue` whose length is 5.
            `read()` will *dequeue* a frame from it if it is not null, otherwise wait until have one.
            When the camera has a new frame, if the queue is not full, the frame will be *enqueue*, otherwise be thrown away.
            So in this case, the frame rate is far bigger than the times the while loop is executed. So when the code comes to here, the queue is full.
            Therefore, if we want the newest frame, we need to dequeue the 5 frames in the queue, which is useless because it is old. That's why.
            '''
            for i in range(0,self.CV_SYSTEM_CACHE_CNT):
                #print 'Read2Throw', time.time()
                self.cam.read()
            #print 'Read2Use', time.time()
            img = self.cam.read()
            self.scan(img[1])

            cv2.imshow(self.WINDOW_NAME, img[1])
            cv.WaitKey(1)
            #print 'Sleep', time.time()
            time.sleep(self.LOOP_INTERVAL_TIME)

        cam.release()

scanner = BarCodeScanner()
scanner.start()

撰写回答