Spaces:
Sleeping
Sleeping
| from ultralytics import YOLO | |
| import cv2 | |
| from pathlib import Path | |
| import numpy as np | |
| import os | |
| # Get project root | |
| PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| # ---------------- USER CONFIG ---------------- | |
| MODEL_PATH = os.path.join(PROJECT_ROOT, "models/model_a.pt") # YOLOv8 model path | |
| DEVICE = "cpu" # or "0" for GPU | |
| IMG_SIZE = 640 | |
| CONF_THR = 0.25 | |
| IOU_THR = 0.5 | |
| OUTPUT_DIR = os.path.join(PROJECT_ROOT, "outputs/3_detection") # annotated images folder | |
| LABEL_DIR = os.path.join(PROJECT_ROOT, "outputs/3_detection/labels") # save labels folder | |
| # -------------------------------------------- | |
| def ensure_dir(p): | |
| Path(p).mkdir(parents=True, exist_ok=True) | |
| def xyxy_to_xywh(xyxy): | |
| """Convert (x1,y1,x2,y2) to (cx,cy,w,h)""" | |
| x1, y1, x2, y2 = xyxy | |
| w = x2 - x1 | |
| h = y2 - y1 | |
| cx = x1 + w/2 | |
| cy = y1 + h/2 | |
| return cx, cy, w, h | |
| def non_max_suppression(boxes, scores, iou_thresh=0.5): | |
| """Simple NMS to remove overlapping boxes""" | |
| if len(boxes) == 0: | |
| return [] | |
| idxs = cv2.dnn.NMSBoxes( | |
| bboxes=[[int(x1), int(y1), int(x2-x1), int(y2-y1)] for (x1,y1,x2,y2) in boxes], | |
| scores=[float(s) for s in scores], | |
| score_threshold=0.0, | |
| nms_threshold=iou_thresh | |
| ) | |
| if len(idxs) == 0: | |
| return [] | |
| return idxs.flatten().tolist() | |
| def save_labels_yolo_format(image_path, hbb_list, label_dir=LABEL_DIR): | |
| """Saves labels in YOLO TXT format and returns path""" | |
| ensure_dir(label_dir) | |
| stem = Path(image_path).stem | |
| label_path = Path(label_dir) / f"{stem}.txt" | |
| img = cv2.imread(str(image_path)) | |
| h, w = img.shape[:2] | |
| with open(label_path, "w") as f: | |
| for det in hbb_list: | |
| cls_id = det["class_id"] | |
| cx, cy, bw, bh = det["xywh"] | |
| cxn, cyn, w_n, h_n = cx/w, cy/h, bw/w, bh/h | |
| f.write(f"{cls_id} {cxn:.6f} {cyn:.6f} {w_n:.6f} {h_n:.6f}\n") | |
| return str(label_path) | |
| def run_modelA(image_path, model_path=MODEL_PATH, device=DEVICE, imgsz=IMG_SIZE, | |
| conf_thr=CONF_THR, save_annotated=True, outdir=OUTPUT_DIR, | |
| iou_thr=IOU_THR, save_labels=True, model=None): | |
| """ | |
| Runs YOLOv8 HBB detection on image and optionally saves labels. | |
| Args: | |
| model (ultralytics.YOLO, optional): Preloaded YOLO model instance. | |
| Returns: | |
| hbb_list (list of dicts): Each detection info | |
| label_path (str or None): Path to saved label file | |
| """ | |
| ensure_dir(outdir) | |
| ensure_dir(LABEL_DIR) | |
| img_bgr = cv2.imread(str(image_path)) | |
| if img_bgr is None: | |
| print(f"❌ Could not read image: {image_path}") | |
| return [], None | |
| # ⚠️ No resizing here, image is already 640×640 from capture.py | |
| model = model if model is not None else YOLO(model_path) | |
| results = model.predict(source=img_bgr, device=device, imgsz=imgsz, conf=conf_thr, verbose=False) | |
| result = results[0] | |
| try: | |
| names = model.names | |
| except Exception: | |
| names = {i: str(i) for i in range(1000)} | |
| hbb_list = [] | |
| label_path = None | |
| if len(result.boxes) > 0: | |
| xyxy_all = result.boxes.xyxy.cpu().numpy() | |
| conf_all = result.boxes.conf.cpu().numpy() | |
| cls_all = result.boxes.cls.cpu().numpy().astype(int) | |
| keep_idxs = non_max_suppression(xyxy_all, conf_all, iou_thresh=iou_thr) | |
| for i in keep_idxs: | |
| x1, y1, x2, y2 = xyxy_all[i] | |
| cx, cy, w_box, h_box = xyxy_to_xywh((x1, y1, x2, y2)) | |
| conf = float(conf_all[i]) | |
| cls_id = int(cls_all[i]) | |
| cls_name = names.get(cls_id, str(cls_id)) | |
| entry = { | |
| "class_id": cls_id, | |
| "class_name": cls_name, | |
| "conf": conf, | |
| "xyxy": (float(x1), float(y1), float(x2), float(y2)), | |
| "xywh": (float(cx), float(cy), float(w_box), float(h_box)) | |
| } | |
| hbb_list.append(entry) | |
| # Annotate image | |
| if save_annotated and len(hbb_list) > 0: | |
| annotated = result.plot() | |
| outpath = Path(outdir) / (Path(image_path).stem + "_annotated.jpg") | |
| cv2.imwrite(str(outpath), cv2.cvtColor(annotated, cv2.COLOR_RGB2BGR)) | |
| # Save labels | |
| if save_labels and len(hbb_list) > 0: | |
| label_path = save_labels_yolo_format(image_path, hbb_list, label_dir=LABEL_DIR) | |
| return hbb_list, label_path | |
| # ----------------- Test run ----------------- | |
| if __name__ == "__main__": | |
| IMAGE_PATH = "outputs/1_captured_images/capture_1757686922_0.jpg" # replace with your test image | |
| detections, label_path = run_modelA(IMAGE_PATH) | |
| if detections: | |
| print("\nDetected objects:") | |
| for i, det in enumerate(detections): | |
| print(f"{i+1}. {det['class_name']} conf={det['conf']:.3f}, xyxy={tuple(round(v,2) for v in det['xyxy'])}") | |
| print(f"\nLabels saved at: {label_path}") | |
| else: | |
| print("No objects detected.") | |