目錄
抽煙檢測的運用
1. 安全監控
(1) 公共場所禁煙監管
(2) 工業安全
2. 智能城市與執法
(1) 城市違章吸煙檢測
(2) 無人值守管理
3. 健康管理與醫療
(1) 吸煙習慣分析
(2) 遠程監護
4. AI 監控與商業分析
(1) 保險行業
(2) 商場營銷
5. 技術實現
(1) 計算機視覺
(2) 傳感器檢測
(3) 結合物聯網(IoT)
6. 挑戰與優化
(1) 誤報問題
(2) 夜間檢測難度
(3) 隱私問題
代碼實現思路
實現思路
1. 初始化檢測模型
2. 讀取視頻流
3. 手部檢測
4. 香煙檢測
5. 嘴部檢測
6. 抽煙行為判斷
7. 可視化輸出
8. 運行主循環
完整代碼
效果展示
抽煙檢測的運用
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:# 計算IOUx1 = 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 Truereturn Falsedef 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 // 2y = center_y - h // 2cigarette_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 = Falsefor 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 = Trueelif 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:breakframe = cv2.flip(frame, 1) # 鏡像翻轉result = detect_smoking(frame)cv2.imshow('Smoking Detection', result)if cv2.waitKey(1) == 27:breakcap.release()
cv2.destroyAllWindows()