HOME/Articles/

pil example QRCode (snippet)

Article Outline

Python pil example 'QRCode'

Modules used in program:

  • import time
  • import numpy as np
  • import cv2
  • import zbar
  • import sympy
  • import os

python QRCode

Python pil example: QRCode

import os
import sympy
from sympy import *

import zbar
from PIL import Image
import cv2
#import cv2.cv as cv
import numpy as np
import time

class QRCode:
    def __init__(self,tvec,rvec,value, top_right):
        self.tvec = tvec
        self.rvec = rvec
        self.value = value
        self.tr = top_right

class Camera:
    def  __init__(self):
        # Image processing 
        self.cam = cv2.VideoCapture(1)

        # Camera dimensions had to be set manually
        #self.cam.set(3,1280)
        #self.cam.set(4,720)

        # QR scanning tools.
        self.scanner = zbar.ImageScanner()
        self.scanner.parse_config('enable')

        # Figure out what camera is being used
        #cam_model = arm_config["camera_model"]

        # Constants based on calibration for image processing
        self.cam_matrix  = np.float32(
                        [[4.2267683970564178e+02, 0.0, 3.1950000000000000e+02],
                        [0.0, 4.2267683970564178e+02, 2.3950000000000000e+02],
                        [0.0, 0.0, 1.0]])
        self.dist_coeffs = np.float32([
                        8.5064298756821945e-03
                        , 4.5102325220501040e-04
                        , 0.0
                        , 0.0
                        , -4.0826402839032952e-02])

        self.cam.set(3,1280)
        self.cam.set(4,720)

        """
        self.cam_matrix  = np.float32(
                        [[4.6005493419302496e+02, 0., 3.1950000000000000e+02],
                        [0.0, 4.6005493419302496e+02, 2.3950000000000000e+02],
                        [0.0, 0.0, 1.0]])
        self.dist_coeffs = np.float32([
                        2.2888445335485098e-02
                        , 2.3578180642677610e-02
                        , 0.0
                        , 0.0
                        , -2.2172306449254131e-01])
        """

        # initialize vertices of QR code
        l = 1.5
        self.qr_verts = np.float32([[-l/2, -l/2, 0],
                            [-l/2,  l/2, 0],
                            [ l/2, -l/2, 0],
                            [ l/2,  l/2, 0]])



    def readQR(self):

        while(True):
            QRList = []
            self.cam.grab()
            self.cam.grab()
            self.cam.grab()
            self.cam.grab()
            ret, frame = self.cam.read()   
            # Direct conversion cv -> zbar images did not work.
            # Buffer file used to have native data structures.

            # PIL -> zbar
            """
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            cv2.imwrite('buffer.png', gray)
            pil_im = Image.open('buffer.png').convert('L')
            bw = pil_im.point(lambda x: 0 if x<75 else 255, '1')
            bw.save("gray.png")
            pil_im = Image.open('gray.png').convert('L')
            """

            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            frame = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
            frame = cv2.bilateralFilter(frame, 9, 75, 75) 
            cv2.imwrite('buffer.png', frame)
            pil_im = Image.open('buffer.png').convert('L')

            width, height = pil_im.size
            raw = pil_im.tobytes()
            z_im = zbar.Image(width, height, 'Y800', raw)

            # Find codes in image
            self.scanner.scan(z_im)
            count = 0
            for symbol in z_im:
                tl, bl, br, tr = [item for item in symbol.location]
                points = np.float32([[tl[0], tl[1]],
                                     [tr[0], tr[1]],
                                     [bl[0], bl[1]],
                                     [br[0], br[1]]])

                ret, rvec, tvec = cv2.solvePnP(self.qr_verts, points
                                          , self.cam_matrix
                                          , self.dist_coeffs)

                cv2.line(frame, tl, bl, (100,0,255), 8, 8)
                cv2.line(frame, bl, br, (100,0,255), 8, 8)
                cv2.line(frame, br, tr, (100,0,255), 8, 8)
                cv2.line(frame, tr, tl, (100,0,255), 8, 8)

                QRList.append(QRCode(tvec, rvec, symbol.data, tr)) 
                count += 1
            if count == 0:
                #print("NO QR FOUND")
                cv2.imshow('test', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
                continue
            # find the best QRCode to grab (closest x then highest y)
            QRList.sort(key=lambda qr: qr.tvec[0], reverse=False)       # sort the list of qr codes by x, smallest to largest
            x_min = 100
            min_qr = 0
            count = 0
            for qr in QRList:
                #find smallest x
                if abs(qr.tvec[0]) < abs(x_min):
                    x_min = qr.tvec[0]
                    min_qr = count
                count += 1
            targetQR = None
            #check upper and lower qrs in list for height
            if (min_qr > 0):
                if (abs(QRList[min_qr -1].tvec[0] - x_min) < .1):
                    if QRList[min_qr -1].tvec[1] < QRList[min_qr].tvec[1]:
                        targetQR = QRList[min_qr-1]
                    else:
                        targetQR = QRList[min_qr]
            if ((min_qr < (len(QRList) - 1)) and (targetQR == None)):
                if (abs(QRList[min_qr +1].tvec[0] - x_min) < .1):
                    if QRList[min_qr +1].tvec[1] < QRList[min_qr].tvec[1]:
                        targetQR = QRList[min_qr+1]
                    else:
                        targetQR = QRList[min_qr]
            if targetQR == None:
                targetQR = QRList[min_qr]
            print("value: ", targetQR.value)
            print("X:     ", targetQR.tvec[0])
            print("Y:     ", targetQR.tvec[1])
            print("=====================================")
            cv2.circle(frame,targetQR.tr, 10, (0,255,0), -1)
            cv2.imshow('test', frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
        self.cam.release()
        cv2.destroyAllWindows()


    def QRSweep(self):

        while(True):
            QRList = []
            self.cam.grab()
            self.cam.grab()
            self.cam.grab()
            self.cam.grab()
            ret, frame = self.cam.read()   
            # Direct conversion cv -> zbar images did not work.
            # Buffer file used to have native data structures.

            # PIL -> zbar

            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            ret, frame = cv2.threshold(gray,85,255,cv2.THRESH_BINARY)
            cv2.imwrite('buffer.png', frame)
            pil_im = Image.open('buffer.png').convert('L')

            width, height = pil_im.size
            raw = pil_im.tobytes()
            z_im = zbar.Image(width, height, 'Y800', raw)

            # Find codes in image
            self.scanner.scan(z_im)
            count = 0
            for symbol in z_im:
                tl, bl, br, tr = [item for item in symbol.location]
                points = np.float32([[tl[0], tl[1]],
                                     [tr[0], tr[1]],
                                     [bl[0], bl[1]],
                                     [br[0], br[1]]])

                tvec = self.solveQR(tl, tr, bl, br)

                print("X_Displacement = ", tvec[0])
                print("Y_Displacement = ", tvec[1])
                print("Z_Displacement = ", tvec[2])
                print("=========================================")


                #ret, rvec, tvec = cv2.solvePnP(self.qr_verts, points
                #                          , self.cam_matrix
                #                          , self.dist_coeffs)

                cv2.line(frame, tl, bl, (100,0,255), 8, 8)
                cv2.line(frame, bl, br, (100,0,255), 8, 8)
                cv2.line(frame, br, tr, (100,0,255), 8, 8)
                cv2.line(frame, tr, tl, (100,0,255), 8, 8)

                #QRList.append(QRCode(tvec, rvec, symbol.data, tr)) 


            cv2.imshow('test', frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

        self.cam.release()
        cv2.destroyAllWindows()



    #new solvepnp
    def solveQR(self, tl, tr, bl, br):
        QRSize = 1.5                # units = inches

        resX = 1280
        resY = 720
        center = [resX/2, resY/2]

        #find the center of the QRCode
        QRCenter = [int((tl[0] + tr[0] + bl[0] + br[0])/4), int((tl[1] + tr[1] + bl[1] + br[1])/4)]

        #find pixel displacement
        pixel_displacement = [(QRCenter[0] - center[0]),(QRCenter[1] - center[1])]

        #find Z distance to QRCode
        north = int(sqrt(pow(abs(tl[0] - tr[0]), 2) + pow(abs(tl[1] - tr[1]), 2)))
        south = int(sqrt(pow(abs(bl[0] - br[0]), 2) + pow(abs(bl[1] - br[1]), 2)))
        east = int(sqrt(pow(abs(tr[0] - br[0]), 2) + pow(abs(tr[1] - br[1]), 2)))
        west = int(sqrt(pow(abs(tl[0] - bl[0]), 2) + pow(abs(tl[1] - bl[1]), 2)))

        if (north >= south and north >= east and north >= west):
            largest_edge = north
        elif (south >= east and south >= west):
            largest_edge = south
        elif (east >= west):
            largest_edge = east
        else:
            largest_edge = west

        z_units = self.getDistance(largest_edge)

        #find x and y based on distance
        x_units = (QRSize)*(pixel_displacement[0]/float(largest_edge))
        y_units = -1 * (QRSize)*(pixel_displacement[1]/float(largest_edge))

        return [x_units, y_units, z_units]


    def getDistance(self, length):
        a = 2453.3
        n = -1.0235
        return a*math.pow(length, n)