{"id":50309355,"url":"https://github.com/cinderblock/balls-counter","last_synced_at":"2026-05-28T19:30:26.098Z","repository":{"id":352517746,"uuid":"1184790193","full_name":"cinderblock/balls-counter","owner":"cinderblock","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-24T00:56:49.000Z","size":503,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-24T02:27:00.646Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cinderblock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-17T23:55:26.000Z","updated_at":"2026-05-24T00:56:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cinderblock/balls-counter","commit_stats":null,"previous_names":["cinderblock/balls-counter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cinderblock/balls-counter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cinderblock%2Fballs-counter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cinderblock%2Fballs-counter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cinderblock%2Fballs-counter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cinderblock%2Fballs-counter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cinderblock","download_url":"https://codeload.github.com/cinderblock/balls-counter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cinderblock%2Fballs-counter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33624202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-28T19:30:25.312Z","updated_at":"2026-05-28T19:30:26.084Z","avatar_url":"https://github.com/cinderblock.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ball Counter\n\nComputer vision system to count ball scoring events in FIRST Robotics competitions. Detects bright yellow spheres using motion-based background subtraction combined with HSV color thresholding.\n\nSupports multiple simultaneous camera streams with independent tuning. Two geometry modes:\n\n- **Line band**: a narrow band around a counting line — for side-view cameras watching balls drop through an outlet\n- **ROI ring**: a thin ring around a polygon perimeter — for top-down cameras watching balls enter a goal opening\n\nBoth use the same core algorithm: MOG2 background subtraction isolates moving pixels, HSV masking isolates yellow, and peak detection on the \"moving yellow in zone\" signal triggers scoring events.\n\n## Setup\n\n```bash\nuv sync\n```\n\n## Usage\n\n### 1. Draw counting geometry\n\nFor side-view cameras (outlet), draw a counting line:\n```bash\nuv run python scripts/draw_line.py path/to/video.mp4\n```\n\nFor top-down cameras (inlet), draw the goal opening polygon:\n```bash\nuv run python scripts/draw_roi.py path/to/video.mp4\n```\n\n### 2. Create a config file\n\nCopy `config.example.json` to `config.json`. Each stream needs:\n- `source`: RTSP URL or video file path\n- `line` or `roi_points`: counting geometry from step 1\n- `ball_area`, `band_width`, `cooldown`: tuning parameters\n\n### 3. Run the counter\n\n```bash\nuv run ball-counter config.json\n```\n\nOptions:\n\n| Flag | Description |\n|------|-------------|\n| `--web-port PORT_OR_SOCKET` | Enable web UI on a TCP port (e.g. `8080`) or Unix socket path |\n| `--host HOST` | Interface to bind the web server to (default: `0.0.0.0`) |\n| `--trusted-proxies IPS` | Comma-separated IPs (or `*`) to trust for `X-Forwarded-*` headers |\n| `--yolo-model PATH` | YOLO ball detector model — uses object tracking instead of signal peak detection |\n| `--model PATH` | Trained ML peak detector — replaces threshold-based counting |\n| `--wizard` | Launch setup wizard even if config already exists |\n| `--progress-interval N` | Print video-file progress every N frames (default: 300, 0 = off) |\n\nAll streams are viewable via the web UI with real-time signal overlay and running counts.\n\n### 4. Calibrate HSV thresholds (optional)\n\n```bash\nuv run python -m ball_counter.calibrate path/to/video.mp4\n```\n\n### Field zone counter\n\nCount total balls in 3 field zones (red/middle/blue) from a stitched overhead RTSP stream:\n\n```bash\nuv run python scripts/count_field_zones.py\nuv run python scripts/live_field_count.py  # live view\n```\n\n## Tools\n\n| Script | Purpose |\n|--------|---------|\n| `scripts/draw_line.py` | Draw a counting line on a video frame |\n| `scripts/draw_roi.py` | Draw a polygon ROI on a video frame |\n| `scripts/draw_zones.py` | Draw field zone boundaries interactively |\n| `scripts/annotate.py` | Frame-by-frame score annotation for ground truth |\n| `scripts/count_field_zones.py` | Snapshot ball count by field zone |\n| `scripts/live_field_count.py` | Live RTSP field zone counter |\n\n## Running as a systemd service\n\nInstall and enable the service:\n\n```bash\nsudo cp balls-counter.service /etc/systemd/system/\nsudo systemctl daemon-reload\nsudo systemctl enable --now balls-counter\n```\n\nEdit [balls-counter.env](balls-counter.env) to change settings (config path, web port, YOLO model), then restart:\n\n```bash\nsudo systemctl restart balls-counter\n```\n\nView logs:\n\n```bash\njournalctl -u balls-counter -f\n```\n\n## Architecture\n\n```\nsrc/ball_counter/\n  detector.py   - HSV color masking (create_mask, detect_balls)\n  counter.py    - MotionCounter (line band + ROI ring modes)\n  config.py     - Per-stream JSON configuration\n  stream.py     - StreamProcessor (wraps MotionCounter + video capture)\n  main.py       - Multi-stream runner with tiled display\n  calibrate.py  - Interactive HSV calibration\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcinderblock%2Fballs-counter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcinderblock%2Fballs-counter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcinderblock%2Fballs-counter/lists"}