OpenCV+Python二維碼條形碼識別

先上源碼,github地址:https://github.com/DerrickRose25/Opencv_QRcode_recognition
環境:Pycharm 、Python3.7
在pycharm裏安裝opencv-python、pyzbar的包

簡版代碼識別度高,但是直接使用庫函數沒有任何意義,但是很實用:

import cv2
import pyzbar.pyzbar as pyzbar


def decodeDisplay(image):
    barcodes = pyzbar.decode(image)
    for barcode in barcodes:
        # 提取條形碼的邊界框的位置
        # 畫出圖像中條形碼的邊界框
        (x, y, w, h) = barcode.rect
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)

        # 條形碼數據爲字節對象,所以如果我們想在輸出圖像上
        # 畫出來,就需要先將它轉換成字符串
        barcodeData = barcode.data.decode("utf-8")
        barcodeType = barcode.type

        # 繪出圖像上條形碼的數據和條形碼類型
        text = "{} ({})".format(barcodeData, barcodeType)
        cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX,
                    .5, (0, 0, 125), 2)

        # 向終端打印條形碼數據和條形碼類型
        print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData))
    return image


def detect():
    camera = cv2.VideoCapture(0)
    #camera = cv2.VideoCapture('http://192.168.1.104:8080/?action=stream')
    while True:
        # 讀取當前幀
        ret, frame = camera.read()
        # 轉爲灰度圖像
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        im = decodeDisplay(gray)
        cv2.imshow("camera", im)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    camera.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    detect()

複雜版採用了opencv的原理,聽過對圖像進行濾波、開閉運算等一系列操作得到二維碼或者條形碼位置,雖然最後也是調用了庫進行解析,但是在定位上還是採用了OpenCV的傳統算法:

import cv2
import numpy as np
import pyzbar.pyzbar as pyzbar

# 二維碼定位
global check  # 全局變量check爲校驗位


def detect(image):
    global check
    # 把圖像從RGB轉換成灰度圖
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 進行Sobel算子運算
    # 使用scharr操作(指定ksize=-1)構造灰度圖在水平和豎直方向上的梯度幅值表示
    gradX = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)  # 對x方向求導
    gradY = cv2.Sobel(gray, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=-1)  # 對y方向求導
    # Scharr操作後,從X梯度減去Y梯度得到輪廓圖,此時噪點較多
    gradient = cv2.subtract(gradX, gradY)
    # 經過處理後,用convertScaleAbs()函數將其轉回原來的uint8形式。否則將無法顯示圖像,而只是一副灰色的窗口
    gradient = cv2.convertScaleAbs(gradient)
    # cv2.imshow('gradient', gradient)
    # 然後對梯度圖採用用9x9的核進行平均模糊,進行於降噪
    # 然後進行二值化處理,要麼是255(白)要麼是0(黑)
    blurred = cv2.blur(gradient, (9, 9))  # 通過低通濾波平滑圖像
    ret, thresh = cv2.threshold(blurred, 225, 255, cv2.THRESH_BINARY)  # 進行圖像固定閾值二值化
    # cv2.imshow("thresh",thresh)
    # 通過形態學操作,建立一個7*21的長方形內核,內核寬度大於長度,因此可以消除條形碼中垂直條之間的縫隙
    # 將建立的內核應用到二值圖中,以此來消除豎槓間的縫隙
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))  # 條形碼
    '''kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))              #二維碼'''
    # 對圖像進行閉運算
    closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
    # cv2.imshow("closed",closed)
    # 所得圖像仍有許多白點,通過腐蝕和膨脹去除白點,最後一個參數是腐蝕的次數
    closed = cv2.erode(closed, None, iterations=4)
    # cv2.imshow("closed1",closed)
    closed = cv2.dilate(closed, None, iterations=6)
    # cv2.imshow("closed2", closed)
    # 尋找輪廓
    contours, hierarchy = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 如果沒有找到,返回空9
    if len(contours) == 0:
        check = False
        return None
    # e
    c = sorted(contours, key=cv2.contourArea, reverse=True)[0]
    rect = cv2.minAreaRect(c)  # 生成最小外接矩形
    # box爲一個ndarry數組,返回4個頂點位置
    box = np.int0(cv2.boxPoints(rect))
    cv2.drawContours(frame, [box], 0, (255, 0, 0), 2)
    # cv2.imshow("frame", frame)
    check = True
    return box


# 二維碼識別
def scan():
    box = detect(frame)  # 調用detect()函數來查找二維碼返回二維碼的位置
    # print(box)
    # 這下面的3步得到掃描區域,掃描區域要比檢測出來的位置要大
    if check == True:
        min = np.min(box, axis=0)
        max = np.max(box, axis=0)
        roi = frame[min[1] - 10:max[1] + 10, min[0] - 10:max[0] + 10]
        # 把區域裏的二維碼傳換成RGB,並把它轉換成pil裏面的圖像,因爲zbar得調用pil裏面的圖像,而不能用opencv的圖像
        # roi = cv2.cvtColor(roi, cv2.COLOR_BGR2RGB)
        # cv2.imshow("roi",roi)
        # print(roi.shape)
        if roi.any() != 0:
            barcodes = pyzbar.decode(roi)
            for barcode in barcodes:
                # 提取條形碼的邊界框的位置
                # 畫出圖像中條形碼的邊界框
                # (x, y, w, h) = barcode.rect
                # cv2.rectangle(roi, (x, y), (x + w, y + h), (0, 0, 255), 2)
                # 條形碼數據爲字節對象,所以如果我們想在輸出圖像上
                # 畫出來,就需要先將它轉換成字符串
                barcodeData = barcode.data.decode("utf-8")
                barcodeType = barcode.type
                # 向終端打印條形碼數據和條形碼類型
                print("[INFO] Found {} barcode: {}".format(barcodeType, barcodeData))
                return roi


if __name__ == '__main__':
    cameraCapture = cv2.VideoCapture(0)
    while True:
        # 獲取當前幀
        ret, frame = cameraCapture.read()
        scan()
        cv2.imshow("camera", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cameraCapture.release()
    cv2.destroyAllWindows()

    '''image = cv2.imread("Images/coke.jpg")
    detect(image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()'''
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章