辨识 QRCode 和 BarCode
这篇教学会使用 OpenCV 读取包含 QRCode ( 二维条码 ) 和 BarCdoe ( 条码 ) 的图像,搭配 QRCodeDetector() 和 barcode_BarcodeDetector() 方法,实现在摄影机图像中即时辨识 QRCode 和 BarCode 的功能。
快速导览:
因为程序中的 OpenCV 会需要使用镜头或 GPU,所以请使用本机环境 ( 参考:使用 Python 虚拟环境 ) 或使用 Anaconda Jupyter 进行实作 ( 参考:使用 Anaconda ) ,并安装 OpenCV 函数库 ( 参考:OpenCV 函数库 )。
辨识 QRCode
OpenCV 打开图片后,使用 QRCodeDetector() 建立 QRCode 侦测器,接着就能使用 detectAndDecode() 方法开始侦测图片中的 QRCode,侦测 QRCode 之后会回传三个数值:
| 回传数值 | 说明 |
|---|---|
| data | 侦测到的数据。 |
| bbox | 侦测到的座标范围,如果没有侦测到 QRCode 会是 None。 |
| rectified | 将带有角度的 QRCode 转换成垂直 90 度的数组。 |
import cv2
import numpy as np
img = cv2.imread("qrcode.jpg") # 開啟圖片
qrcode = cv2.QRCodeDetector() # 建立 QRCode 偵測器
data, bbox, rectified = qrcode.detectAndDecode(img) # 偵測圖片中的 QRCode
# 如果 bbox 是 None 表示圖片中沒有 QRCode
if bbox is not None:
print(data) # QRCode 的內容
print(bbox) # QRCode 的邊界
print(rectified) # 換成垂直 90 度的陣列
cv2.imshow('oxxostudio', img) # 預覽圖片
cv2.waitKey(0) # 按下任意鍵停止
cv2.destroyAllWindows() # 結束所有圖片視窗
因为 bbox 的内容为 QRCode 边界四个点的座标,所以可以建立一个简单的函数,取出左上和右下的座标,透过座标标记出 QRCode 的外框,下方的程序码执行后,会在侦测到的 QRCode 边缘标记出红色外框。
import cv2
import numpy as np
img = cv2.imread("qrcode.jpg")
qrcode = cv2.QRCodeDetector()
data, bbox, rectified = qrcode.detectAndDecode(img)
# 取得座標的函式
def boxSize(arr):
global data
box_roll = np.rollaxis(arr,1,0) # 轉置矩陣,把 x 放在同一欄,y 放在同一欄
xmax = int(np.amax(box_roll[0])) # 取出 x 最大值
xmin = int(np.amin(box_roll[0])) # 取出 x 最小值
ymax = int(np.amax(box_roll[1])) # 取出 y 最大值
ymin = int(np.amin(box_roll[1])) # 取出 y 最小值
return (xmin,ymin,xmax,ymax)
# 如果 bbox 是 None 表示圖片中沒有 QRCode
if bbox is not None:
print(data)
print(bbox)
print(rectified)
box = boxSize(bbox[0])
cv2.rectangle(img,(box[0],box[1]),(box[2],box[3]),(0,0,255),5) # 畫矩形
cv2.imshow('oxxostudio', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
如果是带有角度的 QRCode,也可以正常侦测并标记外框。
额外建立一个放入文字的函数,就能根据读取到的内容和座标,将文字显示在 QRCode 的正下方。
参考:使用中文字型
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image # 載入 PIL ( 為了放中文字 )
img = cv2.imread("qrcode.jpg")
qrcode = cv2.QRCodeDetector()
data, bbox, rectified = qrcode.detectAndDecode(img)
# 建立放入文字的函式
def putText(x,y,text,color=(0,0,0)):
global img
fontpath = 'NotoSansTC-Regular.otf' # 字體 ( 從 Google Font 下載 )
font = ImageFont.truetype(fontpath, 20) # 設定字型與大小
imgPil = Image.fromarray(img) # 將 img 轉換成 PIL 圖片物件
draw = ImageDraw.Draw(imgPil) # 建立繪圖物件
draw.text((x, y), text, fill=color, font=font) # 寫入文字
img = np.array(imgPil) # 轉換回 np array
def boxSize(arr):
global data
box_roll = np.rollaxis(arr,1,0)
xmax = int(np.amax(box_roll[0]))
xmin = int(np.amin(box_roll[0]))
ymax = int(np.amax(box_roll[1]))
ymin = int(np.amin(box_roll[1]))
return (xmin,ymin,xmax,ymax)
if bbox is not None:
print(data)
print(bbox)
print(rectified)
box = boxSize(bbox[0])
cv2.rectangle(img,(box[0],box[1]),(box[2],box[3]),(0,0,255),5)
cv2.imshow('oxxostudio', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
辨识多组 QRCode
如果图像中有“多组”QRcode 需要辨识,则需要改用 detectAndDecodeMulti() 方法进行侦测,detectAndDecodeMulti() 方法会回传四个数值:
| 回传数值 | 说明 |
|---|---|
| ok | 是否有侦测到,True 表示有,False 表示没有。 |
| data | 侦测到的数据,使用数组依序纪录不同 QRCode 的内容。 |
| bbox | 侦测到的座标范围,使用数组依序纪录不同 QRCode 的座标范围。 |
| rectified | 旋转成正九十度的 QRCode 矩阵,使用数组依序纪录不同 QRCode 的矩阵。 |
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image
img = cv2.imread("many-qrcode.jpg")
def putText(x,y,text,color=(0,0,0)):
global img
fontpath = 'NotoSansTC-Regular.otf'
font = ImageFont.truetype(fontpath, 20)
imgPil = Image.fromarray(img)
draw = ImageDraw.Draw(imgPil)
draw.text((x, y), text, fill=color, font=font)
img = np.array(imgPil)
def boxSize(arr):
global data
box_roll = np.rollaxis(arr,1,0)
xmax = int(np.amax(box_roll[0]))
xmin = int(np.amin(box_roll[0]))
ymax = int(np.amax(box_roll[1]))
ymin = int(np.amin(box_roll[1]))
return (xmin,ymin,xmax,ymax)
qrcode = cv2.QRCodeDetector()
ok, data, bbox, rectified = qrcode.detectAndDecodeMulti(img) # 改用 detectAndDecodeMulti
# 如果有偵測到
if ok:
# 使用 for 迴圈取出每個 QRCode 的資訊
for i in range(len(data)):
print(data[i])
print(bbox[i])
text = data[i] # QRCode 內容
box = boxSize(bbox[i]) # QRCode 左上與右下座標
cv2.rectangle(img,(box[0],box[1]),(box[2],box[3]),(0,0,255),5) # 標記外框
putText(box[0],box[3],text) # 寫出文字
cv2.imshow('oxxostudio', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
辨识 BarCode
OpenCV 打开图片后,使用 BarcodeDetector() 建立 QRCode 侦测器,接着就能使用 barcode_BarcodeDetector() 方法开始侦测图片中的 BarCode ( 可以同时侦测多组 BarCode ),侦测 BarCode 之后会回传四个数值:
| 回传数值 | 说明 |
|---|---|
| ok | 是否有侦测到,True 表示有,False 表示没有。 |
| data | 侦测到的数据,使用 Tuple 依序纪录不同 BarCode 的内容。 |
| data_type | 侦测到的型态。 |
| bbox | 侦测到的座标范围,使用数组依序纪录不同 BarCode 的座标范围。 |
下方的程序码延伸前面辨识 QRCode 的 putText 和 boxSize 函数,同时侦测画面中的两个 BarCode,侦测到 BarCode 后会使用红色框标记并显示内容。
注意,BarCode 图片的上下左右需要保持一定距离,不然发生侦测不到的状况
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image
img = cv2.imread("barcode.jpg")
def putText(x,y,text,color=(0,0,0)):
global img
fontpath = 'NotoSansTC-Regular.otf'
font = ImageFont.truetype(fontpath, 20)
imgPil = Image.fromarray(img)
draw = ImageDraw.Draw(imgPil)
draw.text((x, y), text, fill=color, font=font)
img = np.array(imgPil)
def boxSize(arr):
global data
box_roll = np.rollaxis(arr,1,0)
xmax = int(np.amax(box_roll[0]))
xmin = int(np.amin(box_roll[0]))
ymax = int(np.amax(box_roll[1]))
ymin = int(np.amin(box_roll[1]))
return (xmin,ymin,xmax,ymax)
barcode = cv2.barcode_BarcodeDetector() # 建立 BarCode 偵測器
ok, data, data_type, bbox = barcode.detectAndDecode(img) # 偵測 BarCode
# 如果有 BarCode
if ok:
# 依序取出所有 BarCode 內容
for i in range(len(data)):
box = boxSize(bbox[i]) # 取出座標
text = data[i] # 取出內容
cv2.rectangle(img,(box[0],box[1]),(box[2],box[3]),(0,0,255),5) # 繪製外框
putText(box[0],box[3],text,color=(0,0,255)) # 放入文字
cv2.imshow('oxxostudio', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
即时图像辨识 QRCode
参考“读取并播放影片”文章,将读取摄影镜头图像的范例,结合辨识 QRCode 的范例,就能即时透过摄影机,侦测并辨识 QRCode。
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image
cap = cv2.VideoCapture(0)
def putText(x,y,text,color=(0,0,0)):
global img
fontpath = 'NotoSansTC-Regular.otf'
font = ImageFont.truetype(fontpath, 20)
imgPil = Image.fromarray(img)
draw = ImageDraw.Draw(imgPil)
draw.text((x, y), text, fill=color, font=font)
img = np.array(imgPil)
def boxSize(arr):
global data
box_roll = np.rollaxis(arr,1,0)
xmax = int(np.amax(box_roll[0]))
xmin = int(np.amin(box_roll[0]))
ymax = int(np.amax(box_roll[1]))
ymin = int(np.amin(box_roll[1]))
return (xmin,ymin,xmax,ymax)
qrcode = cv2.QRCodeDetector() # QRCode 偵測器
while True:
ret, frame = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(frame,(720,420)) # 縮小尺寸,加快速度
ok, data, bbox, rectified = qrcode.detectAndDecodeMulti(img) # 辨識 QRCode
if ok:
for i in range(len(data)):
text = data[i] # QRCode 內容
box = boxSize(bbox[i]) # QRCode 座標
cv2.rectangle(img,(box[0],box[1]),(box[2],box[3]),(0,0,255),5) # 繪製外框
putText(box[0],box[3],text,color=(0,0,255)) # 顯示文字
cv2.imshow('oxxostudio', img)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
微信扫码关注
抖音扫码关注