Vai al contenuto

Software engineering · Feb 2025–oggi

HealthCam

Una piattaforma modulare che trasforma i video della webcam in misure associate all’uso delle applicazioni desktop. Stima del battito e rilevamento dei blink sono analizzatori locali sostituibili; una dashboard cloud permette di consultarne i risultati nel tempo.

Il mio contributo · progettazione e sviluppo end-to-end

Backend e sistemi
Servizi Python, orchestrazione asincrona e API autenticate.
Data engineering
PostgreSQL, aggregazione SQL incrementale e medie pesate.
Prodotto web
Dashboard Next.js per utente e demo in sola lettura.

Nella pagina di accesso scegli “Explore the demo”. Nessun account o webcam richiesti. Dati sintetici in sola lettura.

Ruolo
Progettazione e sviluppo end-to-end
Stack
Python · FastAPI · TypeScript · Next.js · PostgreSQL · Docker
Origine
Tesi di laurea · Università degli Studi di Milano
Stato
3.0 · Plugin · Anteprima per sviluppatori
Dashboard BPM con tre riferimenti: andamento giornaliero, confronto tra applicazioni e durata del monitoraggio
  1. Andamento nel tempo

    Medie giornaliere della frequenza cardiaca stimata.

  2. Confronto tra app

    Misure associate al contesto dell’applicazione attiva.

  3. Copertura dei dati

    Durata del monitoraggio per interpretare i risultati.

Demo con dati sintetici. Illustra il prodotto e le query; non dimostra l’accuratezza delle misure.

01 · Architettura

Una sola acquisizione, più analizzatori

HealthCam 3.0 invia ogni segmento webcam agli analizzatori abilitati in parallelo e allinea i risultati al contesto dell’app attiva. Un registro individua le capacità dei servizi; i manifest configurano processi nativi e container Docker. I risultati strutturati raggiungono un’API cloud autenticata.

Sul computer · Python / FastAPI

Webcam → segmenti da 20 secondi

Video locale e metadati temporali

Orchestratore + registro dei servizi

Manifest: runtime, ciclo di vita e capacità

Stesso segmento → analisi in parallelo

Battito · pyVHR

Plugin sostituibile

Blink · MediaPipe

Logica blink dedicata

Risultati + contesto dell’app → coda

App-Tracker → eventi allineati ai timestamp del segmento

Risultati e contesto

JSON autenticato · nessun video

Nel cloud · Next.js / PostgreSQL

API di ingestione

API key → utente · validazione Zod

Telemetria grezza · JSONB

Risultati, contesto e metadati

GitHub Actions → riepiloghi SQL

Incrementale ogni 5 min · riconciliazione giornaliera

Dashboard per utente

Andamenti, confronti e copertura

Architettura attuale 3.0. La pipeline di analisi accetta plugin; gli aggregati SQL e la dashboard mostrati sono quelli BPM attualmente implementati.

02 · Scelte tecniche

Progettare componenti sostituibili

Ho sviluppato acquisizione, orchestrazione, ciclo di vita dei servizi, ingestione, pipeline dati e applicazione web. L’analizzatore cardiaco integra pyVHR; quello dei blink usa i landmark di MediaPipe con logica di elaborazione scritta da me.

Inferenza locale, analisi storica in cloud

Il video resta sul dispositivo; il cloud conserva risultati consultabili nel tempo.

Costo: Configurazione manuale e vincoli hardware: l’attuale analizzatore BPM richiede NVIDIA/CUDA.

Processi nativi e container

Acquisizione e contesto usano le API del sistema operativo; i container isolano le dipendenze di analisi.

Costo: Più ambienti da avviare, collegare e diagnosticare.

Manifest e contratto comune

Un nuovo analizzatore dichiara runtime, comandi e capacità; il registro lo rende individuabile dall’orchestratore.

Costo: SQL e interfacce specifiche di nuove metriche richiedono ancora codice dedicato.

Componenti di ricerca integrati: pyVHR / PHuSe Lab per il battito; MediaPipe per i landmark del volto. Non ho sviluppato i loro algoritmi o modelli di base.

03 · Data engineering

Preparare i riepiloghi prima della lettura

PostgreSQL conserva la telemetria grezza in JSONB. GitHub Actions pianifica l’aggregazione SQL incrementale ogni cinque minuti e una riconciliazione giornaliera. Le dashboard leggono tabelle di riepilogo, evitando di scandire l’intero storico a ogni richiesta.

  1. Ogni 5 minuti

    Aggregazione incrementale

    Un cursore seleziona i nuovi payload. Gli upsert SQL aggiornano somme, conteggi, estremi e durata per utente, giorno e app.

  2. Ogni giorno · 02:10 UTC

    Riconciliazione

    Ricostruisce i riepiloghi recenti dai dati grezzi, partendo da due giorni prima dell’ultima data aggregata, per recuperare anche dati in ritardo in quella finestra.

  3. A ogni lettura

    Query sulle tabelle di riepilogo

    Le medie sono pesate per il numero di campioni: giornate con pochi dati non hanno lo stesso peso di giornate con molti dati.

Il calcolo anticipato sposta le scansioni dello storico fuori dalle richieste delle dashboard. Il compromesso è un aggiornamento differito e la manutenzione di dati grezzi e riepiloghi.

Assunzioni e limiti delle trasformazioni

Il segmento è attribuito all’ultimo evento di contesto dell’app e al giorno di invio (o ricezione), non suddiviso tra i cambi di app. Il timestamp di acquisizione resta nei dati grezzi. La riconciliazione copre una finestra recente: non recupera automaticamente ogni ritardo storico. Le esecuzioni pianificate possono subire ritardi; non è stato misurato un incremento di prestazioni.

Verifiche tecniche · 30 settembre 2026

15 test superati · contratto dati e API

6 verifiche dell’API e 9 del contratto dati, con database simulato. Verificano autenticazione, validazione, richiesta di scrittura e limite di richieste; non l’intera pipeline.

Risultati, esempio di test e report riproducibile
Autenticazione
Chiave assente o sconosciuta → 401.
Validazione
JSON non valido o payload fuori contratto → 400.
Ingestione
Payload valido → 201 e chiamata di scrittura associata all’utente autenticato.
Limite di richieste
Soglia raggiunta → 429, Retry-After: 60 e nessuna chiamata di scrittura.
Leggi un test: rifiutare richieste oltre il limite

Estratto dal test esistente. Il database simulato restituisce un conteggio pari alla soglia; le asserzioni controllano la risposta e l’assenza di scritture. La chiave mostrata è un valore fittizio.

db.user.findUnique.mockResolvedValue({ id: "user-1" });
db.recordingPayload.count.mockResolvedValue(INGEST_RATE_LIMIT.max);

const response = await POST(
  request(validCloudRequest(), { "x-api-key": "valid-key" }),
);

expect(response.status).toBe(429);
expect(response.headers.get("Retry-After")).toBe("60");
expect(db.recordingPayload.create).not.toHaveBeenCalled();

Ambito: test isolati del contratto e dell’API. Non verificano PostgreSQL reale, richieste concorrenti, retry dell’agente o accuratezza delle misure. Il contratto accetta un risultato di analisi in errore; l’invio di quel risultato dall’agente non è verificato da queste suite.

Scarica il report: risultati, versione e riproduzione (Markdown)

04 · Ricerca e valutazione

Valutare un analizzatore della piattaforma

Per la tesi ho integrato pyVHR, pacchetto di ricerca del PHuSe Lab dell’Università degli Studi di Milano, come analizzatore del battito da webcam (rPPG). Acquisizione, orchestrazione, archiviazione e dashboard di HealthCam sono separate dall’accuratezza di questo stimatore.

Postazione di validazione della tesi con webcam, dashboard e sensore Arduino sulla scrivania
Contesto storico: postazione della tesi HealthCam 2.0, slide 8. La versione attuale è la 3.0 con plugin.
L’errore superava le differenze da distinguere

Errore assoluto medio≈ 11,6 BPM

Differenze tra app≈ 3 BPM

Ho allineato le stime al Polar H10 e analizzato errore, concordanza e luminosità. La configurazione valutata non distingueva queste differenze in modo affidabile; il risultato riguarda quella configurazione dello stimatore e quell’esperimento.

Fonte: presentazione di tesi HealthCam 2.0, slide 10–11.

La sostituibilità degli analizzatori era un obiettivo architetturale del progetto. Il risultato sperimentale delimita l’uso di questo stimatore; un altro analizzatore può riutilizzare acquisizione, orchestrazione e ingestione.

Metodo, fonti e limiti di riproducibilità

05 · Gestione dei dati

Il video resta sul dispositivo

L’agente invia risultati, contesto di applicazioni e finestre, timestamp e metadati dei file. Video e file di metadati vengono eliminati dopo analisi e upload riusciti; gli errori possono lasciarli sul dispositivo. I dati derivati restano sensibili. Le API locali non sono autenticate e richiedono una macchina di sviluppo fidata.

Cosa si può valutare oggi

Il sistema, il risultato e i prossimi passi.

2.0 · Tesi: servizi separati e prima valutazione rPPG. 3.0 · Oggi: manifest, registro delle capacità e più analizzatori in parallelo.

Implementato

  • Acquisizione webcam e contesto dell’app attiva
  • Analizzatori rPPG e blink eseguiti in parallelo
  • Servizi configurati tramite manifest
  • Ingestione autenticata, PostgreSQL e dashboard BPM

Ancora in sviluppo

  • Installer per utenti finali
  • Installazione dei servizi dallo Store
  • Dashboard e notifiche complete per il blink
  • Invio persistente, ingestione idempotente e test di integrazione più ampi
Limiti attuali di affidabilità

La coda di invio è in memoria. Un errore di un analizzatore blocca l’invio dell’intero segmento, anche quando gli altri riescono; gli upload falliti vengono ritentati. Persistenza della coda, gestione dei risultati parziali e idempotenza dell’ingestione sono i prossimi interventi. I test pubblicati non verificano il percorso completo webcam/GPU/database.

La demo mostra l’interfaccia. L’esecuzione locale richiede accesso al repository privato, configurazione manuale e hardware compatibile. Progetto sperimentale di tesi e portfolio, non un dispositivo medico.

Sito del progetto

Contatti

Parliamo di lavoro, collaborazioni o idee da costruire.

Se stai valutando il mio profilo, vuoi propormi una collaborazione o hai un problema operativo da risolvere, scrivimi. Possiamo partire dal contesto, anche senza avere già tutte le risposte.

Oppure scrivimi diretto diego.webwork@gmail.com

Verona e Nord Italia. Disponibile per lavoro e progetti freelance. Disponibile al trasferimento.

Prepara un messaggio

Si apre il programma di posta verso diego.webwork@gmail.com. Non serve creare un account.