Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from pathlib import Path | |
| from time import perf_counter | |
| import cv2 | |
| import gradio as gr | |
| from ultralytics import YOLO | |
| MODEL_PATH = "yolov8n.pt" if Path("yolov8n.pt").exists() else "yolo11n.pt" | |
| # Initial COCO proxy classes: | |
| # 29 = frisbee, 39 = bottle, 54 = donut | |
| # These are not biscuit classes. They are only proxies for round biscuit-like shapes. | |
| BISCUIT_LIKE_CLASS_IDS = {29, 39, 54} | |
| INFERENCE_IMAGE_SIZE = 320 | |
| model = YOLO(MODEL_PATH) | |
| def detect_biscuit_defects(image, confidence): | |
| started_at = perf_counter() | |
| if image is None or image.size == 0: | |
| return None, "Waiting for webcam frame." | |
| bgr_image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| results = model( | |
| bgr_image, | |
| conf=confidence, | |
| imgsz=INFERENCE_IMAGE_SIZE, | |
| max_det=10, | |
| verbose=False, | |
| )[0] | |
| detected_pieces = [] | |
| for box in results.boxes: | |
| class_id = int(box.cls[0]) | |
| if class_id in BISCUIT_LIKE_CLASS_IDS: | |
| detected_pieces.append(box) | |
| if len(detected_pieces) == 1: | |
| box = detected_pieces[0] | |
| x1, y1, x2, y2 = map(int, box.xyxy[0]) | |
| draw_box(bgr_image, x1, y1, x2, y2, "INTACT BISCUIT", float(box.conf[0]), (0, 255, 0)) | |
| status = "Proxy method: 1 biscuit-like object -> intact" | |
| elif len(detected_pieces) >= 2: | |
| for box in detected_pieces: | |
| x1, y1, x2, y2 = map(int, box.xyxy[0]) | |
| draw_box(bgr_image, x1, y1, x2, y2, "BROKEN PIECE", float(box.conf[0]), (0, 0, 255)) | |
| status = f"Proxy method: {len(detected_pieces)} biscuit-like objects -> broken" | |
| else: | |
| status = "Proxy method: no biscuit-like object detected" | |
| elapsed_ms = int((perf_counter() - started_at) * 1000) | |
| status = f"{status} | {elapsed_ms} ms/frame at imgsz={INFERENCE_IMAGE_SIZE}" | |
| return cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB), status | |
| def draw_box(bgr_image, x1, y1, x2, y2, label, confidence, color): | |
| cv2.rectangle(bgr_image, (x1, y1), (x2, y2), color, 3) | |
| cv2.putText( | |
| bgr_image, | |
| f"{label} {confidence:.2f}", | |
| (x1, max(y1 - 10, 25)), | |
| cv2.FONT_HERSHEY_SIMPLEX, | |
| 0.8, | |
| color, | |
| 2, | |
| cv2.LINE_AA, | |
| ) | |
| with gr.Blocks(title="YOLO Proxy Biscuit Detector") as demo: | |
| gr.Markdown("# YOLO Proxy Biscuit Detector") | |
| gr.Markdown( | |
| f"Loaded proxy model: `{MODEL_PATH}`. " | |
| "This uses COCO proxy classes, not custom biscuit weights." | |
| ) | |
| with gr.Row(): | |
| webcam = gr.Image( | |
| sources=["webcam"], | |
| type="numpy", | |
| streaming=True, | |
| label="Webcam", | |
| ) | |
| annotated = gr.Image(type="numpy", label="Proxy detection") | |
| confidence = gr.Slider( | |
| 0.05, | |
| 0.80, | |
| value=0.20, | |
| step=0.05, | |
| label="Confidence threshold", | |
| ) | |
| status = gr.Textbox(label="Status") | |
| webcam.stream( | |
| fn=detect_biscuit_defects, | |
| inputs=[webcam, confidence], | |
| outputs=[annotated, status], | |
| stream_every=0.10, | |
| queue=False, | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |