任务三
使用计算机读取视频或者摄像头,实时检测图像中的二维码(可用手机屏幕显示二维码),并将二维码的四角位置实时叠加在显示画面中,控制台打印输出二维码内容。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import cv2 from PIL import Image, ImageDraw, ImageFont import numpy as np
def draw_text(img, text, pos, font, color): img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(img_pil) draw.text(pos, text, font=font, fill=color) return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
cap = cv2.VideoCapture(0)
qr_code_detector = cv2.QRCodeDetector()
font_path = "simhei.ttf" font = ImageFont.truetype(font_path, 24)
while True: ret, frame = cap.read() if not ret: break
retval, decoded_info, points, straight_qrcode = qr_code_detector.detectAndDecodeMulti(frame)
if retval: for info in decoded_info: print("QR Code Content:", info)
if points is not None: points = points.astype(int) for i in range(len(points)): for j in range(4): cv2.line(frame, tuple(points[i][j]), tuple(points[i][(j + 1) % 4]), (0, 255, 0), 3) text = f"{points[i]}" frame = draw_text(frame, text, (10, 30 + i * 30), font, (0, 255, 0)) else: frame = draw_text(frame, "未检测到二维码", (10, 30), font, (255, 0, 0))
cv2.imshow("QR Code Detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'): break
cap.release() cv2.destroyAllWindows()
|
代码思路
先将需要的资源进行初始化,初始化摄像头资源,二维码检测器,并预先写好字体显示函数,要注意PIL库和openCV库的色彩顺序分别为RGB和BGR。然后在循环内持续读取视频帧实现连续播放,退出循环后的release()和destroyAllWindows()函数释放资源。循环内部先读取视频帧,然后对视频帧使用detectAndDecodeMulti()函数进行检测读取内容。使用line()函数依次连接判断出的二维码四角。。
代码解释
1
| def draw_text(img, text, pos, font, color):
|
定义一个函数draw_text
,用于在图像上绘制文本。
img
: 输入的 OpenCV 图像(NumPy 数组格式)。
text
: 要绘制的文本内容。
pos
: 文本绘制的起始位置(左上角坐标)。
font
: 使用的字体对象。
color
: 文本颜色(BGR 格式)。
1
| img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
将 OpenCV 图像(BGR 格式)转换为 RGB 格式,并使用 Image.fromarray 将其转换为 PIL 图像对象
1
| draw = ImageDraw.Draw(img_pil)
|
创建一个 ImageDraw 对象,用于在 PIL 图像上绘制图形或文字。
1
| draw.text(pos, text, font=font, fill=color)
|
在 PIL 图像上绘制文本。
pos
: 文本绘制的起始位置。
text
: 要绘制的文本内容。
font
: 使用的字体对象。
fill
: 文本的颜色
1
| return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
|
将 PIL 图像转换回 NumPy 数组,并将其从 RGB 格式转换回 BGR 格式,以便与 OpenCV 兼容
1
| cap = cv2.VideoCapture(0)
|
初始化摄像头,0 表示默认摄像头。
1
| qr_code_detector = cv2.QRCodeDetector()
|
创建二维码检测器对象qr_code_detector
,用于检测和解码二维码。
1
| font_path = "simhei.ttf"
|
设置字体,simhei.ttf 是黑体字体文件,用于支持中文显示。
1
| font = ImageFont.truetype(font_path, 24)
|
加载指定路径的字体文件,并设置字体大小为 24。
摄像头传输数据连续,读取摄像头数据时需要一个无限循环保证连续读取视频帧
读取摄像头的一帧图像,返回值分别赋给ret
和frame
ret
: 布尔值,表示是否成功读取帧。
frame
: 读取到的图像帧(NumPy 数组格式)。
如果未能成功读取帧ret
为0,退出循环,结束运行。
1
| retval, decoded_info, points, straight_qrcode = qr_code_detector.detectAndDecodeMulti(frame)
|
使用二维码检测器检测并解码图像中的二维码。
retval
: 布尔值,表示是否成功检测到二维码。
decoded_info
: 解码后的二维码内容列表。
points
: 检测到的二维码的四角坐标列表。
straight_qrcode
: 矫正后的二维码图像
1 2 3
| if retval: for info in decoded_info: print("QR Code Content:", info)
|
如果成功检测到二维码,将解码后的二维码内容输出。
使用for
循环是因为detectAndDecodeMulti()
的返回值为列表,可能有多个元素。
1 2
| if points is not None: points = points.astype(int)
|
如果二维码的四角坐标不为空,将坐标数据类型转换为整数。
为什么要转化为整数?
OpenCV 的绘图函数(后面用到了cv2.line()
函数)要求坐标点的值必须是整数。
points 是通过二维码检测器返回的坐标数组,可能包含浮点数。如果直接使用返回的坐标,函数会报错。
1 2 3
| for i in range(len(points)): for j in range(4): cv2.line(frame, tuple(points[i][j]), tuple(points[i][(j + 1) % 4]), (0, 255, 0), 3)
|
遍历每个二维码的四角坐标,并绘制绿色线条连接四个角点,形成矩形框。
(0, 255, 0) 表示绿色,3 表示线条宽度。
1 2
| text = f"{points[i]}" frame = draw_text(frame, text, (10, 30 + i * 30), font, (0, 255, 0))
|
将二维码的四角坐标转换为字符串,并调用 draw_text 函数在图像左上角显示坐标信息。
1 2
| else: frame = draw_text(frame, "未检测到二维码", (10, 30), font, (255, 0, 0))
|
如果未检测到二维码,在图像左上角显示“未检测到二维码”文本,颜色为红色。
1
| cv2.imshow("QR Code Detection", frame)
|
使用 OpenCV 显示处理后的图像帧,在循环之中连续播放的帧成为的实时的视频画面,窗口标题为“QR Code Detection”。
1 2
| if cv2.waitKey(1) & 0xFF == ord('q'): break
|
检查用户是否按下键盘上的 ‘q’ 键,如果按下,则退出循环。如果不是视频不能这样写。
释放摄像头资源。
关闭所有 OpenCV 窗口。
运行结果


opencv #二维码 #QR #二维码识别