YuAnTsai commited on
Commit
8f9405e
·
verified ·
1 Parent(s): 698dff7

Update emotion.py

Browse files
Files changed (1) hide show
  1. emotion.py +30 -18
emotion.py CHANGED
@@ -5,7 +5,6 @@ from typing import Optional, Dict, Tuple, List
5
  import gradio as gr
6
  import numpy as np
7
  import cv2
8
- from PIL import Image
9
  import mediapipe as mp
10
  from mediapipe.tasks import python
11
  from mediapipe.tasks.vision import FaceDetector, FaceDetectorOptions, RunningMode
@@ -15,7 +14,7 @@ from deepface import DeepFace
15
  # =========================
16
  # 參數與 MediaPipe 初始化
17
  # =========================
18
- ANALYZE_EVERY_SEC = 0.6 # MediaPipe 很快,可以稍微縮短間隔
19
  DOWNSAMPLE_W = 720
20
  EMA_ALPHA = 0.45
21
  CONF_TH = 40.0
@@ -23,7 +22,7 @@ SWITCH_CONFIRM_SEC = 2.0
23
  IOU_TH = 0.25
24
  ZOOM_FACTOR = 1.3
25
 
26
- # MediaPipe 設定
27
  MODEL_PATH = 'model/blaze_face_short_range.tflite'
28
  base_options = python.BaseOptions(model_asset_path=MODEL_PATH)
29
  options = FaceDetectorOptions(
@@ -109,36 +108,49 @@ def _zoom_center(frame: np.ndarray, zoom: float) -> np.ndarray:
109
  return frame[sy:sy+nh, sx:sx+nw]
110
 
111
  # =========================
112
- # MediaPipe 偵測邏輯
113
  # =========================
114
  def get_face_mediapipe(frame: np.ndarray, prev_bbox: Optional[Tuple[int, int, int, int]]):
115
  h, w = frame.shape[:2]
 
116
  mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame)
117
  res = mp_detector.detect(mp_image)
118
 
119
  if not res.detections:
120
- return None, None
121
 
122
  candidates = []
123
  for det in res.detections:
124
  b = det.bounding_box
125
- # 數值校正,避免超出邊界導致 DeepFace 報錯
126
  nx, ny = max(0, b.origin_x), max(0, b.origin_y)
127
  nw, nh = min(b.width, w - nx), min(b.height, h - ny)
 
 
 
 
 
 
 
 
 
 
128
  if nw > 10 and nh > 10:
129
  candidates.append(((nx, ny, nw, nh), frame[ny:ny+nh, nx:nx+nw]))
130
 
131
  if not candidates:
132
- return None, None
133
 
134
- # 優先匹配上一幀的位置
 
135
  if prev_bbox:
136
  best_match = max(candidates, key=lambda c: _iou(prev_bbox, c[0]), default=(None, None))
137
- if best_match[0] and _iou(prev_bbox, best_match[0]) > IOU_TH:
138
- return best_match
139
 
140
- # 否則回傳面積最大者
141
- return max(candidates, key=lambda c: c[0][2] * c[0][3])
 
 
142
 
143
  # =========================
144
  # Gradio 事件處理
@@ -160,20 +172,20 @@ def on_stream(frame_rgb: np.ndarray, st: AppState):
160
 
161
  st.last_analyze_t = now
162
 
163
- # 使用 MediaPipe 找臉
164
- bbox, face_crop = get_face_mediapipe(frame_rgb, st.track_bbox)
165
 
166
  if face_crop is None:
167
- return gr.update(), gr.update(), gr.update(), _hint_html("未偵測到人臉"), st, gr.update()
168
 
169
  st.track_bbox = bbox
170
 
171
- # DeepFace 情緒分析 (傳入已裁切的人臉)
172
  try:
173
  res = DeepFace.analyze(face_crop, actions=['emotion'], enforce_detection=False, silent=True)
174
  emo_dict = res[0]['emotion'] if isinstance(res, list) else res['emotion']
175
  except:
176
- return gr.update(), gr.update(), gr.update(), gr.update(), st, gr.update()
177
 
178
  top_key = max(emo_dict, key=lambda k: float(emo_dict[k]))
179
  top_conf = float(emo_dict[top_key])
@@ -198,7 +210,7 @@ def on_stream(frame_rgb: np.ndarray, st: AppState):
198
  st.finished, st.running = True, False
199
 
200
  return (
201
- gr.update(visible=not st.finished),
202
  gr.update(value=_result_card_html(st.stable_emo, top_conf)),
203
  gr.update(value=_score_radio_value(EMO_TO_SCORE.get(st.stable_emo, 3))),
204
  gr.update(value=_hint_html("正在辨識中,請保持表情穩定...")),
 
5
  import gradio as gr
6
  import numpy as np
7
  import cv2
 
8
  import mediapipe as mp
9
  from mediapipe.tasks import python
10
  from mediapipe.tasks.vision import FaceDetector, FaceDetectorOptions, RunningMode
 
14
  # =========================
15
  # 參數與 MediaPipe 初始化
16
  # =========================
17
+ ANALYZE_EVERY_SEC = 0.6
18
  DOWNSAMPLE_W = 720
19
  EMA_ALPHA = 0.45
20
  CONF_TH = 40.0
 
22
  IOU_TH = 0.25
23
  ZOOM_FACTOR = 1.3
24
 
25
+ # MediaPipe 設定 (請確保 model 資料夾內有此檔案)
26
  MODEL_PATH = 'model/blaze_face_short_range.tflite'
27
  base_options = python.BaseOptions(model_asset_path=MODEL_PATH)
28
  options = FaceDetectorOptions(
 
108
  return frame[sy:sy+nh, sx:sx+nw]
109
 
110
  # =========================
111
+ # MediaPipe 偵測邏輯 (整合繪圖)
112
  # =========================
113
  def get_face_mediapipe(frame: np.ndarray, prev_bbox: Optional[Tuple[int, int, int, int]]):
114
  h, w = frame.shape[:2]
115
+ draw_frame = frame.copy() # 建立一個複本用來畫圖
116
  mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame)
117
  res = mp_detector.detect(mp_image)
118
 
119
  if not res.detections:
120
+ return None, None, draw_frame
121
 
122
  candidates = []
123
  for det in res.detections:
124
  b = det.bounding_box
 
125
  nx, ny = max(0, b.origin_x), max(0, b.origin_y)
126
  nw, nh = min(b.width, w - nx), min(b.height, h - ny)
127
+
128
+ # 繪製白色正方形外框 (Thickness=5)
129
+ cv2.rectangle(draw_frame, (nx, ny), (nx + nw, ny + nh), (255, 255, 255), 5)
130
+
131
+ # 繪製紅色五官小圓點
132
+ if det.keypoints:
133
+ for kp in det.keypoints:
134
+ kx, ky = int(kp.x * w), int(kp.y * h)
135
+ cv2.circle(draw_frame, (kx, ky), 6, (0, 0, 255), -1)
136
+
137
  if nw > 10 and nh > 10:
138
  candidates.append(((nx, ny, nw, nh), frame[ny:ny+nh, nx:nx+nw]))
139
 
140
  if not candidates:
141
+ return None, None, draw_frame
142
 
143
+ # 優先匹配上一幀位置
144
+ best_match = None
145
  if prev_bbox:
146
  best_match = max(candidates, key=lambda c: _iou(prev_bbox, c[0]), default=(None, None))
147
+ if best_match[0] and _iou(prev_bbox, best_match[0]) < IOU_TH:
148
+ best_match = None
149
 
150
+ if best_match is None:
151
+ best_match = max(candidates, key=lambda c: c[0][2] * c[0][3])
152
+
153
+ return best_match[0], best_match[1], draw_frame
154
 
155
  # =========================
156
  # Gradio 事件處理
 
172
 
173
  st.last_analyze_t = now
174
 
175
+ # 使用 MediaPipe 找臉並繪圖
176
+ bbox, face_crop, frame_with_drawings = get_face_mediapipe(frame_rgb, st.track_bbox)
177
 
178
  if face_crop is None:
179
+ return gr.update(value=frame_with_drawings), gr.update(), gr.update(), _hint_html("未偵測到人臉"), st, gr.update()
180
 
181
  st.track_bbox = bbox
182
 
183
+ # DeepFace 情緒分析
184
  try:
185
  res = DeepFace.analyze(face_crop, actions=['emotion'], enforce_detection=False, silent=True)
186
  emo_dict = res[0]['emotion'] if isinstance(res, list) else res['emotion']
187
  except:
188
+ return gr.update(value=frame_with_drawings), gr.update(), gr.update(), gr.update(), st, gr.update()
189
 
190
  top_key = max(emo_dict, key=lambda k: float(emo_dict[k]))
191
  top_conf = float(emo_dict[top_key])
 
210
  st.finished, st.running = True, False
211
 
212
  return (
213
+ gr.update(value=frame_with_drawings, visible=not st.finished),
214
  gr.update(value=_result_card_html(st.stable_emo, top_conf)),
215
  gr.update(value=_score_radio_value(EMO_TO_SCORE.get(st.stable_emo, 3))),
216
  gr.update(value=_hint_html("正在辨識中,請保持表情穩定...")),