smart-parking-api / src /generate_crops.py
rohanv56's picture
Upload 380 files
2d30bad verified
Raw
History Blame Contribute Delete
3.83 kB
import json
import os
from pathlib import Path
from PIL import Image
# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
BASE_DIR = Path(__file__).resolve().parents[1]
RAW_DIR = BASE_DIR / "data" / "raw"
CROPS_DIR = BASE_DIR / "data" / "crops"
OCCUPIED_DIR = CROPS_DIR / "occupied"
EMPTY_DIR = CROPS_DIR / "empty"
SPLITS = ["train", "valid", "test"]
# Max crops per class — more than enough for MobileNetV2
MAX_PER_CLASS = 4000
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def load_coco(path: Path) -> dict:
with open(path, "r") as f:
return json.load(f)
def get_category_map(coco: dict) -> dict:
cat_map = {}
for c in coco["categories"]:
if c["name"] == "space-occupied":
cat_map[c["id"]] = "occupied"
elif c["name"] == "space-empty":
cat_map[c["id"]] = "empty"
return cat_map
def get_image_map(coco: dict, split_dir: Path) -> dict:
return {
img["id"]: split_dir / img["file_name"]
for img in coco["images"]
}
# ---------------------------------------------------------------------------
# Core
# ---------------------------------------------------------------------------
def process_split(split: str, counters: dict):
split_dir = RAW_DIR / split
coco_file = split_dir / "_annotations.coco.json"
if not coco_file.exists():
print(f" Skipping {split} — not found.")
return
print(f"\nProcessing: {split}")
coco = load_coco(coco_file)
cat_map = get_category_map(coco)
image_map = get_image_map(coco, split_dir)
for ann in coco["annotations"]:
# Stop early if both classes are full
if counters["occupied"] >= MAX_PER_CLASS and counters["empty"] >= MAX_PER_CLASS:
print(f" Cap reached — stopping {split} early.")
break
category_id = ann["category_id"]
if category_id not in cat_map:
continue
label = cat_map[category_id]
# Skip if this class is already full
if counters[label] >= MAX_PER_CLASS:
continue
img_path = image_map.get(ann["image_id"])
if img_path is None or not img_path.exists():
continue
x, y, w, h = [int(v) for v in ann["bbox"]]
if w <= 0 or h <= 0:
continue
try:
img = Image.open(img_path).convert("RGB")
crop = img.crop((x, y, x + w, y + h))
except Exception:
continue
filename = f"{split}_{ann['image_id']}_{ann['id']}.jpg"
if label == "occupied":
crop.save(OCCUPIED_DIR / filename)
else:
crop.save(EMPTY_DIR / filename)
counters[label] += 1
print(f" occupied so far : {counters['occupied']}")
print(f" empty so far : {counters['empty']}")
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
OCCUPIED_DIR.mkdir(parents=True, exist_ok=True)
EMPTY_DIR.mkdir(parents=True, exist_ok=True)
counters = {"occupied": 0, "empty": 0}
for split in SPLITS:
if counters["occupied"] >= MAX_PER_CLASS and counters["empty"] >= MAX_PER_CLASS:
break
process_split(split, counters)
print(f"\nDone.")
print(f"Total occupied : {counters['occupied']}")
print(f"Total empty : {counters['empty']}")
if __name__ == "__main__":
main()