Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -40,7 +40,7 @@ AG_REPO_ID = "emkessle/2024-24679-fencing-touch-predictor"
|
|
| 40 |
AG_ZIP_NAME = "autogluon_predictor_dir.zip"
|
| 41 |
|
| 42 |
FRAME_SKIP = 2
|
| 43 |
-
KEEP_CONF = 0.
|
| 44 |
YOLO_CONF = 0.25
|
| 45 |
YOLO_IOU = 0.50
|
| 46 |
CLIP_PAD_S = 2.0
|
|
@@ -128,34 +128,76 @@ def isolate_scoreboard_color(frame_bgr: np.ndarray,
|
|
| 128 |
conf: float = YOLO_CONF,
|
| 129 |
iou: float = YOLO_IOU,
|
| 130 |
keep_conf: float = KEEP_CONF,
|
| 131 |
-
debug: bool =
|
| 132 |
frame_id: int = None) -> np.ndarray:
|
| 133 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
H, W = frame_bgr.shape[:2]
|
|
|
|
|
|
|
| 135 |
gray = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY)
|
| 136 |
gray = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
|
| 137 |
|
|
|
|
|
|
|
|
|
|
| 138 |
chosen_box = None
|
| 139 |
res = yolo().predict(frame_bgr, conf=conf, iou=iou, verbose=False)
|
| 140 |
if len(res):
|
| 141 |
r = res[0]
|
| 142 |
if getattr(r, "boxes", None) is not None and len(r.boxes) > 0:
|
| 143 |
-
boxes
|
| 144 |
scores = r.boxes.conf.cpu().numpy()
|
| 145 |
-
|
| 146 |
-
if valid:
|
| 147 |
-
chosen_box, _ = max(valid, key=lambda bs: (bs[0][2]-bs[0][0])*(bs[0][3]-bs[0][1]))
|
| 148 |
-
x1, y1, x2, y2 = [int(v) for v in chosen_box]
|
| 149 |
-
gray[y1:y2, x1:x2] = frame_bgr[y1:y2, x1:x2]
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
if debug and frame_id is not None:
|
| 152 |
dbg = gray.copy()
|
| 153 |
if chosen_box is not None:
|
| 154 |
-
x1, y1, x2, y2 = [int(v) for v in chosen_box]
|
| 155 |
-
cv2.rectangle(dbg, (x1, y1), (x2, y2), (0,255,0), 2)
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
| 157 |
return gray
|
| 158 |
|
|
|
|
| 159 |
def _count_color_pixels(rgb, ch):
|
| 160 |
R, G, B = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
|
| 161 |
if ch==0: mask=(R>150)&(R>1.2*G)&(R>1.2*B)
|
|
|
|
| 40 |
AG_ZIP_NAME = "autogluon_predictor_dir.zip"
|
| 41 |
|
| 42 |
FRAME_SKIP = 2
|
| 43 |
+
KEEP_CONF = 0.85
|
| 44 |
YOLO_CONF = 0.25
|
| 45 |
YOLO_IOU = 0.50
|
| 46 |
CLIP_PAD_S = 2.0
|
|
|
|
| 128 |
conf: float = YOLO_CONF,
|
| 129 |
iou: float = YOLO_IOU,
|
| 130 |
keep_conf: float = KEEP_CONF,
|
| 131 |
+
debug: bool = False,
|
| 132 |
frame_id: int = None) -> np.ndarray:
|
| 133 |
+
"""
|
| 134 |
+
Improved version:
|
| 135 |
+
- Choose the largest bbox among candidates meeting confidence.
|
| 136 |
+
- Primary threshold: >= max(0.80, keep_conf)
|
| 137 |
+
- Fallback threshold: >= (primary - 0.05)
|
| 138 |
+
- Entire chosen bbox is restored to color; everything else is grayscale.
|
| 139 |
+
- Rejects low-saturation ROIs (flat/neutral areas).
|
| 140 |
+
"""
|
| 141 |
H, W = frame_bgr.shape[:2]
|
| 142 |
+
|
| 143 |
+
# Start fully grayscale
|
| 144 |
gray = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2GRAY)
|
| 145 |
gray = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
|
| 146 |
|
| 147 |
+
primary_thr = max(0.80, keep_conf)
|
| 148 |
+
fallback_thr = max(0.7, primary_thr - 0.05)
|
| 149 |
+
|
| 150 |
chosen_box = None
|
| 151 |
res = yolo().predict(frame_bgr, conf=conf, iou=iou, verbose=False)
|
| 152 |
if len(res):
|
| 153 |
r = res[0]
|
| 154 |
if getattr(r, "boxes", None) is not None and len(r.boxes) > 0:
|
| 155 |
+
boxes = r.boxes.xyxy.cpu().numpy()
|
| 156 |
scores = r.boxes.conf.cpu().numpy()
|
| 157 |
+
candidates = list(zip(boxes, scores))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
+
# Prefer largest box meeting primary threshold
|
| 160 |
+
strong = [(b, s) for (b, s) in candidates if float(s) >= primary_thr]
|
| 161 |
+
if strong:
|
| 162 |
+
chosen_box, _ = max(strong, key=lambda bs: (bs[0][2]-bs[0][0]) * (bs[0][3]-bs[0][1]))
|
| 163 |
+
else:
|
| 164 |
+
# Fallback: largest box meeting fallback threshold
|
| 165 |
+
medium = [(b, s) for (b, s) in candidates if float(s) >= fallback_thr]
|
| 166 |
+
if medium:
|
| 167 |
+
chosen_box, _ = max(medium, key=lambda bs: (bs[0][2]-bs[0][0]) * (bs[0][3]-bs[0][1]))
|
| 168 |
+
|
| 169 |
+
if chosen_box is not None:
|
| 170 |
+
x1, y1, x2, y2 = [int(round(v)) for v in chosen_box]
|
| 171 |
+
x1, y1 = max(0, x1), max(0, y1)
|
| 172 |
+
x2, y2 = min(W-1, x2), min(H-1, y2)
|
| 173 |
+
|
| 174 |
+
if x2 > x1 and y2 > y1:
|
| 175 |
+
# Single safeguard: reject very low-saturation ROIs
|
| 176 |
+
roi_color = frame_bgr[y1:y2, x1:x2]
|
| 177 |
+
if roi_color.size > 0:
|
| 178 |
+
hsv = cv2.cvtColor(roi_color, cv2.COLOR_BGR2HSV)
|
| 179 |
+
sat_mean = hsv[:, :, 1].mean()
|
| 180 |
+
if sat_mean < 25:
|
| 181 |
+
print(f"[WARN] Rejected bbox due to low saturation (mean={sat_mean:.1f})")
|
| 182 |
+
chosen_box = None
|
| 183 |
+
|
| 184 |
+
# If accepted, restore whole bbox to color
|
| 185 |
+
if chosen_box is not None:
|
| 186 |
+
gray[y1:y2, x1:x2] = frame_bgr[y1:y2, x1:x2]
|
| 187 |
+
|
| 188 |
+
# Optional debug save
|
| 189 |
if debug and frame_id is not None:
|
| 190 |
dbg = gray.copy()
|
| 191 |
if chosen_box is not None:
|
| 192 |
+
x1, y1, x2, y2 = [int(round(v)) for v in chosen_box]
|
| 193 |
+
cv2.rectangle(dbg, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
| 194 |
+
out_path = DEBUG_DIR / f"frame_{frame_id:06d}.jpg"
|
| 195 |
+
cv2.imwrite(str(out_path), dbg)
|
| 196 |
+
print(f"[DEBUG] Saved debug frame → {out_path}")
|
| 197 |
+
|
| 198 |
return gray
|
| 199 |
|
| 200 |
+
|
| 201 |
def _count_color_pixels(rgb, ch):
|
| 202 |
R, G, B = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
|
| 203 |
if ch==0: mask=(R>150)&(R>1.2*G)&(R>1.2*B)
|