yolo8 / src /main.py
mayanktak15's picture
Upload folder using huggingface_hub
c94751c verified
Raw
History Blame Contribute Delete
5.43 kB
"""Command-line pipeline for multi-object detection and persistent ID tracking."""
from __future__ import annotations
import argparse
import json
from dataclasses import asdict
from pathlib import Path
from typing import Any
import cv2
import yaml
from src.analytics.evaluator import evaluate_track_continuity
from src.analytics.metrics import TrackingMetrics
from src.analytics.statistics import confidence_histogram, plot_object_counts
from src.tracking.botsort_tracker import BotSortTracker
from src.tracking.tracker import TrackHistory
from src.utils.logger import setup_logger
from src.utils.video_reader import VideoReader
from src.utils.video_writer import VideoWriter
from src.visualization.annotator import TrackAnnotator
from src.visualization.heatmap import generate_heatmap
from src.visualization.trajectory import relative_speed
def load_yaml(path: str | Path) -> dict[str, Any]:
with Path(path).open("r", encoding="utf-8") as file:
return yaml.safe_load(file) or {}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="YOLO + BoT-SORT multi-object tracking pipeline")
parser.add_argument("--input", type=Path, default=None, help="Input video path")
parser.add_argument("--output", type=Path, default=None, help="Output annotated video path")
parser.add_argument("--detector-config", type=Path, default=Path("configs/detector.yaml"))
parser.add_argument("--tracker-config", type=Path, default=Path("configs/tracker.yaml"))
parser.add_argument("--pipeline-config", type=Path, default=Path("configs/pipeline.yaml"))
parser.add_argument("--no-video", action="store_true", help="Run analytics without writing video")
parser.add_argument("--display", action="store_true", help="Display annotated frames while processing")
return parser.parse_args()
def main() -> None:
args = parse_args()
pipeline_config = load_yaml(args.pipeline_config)
logger = setup_logger(str(pipeline_config.get("log_level", "INFO")))
input_video = args.input or Path(pipeline_config["input_video"])
output_video = args.output or Path(pipeline_config["output_video"])
analytics_json = Path(pipeline_config["analytics_json"])
logger.info("Starting tracking pipeline")
logger.info("Input video: %s", input_video)
tracker = BotSortTracker(args.detector_config, args.tracker_config)
history = TrackHistory()
metrics = TrackingMetrics()
annotator = TrackAnnotator(
draw_trajectories=bool(pipeline_config.get("draw_trajectories", True)),
trajectory_length=int(pipeline_config.get("trajectory_length", 40)),
line_thickness=int(pipeline_config.get("line_thickness", 2)),
)
last_frame_shape: tuple[int, int, int] | None = None
save_video = bool(pipeline_config.get("save_video", True)) and not args.no_video
display_window = bool(pipeline_config.get("display_window", False)) or args.display
with VideoReader(input_video) as reader:
writer_context = (
VideoWriter(output_video, reader.fps, (reader.width, reader.height))
if save_video
else None
)
try:
for frame_index, frame in reader:
tracks = list(tracker.update(frame, frame_index))
history.update(tracks)
metrics.update(frame_index, tracks)
last_frame_shape = frame.shape
annotated = annotator.draw(frame, tracks, history)
if writer_context is not None:
writer_context.write(annotated)
if display_window:
cv2.imshow("Multi-Object Tracking", annotated)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
if frame_index % 100 == 0:
logger.info("Processed frame %s with %s active tracks", frame_index, len(tracks))
finally:
if writer_context is not None:
writer_context.release()
if display_window:
cv2.destroyAllWindows()
summary = metrics.summarize()
continuity = evaluate_track_continuity(metrics.track_df)
summary["continuity_diagnostics"] = asdict(continuity)
summary["relative_speed_pixels_per_frame"] = {
str(track_id): round(relative_speed(points), 4)
for track_id, points in history.points.items()
}
if pipeline_config.get("save_analytics", True):
analytics_json.parent.mkdir(parents=True, exist_ok=True)
analytics_json.write_text(json.dumps(summary, indent=2), encoding="utf-8")
metrics.save_tables(
pipeline_config["frame_counts_csv"],
pipeline_config["track_history_csv"],
)
if pipeline_config.get("save_plots", True):
plot_object_counts(metrics.frame_df, pipeline_config["count_plot"])
confidence_histogram(metrics.track_df, Path("reports/figures/confidence_distribution.png"))
if last_frame_shape is not None:
generate_heatmap(history, last_frame_shape, pipeline_config["heatmap_path"])
logger.info("Completed tracking pipeline")
logger.info("Unique objects tracked: %s", summary["total_unique_objects"])
if save_video:
logger.info("Annotated video saved to: %s", output_video)
logger.info("Analytics saved to: %s", analytics_json)
if __name__ == "__main__":
main()