Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# FRS Project Documentation

Welcome to the documentation for **FRS**, a Flask-based face recognition system that captures webcam video, detects faces, recognizes registered people, and lets users register newly detected faces from a browser interface.

## Documentation Index

- [Project Overview](./project-overview.md) - Purpose, features, repository structure, and high-level behavior.
- [Architecture](./architecture.md) - Component layout, threading model, data flow, and runtime lifecycle.
- [Backend Reference](./backend-reference.md) - Flask routes, recognition pipeline, constants, globals, and persistence behavior.
- [Frontend Reference](./frontend-reference.md) - HTML, CSS, JavaScript, UI behavior, and browser-to-server interactions.
- [Setup and Running](./setup-and-running.md) - Requirements, installation, platform notes, and run instructions.
- [Data Storage and Privacy](./data-storage-and-privacy.md) - Face encoding storage, generated files, privacy considerations, and safe handling.
- [Operations and Troubleshooting](./operations-and-troubleshooting.md) - Common runtime issues, camera problems, dependency problems, and tuning guidance.
- [Development Guide](./development-guide.md) - Code organization, contribution workflow, testing ideas, and extension points.

## Quick Start

```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
```

After the server starts, open `http://localhost:5000` in a browser with access to the machine running the webcam.

> Note: This project requires a working camera device and native dependencies used by OpenCV, dlib, and `face_recognition`.
87 changes: 87 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Architecture

## High-Level Architecture

FRS is a single Flask application with three major layers:

1. **Camera and recognition layer** in `app.py`.
2. **HTTP API and streaming layer** in `app.py`.
3. **Browser interface layer** in `templates/index.html`, `static/script.js`, and `static/style.css`.

```text
Webcam
VideoProcessor capture thread
│ stores latest frame
VideoProcessor recognition thread
│ analyzes periodic frames
latest detections + status
├── /video -> annotated MJPEG stream
├── /status -> JSON status text
└── /register -> saves latest unknown face encoding
```

## Runtime Lifecycle

1. Python imports `app.py`.
2. The app checks for `registered_faces.pkl`.
3. Registered face encodings are loaded if the file exists; otherwise an empty registry is created.
4. The known-face cache is initialized.
5. `VideoProcessor().start()` opens the default camera and starts two daemon threads.
6. Flask serves routes when `app.py` is executed directly.
7. On application shutdown, `video_processor.release()` attempts to stop threads and release the camera.

## Threading Model

The application separates capture, recognition, and streaming so the live feed remains responsive even when face recognition is slower than camera capture.

### Capture Thread

The capture thread continuously reads frames from OpenCV's `VideoCapture` object and stores the latest frame in memory. It targets `CAPTURE_FPS` and uses a lock to protect frame access.

### Recognition Thread

The recognition thread periodically copies the latest frame, downsizes it, detects faces, computes encodings, compares them to known encodings, and updates the latest detection list and status text. It targets `RECOGNITION_FPS`, which is lower than the stream frame rate to reduce CPU usage.

### Request Threads

Flask handles HTTP requests. Route handlers read shared state through lock-protected methods and return HTML, JSON, or streaming frame data.

## Shared State

| State | Purpose | Protection |
| --- | --- | --- |
| `registered_faces` | Persistent in-memory mapping of names to encodings | Refreshed through helper functions |
| `known_face_names` | Cached list of registered names for matching | `known_faces_lock` |
| `known_face_encodings` | Cached list of registered encodings for matching | `known_faces_lock` |
| `temp_face_encoding` | Latest unknown face encoding available for registration | `temp_face_lock` |
| `latest_frame` | Most recent webcam frame | `frame_lock` |
| `latest_detections` | Most recent recognition results | `detections_lock` |
| `latest_status` | Text shown by `/status` | `status_lock` |

## Face Recognition Data Flow

1. A full-size BGR camera frame is captured by OpenCV.
2. Recognition resizes the frame by `RECOGNITION_SCALE`.
3. The resized frame is converted from BGR to RGB for `face_recognition`.
4. Face locations are found using the HOG model.
5. Face encodings are computed for detected locations.
6. Each encoding is compared with the cached registered encodings.
7. The closest match is accepted only if its distance is less than or equal to `MATCH_TOLERANCE`.
8. Small-frame coordinates are scaled back up to the displayed frame size.
9. Detections are cached for rendering and status updates.

## Video Streaming Flow

The `/video` route returns a multipart MJPEG response. Each loop iteration:

1. Copies the latest frame.
2. Draws the latest detections on top of the copied frame.
3. Encodes the frame as JPEG.
4. Yields the encoded bytes with the multipart boundary expected by browsers.

This lets a normal `<img>` element display a continuously updating camera stream.
137 changes: 137 additions & 0 deletions docs/backend-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Backend Reference

## Entry Point

The backend lives in `app.py`. Running `python app.py` starts the Flask development server on `0.0.0.0:5000` and initializes webcam processing.

## Configuration Constants

| Constant | Purpose |
| --- | --- |
| `REGISTERED_FACES_PATH` | File path used to persist registered face encodings. |
| `FRAME_WIDTH` / `FRAME_HEIGHT` | Requested webcam capture resolution. |
| `CAPTURE_FPS` | Target camera capture rate. |
| `STREAM_FPS` | Target MJPEG streaming rate. |
| `JPEG_QUALITY` | JPEG encoding quality for streamed frames. |
| `RECOGNITION_SCALE` | Downscale factor used before recognition for speed. |
| `RECOGNITION_FPS` | Target recognition loop rate. |
| `MATCH_TOLERANCE` | Maximum face distance accepted as a match. Lower values are stricter. |

## Persistent Registry

At startup, the application looks for `registered_faces.pkl` in the working directory. If it exists, the file is loaded with `pickle`. If it does not exist, the app starts with an empty registry.

The registry structure is:

```python
{
"Person Name": {
"encoding": <face encoding vector>
}
}
```

When a new face is registered, the registry is written back to `registered_faces.pkl`.

## Known-Face Cache

`refresh_known_face_cache()` converts the registry dictionary into two lists:

- `known_face_names`
- `known_face_encodings`

These lists make recognition matching simpler and faster because the code can compare a detected encoding against all cached known encodings.

## `VideoProcessor`

`VideoProcessor` owns the webcam capture object and the background processing threads.

### Responsibilities

- Open and configure the camera.
- Capture frames continuously.
- Run recognition periodically.
- Store the latest frame, detections, and status.
- Produce annotated JPEG frames for the video stream.
- Release the camera on shutdown.

### Important Methods

| Method | Description |
| --- | --- |
| `start()` | Starts capture and recognition daemon threads. |
| `_capture_loop()` | Reads camera frames and stores the latest frame. |
| `_recognition_loop()` | Runs face recognition against the latest frame. |
| `get_frame()` | Returns a copy of the latest frame. |
| `get_detections()` | Returns the most recent detection list. |
| `get_status()` | Returns the current status text. |
| `_update_status()` | Converts detections into user-facing status text. |
| `get_jpeg_frame()` | Draws detections and returns a JPEG-encoded frame. |
| `release()` | Stops processing and releases camera resources. |

## Face Registration

`register_new_face(name, face_encoding)` stores the supplied encoding in `registered_faces`, refreshes the known-face cache, and serializes the registry to disk.

The `/register` route uses `temp_face_encoding`, which is set when the rendering layer sees an unknown face in the latest detections. Registration can fail if:

- The submitted name is empty.
- No new face encoding is currently available.

## Recognition Pipeline

`recognize_faces(frame)` performs the following steps:

1. Resize the frame using `RECOGNITION_SCALE`.
2. Convert the resized frame from BGR to RGB.
3. Locate faces with `face_recognition.face_locations(..., model="hog")`.
4. Generate encodings with `face_recognition.face_encodings(...)`.
5. Compare each encoding against cached known encodings using face distance.
6. Select the closest known face with `numpy.argmin`.
7. Accept the match only when the distance is within `MATCH_TOLERANCE`.
8. Scale face coordinates back to the original frame size.
9. Return tuples of `(match, face_location, face_encoding)`.

## Detection Rendering

`draw_detections(frame, detections)` draws a rectangle and label for each detected face:

- Green rectangle and registered name for known faces.
- Red rectangle and `New Face` for unknown faces.

When an unknown face is rendered, its encoding is stored as `temp_face_encoding` so it can be registered by the form.

## Flask Routes

| Route | Method | Response | Purpose |
| --- | --- | --- | --- |
| `/` | `GET` | HTML | Renders the main web UI. |
| `/video` | `GET` | Multipart MJPEG stream | Streams annotated camera frames. |
| `/status` | `GET` | JSON | Returns current face recognition status. |
| `/register` | `POST` | JSON | Registers the latest unknown face with a submitted name. |

### `/status` Response Example

```json
{
"status": "Recognized: Ada"
}
```

### `/register` Request Example

```http
POST /register
Content-Type: application/x-www-form-urlencoded

name=Ada
```

### `/register` Success Example

```json
{
"ok": true,
"message": "Registered Ada."
}
```
57 changes: 57 additions & 0 deletions docs/data-storage-and-privacy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Data Storage and Privacy

## What Data Is Stored

FRS stores registered face encodings, not raw face images. A face encoding is a numerical representation generated from a detected face. These encodings are still biometric identifiers and should be handled carefully.

## Storage Location

Registered faces are persisted in:

```text
registered_faces.pkl
```

The file is created in the process working directory when a user successfully registers a face.

## Storage Format

The file is serialized with Python `pickle`. The in-memory structure maps a submitted name to an encoding object:

```python
{
"Name": {
"encoding": encoding
}
}
```

## Security Considerations

- Do not commit `registered_faces.pkl` to source control.
- Do not share the pickle file publicly.
- Restrict filesystem access to the machine running the app.
- Avoid accepting untrusted pickle files because loading pickle data can execute arbitrary code.
- Consider replacing pickle with a safer format or database layer before production use.
- Add authentication before exposing the application outside a trusted local environment.

## Privacy Considerations

Face encodings can identify people and should be treated as sensitive biometric data. Before using the system with real people:

- Get clear consent.
- Explain what is stored and why.
- Provide a way to remove registered identities.
- Define a retention policy.
- Secure backups and exported data.
- Follow applicable privacy laws and organizational policies.

## Data Deletion

To remove all registered faces, stop the application and delete:

```bash
rm registered_faces.pkl
```

To remove one person, a management function or route would need to be added because the current application only supports adding or replacing entries by name.
81 changes: 81 additions & 0 deletions docs/development-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Development Guide

## Code Organization

The project is intentionally compact:

- `app.py` contains backend configuration, face recognition logic, streaming, registration, and Flask routes.
- `templates/index.html` contains the single rendered page.
- `static/script.js` contains browser behavior.
- `static/style.css` contains UI styling.
- `requirements.txt` lists Python dependencies.
- `docs/` contains this documentation set.

## Local Development Workflow

```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
```

After making changes, manually verify:

1. The server starts without import errors.
2. The browser loads `/`.
3. The video feed renders.
4. `/status` returns JSON.
5. Registration succeeds when an unknown face is visible.
6. `registered_faces.pkl` is created or updated.

## Suggested Automated Checks

This repository currently does not include a dedicated test suite. Useful future checks include:

- Python syntax compilation with `python -m py_compile app.py`.
- Unit tests for registration and recognition helper functions with mocked encodings.
- Flask route tests using Flask's test client.
- Frontend smoke checks for expected DOM elements.
- Formatting and linting with tools such as Ruff or Black.

## Extension Points

### Configuration

Move constants from `app.py` into environment variables or a config file to make deployment easier.

### Storage

Replace `registered_faces.pkl` with SQLite or another database if you need safer querying, deletion, migrations, or metadata.

### Identity Management

Add routes and UI for:

- Listing registered people.
- Deleting a registered person.
- Updating names.
- Exporting or backing up registrations.

### Security

Add authentication before exposing the app beyond a trusted local environment. Registration currently accepts any form submission that can reach the server.

### Recognition Models

The current face location model is `hog`, which is CPU-friendly. Systems with GPU support could experiment with CNN-based detection, but this requires additional native setup and more compute.

### API Design

If the frontend grows, consider documenting and versioning the JSON endpoints, for example under `/api/status` and `/api/register`.

## Contribution Guidelines

When contributing:

- Keep changes focused and easy to review.
- Update documentation when behavior changes.
- Avoid committing generated biometric data such as `registered_faces.pkl`.
- Test with an actual camera when changing recognition or streaming behavior.
- Be careful with threading changes and protect shared mutable state with locks.
Loading