Small Python CLI application for local video processing with object detection, tracking, structured CSV output, and annotated video generation.
The current implementation is intentionally scoped as a local offline prototype. It processes a local video file, runs a pre-trained YOLO detector with ByteTrack-based tracking through Ultralytics, writes structured detections to CSV, and optionally renders an annotated MP4 artifact.
- Reads a local video file frame by frame.
- Detects and tracks objects over time.
- Writes a structured detection CSV.
- Produces an optional annotated output video.
- Validates generated output and emits validation warnings when needed.
- Python 3.10+
- OpenCV for local video I/O and annotation rendering
- Ultralytics YOLO for detection and tracking runtime
- Pytest for unit testing
spoor_case/
cli.py
detection/
exceptions/
model/
output/
pipeline/
validation/
video/
visualization/
tests/
testinputdata/
testoutputdata/
-
YoloObjectDetectorRuns the detection and tracking step using a YOLO model through Ultralytics. -
OpenCvVideoSourceFactoryOpens a local video file and exposes frames through a simple video-source protocol. -
VideoProcessingPipelineOrchestrates decoding, frame processing, CSV writing, output validation, and optional annotated video writing. -
CsvResultWriterandValidationErrorWriterPersist structured output and validation findings. -
OpenCvAnnotatedVideoWriterDraws bounding boxes and labels on output frames and writes the final MP4 artifact.
Create and activate a virtual environment, then install dependencies.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtIf you are using this repository exactly as developed during the task, prefer the checked-in virtual environment commands that were used throughout testing:
./.venv/bin/pip install -r requirements.txtBasic run:
./.venv/bin/python -m spoor_case \
--video testinputdata/IMG_0763.MOV \
--output testoutputdata/img_0763_detections.csv \
--annotated-output testoutputdata/img_0763_annotated.mp4Bird-focused run:
./.venv/bin/python -m spoor_case \
--video testinputdata/birds-with-insects_13456098-hd_1920_1080_30fps.mp4 \
--output testoutputdata/birds.csv \
--annotated-output testoutputdata/birds.mp4 \
--track-classes birdShort smoke run:
./.venv/bin/python -m spoor_case \
--video testinputdata/birds-with-insects_13456098-hd_1920_1080_30fps.mp4 \
--output testoutputdata/birds_smoke.csv \
--annotated-output testoutputdata/birds_smoke.mp4 \
--max-frames 20Dry-run validation only:
./.venv/bin/python -m spoor_case \
--video testinputdata/IMG_0763.MOV \
--output testoutputdata/dry_run.csv \
--dry-run-
--videoInput local video file path. -
--outputDetection CSV output path. -
--annotated-outputOptional annotated video output path. -
--track-classesOptional subset of class labels to keep. -
--modelYOLO model name or local path. Default isyolov8n.pt. -
--imgszInference image size. Higher values can improve small-object recall at higher compute cost. -
--confidenceDetection confidence threshold. -
--frame-skipProcess every Nth frame. -
--max-framesOptional cap on processed frames. -
--dry-runValidate inputs without running the pipeline. -
--log-levelLogging verbosity.
The pipeline produces:
- Detection CSV with one row per detection.
- Optional annotated MP4 output.
- Optional
validation_errors.csvwhen output validation reports issues.
Detection CSV columns:
frame_idtimestamp_sectrack_idclass_nameclass_groupconfidencebbox_xbbox_ybbox_wbbox_h
Run the full unit test suite:
./.venv/bin/python -m pytest -qRun a focused test file:
./.venv/bin/python -m pytest tests/unit/test_cli.py- This solution is optimized for clarity, inspectability, and repeatability rather than production-grade vision quality.
- The default
yolov8n.ptmodel is a lightweight baseline chosen for speed and reproducibility. - Small, distant, partially occluded, or stationary birds in cluttered scenes may be missed or occasionally duplicated.
- The application currently targets local file processing only.
- Streaming, deployment hardening, containerization, and hardware-specific optimization were intentionally left out of scope for this task.
- Writeup: WRITEUP.md
- ChatGPT conversation log (requirements analysis and technical requirement shaping): CHATGPT_CONVERSATION_LOG.md
- Copilot conversation log (implementation, testing, refactoring, and documentation): COPILOT_CONVERSATION_LOG.md
AI conversation logs note:
I did not include the full raw AI chat log because it contained unrelated/private conversation and low-signal iteration. Instead, this document contains the relevant technical excerpts and summaries of how AI tools shaped the implementation, including questions I asked, concerns I raised, suggestions I accepted or rejected, and the decisions I made manually.
For convenience during review, this repository includes local test videos and generated output artifacts under testinputdata/ and testoutputdata/.
Test input videos used during manual validation:
- testinputdata/IMG_0763.MOV
- testinputdata/birds-with-insects_13456098-hd_1920_1080_30fps.mp4
- testinputdata/kingfisher-sitting-on-a-tree-branch_4849093-hd_1920_1080_30fps.mp4
- testinputdata/time-lapse-video-of-a-road-and-cars_5266870-hd_1280_720_30fps.mp4
- testinputdata/a-person-walking-down-a-street-at-night_16538871-uhd_2160_3840_24fps.mp4
Generated outputs included for inspection:
- testoutputdata/img_0763_detections.csv
- testoutputdata/img_0763_annotated.mp4
- testoutputdata/birds_file_smoke.csv
- testoutputdata/birds_file_smoke.mp4
- testoutputdata/kingfisher_file_smoke.csv
- testoutputdata/kingfisher_file_smoke.mp4
- testoutputdata/road_file_smoke.csv
- testoutputdata/road_file_smoke.mp4
- testoutputdata/street_vertical_file_smoke.csv
- testoutputdata/street_vertical_file_smoke.mp4
AWS link limitation during testing:
The original test-task AWS S3 link was a short-lived presigned URL. In practice, it was accessible in browser context but returned 403 Forbidden for programmatic access from CLI/runtime tools after expiration, and the downloaded placeholder was XML (not a valid video stream). Because of that, I could not reliably process that source directly from the local runtime and used locally available/captured videos for reproducible validation.
In a normal production repository, generated videos, detection outputs, logs, and large input files would not be committed. They would usually be stored externally or regenerated as part of a reproducible run. They are included here only to make the case submission easy to inspect.
Still ignore future generated files, but force-include selected artifacts if needed.