Download src/vision/detector.py from SahilCodevally/codevally-vision-language-action: direct link, hf CLI and curl.
- Browser
- Download file 4.45 kB
-
https://huggingface.co/spaces/SahilCodevally/codevally-vision-language-action/resolve/main/src/vision/detector.py
- Command line
-
hf download hf://spaces/SahilCodevally/codevally-vision-language-action/src/vision/detector.py
-
curl -L -o detector.py https://huggingface.co/spaces/SahilCodevally/codevally-vision-language-action/resolve/main/src/vision/detector.py
4.45 kB
| """ | |
| Vision Module — Object detection using YOLOv8. | |
| Wraps the Ultralytics YOLO model to detect objects in images and return | |
| structured results. The model is loaded **once** in the constructor and | |
| reused for all subsequent detections. | |
| """ | |
| import logging | |
| from typing import Any | |
| import numpy as np | |
| logger = logging.getLogger(__name__) | |
| class ObjectDetector: | |
| """ | |
| Detects objects in images using a pretrained YOLOv8 model. | |
| Attributes: | |
| model: The loaded YOLO model instance (cached after first load). | |
| """ | |
| def __init__(self, model_name: str = "yolov8n.pt", confidence: float = 0.25): | |
| """ | |
| Initialize the detector and load the YOLO model. | |
| Args: | |
| model_name: Name or path of the YOLO model weights. | |
| confidence: Minimum confidence threshold for detections. | |
| """ | |
| self.model_name = model_name | |
| self.confidence = confidence | |
| self.model = None | |
| self._load_model() | |
| def _load_model(self) -> None: | |
| """Load the YOLO model. Called once during initialization.""" | |
| try: | |
| from ultralytics import YOLO | |
| logger.info("Loading YOLO model: %s", self.model_name) | |
| self.model = YOLO(self.model_name) | |
| logger.info("YOLO model loaded successfully.") | |
| except Exception as exc: | |
| logger.error("Failed to load YOLO model '%s': %s", self.model_name, exc) | |
| raise RuntimeError( | |
| f"Could not load YOLO model '{self.model_name}'. " | |
| f"Ensure ultralytics is installed and the model file is accessible." | |
| ) from exc | |
| def detect(self, image: np.ndarray) -> list[dict[str, Any]]: | |
| """ | |
| Run object detection on an image. | |
| Args: | |
| image: Input image as a NumPy array (H, W, C) in BGR or RGB format. | |
| Returns: | |
| List of detected objects, each containing: | |
| - ``label`` (str): Class name. | |
| - ``confidence`` (float): Detection confidence (0-1). | |
| - ``bbox`` (list[int]): Bounding box as [x1, y1, x2, y2]. | |
| Raises: | |
| ValueError: If the input image is invalid. | |
| RuntimeError: If the model is not loaded. | |
| """ | |
| if self.model is None: | |
| raise RuntimeError("YOLO model is not loaded.") | |
| if image is None or not isinstance(image, np.ndarray): | |
| raise ValueError("Invalid image: expected a NumPy ndarray.") | |
| if image.ndim < 2: | |
| raise ValueError( | |
| f"Invalid image dimensions: expected 2D or 3D array, " | |
| f"got {image.ndim}D." | |
| ) | |
| logger.info( | |
| "Running detection on image of shape %s with confidence=%.2f", | |
| image.shape, | |
| self.confidence, | |
| ) | |
| try: | |
| results = self.model(image, conf=self.confidence, verbose=False) | |
| except Exception as exc: | |
| logger.error("YOLO inference failed: %s", exc) | |
| return [] | |
| detections = self._parse_results(results) | |
| logger.info("Detected %d objects.", len(detections)) | |
| return detections | |
| def _parse_results(self, results: Any) -> list[dict[str, Any]]: | |
| """ | |
| Parse YOLO results into a structured list of detections. | |
| Args: | |
| results: Raw YOLO results object. | |
| Returns: | |
| Structured list of detection dictionaries. | |
| """ | |
| detections: list[dict[str, Any]] = [] | |
| for result in results: | |
| boxes = result.boxes | |
| if boxes is None: | |
| continue | |
| for i in range(len(boxes)): | |
| try: | |
| bbox = boxes.xyxy[i].cpu().numpy().tolist() | |
| confidence = float(boxes.conf[i].cpu().numpy()) | |
| class_id = int(boxes.cls[i].cpu().numpy()) | |
| label = result.names.get(class_id, f"class_{class_id}") | |
| detections.append( | |
| { | |
| "label": label, | |
| "confidence": round(confidence, 4), | |
| "bbox": [int(coord) for coord in bbox], | |
| } | |
| ) | |
| except (IndexError, KeyError, AttributeError) as exc: | |
| logger.warning("Failed to parse detection %d: %s", i, exc) | |
| continue | |
| return detections | |