Computer vision attendance with a PTZ camera

Global InfoVentures Pvt. Ltd. · Assistant AI Engineer · classroom attendance from a single pan-tilt-zoom camera

PTZ classroom attendance

The problem

Marking attendance in a lecture hall by hand costs minutes of every class. Doing it with cameras usually means one fixed camera per section of the room, which multiplies hardware, cabling and cost across a campus.

The constraint that shaped this system: one pan-tilt-zoom camera per room. A PTZ camera can cover a whole hall, but only one part of it at a time — so the design problem becomes scheduling attention, and accepting that attendance is assembled from a sweep rather than captured in an instant.

The system

Each camera runs an ONVIF preset sweep: move to the faculty position first, wait out the zoom and focus settling time, capture, then traverse the configured class presets, then the wider positions used for counting. The sweep is gated to class hours, and frames are published over RTSP into a shared buffer.

Recognition workers consume that buffer, call an InsightFace service for 512-dimension embeddings, and do a top-1 nearest-neighbour search against a Milvus collection with an L2 distance threshold. Matches become attendance records in a PostgreSQL table partitioned by year. A scheduled job pushes the current lecture's attendance to the institution's ERP at a fixed interval during class hours, with a manual resend endpoint for staff.

Running alongside it, on a second buffer, is a YOLOv9 person-detection service producing a headcount from the wide positions.

Why a second model that recognises nobody

The headcount service is the part I would point at first. Face recognition can only tell you about people it matched; it is structurally incapable of telling you it missed someone. A student facing away, badly lit or occluded simply does not appear, and the system has no internal signal that anything is wrong.

An independent headcount does have that signal. If recognition names eleven people and detection counts nineteen bodies, the gap is visible without anyone reviewing footage. It is a cheap, deliberately redundant cross-check against a failure mode the primary model cannot self-report — and it is the difference between a system that is confidently wrong and one that knows it should be doubted.

It has its own weakness: counts are summed across camera positions, and overlapping fields of view can double-count the same person. That makes it a discrepancy signal, not a ground truth, and it should be read as one.

Decisions and their costs

Timed sweeps over event-driven capture. The cycle is built from fixed waits for zoom and focus settling — several minutes of deliberate sleeping per full cycle. It is simple, predictable, and gentle on the camera's motors. It also means a short faculty window can be missed entirely, because the camera is simply pointed elsewhere when it opens.

Top-1 matching with a distance threshold and no margin check. Cheap and fast. But with no comparison against the second-best match, two similar-looking faces produce a confident wrong answer rather than an abstention. A margin test is the first accuracy change I would make.

Acknowledge before persist. The ingestion endpoint returns success before the database write completes, which keeps the capture loop from blocking. A failed write is logged and never retried, so a record can be acknowledged and still not exist. For attendance this is the wrong default — an outbox with retries is the fix, and it is the first reliability change I would make.

Unbounded queues. No backpressure logic to write, and no limit on memory growth if consumers fall behind producers. Fine while throughput holds; a slow failure when it does not.

Liveness detection built but gated off. A separate face-liveness service exists and is deployed, and the system currently runs with it disabled. Without it, a photograph held to a camera is a live question rather than a solved one.

What is not measured

No recognition accuracy, latency or throughput figures were produced for this system. The sweep cycle time is calculable from the configured waits, but that is arithmetic over configuration, not a measurement of a running deployment.

The instrumentation worth adding, in order: the recognition-versus-headcount discrepancy per lecture, which is already the system's own health signal; end-to-end lag from capture to ERP push; and queue depth, which is the leading indicator for the unbounded-queue problem above.

What this demonstrates

Designing around a hardware constraint rather than against it, and building a cheap independent check against the failure a model cannot report about itself. The judgement on display is mostly about what the system does not know — and making that visible instead of letting a confident number stand unchallenged.

Client institutions are not named here, and no student data, imagery or configuration appears in this write-up.

Other case studies

Face recognition attendance across 20+ camera streams · Production voice AI for drive-thru ordering