CCTV IQ - Face ID Attendance
At a glance
| In short | A face-recognition attendance system on two office doorway cameras. It logs clock-in, clock-out and time inside, and answers questions about attendance in plain English. |
|---|---|
| Problem | Track office attendance automatically, using the cameras already at the door. |
| What it does | Recognises enrolled people on two doorway cameras, records clock-in, clock-out and time inside with a face photo for each, and writes one Excel file per day. |
| How it works | Find faces, turn each into an embedding, match it against 182 people. Clock-in is the first sighting of the day on either camera; clock-out is the last. |
| Ask Iris | Type a question like "who was late this week?". Claude Haiku 4.5 on AWS Bedrock writes one SQL query, runs it, and answers from the rows. |
| Result | 0.31 s per frame (from 9.86 s) on an office PC. A score floor fitted to a hand-checked day removed every false name and lost no real person. |
| Tech | Python, InsightFace (SCRFD + antelopev2), OpenVINO on the Intel iGPU, FastAPI, SQLite, AWS Bedrock |
Flow
- 2 doorway camerasRTSP, H.264
- Mask zonesdesks, clear glass
- Find + embed facesSCRFD + antelopev2 on iGPU
- Match182 people, floor 0.41
- Build the dayfirst in, last out, breaks
- Outputsdashboard, Excel, Ask Iris
Key points
- Works with either door both ways: the first and last sighting on any camera decide the day
- Time inside = first to last seen, minus the breaks the cameras saw; a missed exit never docks an honest employee
- Masks the clear glass strip where the lobby camera could see people at their desks, which had cut about 1 in 8 long days short by 2.7 hours
- Refuses a name when two people look too alike: no name beats a confident wrong one
- Runs unattended: starts at login, a supervisor restarts it in about 15 s, and the dashboard never says it is recording when it is not
- Iris never sees staff data to decide what to fetch, only the table layout; it declines off-topic or destructive requests
Results
| Setup | Seconds per frame |
|---|---|
| Both models on CPU | 9.86 |
| Recogniser on iGPU, detector on CPU | 0.69 |
| Both on iGPU | 0.31 |
| Floor | Names kept | Wrong names kept | Real people lost |
|---|---|---|---|
| 0.24 | 70 | 26 | 0 |
| 0.35 | 55 | 11 | 0 |
| 0.40 | 47 | 2 | 0 |
| 0.41 (chosen) | 45 | 0 | 0 |
| Distance between the eyes | Share of sightings | Identified |
|---|---|---|
| 0-20 px | 26% | 15.8% |
| 20-32 px | 54% | 18.1% |
| 32-48 px | 20% | 38.9% |
| 48 px + | 0.3% | 44.4% |
Architecture
Technical details
- The older in/out pairing fully measured only 14% of person-days; the new span-minus-breaks rule gives every day a number and agrees with pairing within 15 minutes 94% of the time
- A break needs a lobby sighting, then 10+ minutes unseen, then proof of coming back in; days that could hide an early exit get a check flag instead
- Switching both cameras from H.265 to H.264 fixed 60-73% of frames arriving with a broken bottom band (ping and substream tests had ruled out WiFi)
- Found and fixed a 9-day silent outage: a thread that crashed quietly, a status flag set before the work started, and nothing watching the process
- A stalled camera is rebooted over HTTP, and reconnects back off instead of hammering it
- Iris retries a failed query with the database's own error; every checkable answer in the tests has hand-written SQL beside it
- Compared four recognition models (antelopev2, AdaFace, buffalo_l, AuraFace): all tied within noise, so camera placement matters more than the model
- Real phone photos matched about 9.5 points better than AI-edited directory photos
- Recognition runs in its own thread; the dashboard only reads snapshots, so closing the browser changes nothing
- Regression tests cover the visit logic, the arrival rules and the ignore-zone geometry
- computer vision
- face recognition
- CCTV analytics
- attendance system
- InsightFace
- OpenVINO
- edge AI
- Intel iGPU
- LLM text-to-SQL
- AWS Bedrock
- Claude
- Python
The code for this project is private, so there is no public link.
All projects