Spaces:
Sleeping
Sleeping
| """ | |
| Prepare Classifier Dataset — TrafficSentinel AI. | |
| Parses the full traffic scene images in 'Training data' and 'validation data' | |
| folders, detects riders on two-wheelers using YOLO, crops their head regions, | |
| and organizes them into a structured dataset for training a binary classifier. | |
| """ | |
| import os | |
| import sys | |
| import cv2 | |
| import logging | |
| from pathlib import Path | |
| from tqdm import tqdm | |
| PROJECT_ROOT = Path(__file__).parent.parent | |
| sys.path.insert(0, str(PROJECT_ROOT)) | |
| from config.settings import SETTINGS, EntityClass | |
| from core.entity_detector import EntityDetector | |
| from core.scene_graph import SceneGraph, RIDES | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") | |
| logger = logging.getLogger(__name__) | |
| def crop_rider_heads(): | |
| # Source dataset | |
| dataset_dir = PROJECT_ROOT / "Traffic Violations Analysis Dataset" | |
| if not dataset_dir.exists(): | |
| logger.error(f"Dataset directory not found at {dataset_dir}") | |
| return | |
| # Target directory | |
| target_dir = PROJECT_ROOT / "data" / "classifier_dataset" | |
| target_dir.mkdir(parents=True, exist_ok=True) | |
| logger.info("Initializing entity detector for dataset preparation...") | |
| detector = EntityDetector(SETTINGS) | |
| # Process splits | |
| splits = [ | |
| ("Training data", "train"), | |
| ("validation data", "val") | |
| ] | |
| categories = ["helmet", "no_helmet"] | |
| for src_split, dest_split in splits: | |
| logger.info(f"Processing split: {src_split} -> {dest_split}") | |
| for category in categories: | |
| src_cat_dir = dataset_dir / src_split / category | |
| dest_cat_dir = target_dir / dest_split / category | |
| dest_cat_dir.mkdir(parents=True, exist_ok=True) | |
| if not src_cat_dir.exists(): | |
| logger.warning(f"Source folder not found: {src_cat_dir}") | |
| continue | |
| images = list(src_cat_dir.glob("*.jpg")) + list(src_cat_dir.glob("*.png")) | |
| limit = 100 if dest_split == "train" else 30 | |
| images = images[:limit] | |
| logger.info(f"Found {len(images)} images in {src_cat_dir.name} (capped at {limit})") | |
| crop_count = 0 | |
| for img_path in tqdm(images, desc=f"Cropping {dest_split}/{category}"): | |
| frame = cv2.imread(str(img_path)) | |
| if frame is None: | |
| continue | |
| h, w = frame.shape[:2] | |
| # Run direct entity detector (person/vehicles) | |
| detections = detector.detect(frame, use_sahi=False) | |
| # Build scene graph to associate riders with vehicles | |
| sg = SceneGraph(SETTINGS) | |
| sg.build(detections) | |
| # Query person RIDES motorcycle/bicycle | |
| # Any rider detected in a 'helmet' folder is assumed to be 'helmet' target, | |
| # and any rider in 'no_helmet' is assumed to be 'no_helmet' target. | |
| riders = [] | |
| for node_id, node in sg.nodes.items(): | |
| if node.entity_class in [EntityClass.PERSON, EntityClass.RIDER]: | |
| # Check if riding a two-wheeler | |
| vehicle = sg.get_vehicle_of(node_id) | |
| if vehicle and vehicle.entity_class in [EntityClass.MOTORCYCLE, EntityClass.BICYCLE]: | |
| riders.append(node) | |
| # Fallback: if no associated riders found, take all persons in the scene | |
| if not riders: | |
| riders = [n for n in sg.nodes.values() if n.entity_class in [EntityClass.PERSON, EntityClass.RIDER]] | |
| for idx, rider in enumerate(riders): | |
| rx1, ry1, rx2, ry2 = rider.bbox | |
| rider_h = ry2 - ry1 | |
| # Expand crop upward by 35% to fully capture head/helmet | |
| y1_expanded = max(0, ry1 - int(rider_h * 0.35)) | |
| # We only need the top 50% of the expanded rider bbox (head + shoulders) | |
| head_h = (ry2 - y1_expanded) // 2 | |
| y2_head = y1_expanded + head_h | |
| head_crop = frame[y1_expanded:y2_head, rx1:rx2] | |
| if head_crop.size == 0 or head_crop.shape[0] < 16 or head_crop.shape[1] < 16: | |
| continue | |
| # Save crop | |
| dest_filename = f"{img_path.stem}_rider_{idx}.jpg" | |
| dest_path = dest_cat_dir / dest_filename | |
| cv2.imwrite(str(dest_path), head_crop) | |
| crop_count += 1 | |
| logger.info(f"Saved {crop_count} head crops to {dest_cat_dir}") | |
| if __name__ == "__main__": | |
| crop_rider_heads() | |