先上源碼,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()'''