Send a photo or a short video to your bot and get back the people count with age group and gender demographics β powered by MiVOLO (YOLOv8 + transformer). Privacy-first: media is deleted right after the analysis.
$ python3 bot.py
No camera integration, no dashboards to deploy β the chat you already use is the interface.
A photo, or a slow 20β30s pan video, sent to your private bot on Telegram.
YOLOv8 finds every person and face in the scene β bodies and faces are matched.
MiVOLO estimates age and gender per person; videos track unique people across frames.
You get the summary in chat; only aggregated numbers go to a CSV history. Media is deleted.
The exact summary below was produced by the bot for this public sample photo β 13 people found, all 13 with demographics.
people.jpg) β also used by the integration tests
Sunday meeting
14:32 ββ
Summary - Sunday meeting Total people: 13 With estimated demographics: 13 Children: 0 M, 1 F Adults: 4 M, 8 F14:32
A person that appears in 300 frames is still one person: the tracker follows each individual across the pan, drops noisy tracks, and aggregates age by median and gender by majority vote.
people.mp4) β 13.7s, 343 frames @ 25 fpsTracks shorter than 3 frames are discarded as tracker noise, so ID switches don't inflate the count.
Every number below is reproducible from the repository.
Five commands from clone to a running bot. The first run downloads ~250 MB of model weights and verifies their SHA256 before loading.
Python 3.12 is recommended (brew install python@3.12).
The PIP_CONSTRAINT works around a MiVOLO build quirk with
modern setuptools β it's already in the repo.
Copy .env.example to .env,
fill in your bot token and your Telegram user id. The file stays out of git and
readable only by you.
Send /start to your bot, then a photo. That's it.
$ git clone https://github.com/HectorIFC/count-people && cd count-people $ python3.12 -m venv .venv && source .venv/bin/activate $ PIP_CONSTRAINT=$PWD/constraints.txt pip install -r requirements.txt # ... resolves torch (MPS on Apple Silicon), MiVOLO, OpenCV ... $ cp .env.example .env && chmod 600 .env && $EDITOR .env $ set -a && source .env && set +a && python3 bot.py INFO Loading models (first run downloads ~250 MB)... INFO Models ready (device: mps). INFO Bot running (long polling). Ctrl+C to stop.
The bot only makes outbound TLS connections to api.telegram.org β no webhook, no reverse proxy, no port to expose. Lock the box down and let systemd keep it alive.
Key-only SSH, default-deny firewall, brute-force protection and automatic
security patches. The full hardened systemd unit (sandboxed with
ProtectSystem=strict, secrets via root-only
EnvironmentFile) is in the
README.
peoplebot)people-counter systemd service# firewall: deny everything inbound except SSH $ sudo ufw default deny incoming && sudo ufw allow OpenSSH && sudo ufw enable # CPU-only torch, then the project deps $ pip install torch --index-url https://download.pytorch.org/whl/cpu $ PIP_CONSTRAINT=$PWD/constraints.txt pip install -r requirements.txt # secrets root-only, service sandboxed $ sudo install -m 600 -o root -g root /dev/null /etc/people-counter/env $ sudo systemctl enable --now people-counter β people-counter.service β active (running)
Two chats with official Telegram bots and one line in your .env. The allowlist is mandatory β the bot refuses to start without it and silently ignores anyone else.
8613β¦:AAFβ¦ π14:2111111111114:22/newbot β save the token (it's a secret β only in .env, never in git or chats)..env:TELEGRAM_BOT_TOKEN=β¦ALLOWED_USER_IDS=111111111Optional: send /setuserpic to @BotFather and upload
the project logo.
OWASP-guided hardening, verified by tests.
Photos and videos are processed in a temporary directory and deleted right after. Only aggregated numbers reach the CSV (created with mode 600).
No ALLOWED_USER_IDS, no startup. Strangers get no reply at all β the bot never confirms it exists; attempts are only logged.
Weights are pickle-loaded, so every startup verifies their SHA256 against pinned hashes. Tampered file β deleted β abort.
Long polling means outbound-only TLS to api.telegram.org. No webhook, no open port, nothing for strangers to scan.
Captions are truncated, stripped of control characters and formula prefixes before touching the CSV.
Token only via environment; .env is gitignored and chmod 600; the httpx logger is silenced so the token never reaches logs.