1. 项目解读与整体方案
把“MASK检测、行人安全距离检测、bioassay”三个词放进同一个项目标题里,乍看确实有点杂,但落到实际需求上,它们其实是一条完整的人员安全巡检链路。我在做这个Python项目时,将标题拆成了三块:第一块是口罩佩戴识别,判断画面里的人是否戴口罩、戴得规不规范;第二块是行人安全距离检测,在固定监控视角下估算人与人之间的实际间距,低于阈值就告警;第三块bioassay,也就是生物测定,常见落地方式包括远程心率估计、红外体表温度读取和活体检测。三个模块共用同一个目标检测结果,最终输出“谁没戴口罩、谁和谁离得太近、体表状态是否异常”这类结构化信息。
这个项目适合两类人看:一类是刚接触OpenCV和深度学习目标检测的开发者,另一类是做工业安防、园区巡检集成的工程师。如果你只是想单独跑通一个口罩检测模型,网上有大量现成demo,但把口罩检测、距离估计、生物测定串成一套联动系统,需要处理的问题会完全不同。这篇文章会把我踩过的坑、实际用到的代码思路和标定方法都写出来,重点不是堆模型,而是告诉你怎么把三个模块变成一条能稳定跑起来的流水线。
1.1 标题里的三个关键词分别指什么
MASK检测并不是一个新话题。单纯用目标检测模型区分戴口罩和没戴口罩,已经有很多开源数据集和预训练权重可用。但在实际项目里要做的是“人员状态判定”,不是单纯分类:一个人背对镜头时没有脸,能不能判成“未戴口罩”?一个人用手捂嘴,算不算佩戴口罩?一个人虽然戴了口罩但露出了鼻子,该怎么处理?这些问题决定了你面对的不只是模型精度,还有一套基于上下文的规则逻辑。
Pedestrian safety distance检测的本质是单目测距。摄像头的画面是二维透视投影,近处的人大、远处的人小,直接用像素坐标算距离是没有意义的。我在这套系统里采用了两种方法:一种是基于标定的拟合曲线,用人的检测框高度估算距离;另一种是更稳妥的地面单应矩阵法,把图像坐标投影到真实地面坐标后再计算人和人的欧氏距离。后者对横向、纵向距离都能处理,适合安装在固定位置的监控相机。
bioassay这个英文词容易劝退人,它在生物实验里通常指“生物检定”,比如检测某个物质对生物体的活性影响。但在计算机视觉项目中,我把它落地成三个具体能力:一是rPPG远程心率估计,利用面部皮肤颜色变化推算心率;二是红外热像仪体表温度读取;三是活体检测,判断镜头前是真人还是照片,防止通过打印照片绕过口罩检测。这三个能力都不需要接触人体,完全符合“非接触式生物测定”的概念,但要注意,它们只能做筛查参考,不能当作医疗诊断设备。
1.2 这套系统能用在哪些场景
我最初接触这个方向,是因为一个厂区出入口的安全整改需求。厂区要求员工进入生产区域前必须正确佩戴口罩,同时排队时人和人之间要保持安全间距,另外门口还要做体表状态初筛。单独买三套设备不但贵,而且操作流程割裂,所以我想到用一套Python视觉系统把这三个功能统一到同一个摄像头上。
具体落地的场景并不止厂区。比如建筑工地的施工通道,工人经常摘帽摘口罩,人员密集时存在碰撞风险;园区食堂或班车候车区,需要自动提醒保持间隔;还有一些实验机构,需要在入口做人员状态登记,但又不能记录太多可识别身份的信息。这套系统的输出可以设计成只有“编号加告警原因”,例如“P01 未佩戴口罩”“P03 与P05距离过近”“P07 体表温度偏高,建议复测”,所有结果只保留日志和匿名ID,不落地面部图片,这样能减少隐私合规压力。
无论哪种场景,前提都是相机固定安装、角度基本稳定。如果相机被频繁移动,距离标定和背景模型都会失效,这一点在部署时一定要先和现场确认。
1.3 技术选型背后的取舍
技术栈我选的是Python 3.10加OpenCV、YOLOv8、MediaPipe、SciPy和NumPy。检测模型用YOLO系列而不是自己训练的CNN分类器,原因很简单:YOLO一次推理能同时输出人员位置和口罩状态,省去了“先找人、再裁脸、再分类”的串联延迟。虽然也可以把人脸检测和口罩分类拆成两个模型,但两个模型在低算力设备上很难跑满实时,所以我最终采用一个多类别模型,类别设计为person、with_mask、without_mask、mask_incorrect。
距离检测没有选3D结构光或激光雷达,成本和维护都是问题。单目摄像头的精度虽然有限,但在告警场景下足够:我只需要知道“这两个人是不是靠得太近”,不需要精确到厘米。真正要花时间的是标定,而不是换传感器。
生物测定部分,心率估计用普通USB摄像头就能做,体温测量则建议单独接一个红外热成像模块。活体检测可以用MediaPipe的人脸关键点眨眼逻辑,纯CPU也能跑。整体系统我先在本地CPU上跑通,再迁移到带GPU的边缘设备,这样调试成本和部署成本都比较可控。
2. 核心模块原理与实现要点
2.1 口罩佩戴检测:数据决定算法上限
口罩检测模型看起来简单,真正做起来容易翻车。我一开始用的策略是“先检测人,再检测人脸,最后给人脸分类是否戴口罩”,但实际发现人头在画面里占比很小,人脸检测模型经常漏检,特别是在背光和侧脸场景下。后来改成统一YOLO模型,让模型同时输出人物框和口罩状态框,情况才好转。
类别设计上,我建议不要只分“戴口罩”和“没戴口罩”二类。加入一个“佩戴不规范”类,比如口罩拉到下巴、露出鼻子,这类样本能显著减少漏报。数据集不能光靠公开数据,一定要自己采集一部分现场照片,因为公开数据集很难覆盖你们实际现场的光照、摄像头高度和人员穿着。我在训练前对数据做了几个处理:把所有图统一缩放到640分辨率,随机调整亮度对比度,增加HSV扰动,模拟早晚光线变化。口罩颜色也要尽量覆盖,特别是黑色和深蓝色口罩,因为很多模型对深色口罩的响应很弱。
推理时我做了归属逻辑:一个人脸框必须落在某个人员框内部,才能把口罩状态绑定到这个人员身上。如果画面里有人背对摄像头,模型没有检测到人脸,这时候不要直接判成“未戴口罩”,而是输出“未采集到人脸”。这一点很重要,否则现场会出现大量误报,管理者很快就会失去对系统的信任。
训练时还有一个容易忽略的点:类别不平衡。很多数据集里“没戴口罩”的样本比“戴口罩”的样本少得多,模型会倾向于把所有脸都预测成“已戴口罩”。我的做法是在训练时不只要均衡采样,还要在每轮训练里随机丢弃一部分“已戴口罩”样本,让模型在少数类别上不过度衰减。微调阶段用YOLOv8m,关闭Too aggressive的增强,大概训练50个epoch就能达到实用水平。
2.2 行人安全距离检测:不要迷信模型,标定才是关键
单目相机测距的原理很多人背得出来:近大远小。但放到实际项目中,不能指望模型直接输出“某个人距离摄像头3.2米”,更可行的是先做标定,再拟合关系。
我最先尝试的是根据YOLO检测框高度拟合距离。具体做法是:把相机固定在现场的实际高度和角度,让人站在离镜头1米、2米、3米、5米的位置,分别记录检测框的高度,然后用指数函数d = a * exp(-b * h)去拟合距离和像素高的关系。这种方法实现很快,不需要测量相机内参,对固定位置的人群距离告警完全够用。但要注意,它隐含的假设是“行人身高基本一致”,如果有人推着设备或弯腰,检测框高度会异常,距离结果自然不准。
更稳的方案是地面单应矩阵。在画面地面取四个角点,测量它们在真实世界里的相对坐标,然后用OpenCV的findHomography算出图像平面到地面平面的单应矩阵。之后画面里每个人员框的底部中心点都可以通过perspectiveTransform映射成地面坐标,再用平面欧氏距离判断人和人的间距。这种方法能把透视效应完全去掉,远处的人和近处的人之间的距离计算才公平。
两种方法我都保留了。单应矩阵法作为主方案,拟合曲线法作为相机标定失效时的兜底。不管用哪种,都要记住一点:安全距离不是绝对的物理边界。我一般把“1.5米”设置成默认阈值,但会根据现场环境调整,比如走廊、闸机通道,物理宽度本身就窄,一味设大阈值只会导致告警刷屏。
2.3 生物测定模块:从rPPG心率到热成像体温
bioassay部分从我理解上分三条线,分别应对不同的应用需求。
第一条线是rPPG远程心率估计。原理是心脏泵血会导致面部皮肤颜色出现极其微弱的周期性变化,摄像头能捕捉到这种变化,虽然肉眼看不见。实现时选取额头中间区域作为感兴趣区,连续采集该区域的颜色均值,提取绿色通道或者红色通道的时序信号,经过带通滤波后做FFT,取频谱峰值对应的心率值。实际操作中,环境光稳定时效果还不错,但一旦人开始转头或者光线被遮挡,信号就会崩。所以我的代码里加入了运动检测,当检测到头部大范围移动时,直接丢弃这段时间的帧。
第二条线是红外热成像体温。普通摄像头没法测温,需要外接热像仪模块。我用的方案是读取热像仪输出的温度矩阵,同时把热像仪和可见光摄像头做粗略的像素对齐,再把YOLO检测到的人脸框映射到温度矩阵上,取人脸区域的最高温度作为体表温度参考。皮肤发射率一般设置为0.98,测量距离控制在1到2米以内。这条线只能做初筛,不能直接当临床体温,交付时要明确标注“非医疗设备”。
第三条线是活体检测。很多安防系统担心有人拿照片混过口罩检测,bioassay在这里可以指“判断镜头前是不是真人”。我用MediaPipe的468点人脸网格提取眼睛关键点,计算睁眼纵横比EAR,检测自然的眨眼动作。真人的眨眼频率通常在每分钟15到20次,而且单次眨眼时间很短,照片和视频很难模拟出这种动态模式。把这条线加进去后,整个项目的bioassay语义就完整了。
2.4 三个模块的联动逻辑
三个模块不是各跑各的,而是共享一条流水线:每一帧进来,先跑YOLO模型,得到所有人员框和口罩状态框;再把人脸框归属到人员框上,得到“哪个人员戴没戴口罩”;随后取出每个人员框的底部中心坐标,通过单应矩阵映射成地面坐标,计算两两距离;最后,针对每张人脸区域,采集一定帧数的心率信号,同时读取热成像温度,得到生理指标。
联动后的告警规则应该具备叠加能力。比如两个人距离小于阈值,其中一个人没有正确佩戴口罩,这是一类告警;一个人体表温度超过设定参考值,这又是一类告警。多条规则可以同时命中,我倾向于把告警分成不同级别:距离过近是黄色,未佩戴口罩是橙色,距离过近且未戴口罩是红色,体表温度偏高则单独提示复测。这些告警写入日志,并叠画在视频画面上,方便值守人员第一时间定位位置。
这套联动逻辑最大的意义是减少人工低头看多个屏幕的工作量。管理者只需要盯一块屏,看到颜色变化就能知道该处理什么。这也是我把三个标题词合并成一个系统的主要原因。
3. 实操过程:从环境搭建到整机联调
3.1 环境准备与依赖安装
我建议用Python 3.10以上的版本,避免一些库在新老版本之间出现兼容问题。创建虚拟环境后,安装以下主要依赖:
pip install opencv-python numpy scipy ultralytics mediapipe如果要用GPU训练或推理,再补上对应版本的PyTorch。没有GPU也能跑,只是推理帧率会比较低,建议先在CPU上验证逻辑,再迁移到带GPU的边缘设备。
摄像头方面,普通USB摄像头就能满足口罩检测和距离检测。如果要做rPPG,尽量选择帧率稳定、手动关闭自动白平衡的摄像头,因为自动白平衡会不断调整颜色,严重影响rPPG信号质量。热成像模块按需选型,体积小、API简单的型号比较适合原型验证。
准备模型权重时,不要试图从零训练一个大模型。用YOLOv8在公开人员/口罩数据集上做迁移学习,把你的自定义类别替换进去,训练量会小很多。模型文件我命名为mask_person.pt,方便在代码里调用。
3.2 口罩检测核心代码
加载模型并执行推理的代码很简单,关键是要把检测结果结构化,方便后续模块使用。
from ultralytics import YOLO import cv2 model = YOLO("mask_person.pt") def run_mask_detect(frame): results = model(frame, conf=0.35, iou=0.45, verbose=False) detections = [] for r in results: for box in r.boxes: x1, y1, x2, y2 = [int(v) for v in box.xyxy[0]] cls_id = int(box.cls[0]) conf = float(box.conf[0]) label = model.names[cls_id] detections.append({ "bbox": (x1, y1, x2, y2), "cls": label, "conf": conf }) return detections拿到检测结果后,我会做一次关联处理:遍历所有类别为“person”的框,再遍历所有口罩状态框,如果口罩状态框中心点落在某个person框内,就把它绑定到那个人上。这一步能把“person A with_mask”“person B without_mask”这样的信息整理成结构化列表。
def associate_mask_to_person(detections): persons = [d for d in detections if d["cls"] == "person"] masks = [d for d in detections if d["cls"] in ("with_mask", "without_mask", "mask_incorrect")] for p in persons: p["mask_status"] = "unknown" px1, py1, px2, py2 = p["bbox"] for m in masks: mx1, my1, mx2, my2 = m["bbox"] mx_center = (mx1 + mx2) / 2.0 my_center = (my1 + my2) / 2.0 if px1 <= mx_center <= px2 and py1 <= my_center <= py2: p["mask_status"] = m["cls"] break return persons注意:如果一个人脸上同时出现多个口罩状态框,只保留与person框重叠面积最大、置信度最高的那个,避免同一个人员被打上互相矛盾的标签。
3.3 行人距离检测与告警实现
距离检测我同时实现了两种方式。拟合曲线法适合快速验证,单应矩阵法适合正式部署。
拟合曲线法的标定代码:
import numpy as np from scipy.optimize import curve_fit def fit_distance_curve(pixel_heights, real_distances): def func(h, a, b): return a * np.exp(-b * h) popt, _ = curve_fit(func, pixel_heights, real_distances, p0=[5, 0.01]) return popt标定时,我记录几组人员框高度和对应的真实距离。得到拟合参数后,运行时直接传入框高,就能返回估算距离。
单应矩阵法的标定代码:
import cv2 import numpy as np # 图像中的四个地面标定点 src_pts = np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]], dtype=np.float32) # 对应的真实平面坐标,单位米 dst_pts = np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=np.float32) H, _ = cv2.findHomography(src_pts, dst_pts) def to_world_coords(image_points): pts = np.array(image_points, dtype=np.float32).reshape(-1, 1, 2) return cv2.perspectiveTransform(pts, H).reshape(-1, 2)有了世界坐标后,距离告警逻辑就很简单:
MIN_DIST = 1.5 def check_distance(world_points, mask_status_list): alerts = [] n = len(world_points) for i in range(n): for j in range(i + 1, n): dist = np.linalg.norm(world_points[i] - world_points[j]) if dist < MIN_DIST: if mask_status_list[i] != "with_mask" or mask_status_list[j] != "with_mask": alerts.append((i, j, dist)) return alerts这里取的是人员框底部中心点,而不是整个人体中心。底部中心点更接近人在地面上的落脚位置,在单应矩阵变换下,误差会更小。如果是剧烈运动、检测框抖动厉害的场景,建议先对框坐标做简单的一阶低通滤波,否则距离结果会来回跳。
3.4 生物测定集成与结果输出
rPPG心率估计需要一段时间窗口,不能单帧完成。我维护一个固定长度的信号队列,每次取得人脸ROI的颜色均值后加入队列,当队列长度达到采样窗口后再计算心率。
import numpy as np from scipy.signal import butter, filtfilt class RppgHeartRate: def __init__(self, fps, buffer_size=150): self.fps = fps self.buffer_size = buffer_size self.signal = [] self.valid = False def add_roi_mean(self, roi_mean): # 使用绿色通道,也可以根据设备换成红色通道 self.signal.append(roi_mean[1]) if len(self.signal) > self.buffer_size: self.signal.pop(0) self.valid = len(self.signal) == self.buffer_size def _bandpass(self, sig): nyq = 0.5 * self.fps low = 0.75 / nyq high = 3.0 / nyq b, a = butter(order=4, Wn=[low, high], btype="band") return filtfilt(b, a, sig) def get_heart_rate(self): if not self.valid: return None sig = np.array(self.signal, dtype=np.float32) sig -= np.mean(sig) filtered = self._bandpass(sig) fft = np.fft.rfft(filtered) freqs = np.fft.rfftfreq(len(filtered), 1.0 / self.fps) # 只统计0.75~3Hz范围,对应45~180 bpm mask = (freqs >= 0.75) & (freqs <= 3.0) if not np.any(mask): return None peak_freq = freqs[mask][np.argmax(np.abs(fft[mask]))] return int(peak_freq * 60)人脸ROI的选取我固定在额头中央区域。为了减少背景干扰,我用Mask检测获得的人脸框,然后取人脸框上部20%到40%高度的区域作为额头ROI。每次加入信号前,还要根据上一帧ROI与当前帧ROI的像素差判断头部是否发生大幅移动,超过阈值就清空队列重新积累。
热成像部分的代码相对独立,需要读取硬件串口或SDK返回的温度矩阵。我不在这里贴特定型号代码,只强调一个关键点:可见光人脸框和热像仪温度矩阵之间的映射关系要提前标定,否则测出来的温度会是旁边墙壁的温度。
活体检测采用眨眼判断。用MediaPipe的FaceMesh输出眼睛关键点,计算左右眼的EAR值,当EAR快速下降到阈值以下又回升,就记录一次眨眼。
3.5 多线程与性能优化
整个流水线如果每一帧都同步执行目标检测、距离计算、生物测定和绘制,帧率会非常难看。我采用两条线程:主线程读取摄像头帧,放入队列;推理线程从队列取出帧,跑完整流水线,再把结果放回另一个队列用于显示。这样可以隔离IO阻塞和推理耗时。
import threading import queue frame_queue = queue.Queue(maxsize=2) result_queue = queue.Queue(maxsize=2) def capture_loop(cap): while True: ok, frame = cap.read() if not ok: break if frame_queue.full(): try: frame_queue.get_nowait() except queue.Empty: pass frame_queue.put(frame) def inference_loop(): while True: frame = frame_queue.get() persons = run_mask_detect(frame) world_points = [to_world_coords(p["box_bottom_center"]) for p in persons] alerts = check_distance(world_points, [p["mask_status"] for p in persons]) # bioassay模块按需更新 result_queue.put((frame, persons, alerts))我还会根据硬件能力做几项性能取舍:输入图片固定为640分辨率,YOLO推理置信度设为0.35,NMS阈值0.45;检测到人员后再裁剪人脸区域单独做活体检测,而不是整帧跑MediaPipe;生物测定不需要每帧都算心率,每30帧更新一次即可。这样配置下来,在普通CPU设备上勉强能到每秒10帧以上,在GPU设备上跑实时没有问题。
4. 常见问题与排查技巧
4.1 距离总是测不准
测量不准的原因很多,但排在第一位的是相机标定没做好。相机高度、俯仰角一变,整个映射关系就变了。我见过有人把标定用的桌面支架换成三脚架后,没有重新标定就开始跑,结果距离数据全部偏大。标定操作必须和实际部署位置完全一致,并且在相机被挪动后主动触发重标定。
第二个原因是YOLO检测框不稳定。行人框高度受遮挡、姿态、服装影响很大,直接使用框底中心会抖动。我建议检测框先做卡尔曼滤波或指数平滑。第三个原因是把远近目标一视同仁地套用同一个拟合曲线。不同身高的人,同一像素高的实际距离并不一样,所以单目测距只能做区间估计。
| 现象 | 主要原因 | 解决思路 |
|---|---|---|
| 距离整体偏近 | 拟合曲线参数偏差 | 增加更多标定距离点,重拟合 |
| 距离忽远忽近 | 检测框抖动 | 对框坐标做时序平滑 |
| 远处距离不更新 | 小目标容易漏检 | 提高输入分辨率或降低置信度阈值 |
| 横向距离偏大 | 未校正透视 | 改用地面单应矩阵 |
4.2 口罩检测把围巾当成口罩
这类误报相当常见。围巾、领子、手臂遮挡都可能被模型误识别成口罩。我处理时从两条路下手:一条是增加难例样本,专门采集围巾遮脸、手捂嘴、透明面罩等照片;另一条是用人脸关键点做后置校验,确认口罩应该覆盖的人脸下半部区域是否真的被异物遮挡,而不是只看分类模型输出。
如果模型在某个场景下反复误判,我建议保留“不确定”状态。不要强制二分类,而是输出三类:已佩戴口罩、未佩戴口罩、不确定。告警规则里“不确定”和“未佩戴口罩”合并处理,但系统日志里要区分开,方便后续分析是哪类样本导致模型失效。
4.3 rPPG心率结果跳得厉害
rPPG的坑主要在信号质量。我用的是普通USB摄像头,默认开启自动白平衡,导致颜色均值不断偏置,心率结果自然不稳定。关掉自动白平衡后情况立刻改善。另外,头部只要稍微动一下,ROI区域就会滑到背景上,所以我加了帧间位移检测,位移过大时清空历史信号重新积累。
滤波参数也要讲道理。正常人心率在45到180次/分钟之间,对应频率大约0.75到3Hz。我设置带通滤波后,还要对输出做滑动窗口中位数处理,单次FFT的峰值经常不稳定,但连续20次结果的中位数能稳定很多。
| 现象 | 主要原因 | 解决思路 |
|---|---|---|
| 心率偏高 | 环境光频闪干扰 | 加带通滤波,关闭自动白平衡 |
| 心率突然消失 | 头部移动导致ROI漂移 | 检测位移,超出阈值则清空队列 |
| 心率值跳变 | 单次FFT不稳定 | 对多个窗口取中位数 |
| 额头区域曝光过度 | 强光直射 | 降低曝光,避开阳光直射 |
4.4 热成像测温偏差过大
热成像不是简单读一个温度数据就能用的。皮肤发射率、环境反射温度、测温距离都会影响结果。我在实验中发现,如果目标的距离超过3米,温度读数会明显偏低,所以体温初筛必须让人在距离镜头1到2米的区域停留片刻再读。发射率设为0.98比较接近人体皮肤,但环境里如果有高反射物体,比如金属门框,也会把环境温度反射进传感器的视场角。
另外,不要用单帧最高温度做判断,而应该取人脸区域在2秒内的最高温再和参考值比对。我给这个模块的定位是筛查提示,不是诊断工具。如果有人体表温度超过设定值,系统只输出“建议复测”而不是“发热”,这一点无论从合规角度还是从现场接受度来说都更稳妥。
4.5 整体系统卡顿掉帧
卡顿的根源基本是目标检测模型太大、输入分辨率太高、绘制操作太频繁。我调整过一轮之后,效果明显:把YOLOv8的输入分辨率从1280降到640,推理时间直接减半;绘制过程只用薄边框和文字标签,不再叠加半透明蒙版;心率计算从每帧改为每30帧更新一次。
如果设备算力实在不够,可以把口罩检测模型换成更轻的版本,或者用ONNX Runtime、OpenVINO这类推理后端做加速。对于固定机位监控场景,还可以每隔几帧跑一次完整YOLO,相邻帧之间用简单的目标跟踪补位。跟踪算法用OpenCV自带的跟踪器即可,不需要额外引入重模型。
5. 落地经验与个人体会
5.1 现场部署先谈合规,再说效果
这个项目涉及人脸、人体距离和生理信号,都不是可以随便采集的数据。我在给某厂区部署时,第一条不是谈算法精度,而是确认摄像头安装位置是否合理、是否需要告知标识、数据保留多久。最终方案只保存告警日志和匿名ID,原图不落盘,生物测定结果也不与员工身份直接绑定。这个原则必须在项目一开始就写清楚,否则后面需求一变,返工成本很高。
我强烈建议在代码里加入数据最小化策略:只在内存里做分析,检测完成即丢弃,除非触发告警才保存一张打码后的截图。这样既满足了安全巡检需求,又避免了“变成一个监控系统”的观感问题。对于个人开发者而言,不管给别人做demo还是给自己做研究,都应该把这条当作默认配置。
5.2 从原型到产品还差什么,以及我最后的建议
原型能跑只是第一步。我后续至少补了三样东西:一个是相机标定工具,用图形界面点击标定点并自动计算单应矩阵;二是配置化告警参数,让现场人员不用改代码就能调整距离阈值和温度阈值;三是重启恢复逻辑,边缘设备断电重启后进程能自动拉起,摄像头画面丢失时能主动报警。
如果让我给刚接触这个项目的人一个建议,那就是不要上来就追求每个模块的精度。先让所有人、口罩状态、距离、生物测定这几个模块能在一条流水线里输出结果,把逻辑跑通,再回头优化精度。我在早期卡了很久,不是因为模型不好,而是因为距离模块和口罩模块之间的人员ID对不上,后来把数据流整理成统一的person列表,问题才真正解决。项目标题里的三个词看起来各自独立,但只有把它们串成一个整体,这个项目的价值才真正体现出来。