Download src/objectmodel_v1/data.py from bench-labs/objectmodel-v1: direct link, hf CLI and curl.
- Browser
- Download file 6.37 kB
-
https://huggingface.co/bench-labs/objectmodel-v1/resolve/main/src/objectmodel_v1/data.py
- Command line
-
hf download hf://bench-labs/objectmodel-v1/src/objectmodel_v1/data.py
-
curl -L -o data.py https://huggingface.co/bench-labs/objectmodel-v1/resolve/main/src/objectmodel_v1/data.py
6.37 kB
| from __future__ import annotations | |
| import json | |
| import random | |
| from collections import defaultdict | |
| from pathlib import Path | |
| from typing import Any | |
| import numpy as np | |
| from PIL import Image, ImageEnhance | |
| import torch | |
| from torch import Tensor | |
| from torch.utils.data import Dataset | |
| from .boxes import box_xyxy_to_cxcywh | |
| class CocoDetectionDataset(Dataset): | |
| """Minimal COCO detection loader with deterministic category remapping.""" | |
| def __init__( | |
| self, | |
| image_dir: str | Path, | |
| annotation_file: str | Path, | |
| input_size: int, | |
| training: bool, | |
| hflip_prob: float = 0.5, | |
| scale_range: tuple[float, float] = (0.65, 1.0), | |
| mean: tuple[float, float, float] = (0.485, 0.456, 0.406), | |
| std: tuple[float, float, float] = (0.229, 0.224, 0.225), | |
| ) -> None: | |
| self.image_dir = Path(image_dir) | |
| self.annotation_file = Path(annotation_file) | |
| self.input_size = input_size | |
| self.training = training | |
| self.hflip_prob = hflip_prob | |
| self.scale_range = scale_range | |
| self.mean = torch.tensor(mean, dtype=torch.float32)[:, None, None] | |
| self.std = torch.tensor(std, dtype=torch.float32)[:, None, None] | |
| with self.annotation_file.open("r", encoding="utf-8") as handle: | |
| data = json.load(handle) | |
| self.images = sorted(data["images"], key=lambda item: item["id"]) | |
| annotations: dict[int, list[dict[str, Any]]] = defaultdict(list) | |
| for annotation in data["annotations"]: | |
| if annotation.get("iscrowd", 0) == 0 and annotation["bbox"][2] > 0 and annotation["bbox"][3] > 0: | |
| annotations[annotation["image_id"]].append(annotation) | |
| self.annotations = annotations | |
| category_ids = sorted(category["id"] for category in data["categories"]) | |
| self.category_to_label = {category_id: label for label, category_id in enumerate(category_ids)} | |
| self.label_to_category = {label: category_id for category_id, label in self.category_to_label.items()} | |
| self.categories = sorted(data["categories"], key=lambda item: self.category_to_label[item["id"]]) | |
| def __len__(self) -> int: | |
| return len(self.images) | |
| def _letterbox( | |
| self, image: Image.Image, boxes: Tensor | |
| ) -> tuple[Image.Image, Tensor, tuple[float, int, int]]: | |
| width, height = image.size | |
| scale_jitter = random.uniform(*self.scale_range) if self.training else 1.0 | |
| ratio = min(self.input_size / width, self.input_size / height) * scale_jitter | |
| resized_width = max(1, round(width * ratio)) | |
| resized_height = max(1, round(height * ratio)) | |
| image = image.resize((resized_width, resized_height), Image.Resampling.BILINEAR) | |
| max_x = self.input_size - resized_width | |
| max_y = self.input_size - resized_height | |
| if self.training: | |
| offset_x = random.randint(0, max_x) if max_x else 0 | |
| offset_y = random.randint(0, max_y) if max_y else 0 | |
| else: | |
| offset_x, offset_y = max_x // 2, max_y // 2 | |
| canvas = Image.new("RGB", (self.input_size, self.input_size), (114, 114, 114)) | |
| canvas.paste(image, (offset_x, offset_y)) | |
| if boxes.numel(): | |
| boxes = boxes * ratio | |
| boxes[:, [0, 2]] += offset_x | |
| boxes[:, [1, 3]] += offset_y | |
| return canvas, boxes, (ratio, offset_x, offset_y) | |
| def __getitem__(self, index: int) -> tuple[Tensor, dict[str, Tensor]]: | |
| image_info = self.images[index] | |
| image = Image.open(self.image_dir / image_info["file_name"]).convert("RGB") | |
| original_width, original_height = image.size | |
| records = self.annotations.get(image_info["id"], []) | |
| boxes = [] | |
| labels = [] | |
| for record in records: | |
| x, y, width, height = record["bbox"] | |
| boxes.append((x, y, x + width, y + height)) | |
| labels.append(self.category_to_label[record["category_id"]]) | |
| box_tensor = torch.tensor(boxes, dtype=torch.float32).reshape(-1, 4) | |
| label_tensor = torch.tensor(labels, dtype=torch.int64) | |
| if self.training and random.random() < self.hflip_prob: | |
| image = image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) | |
| if box_tensor.numel(): | |
| old_x0 = box_tensor[:, 0].clone() | |
| box_tensor[:, 0] = original_width - box_tensor[:, 2] | |
| box_tensor[:, 2] = original_width - old_x0 | |
| if self.training: | |
| image = ImageEnhance.Color(image).enhance(random.uniform(0.8, 1.2)) | |
| image = ImageEnhance.Contrast(image).enhance(random.uniform(0.8, 1.2)) | |
| image, box_tensor, (ratio, offset_x, offset_y) = self._letterbox(image, box_tensor) | |
| if box_tensor.numel(): | |
| box_tensor = box_xyxy_to_cxcywh(box_tensor) / self.input_size | |
| valid = (box_tensor[:, 2] > 1e-4) & (box_tensor[:, 3] > 1e-4) | |
| box_tensor = box_tensor[valid].clamp(0.0, 1.0) | |
| label_tensor = label_tensor[valid] | |
| image_array = np.asarray(image, dtype=np.float32).copy() / 255.0 | |
| image_tensor = torch.from_numpy(image_array).permute(2, 0, 1) | |
| image_tensor = (image_tensor - self.mean) / self.std | |
| target = { | |
| "boxes": box_tensor, | |
| "labels": label_tensor, | |
| "image_id": torch.tensor(image_info["id"], dtype=torch.int64), | |
| "original_size": torch.tensor([original_height, original_width], dtype=torch.int64), | |
| "transform": torch.tensor([ratio, offset_x, offset_y], dtype=torch.float32), | |
| } | |
| return image_tensor, target | |
| def detection_collate(batch): | |
| images, targets = zip(*batch, strict=True) | |
| return torch.stack(images), list(targets) | |
| def build_dataset(config: dict, root: str | Path, split: str) -> CocoDetectionDataset: | |
| data = config["data"] | |
| root = Path(root) | |
| training = split == "train" | |
| return CocoDetectionDataset( | |
| root / data[f"{split}_image_dir"], | |
| root / data[f"{split}_annotations"], | |
| input_size=int(config["model"]["input_size"]), | |
| training=training, | |
| hflip_prob=float(data.get("hflip_prob", 0.5)), | |
| scale_range=tuple(data.get("scale_range", (0.65, 1.0))) if training else (1.0, 1.0), | |
| mean=tuple(data.get("mean", (0.485, 0.456, 0.406))), | |
| std=tuple(data.get("std", (0.229, 0.224, 0.225))), | |
| ) | |