Сканирование QR-кода через модуль zbar и Raspicam

Я хочу использовать модуль камеры raspi для сканирования QR-кодов. Для обнаружения и декодирования qr-кодов я хочу использовать 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, чтобы проверить, содержится ли в изображении qr-код. Я не могу запустить этот код, эта ошибка является результатом:

Ошибка сегментации

------------------ (программа завершилась с кодом: 139) Нажмите клавишу возврата, чтобы продолжить

Я не нахожу решения, как исправить эту ошибку, есть идеи?

С уважением;


person user3551782    schedule 08.05.2014    source источник


Ответы (7)


Недостаток всех остальных ответов заключается в том, что они имеют большое количество ЗАДЕРЖКИ — например, то, что они сканируют и отображают на экране, на самом деле было кадром, снятым несколько секунд назад, и так далее.

Это связано с медленным процессором Raspberry Pi. Таким образом, frame-rate намного больше, чем скорость, с которой наше программное обеспечение может читать и сканировать.

С большим трудом я, наконец, сделал этот код, который имеет МАЛЕНЬКУЮ ЗАДЕРЖКУ. Поэтому, когда вы дадите ему QRCode/BarCode, он даст вам результат менее чем за секунду.

Трюк, который я использую, объясняется в коде.

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()
person ch271828n    schedule 09.02.2016

В линии

scan.scan(изображение)

вы используете переменную, которая раньше не появлялась в коде. Так как zbar написан на C, он не улавливает, что переменная не определена, и библиотека пытается прочитать мусорные данные, как если бы это было изображение. Отсюда и сегфолт. Я предполагаю, что вы имели в виду my_stream вместо изображения.

person user4095952    schedule 30.09.2014

Я использую 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

Вы можете легко добавить параметры в функцию, такие как 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)

если вам нужен другой размер изображения для декодирования.

person Josip Grggurica    schedule 09.05.2014

После прочтения this, я смог придумать pythonic-решение с использованием OpenCV.

Сначала вы создаете OpenCV на Pi, следуя этим инструкциям< /а>. Это, вероятно, займет несколько часов.

Теперь перезагрузите 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()

Примечание. Мне пришлось повернуть объектив Raspi Camera против часовой стрелки примерно на 1/4–1/3 оборота, прежде чем zbar смог обнаружить QR/штрих-коды.

С помощью приведенного выше кода всякий раз, когда zbar обнаруживает QR/штрих-код, декодированные данные печатаются в консоли. Он работает непрерывно, останавливаясь только при нажатии клавиши n

person Ryan    schedule 12.10.2014

Для тех, кто все еще ищет решение этой проблемы... Этот код уродлив, но он работает с обычной веб-камерой довольно хорошо, камеру Pi еще не пробовал. Я новичок в python, так что это лучшее, что я мог придумать, что работало как в Python2, так и в 3.

Создайте сценарий bash с именем kill.sh и сделайте его исполняемым... (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()

Надеюсь, это поможет людям с теми же вопросами в будущем!

person Dan2theR    schedule 22.11.2014

Довольно поздний ответ, но я столкнулся с рядом проблем, пытаясь заставить Zbar работать. Хотя я использовал веб-камеру USB, мне пришлось установить несколько библиотек, прежде чем я смог установить zbar. Я установил fswebcam, python-zbar, libzbar-dev и, наконец, запустил setup.py.

Что еще более важно, zbar из sourceforge у меня не работал, а zbar из github, у которого есть оболочка Python, у меня работал.

Я описал свои действия шаг за шагом на странице http://techblog.saurabhkumar.com/2015/09/scanning-barcodes-using-raspberry-pi.html, если это может помочь

person Saurabh Kumar    schedule 23.09.2015

Просто небольшая модификация от Dan2theR, потому что я не хочу создавать еще один файл оболочки.

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()
person Mike    schedule 03.08.2016