Skip to main content

口罩识别

前言

识别人脸是否戴口罩。

实验目的

识别人脸是否有配戴口罩并通过写字符和画图指示。

实验讲解

口罩识别相关模型已经预先训练好,存放在代码同一目录下,我们只需要加载模型文件,使用kpu库进行推理,并将返回的结果画图并显示即可。

具体编程思路如下:

参考代码

'''
实验名称:口罩识别
实验平台:CyberCAM
'''

import cv2, time, os
from walnutpi import Display,Sensor,kpu,IDE,direction

# 优先当前文件夹下相对路径(app离线部署)
if os.path.exists("./face_detection_320.kmodel") and os.path.exists("./prior_data_320.bin") and os.path.exists("./face_mask.kmodel"):
det_model_path = "./face_detection_320.kmodel"
mask_model_path = "./face_mask.kmodel"
anchors_path = "./prior_data_320.bin"

# 使用系统绝对路径(IDE运行调试)
elif os.path.exists("/data/app/mask-det/face_detection_320.kmodel") and os.path.exists("/data/app/mask-det/prior_data_320.bin") and os.path.exists("/data/app/mask-det/face_mask.kmodel"):
det_model_path = "/data/app/mask-det/face_detection_320.kmodel"
anchors_path = "/data/app/mask-det/prior_data_320.bin"
mask_model_path = "/data/app/mask-det/face_mask.kmodel"
else:
raise FileNotFoundError("模型文件缺失,请检查当前路径与系统路径下的模型文件是否存在。")

model_size = 320 #模型尺寸
detector = kpu.FACE_MASK(det_model_path, anchors_path, model_size, mask_model_path) #加载模型

#字符显示改进,支持中英文显示
ft = cv2.freetype.createFreeType2() #创建freetype渲染器
ft.loadFontData("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", 0) #加载字体文件, 文泉驿正黑

def putText_Chinese(img, text, org, fontScale=30, color=(0, 255, 0)):

global ft # 使用全局的 FreeType 渲染器实例

# 绘制中文
ft.putText(
img=img,
text=text,
org=org,
fontHeight=fontScale,
color=color,
thickness=-1, # 笔画粗细
line_type=cv2.LINE_AA, # 抗锯齿,文字更平滑
bottomLeftOrigin=True # False:坐标为左上角; True:与原生cv2.putText一致(左下角)
)
return img

# 初始化屏幕
Display.init()

# 初始化摄像头
cap = Sensor.Sensor(640, 480)
if not cap.isOpened():
print("Cannot open camera")
exit()

#获取当前显示屏方向,0表示默认,2表示180度翻转。
lcd_dir=direction.get_lcd()
#print(lcd_dir)

# 判断显示屏是否翻转,如果翻转,则设置显示旋转180°,摄像头同时设置为前置模式(水平镜像)
if lcd_dir == 2: #翻转了
Display.set_rotation(2)
cap.set_hmirror(1)

# ========== FPS计算 ==========
frame_count = 0 # 帧数计数器
start_time = time.time()
fps = 0.0

while True:

# 摄像头读取一帧图像
ret, img = cap.read()

# 执行目标检测,设置置信度阈值为 0.6,IoU 阈值为 0.7
results = detector.run(img, 0.6, 0.7)

# 打印并绘制结果
for result in results:

# 打印结果,当人脸数量过多时会降低识别帧率。
'''
# ========== 人脸框坐标 ==========
print("=" * 40)
print(f"人脸框: 左上角({result.x}, {result.y}) 宽:{result.w} 高:{result.h}")
print(f"置信度: {result.reliability:.3f}")

# ========== 5个关键点坐标 ==========
print(f"左眼 : ({result.left_eye.x:3d}, {result.left_eye.y:3d})")
print(f"右眼 : ({result.right_eye.x:3d}, {result.right_eye.y:3d})")
print(f"鼻子 : ({result.nose.x:3d}, {result.nose.y:3d})")
print(f"左嘴角 : ({result.left_mouth.x:3d}, {result.left_mouth.y:3d})")
print(f"右嘴角 : ({result.right_mouth.x:3d}, {result.right_mouth.y:3d})")
print("=" * 40)
'''

# 将口罩概率大于0.6的视为戴口罩
is_mask = result.mask > 0.6

color = (0, 0, 255)
if is_mask :
color = (0, 255, 0)
# 绘制 人脸识别框
cv2.rectangle(img, (result.x, result.y),
(result.x + result.w, result.y + result.h),
color, 2)

label = f"{result.reliability:.2f}"
if is_mask :
label += "戴口罩"
else :
label += "无"

# 绘制戴口罩的概率
putText_Chinese(img, label, (result.x+5, max(result.y-5, 24)), fontScale=30, color=color)

# 绘制 5 个关键点
cv2.circle(img, (result.left_eye.x, result.left_eye.y), 4, (0, 0, 255), -1) # 左眼
cv2.circle(img, (result.right_eye.x, result.right_eye.y), 4, (0, 255, 255), -1) # 右眼
cv2.circle(img, (result.nose.x, result.nose.y), 4, (255, 0, 255), -1) # 鼻子
cv2.circle(img, (result.left_mouth.x, result.left_mouth.y), 4, (0, 255, 0), -1) # 左嘴角
cv2.circle(img, (result.right_mouth.x, result.right_mouth.y), 4, (255, 0, 0), -1) # 右嘴角

# 每满1秒计算一次平均FPS
frame_count += 1
current_time = time.time()
if current_time - start_time >= 1.0:
fps = frame_count / (current_time - start_time)
frame_count = 0 # 重置帧数计数器
start_time = current_time # 重置计时起点
print("FPS: ", f'FPS: {fps:.1f}')

# 添加文字标签和FPS显示
cv2.putText(img, f'FPS: {fps:.1f}', (10, 30), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)

Display.show(img) #显示到屏幕上
IDE.show(img) # 发送到ide窗口内显示

实验结果

运行代码,将摄像头对准戴口罩人员,可以看到被正确的识别出来,支持多个口罩识别。

  • 戴口罩场景:

原图:

hand_detection

识别结果:

hand_detection

  • 未戴口罩场景:

原图:

hand_detection

识别结果:

hand_detection