TrungTran commited on
Commit
049d210
·
verified ·
1 Parent(s): 00c12ae

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +24 -26
app.py CHANGED
@@ -4,7 +4,6 @@ FaceAge-DINOv3 — Gradio demo for HuggingFace Spaces.
4
  Face detection : YuNet (OpenCV built-in, ~350 KB model, no extra deps)
5
  Age/gender : FaceAge-DINOv3 ONNX (CPU, ~1.2 GB)
6
  """
7
- import urllib.request
8
  import os
9
  import numpy as np
10
  import gradio as gr
@@ -83,14 +82,11 @@ def _predict_crop(face_rgb: np.ndarray) -> dict:
83
 
84
 
85
  # ---------------------------------------------------------------------------
86
- # YuNet face detector (cv2.FaceDetectorYN, OpenCV 4.5.4)
87
  # ---------------------------------------------------------------------------
88
 
89
- _YUNET_URL = (
90
- "https://github.com/opencv/opencv_zoo/raw/main/models/"
91
- "face_detection_yunet/face_detection_yunet_2023mar.onnx"
92
- )
93
- _YUNET_PATH = "/tmp/face_detection_yunet_2023mar.onnx"
94
  _DETECTOR = None
95
 
96
 
@@ -99,21 +95,20 @@ def _load_detector():
99
  if _DETECTOR is not None:
100
  return
101
 
102
- # Download model if not cached
103
- if not os.path.exists(_YUNET_PATH):
104
- print(f"[YuNet] Downloading model …")
105
- try:
106
- urllib.request.urlretrieve(_YUNET_URL, _YUNET_PATH)
107
- print(f"[YuNet] Saved to {_YUNET_PATH}")
108
- except Exception as e:
109
- print(f"[YuNet] Download failed: {e} — face detection disabled")
110
- _DETECTOR = "unavailable"
111
- return
112
-
113
  import cv2
 
 
 
 
 
 
 
 
 
114
  try:
115
  _DETECTOR = cv2.FaceDetectorYN.create(
116
- model = _YUNET_PATH,
117
  config = "",
118
  input_size = (320, 320),
119
  score_threshold = 0.6,
@@ -127,21 +122,21 @@ def _load_detector():
127
 
128
 
129
  def _detect_faces(img_rgb: np.ndarray,
130
- min_face_px: int = 20) -> list[tuple[int, int, int, int]]:
 
131
  """
132
- Returns list of (x0, y0, x1, y1) sorted by area (largest first).
133
  Falls back to empty list if YuNet is unavailable.
134
  """
135
  if _DETECTOR == "unavailable" or _DETECTOR is None:
136
  return []
137
 
138
  import cv2
139
- h, w = img_rgb.shape[:2]
140
  img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
141
 
142
- # YuNet requires input_size to match the image
143
  _DETECTOR.setInputSize((w, h))
144
- _, faces = _DETECTOR.detect(img_bgr) # faces: None or Nx15
145
 
146
  if faces is None:
147
  return []
@@ -149,8 +144,11 @@ def _detect_faces(img_rgb: np.ndarray,
149
  bboxes = []
150
  for face in faces:
151
  x, y, fw, fh = face[:4].astype(int)
152
- x0, y0 = max(0, x), max(0, y)
153
- x1, y1 = min(w, x + fw), min(h, y + fh)
 
 
 
154
  if (x1 - x0) >= min_face_px and (y1 - y0) >= min_face_px:
155
  bboxes.append((x0, y0, x1, y1))
156
 
 
4
  Face detection : YuNet (OpenCV built-in, ~350 KB model, no extra deps)
5
  Age/gender : FaceAge-DINOv3 ONNX (CPU, ~1.2 GB)
6
  """
 
7
  import os
8
  import numpy as np
9
  import gradio as gr
 
82
 
83
 
84
  # ---------------------------------------------------------------------------
85
+ # YuNet face detector (cv2.FaceDetectorYN, loaded from HuggingFace Hub)
86
  # ---------------------------------------------------------------------------
87
 
88
+ _YUNET_REPO = "opencv/face_detection_yunet"
89
+ _YUNET_FILE = "face_detection_yunet_2023mar.onnx"
 
 
 
90
  _DETECTOR = None
91
 
92
 
 
95
  if _DETECTOR is not None:
96
  return
97
 
98
+ from huggingface_hub import hf_hub_download
 
 
 
 
 
 
 
 
 
 
99
  import cv2
100
+
101
+ try:
102
+ yunet_path = hf_hub_download(repo_id=_YUNET_REPO, filename=_YUNET_FILE)
103
+ print(f"[YuNet] Model: {yunet_path}")
104
+ except Exception as e:
105
+ print(f"[YuNet] Download failed: {e} — face detection disabled")
106
+ _DETECTOR = "unavailable"
107
+ return
108
+
109
  try:
110
  _DETECTOR = cv2.FaceDetectorYN.create(
111
+ model = yunet_path,
112
  config = "",
113
  input_size = (320, 320),
114
  score_threshold = 0.6,
 
122
 
123
 
124
  def _detect_faces(img_rgb: np.ndarray,
125
+ min_face_px: int = 20,
126
+ margin: int = 15) -> list[tuple[int, int, int, int]]:
127
  """
128
+ Returns list of (x0, y0, x1, y1) with margin padding, sorted by area desc.
129
  Falls back to empty list if YuNet is unavailable.
130
  """
131
  if _DETECTOR == "unavailable" or _DETECTOR is None:
132
  return []
133
 
134
  import cv2
135
+ h, w = img_rgb.shape[:2]
136
  img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
137
 
 
138
  _DETECTOR.setInputSize((w, h))
139
+ _, faces = _DETECTOR.detect(img_bgr) # None or Nx15: [x,y,w,h, ...]
140
 
141
  if faces is None:
142
  return []
 
144
  bboxes = []
145
  for face in faces:
146
  x, y, fw, fh = face[:4].astype(int)
147
+ # Add margin, clamp to image bounds
148
+ x0 = max(0, x - margin)
149
+ y0 = max(0, y - margin)
150
+ x1 = min(w, x + fw + margin)
151
+ y1 = min(h, y + fh + margin)
152
  if (x1 - x0) >= min_face_px and (y1 - y0) >= min_face_px:
153
  bboxes.append((x0, y0, x1, y1))
154