Python 通过 zbar 和 Raspicam 模块扫描二维码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23538522/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scanning QR Code via zbar and Raspicam module
提问by user3551782
I want to use my raspi cam modul to scan QR codes. For detecting and decoding qr codes I want to use zbar. My current code:
我想用我的 raspi cam 模块来扫描二维码。为了检测和解码二维码,我想使用 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
As you may see, I want to create a picture stream, send this stream to zbar to check if a qr code is contained in the picture. I am not able to run this code, this error is the result:
如您所见,我想创建一个图片流,将此流发送到 zbar 以检查图片中是否包含二维码。我无法运行此代码,结果是此错误:
Segmentation fault
------------------ (program exited with code: 139) Press return to continue
分段故障
------------------(程序退出,代码:139)按回车继续
I don't find any solution how to fixx this bug, any idea?
我没有找到任何解决方案来修复这个错误,知道吗?
Kind regards;
亲切的问候;
回答by Josip Grggurica
i'm using QR decoding on raspberry for my project. I solved it by using subprocces module. Here is my function for QR decoding:
我正在为我的项目使用 raspberry 上的 QR 解码。我通过使用 subprocces 模块解决了它。这是我的 QR 解码函数:
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
You can easy add parameters to function such as img_widt and img_height and change this part of code
您可以轻松地向函数添加参数,例如 img_widt 和 img_height 并更改这部分代码
"raspistill -n -t 1 -w 120 -h 120 -o cam.png"
to
到
"raspistill -n -t 1 -w %d -h %d -o cam.png" % (img_width, img_height)
if you want different size of image for decoding.
如果您想要不同大小的图像进行解码。
回答by user4095952
In the line
在行中
scanner.scan(image)
扫描仪扫描(图像)
you're using a variable that hasn't appeared in the code before. Because zbar is written in C, it doesn't catch that the variable is undefined, and the library tries to read garbage data as if it were an image. Hence, the segfault. I'm guessing you meant my_stream instead of image.
您正在使用之前未出现在代码中的变量。因为 zbar 是用 C 编写的,所以它不会发现变量未定义,并且库尝试读取垃圾数据,就好像它是一个图像一样。因此,段错误。我猜你的意思是 my_stream 而不是图像。
回答by Ryan
After reading this, I was able to come up with a pythonic solution involving OpenCV.
阅读本文后,我能够想出一个涉及 OpenCV 的 Pythonic 解决方案。
First, you build OpenCV on the Pi by following these instructions. That will probably take several hours to complete.
首先,按照以下说明在 Pi 上构建 OpenCV 。这可能需要几个小时才能完成。
Now reboot the Pi and use the following script (assuming you have python-zbar installed) to get the QR/barcode data:
现在重新启动 Pi 并使用以下脚本(假设您安装了 python-zbar)来获取 QR/条形码数据:
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()
Note: I had to turn the Raspi Camera's lens counterclockwise about 1/4 - 1/3 of a turn before zbar was able to detect the QR/barcodes.
注意:在 zbar 能够检测到 QR/条形码之前,我必须将 Raspi 相机的镜头逆时针旋转约 1/4 - 1/3 圈。
With the above code, whenever zbar detects a QR/barcode, the decoded data is printed in the console. It runs continuously, only stopping if the n
key is pressed
使用上面的代码,每当 zbar 检测到二维码/条形码时,解码后的数据就会打印在控制台中。它连续运行,只有在n
按下键时才会停止
回答by Dan2theR
For anyone that is still looking for a solutions to this... This code is ugly but it works from a regular webcam pretty well, haven't tried the Pi camera yet. I'm new to python so this is the best I could come up with that worked in both Python2 and 3.
对于仍在寻找解决方案的任何人......这段代码很难看,但它可以在普通网络摄像头上很好地工作,还没有尝试过 Pi 摄像头。我是 python 的新手,所以这是我能想出的在 Python2 和 3 中都有效的最好的方法。
Make a bash script called kill.sh and make it executable... (chmod -x)
制作一个名为 kill.sh 的 bash 脚本并使其可执行... (chmod -x)
#kill all running zbar tasks ... call from python
ps -face | grep zbar | awk '{print }' | xargs kill -s KILL
Then do a system call from python like so...
然后像这样从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()
Hopefully this helps people with the same questions in the future!
希望这对将来有相同问题的人有所帮助!
回答by Saurabh Kumar
Quite a late response, but I ran into a number of issues while trying to get Zbar working. Though I was using a USB webcam, but I had to install multiple libraries before i could get to install zbar. I installed fswebcam, python-zbar, libzbar-dev and finally ran setup.py.
回复很晚,但我在尝试让 Zbar 工作时遇到了许多问题。虽然我使用的是 USB 网络摄像头,但我必须先安装多个库才能安装 zbar。我安装了 fswebcam、python-zbar、libzbar-dev,最后运行了 setup.py。
More importantly, the zbar from sourceforge did not work for me, but the one from github, which has a Python wrapper worked for me.
更重要的是,sourceforge 的 zbar 对我不起作用,但来自 github 的 zbar 对我有用,它有一个 Python 包装器。
I documented my steps by steps at http://techblog.saurabhkumar.com/2015/09/scanning-barcodes-using-raspberry-pi.htmlif it might help
我在http://techblog.saurabhkumar.com/2015/09/scanning-barcodes-using-raspberry-pi.html 上按步骤记录了我的步骤,如果有帮助的话
回答by ch271828n
The shortage of all the other answers is that they have a large amount of DELAY - for example, what they are scanning and displaying to the screen was actually a frame taken several seconds ago and so on.
所有其他答案的不足之处在于它们有大量的延迟 - 例如,它们正在扫描并显示到屏幕上的实际上是几秒钟前拍摄的一帧等等。
This is due to the slow CPU of Raspberry Pi. So the frame-rate
is much bigger than the rate our software can read and scan.
这是由于 Raspberry Pi 的 CPU 速度较慢。所以它frame-rate
比我们的软件可以读取和扫描的速率要大得多。
With lots of effort, I finally made this code, which have LITTLE DELAY. So when you give it a QRCode/BarCode, it will give you a result in less than a second.
经过大量的努力,我终于制作了这段代码,它具有LITTLE DELAY。所以当你给它一个二维码/条形码时,它会在不到一秒钟的时间内给你一个结果。
The trick I use is explained in the code.
代码中解释了我使用的技巧。
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()
回答by Mike
Just a small modified from Dan2theR, because I don't want to create another shell file.
只是从 Dan2theR 稍作修改,因为我不想创建另一个 shell 文件。
import sys
import os
p = os.popen('/usr/bin/zbarcam --prescale=300x300 --Sdisable -Sqrcode.enable', 'r')
def start_scan():
global p
while True:
print('Scanning')
data = p.readline()
qrcode = str(data)[8:]
if(qrcode):
print(qrcode)
try:
start_scan()
except KeyboardInterrupt:
print('Stop scanning')
finally:
p.close()