|
1 | | -# emotiontracker |
| 1 | +````markdown |
| 2 | +# Real-Time Emotion and Face Detection App |
| 3 | + |
| 4 | +[](https://github.com/apaezcastro/emotiontracker/actions/workflows/main_andre-emotion-app.yml) |
| 5 | + |
| 6 | +A full-stack web application that performs real-time emotion detection and face tracking from a live webcam feed. This app uses a Python backend for AI inference and a JavaScript frontend to render the results dynamically on an HTML5 canvas. |
| 7 | + |
| 8 | +**Live Demo:** [andre-emotion-app.azurewebsites.net](https://andre-emotion-app.azurewebsites.net) |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## Features |
| 13 | + |
| 14 | +* **Live Emotion Detection:** Identifies the user's emotion (e.g., Happy, Sad, Neutral) in real-time. |
| 15 | +* **Dynamic Bounding Box:** Draws a precise box around the detected face on the video feed. |
| 16 | +* **Responsive UI:** The emotion label is anchored to the bounding box and moves with it. |
| 17 | +* **Robust Concurrency:** The app is built with a client-side "lock" to ensure stable, real-time performance by processing one frame at a time, preventing request stacking. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## Tech Stack |
| 22 | + |
| 23 | +### Backend |
| 24 | +* **Python:** Core application language. |
| 25 | +* **Flask:** Serves the API endpoint. |
| 26 | +* **Gunicorn:** Production web server (used in Docker). |
| 27 | +* **OpenCV (`opencv-python-headless`):** For image processing (`imdecode`) and face detection (Haar Cascades). |
| 28 | +* **ONNXRuntime:** To run the `emotion-ferplus-8.onnx` model. |
| 29 | +* **NumPy:** For high-performance array manipulation. |
| 30 | +* **Flask-Cors:** To handle cross-origin requests from the frontend. |
| 31 | + |
| 32 | +### Frontend |
| 33 | +* **HTML5:** `video` and `canvas` elements for video streaming. |
| 34 | +* **CSS3:** Styling for the video, bounding box, and overlay. |
| 35 | +* **JavaScript (ES6+):** Manages the `setInterval` loop, captures frames, and uses the `fetch` API. |
| 36 | + |
| 37 | +### Deployment |
| 38 | +* **Docker:** Containerizes the Python backend for consistent, isolated deployment. |
| 39 | +* **GitHub:** Source code repository. |
| 40 | +* **Azure (CI/CD):** Automated build and deployment pipeline (via GitHub Actions) to an Azure App Service. |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## 🚀 Getting Started (Running Locally) |
| 45 | + |
| 46 | +There are two ways to run this project locally. Docker is the recommended method as it handles all dependencies automatically. |
| 47 | + |
| 48 | +### Prerequisites |
| 49 | + |
| 50 | +* [Git](https://git-scm.com/downloads) |
| 51 | +* [Node.js (which includes npm)](https://nodejs.org/) |
| 52 | +* [Docker Desktop](https://www.docker.com/products/docker-desktop/) (for the Docker method) |
| 53 | +* [Python 3.8+](https://www.python.org/downloads/) (for the manual Python method) |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +### Option 1: Running with Docker (Recommended) |
| 58 | + |
| 59 | +This method builds the backend in a container, so you do not need to install Python or any packages on your local machine. |
| 60 | + |
| 61 | +**1. Clone the Repository** |
| 62 | +```sh |
| 63 | +git clone [https://github.com/apaezcastro/emotiontracker.git](https://github.com/apaezcastro/emotiontracker.git) |
| 64 | +cd emotiontracker |
| 65 | +```` |
| 66 | + |
| 67 | +**2. Download Model Files** |
| 68 | + |
| 69 | +The Docker build expects the model files to be present. |
| 70 | + |
| 71 | + * **Emotion Model (`emotion-ferplus-8.onnx`):** |
| 72 | + Download it from the ONNX Model Zoo [here](https://www.google.com/search?q=https://github.com/onnx/models/raw/main/vision/body_analysis/emotion_ferplus/model/emotion-ferplus-8.onnx). |
| 73 | + Place this file in the `backend/` directory. |
| 74 | + |
| 75 | + * **Face Detection Model (`haarcascade_frontalface_default.xml`):** |
| 76 | + Download it from the OpenCV repository [here](https://www.google.com/search?q=https://github.com/opencv/opencv/raw/master/data/haarcascades/haarcascade_frontalface_default.xml). |
| 77 | + Place this file in the `backend/` directory. |
| 78 | + |
| 79 | +**3. Build and Run the Backend Container** |
| 80 | + |
| 81 | +```sh |
| 82 | +# Navigate to the backend directory |
| 83 | +cd backend |
| 84 | + |
| 85 | +# Build the Docker image |
| 86 | +docker build -t emotion-app-backend . |
| 87 | + |
| 88 | +# Run the container, mapping port 5000 |
| 89 | +docker run -p 5000:5000 emotion-app-backend |
| 90 | +``` |
| 91 | + |
| 92 | +The backend is now running on `http://localhost:5000`. |
| 93 | + |
| 94 | +**4. Run the Frontend** |
| 95 | + |
| 96 | +In a **new terminal**, navigate to the frontend directory: |
| 97 | + |
| 98 | +```sh |
| 99 | +# (From the root directory) |
| 100 | +cd frontend |
| 101 | + |
| 102 | +# Install npm packages |
| 103 | +npm install |
| 104 | + |
| 105 | +# Run the development server |
| 106 | +npm start |
| 107 | +``` |
| 108 | + |
| 109 | +Your browser will open to `http://localhost:3000`, which will connect to the backend container running on port 5000. |
| 110 | + |
| 111 | +----- |
| 112 | + |
| 113 | +### Option 2: Running Manually (Python & Node.js) |
| 114 | + |
| 115 | +This method requires you to set up both the Python and Node.js environments manually. |
| 116 | + |
| 117 | +**1. Clone the Repository** |
| 118 | + |
| 119 | +```sh |
| 120 | +git clone [https://github.com/apaezcastro/emotiontracker.git](https://github.com/apaezcastro/emotiontracker.git) |
| 121 | +cd emotiontracker |
| 122 | +``` |
| 123 | + |
| 124 | +**2. Download Model Files** |
| 125 | + |
| 126 | + * **Emotion Model (`emotion-ferplus-8.onnx`):** |
| 127 | + Download it from the ONNX Model Zoo [here](https://www.google.com/search?q=https://github.com/onnx/models/raw/main/vision/body_analysis/emotion_ferplus/model/emotion-ferplus-8.onnx). |
| 128 | + Place this file in the `backend/` directory. |
| 129 | + |
| 130 | + * **Face Detection Model (`haarcascade_frontalface_default.xml`):** |
| 131 | + Download it from the OpenCV repository [here](https://www.google.com/search?q=https://github.com/opencv/opencv/raw/master/data/haarcascades/haarcascade_frontalface_default.xml). |
| 132 | + Place this file in the `backend/` directory. |
| 133 | + |
| 134 | +**3. Backend Setup (Python)** |
| 135 | + |
| 136 | +```sh |
| 137 | +# Navigate to the backend directory |
| 138 | +cd backend |
| 139 | + |
| 140 | +# Create a virtual environment |
| 141 | +python -m venv venv |
| 142 | +source venv/bin/activate # On Windows, use `venv\Scripts\activate` |
| 143 | + |
| 144 | +# Install required packages |
| 145 | +pip install -r requirements.txt |
| 146 | + |
| 147 | +# Run the Flask server |
| 148 | +# (It will typically run on http://localhost:5000) |
| 149 | +python app.py |
| 150 | +``` |
| 151 | + |
| 152 | +**4. Frontend Setup (JavaScript)** |
| 153 | + |
| 154 | +In a **new terminal**: |
| 155 | + |
| 156 | +```sh |
| 157 | +# Navigate to the frontend directory |
| 158 | +cd frontend |
| 159 | + |
| 160 | +# Install npm packages |
| 161 | +npm install |
| 162 | + |
| 163 | +# Run the development server |
| 164 | +npm start |
| 165 | +``` |
| 166 | + |
| 167 | +**5. View Your App** |
| 168 | + |
| 169 | +Open your browser and go to `http://localhost:3000`. |
| 170 | + |
| 171 | +----- |
| 172 | + |
| 173 | +## How It Works |
| 174 | + |
| 175 | +1. The JavaScript frontend uses `setInterval` (set to `1000ms` for production) to capture a frame from the user's webcam. |
| 176 | +2. The frame is drawn to an HTML `canvas` and exported as a `image/jpeg` base64 data URL. |
| 177 | +3. This data is sent via a `fetch` POST request to the `/detect_emotion` API endpoint. |
| 178 | +4. A **concurrency lock** (`analyzing` flag) in JavaScript ensures that if the server *does* take longer than the interval, a new request is not sent until the previous one completes. |
| 179 | +5. The Python backend receives the JSON, decodes the base64 string into a NumPy array (`nparr`), and then uses `cv2.imdecode` to create an image. |
| 180 | +6. The AI model runs, returning the emotion and a 1D array for the face coordinates: `[x, y, width, height]`. |
| 181 | +7. The Python backend converts the NumPy array to a standard Python list (`.tolist()`) and returns the emotion and coordinates as JSON. |
| 182 | +8. The frontend receives the JSON, clears any old bounding boxes, and draws a new box and emotion overlay using the received coordinates. |
| 183 | + |
| 184 | +----- |
| 185 | + |
| 186 | +## Design Choices & Key Learnings |
| 187 | + |
| 188 | +This project involved several key design decisions to achieve a stable, real-time feel. |
| 189 | + |
| 190 | + * **Concurrency Lock (`analyzing` flag):** This is the most critical design choice. It prevents "request stacking" and makes the app resilient to performance differences between servers. |
| 191 | + |
| 192 | + * **Locally:** On a 12-core machine, the detection runs in `~100ms`, allowing for a 10 FPS loop. |
| 193 | + * **On Azure (Shared Tier):** The same detection takes `~700-900ms`. The `analyzing` flag gracefully handles this, ensuring the app remains stable and simply updates less frequently, rather than crashing. |
| 194 | + |
| 195 | + * **Lightweight Data Transfer:** The backend only sends back tiny JSON packets (e.g., `~400B`) containing the emotion string and four coordinates. This ensures that the bottleneck is *always* CPU processing time, not network lag. |
| 196 | + |
| 197 | + * **NumPy to List (`.tolist()`):** A `numpy.ndarray` is not JSON serializable. The `.tolist()` method was essential to convert the detection results into a standard Python list that Flask/FastAPI can serialize into JSON. |
| 198 | + |
| 199 | + * **Single Face (1D Array):** The app is currently optimized to detect the single most prominent face, returning a simple 1D array `[x, y, w, h]`. This simplified the frontend logic, though it's a target for future improvement. |
| 200 | + |
| 201 | + * **Frontend Error Handling:** The frontend JavaScript checks if `faceData` exists before trying to draw a box. This prevents a `TypeError` and allows the app to run smoothly, simply hiding the box when no face is detected. |
| 202 | + |
| 203 | +----- |
| 204 | + |
| 205 | +## Future Improvements |
| 206 | + |
| 207 | + * **Upgrade to a Modern Face Detector:** The current implementation uses OpenCV's classic Haar Cascades. A significant improvement would be to replace this with **YuNet**, a lightweight and highly accurate deep-learning detector from the OpenCV model zoo. This would dramatically improve detection accuracy with varied angles, lighting, and partial occlusions. |
| 208 | + * **Support Multiple Faces:** The backend could be refactored to *always* send a 2D array (a list of faces), e.g., `[[x,y,w,h], [x2,y2,w2,h2]]`. The frontend would then use a `forEach` loop to draw a box for every face detected. |
| 209 | + |
| 210 | +----- |
| 211 | + |
| 212 | +## Acknowledgements & Credits |
| 213 | + |
| 214 | + * **AI Model:** This project uses the `emotion-ferplus-8.onnx` model from the [ONNX Model Zoo](https://github.com/onnx/models/tree/main/vision/body_analysis/emotion_ferplus), which is trained on the FER+ dataset. |
| 215 | + * **Libraries:** This project would not be possible without the open-source work from OpenCV, Flask, ONNXRuntime, and NumPy. |
| 216 | + |
| 217 | + |
| 218 | +``` |
| 219 | +``` |
0 commit comments