6.2 KiB
Developer Guide
Technical reference for developers working on or extending the Wildlife Monitoring Dashboard.
Architecture Overview
The application is a single-file Flask server (dashboard.py) with HTML/CSS/JS templates embedded as Python string constants. There is no separate frontend build step.
Browser <──HTTP──> Flask (dashboard.py)
│
├── Model inference (PyTorch, EfficientNet V2-S)
├── XAI pipeline (ScoreCAM, LIME, nearest neighbours)
├── In-memory detection state
└── File-based XAI cache (_xai_cache/)
Key Components
Model & Data Loading (module level)
On startup, the following are loaded once:
- Model:
EfficientNet_V2_S_Weightsbase with a custom 7-class classification head, loaded fromefficientnet_v2_wild_forest_animals.pt. - ScoreCAM: Targets the last convolutional layer (
model.features[-1]). - LIME explainer:
LimeImageExplainerinstance. - Dataset auto-download: If
wild-forest-animals-and-person-1/is missing, the Roboflow SDK downloads it automatically on startup. - Datasets:
test_dsandtrain_dsusingWildForestAnimalsDataset, a customDatasetclass reading from the Roboflow-exported directory structure with_classes.csvlabel files. - Training features: Pre-extracted feature vectors for all training images (used for nearest neighbour lookup). Stored as a normalised matrix
train_featsfor fast cosine similarity via matrix multiplication.
Detection Simulation (/api/simulate)
Each call:
- Picks a random image from
test_dsand a random camera. - Runs model inference to get predicted class and confidence.
- Creates a detection dict with a unique 8-char hex ID.
- Saves the original image to
_xai_cache/<det_id>/original.png. - Kicks off
compute_xaiin a background thread (serialised by_xai_lockto prevent GPU OOM). - Returns the detection as JSON.
XAI Pipeline (compute_xai)
Produces all explanation artefacts for a detection and writes them to _xai_cache/<det_id>/:
| File | Method |
|---|---|
original.png |
Raw input image (224x224) |
chart.png |
Matplotlib probability bar chart (kept for potential offline use) |
scorecam.png |
ScoreCAM heatmap overlay |
lime1.png |
LIME explanation for top-1 predicted class |
lime2.png |
LIME explanation for top-2 predicted class |
contrastive.png |
Contrastive LIME (top-1 vs top-2) |
nb1.png, nb2.png, nb3.png |
Three nearest training neighbours |
meta.json |
Metadata: predictions, probabilities, LIME class labels, neighbour info |
The function is idempotent — it checks for meta.json existence before computing. A threading.Lock (_xai_lock) ensures only one computation runs at a time to avoid GPU memory exhaustion.
Background Pre-computation
When a detection is simulated, XAI computation starts immediately in a background thread. The /api/xai/<det_id> endpoint waits on a threading.Event for that detection. This means:
- If the user clicks a detection after a few seconds, XAI is likely already cached.
- If they click immediately, the request blocks until computation finishes.
- If somehow no background thread was started, it falls back to synchronous computation.
API Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/ |
GET | Home page (map + sidebar) |
/det/<det_id> |
GET | Detection detail page |
/cam/<cam_id> |
GET | Camera feed page |
/api/simulate |
POST | Simulate a new detection |
/api/xai/<det_id> |
GET | Get XAI results (triggers computation if needed) |
/api/detections |
GET | List all detections |
/api/verify/<det_id> |
POST | Verify or correct a detection |
/map.webp |
GET | Serve the map image |
/xai/<det_id>/<filename> |
GET | Serve cached XAI images |
HTML Templates
Three template constants are defined as raw Python f-strings (r"""..."""):
HOME_HTML— fullscreen map, camera markers, sidebar with chart/filters/list, auto-simulation JS loop.DETAIL_HTML— verification bar, XAI carousel with left/right navigation, HTML/CSS probability chart.CAM_HTML— responsive grid of detection cards for a single camera.
Templates use Jinja2 syntax (via Flask's render_template_string) and receive context variables like cameras, class_names, det, etc.
In-Memory State
All detection state is held in the detections Python list. This means:
- State is lost on server restart.
- There is no database.
- This is intentional for a demonstration/prototype.
Extending the Application
Adding a new XAI method
- Add the computation logic inside
compute_xai(), saving the output as a PNG in theoutdirectory. - Add any relevant metadata to the
metadict. - Add a new slide entry in the
slidesarray inDETAIL_HTML's JavaScript.
Adding a new species
- Add the class name to
CLASS_NAMES. - Add an emoji to
SPECIES_ICON. - Retrain the model with the new class and update
efficientnet_v2_wild_forest_animals.pt.
Adding or moving cameras
Edit the CAMERAS dict. The px and py values are percentages relative to the map image dimensions (left/top).
Persisting detections
To add persistence, replace the detections list with a database (e.g. SQLite). Key fields per detection: id, idx, cam, pred, conf, time, verified, manual. The _xai_cache directory already provides file-based persistence for XAI artefacts.
Dependencies
Core runtime dependencies (see pyproject.toml):
| Package | Purpose |
|---|---|
flask |
Web framework |
torch, torchvision |
Model inference and image transforms |
grad-cam |
ScoreCAM implementation |
lime |
LIME explainer |
scipy |
Required by LIME internals |
matplotlib |
Probability chart generation |
Pillow (via torchvision) |
Image I/O |
scikit-image (via lime) |
mark_boundaries for LIME visualisation |
Running in Development
uv run python dashboard.py
Flask runs in debug mode with auto-reload on port 5000. The _xai_cache/ directory can be deleted at any time to force recomputation of all explanations.