Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import logging | |
| import re | |
| from pathlib import Path | |
| import pandas as pd | |
| from src.config import ALL_CATEGORIES_LABEL, PROJECT_ROOT | |
| logger = logging.getLogger(__name__) | |
| IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".bmp", ".webp"} | |
| METADATA_COLUMNS = ["id", "path", "filename", "category"] | |
| class DatasetError(RuntimeError): | |
| """Raised when local dataset metadata is missing or invalid.""" | |
| def _slug(value: str) -> str: | |
| slug = re.sub(r"[^A-Za-z0-9]+", "-", value.strip()).strip("-").lower() | |
| return slug or "image" | |
| def _relative_to_project(path: Path) -> str: | |
| try: | |
| return path.resolve().relative_to(PROJECT_ROOT).as_posix() | |
| except ValueError: | |
| return path.resolve().as_posix() | |
| def scan_images(image_dir: Path) -> list[dict[str, str]]: | |
| image_dir = Path(image_dir) | |
| if not image_dir.exists(): | |
| logger.warning("Image directory does not exist: %s", image_dir) | |
| return [] | |
| rows: list[dict[str, str]] = [] | |
| used_ids: dict[str, int] = {} | |
| files = sorted( | |
| path | |
| for path in image_dir.rglob("*") | |
| if path.is_file() and path.suffix.lower() in IMAGE_EXTENSIONS | |
| ) | |
| for image_path in files: | |
| if image_path.parent == image_dir: | |
| category = "Uncategorized" | |
| else: | |
| category = image_path.parent.name | |
| base_id = f"{_slug(category)}-{_slug(image_path.stem)}" | |
| occurrence = used_ids.get(base_id, 0) | |
| used_ids[base_id] = occurrence + 1 | |
| image_id = base_id if occurrence == 0 else f"{base_id}-{occurrence + 1}" | |
| rows.append( | |
| { | |
| "id": image_id, | |
| "path": _relative_to_project(image_path), | |
| "filename": image_path.name, | |
| "category": category, | |
| } | |
| ) | |
| return rows | |
| def build_metadata(image_dir: Path, output_csv: Path) -> pd.DataFrame: | |
| rows = scan_images(image_dir) | |
| output_csv = Path(output_csv) | |
| output_csv.parent.mkdir(parents=True, exist_ok=True) | |
| metadata = pd.DataFrame(rows, columns=METADATA_COLUMNS) | |
| metadata.to_csv(output_csv, index=False) | |
| logger.info("Wrote metadata for %d image(s) to %s", len(metadata), output_csv) | |
| return metadata | |
| def load_metadata(metadata_csv: Path) -> pd.DataFrame: | |
| metadata_csv = Path(metadata_csv) | |
| if not metadata_csv.exists(): | |
| raise DatasetError( | |
| f"metadata.csv was not found at {metadata_csv}. " | |
| "Run `python scripts/build_metadata.py` after adding images." | |
| ) | |
| metadata = pd.read_csv(metadata_csv, dtype=str).fillna("") | |
| missing = [column for column in METADATA_COLUMNS if column not in metadata.columns] | |
| if missing: | |
| raise DatasetError(f"metadata.csv is missing required column(s): {', '.join(missing)}") | |
| return metadata[METADATA_COLUMNS] | |
| def get_categories(metadata_csv: Path) -> list[str]: | |
| try: | |
| metadata = load_metadata(metadata_csv) | |
| except DatasetError: | |
| return [ALL_CATEGORIES_LABEL] | |
| categories = sorted(category for category in metadata["category"].dropna().unique() if category) | |
| return [ALL_CATEGORIES_LABEL, *categories] | |