目录
抽烟检测的运用
1. 安全监控
(1) 公共场所禁烟监管
- 应用场景:机场、火车站、地铁站、医院、商场、学校等禁烟区域。
- 作用:利用摄像头自动检测吸烟行为,触发警报或通知管理人员干预,减少人工巡逻成本。
(2) 工业安全
- 应用场景:化工厂、加油站、煤矿、仓库等易燃易爆场所。
- 作用:实时监测抽烟行为,防止安全事故,提高生产安全管理。
2. 智能城市与执法
(1) 城市违章吸烟检测
- 应用场景:公交站、公共厕所、电梯、餐厅等区域。
- 作用:结合智能监控系统,对违规吸烟行为进行抓拍、存证,甚至自动处罚。
(2) 无人值守管理
- 应用场景:智能楼宇、写字楼、电影院等无人巡逻区域。
- 作用:通过 AI 检测+语音提醒,劝阻违规吸烟者。
3. 健康管理与医疗
(1) 吸烟习惯分析
- 应用场景:医院、戒烟中心、健康管理 APP。
- 作用:记录个人抽烟次数、时间、环境等数据,帮助戒烟计划制定。
(2) 远程监护
- 应用场景:养老院、精神病院等特殊场所。
- 作用:监测老年人或患者是否有吸烟行为,防止健康风险。
4. AI 监控与商业分析
(1) 保险行业
- 应用场景:人寿保险、健康保险公司。
- 作用:检测投保人是否吸烟,调整保费或健康建议。
(2) 商场营销
- 应用场景:便利店、烟草店。
- 作用:分析吸烟人群的特征,优化营销策略。
5. 技术实现
(1) 计算机视觉
- 算法:基于 YOLO、Faster R-CNN 等目标检测模型。
- 数据:训练数据包含吸烟者的手部、嘴部、烟雾等特征。
(2) 传感器检测
- 红外摄像头:检测烟头的温度特征。
- 空气质量传感器:监测 PM2.5、尼古丁气味等。
(3) 结合物联网(IoT)
- 智能监控摄像头:内置 AI 识别系统,边缘计算本地处理数据。
- 云平台:接收数据并发出警报。
6. 挑战与优化
(1) 误报问题
- 误将吸烟动作与喝水、拿笔等动作混淆。
- 解决方案:使用时间序列分析、骨骼检测等方法提高准确率。
(2) 夜间检测难度
- 夜间光照条件差,普通摄像头难以检测烟雾。
- 解决方案:采用 红外摄像头 结合 AI 算法提高夜间识别率。
(3) 隐私问题
- 监控摄像头涉及个人隐私,可能引发争议。
- 解决方案:使用 边缘计算,仅上传检测结果,不存储人脸信息。
代码实现思路
实现思路
1. 初始化检测模型
- MediaPipe Hands:用于检测 手部位置,得到手的边界框(bounding box)。
- dlib 人脸关键点检测:用于检测 嘴部关键点,确定嘴巴的位置。
- YOLOv3:用于检测 香烟,需要加载权重(
yolov3.weights
)、配置文件(yolov3.cfg
)和类别标签(coco.names
)。
2. 读取视频流
- 通过
cv2.VideoCapture(0)
打开摄像头,逐帧读取视频。
3. 手部检测
- MediaPipe Hands 处理帧图像,返回检测到的手部 关键点。
- 计算手部的 边界框(
x_min, y_min, x_max, y_max
)。 - 使用
cv2.rectangle()
画出手部边界框。
4. 香烟检测
- 通过 YOLOv3 目标检测 识别图像中的物体(包括香烟)。
- 过滤出 类别为 "cigarette" 的目标,并记录香烟的边界框信息(
cigarette_bboxes
)。 - 使用
cv2.rectangle()
画出香烟的位置。
5. 嘴部检测
- 通过 dlib 人脸检测器 定位人脸,并使用 68个面部关键点 识别嘴部(第48-67号点)。
- 计算 嘴部中心位置。
- 用
cv2.polylines()
画出嘴部区域。
6. 抽烟行为判断
- 遍历每只 手的边界框:
- 判断是否持有香烟(手与香烟的 IOU 交并比 是否超过阈值
0.3
)。 - 计算手部到嘴部的距离:
- 获取手部中心
(hand_center_x, hand_center_y)
。 - 计算与 最近的嘴部中心 的欧几里得距离
distance
。
- 获取手部中心
- 综合判断抽烟行为:
- 若 手持香烟 且 距离嘴部<100像素,则判定 正在抽烟。
- 若 手部靠近嘴部<50像素,但未持有香烟,则 可能在抽烟(警告)。
- 判断是否持有香烟(手与香烟的 IOU 交并比 是否超过阈值
7. 可视化输出
- 如果检测到 正在抽烟:
- 在屏幕上显示 "WARNING: Active Smoking Detected!"(红色警告)。
- 如果 疑似抽烟(手靠近嘴但未持烟):
- 显示 "Potential Smoking!"(黄色提示)。
- 画出所有检测到的 手部、香烟、嘴部。
8. 运行主循环
- 不断读取摄像头画面,并调用
detect_smoking(frame)
进行检测。 - 按下
ESC
退出程序。
完整代码
import cv2
import numpy as np
import dlib
import mediapipe as mp
# 初始化MediaPipe手部检测
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
max_num_hands=2,
min_detection_confidence=0.7,
min_tracking_confidence=0.5
)
# 初始化dlib人脸检测
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# 加载YOLOv3模型(需包含自定义训练的香烟类别)
net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers().flatten()]
with open("coco.names", "r") as f:
classes = [line.strip() for line in f.readlines()]
def is_holding_cigarette(hand_bbox, cigarette_bboxes, iou_threshold=0.3):
"""判断手部是否持有香烟(基于IOU)"""
for cig_bbox in cigarette_bboxes:
# 计算IOU
x1 = max(hand_bbox[0], cig_bbox[0])
y1 = max(hand_bbox[1], cig_bbox[1])
x2 = min(hand_bbox[2], cig_bbox[2])
y2 = min(hand_bbox[3], cig_bbox[3])
intersection = max(0, x2 - x1) * max(0, y2 - y1)
area_hand = (hand_bbox[2] - hand_bbox[0]) * (hand_bbox[3] - hand_bbox[1])
area_cig = (cig_bbox[2] - cig_bbox[0]) * (cig_bbox[3] - cig_bbox[1])
iou = intersection / (area_hand + area_cig - intersection)
if iou > iou_threshold:
return True
return False
def detect_smoking(frame):
# 转换为RGB格式(MediaPipe需要)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 手部检测
hand_bboxes = []
results = hands.process(rgb_frame)
if results.multi_hand_landmarks:
for landmarks in results.multi_hand_landmarks:
# 获取手部边界框
x_coords = [lm.x * frame.shape[1] for lm in landmarks.landmark]
y_coords = [lm.y * frame.shape[0] for lm in landmarks.landmark]
x_min, x_max = min(x_coords), max(x_coords)
y_min, y_max = min(y_coords), max(y_coords)
hand_bboxes.append((x_min, y_min, x_max, y_max))
cv2.rectangle(frame, (int(x_min), int(y_min)),
(int(x_max), int(y_max)), (255, 0, 0), 2)
# YOLOv3香烟检测
cigarette_bboxes = []
blob = cv2.dnn.blobFromImage(frame, 0.00392, (320, 320), swapRB=True)
net.setInput(blob)
outs = net.forward(output_layers)
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5 and classes[class_id] == "cigarette":
center_x = int(detection[0] * frame.shape[1])
center_y = int(detection[1] * frame.shape[0])
w = int(detection[2] * frame.shape[1])
h = int(detection[3] * frame.shape[0])
x = center_x - w // 2
y = center_y - h // 2
cigarette_bboxes.append((x, y, x + w, y + h))
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 人脸关键点检测
mouth_positions = []
faces = detector(frame)
for face in faces:
landmarks = predictor(frame, face)
mouth_points = [(landmarks.part(i).x, landmarks.part(i).y)
for i in range(48, 68)]
mouth_center = np.mean(mouth_points, axis=0)
mouth_positions.append(mouth_center)
# 绘制嘴巴区域
cv2.polylines(frame, [np.array(mouth_points, dtype=np.int32)],
True, (0, 0, 255), 2)
# 综合判断逻辑
warning = False
for hand in hand_bboxes:
# 判断是否持烟
holding = is_holding_cigarette(hand, cigarette_bboxes)
# 计算手部中心点
hand_center = ((hand[0] + hand[2]) / 2, (hand[1] + hand[3]) / 2)
# 找最近的人脸
min_distance = float('inf')
for mouth in mouth_positions:
distance = np.sqrt((hand_center[0] - mouth[0]) ** 2 +
(hand_center[1] - mouth[1]) ** 2)
min_distance = min(min_distance, distance)
# 判断条件
if holding and min_distance < 100: # 持烟且距离<100像素
warning = True
elif min_distance < 50: # 未持烟但手部靠近嘴部
cv2.putText(frame, "Potential Smoking!",
(int(hand[0]), int(hand[1]) - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
if warning:
cv2.putText(frame, "WARNING: Active Smoking Detected!",
(20, 50), cv2.FONT_HERSHEY_SIMPLEX,
1, (0, 0, 255), 3, cv2.LINE_AA)
return frame
# 视频处理主循环
cap = cv2.VideoCapture(0)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
frame = cv2.flip(frame, 1) # 镜像翻转
result = detect_smoking(frame)
cv2.imshow('Smoking Detection', result)
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()