How nicholaskajoh/ivy works
Ivy is a single-process Python video analytics pipeline for object counting. The architecture is centered on reading frames from a video source, detecting objects, tracking them across frames, and incrementing counts when tracked objects cross a configured region.
1. Runtime entrypoint and orchestration
- main.py is the application entrypoint.
- It likely wires together configuration, detector selection, video/job setup, the tracker, the counter, and the debug UI.
- ObjectCounter.py appears to be the top-level counting workflow/class that coordinates per-frame processing.
2. Core processing pipeline
- The main data flow is: video/frame source → detector → detection post-processing → tracker → counter → logs/debug output.
- Each frame is analyzed independently, but object identity is preserved across frames by tracking.
- The system is designed around continuous streaming rather than batch processing.
3. Detector abstraction boundary
- detectors/detector.py defines the detector interface/contract.
- The concrete detector implementations are:
- detectors/yolo.py
- detectors/tfoda.py
- detectors/detectron2.py
- detectors/haarcascade.py
- This is the main pluggable boundary in the repo: detection backend can be swapped without changing downstream tracking/counting logic.
4. Detection backends and external ML dependencies
- YOLO, TensorFlow Object Detection API, Detectron2, and Haar cascades are supported.
- These are not just libraries; they materially change deployment/runtime requirements.
- The README indicates optional framework installation depending on detector choice, so the repository supports multiple ML stacks under a common interface.
5. Tracking subsystem
- tracker.py maintains object continuity across frames.
- Its role is to transform per-frame detections into persistent tracked objects, which is necessary for stable counting and for avoiding double-counting.
- Tracking is a separate concern from detection, which keeps detector output simple and lets the counting logic operate on identities rather than raw boxes.
6. Counting and object state
- counter.py implements the counting rules.
- It likely consumes tracked objects and decides when an object has entered/exited a region of interest or crossed a counting boundary.
- util/object_info.py and util/blob.py likely carry object state, geometry, and per-object metadata used by the counter and tracker.
7. Spatial and geometry utilities
- util/bounding_box.py handles box math and intersection/centroid logic.
- util/detection_roi.py likely defines the count line or region-of-interest boundary.
- util/image.py probably provides frame/image preprocessing and annotation helpers.
- These utilities form the geometric layer between raw detector output and higher-level counting decisions.
8. Debugging and visualization
- util/debugger.py and the README’s debug mode indicate an interactive visualization layer.
- The app can display the live processing window, pause/play, capture screenshots, and log clicked coordinates.
- This is an important operational boundary: it does not affect counting logic directly, but it is essential for tuning and validation.
9. Configuration and job model
- settings.py, .env.example, and util/job.py suggest runtime configuration is driven by environment variables plus a job/config object.
- The job layer probably encapsulates video source, detector choice, model paths, thresholds, and output options.
- This keeps deployment settings separate from the processing code.
10. Logging and observability
- util/logger.py centralizes logging behavior.
- The README explicitly mentions analyzing logs, so logs are a first-class output of the application.
- Logging likely captures counts, tracker events, detector selection, and debug/runtime issues.
11. Input data and model assets
- The data/ directory is the local asset boundary for sample videos, detector weights, and demo content.
- The README’s demo package unpacks into data, so runtime assets are expected to live there rather than being fetched dynamically.
12. Testing boundary
- tests/ covers utility components such as blobs, bounding boxes, debugger, job, logger, and vehicle/object info.
- The test suite is focused on deterministic logic rather than model accuracy, which makes sense for a pipeline that depends on external detector frameworks.
- pytest.ini and coverage config show the repo is structured for unit testing of core geometry/state logic.
13. Deployment and packaging
- Dockerfile provides containerized execution.
- .github/workflows/ivy.yml indicates CI automation, likely for tests and linting.
- requirements.txt, .pylintrc, and .coveragerc reinforce that the repo is packaged as a Python application with quality gates.
14. Architectural summary
- Ivy’s main architectural split is between:
- perception: detector backends,
- temporal continuity: tracking,
- business logic: counting,
- supporting concerns: config, logging, debugging, and geometry utilities.
- The most important diagram boundary is the interface between detector output and tracker/counter input, because that is where a raw CV model becomes a counted object stream.
Main entry points: ObjectCounter.py Counter flow · detector.py Detector API · main.py main.py · blob.py Blob
Components 19
Runtime
Entrypoint and orchestration for the single-process pipeline
- main.py · Application entrypoint
main.py - Counter flow · Top-level per-frame orchestration
ObjectCounter.py
Perception
Detector backends and frame-to-detection boundary
- Detector API · Common detector interface and abstraction boundary
detectors/detector.py - YOLO · YOLO-based object detector
detectors/yolo.py - TF-ODA · TensorFlow Object Detection API backend
detectors/tfoda.py - Detectron2 · Detectron2-based object detector
detectors/detectron2.py - Haar cascade · Classic cascade-based detector
detectors/haarcascade.py
Core Flow
Tracking, counting, and object state
- Blob · Per-object geometry and metadata container
util/blob.py - Object info · Persistent object attributes for counting
util/object_info.py - Tracker · Maintains object identity across frames
tracker.py - Counter · Counts tracked objects crossing the configured region
counter.py
Support
Configuration, logging, utilities, data, and packaging
- Settings · Runtime configuration defaults and env wiring
settings.py - Job · Encapsulates run-specific inputs and options
util/job.py - Logger · Central logging setup and formatting
util/logger.py - Debugger · Interactive debug display and inspection
util/debugger.py - Image utils · Frame preprocessing and annotation helpers
util/image.py - ROI · Counting line or region definition
util/detection_roi.py - Boxes · Bounding box math and intersection logic
util/bounding_box.py - Data · Local sample videos and model assets
data
Connections 23
- main.py → Settings: loads · Reads runtime configuration
- main.py → Job: builds · Creates the run/job context
- main.py → Counter flow: starts · Launches the counting workflow
- Counter flow → Detector API: uses · Selects a detector through the common contract
- Counter flow → Tracker: feeds · Sends detections into tracking
- Counter flow → Counter: drives · Applies counting rules to tracked objects
- Counter flow → Debugger: renders · Optionally opens debug visualization
- Counter flow → Logger: logs · Emits processing and count events
- Counter flow → Image utils: annotates · Uses image helpers for frame display/output
- Counter flow → ROI: applies · Uses region or line geometry for counting
- Counter flow → Data: reads · Consumes local videos and model assets
- Tracker → Blob: creates · Represents detected objects as blobs
- Tracker → Object info: updates · Maintains per-object state over time
- Counter → ROI: checks · Evaluates crossings against the counting region
- Counter → Boxes: compares · Uses box geometry for movement and overlap
- Blob → Boxes: contains · Stores geometric extent
- Blob → Object info: shares · Carries object metadata across frames
- Detector API → YOLO: implemented by · One concrete detector backend
- Detector API → TF-ODA: implemented by · One concrete detector backend
- Detector API → Detectron2: implemented by · One concrete detector backend
- Detector API → Haar cascade: implemented by · One concrete detector backend
- Debugger → Logger: emits · Records interactive debug actions
- Settings → Job: configures · Job parameters are derived from settings