Vai al contenuto

Anti-manomissione delle telecamere e sincronizzazione delle telecamere

Analisi del 29/10/2026 sul codice di: - mdm_installation (origin/main 3.2.9, origin/stage 3.9.0); - mdm_mid_server (main, refactor/v1), mdm_web_panel, qAlarm2_be/qAlarm2_fe; - monitoring, lambdaFunctionsPython, televigilanza-mdm.

Nessuna prova sul campo: le valutazioni degli algoritmi sono dedotte dal codice.

La sincronizzazione delle telecamere

È il passo del ciclo di monitoraggio del box (app/services/installation/monitoring.py, ogni monitoring_delay secondi, 120 di default) che aggiorna lo stato di ogni telecamera (CameraManager.update_camera_states).

  1. Chiede al detector l'elenco delle telecamere con enable, detect e online (Frigate tramite wrapper e API di Frigate; GANZ con GET /api/network/vsources/).
  2. Per le telecamere online prende uno snapshot dal detector: Frigate latest.jpg (l'ultimo fotogramma del flusso di rilevamento, senza aprire connessioni verso la telecamera), GANZ GET /api/snapshot/jpeg/?ch=<n>.
  3. Sullo snapshot esegue il controllo anti-manomissione ("controllo danni", check_camera_damage).
  4. Lo stato aggiornato finisce nello stato dell'impianto mandato al midserver (sync_to_server) e in /status.

La stessa funzione viene chiamata anche senza immagini (sync_image=False) dopo i comandi su Frigate, sull'heartbeat del wrapper e, nella stage, prima di inserire l'impianto. In quei casi aggiorna solo enable, detect e online.

"Leggera" (3.1.6, main) vuol dire due cose: - in parallelo: le telecamere sono elaborate con un massimo di 8 thread (_MAX_UPDATE_WORKERS) invece che una dopo l'altra, quindi un detector lento su una telecamera non allunga tutto il ciclo; - snapshot solo quando serve: lo snapshot si prende solo nei cicli in cui gira il controllo anti-manomissione, cioè uno ogni 5 (circa ogni 10 minuti), e il controllo lavora su un'immagine ridotta a 320 px.

Nella stage la sincronizzazione era sequenziale e prendeva lo snapshot di ogni telecamera online a ogni ciclo, con il controllo SIFT a piena risoluzione. Dal 29/10 (modifica sulla stage, MDM-030) la sincronizzazione è una sola chiamata al detector, senza snapshot, e il controllo è disattivato fino al nuovo rilevamento (MDM-082).

Il controllo anti-manomissione oggi

Si accende e si spegne con check_camera_damage (attiva di default; pannello, Installation Config). Riguarda solo due casi, telecamera coperta e telecamera spostata. Non rileva sfocatura, accecamento con una luce, spruzzo di vernice, immagine congelata o perdita del segnale (quest'ultima è coperta dallo stato online).

main (dalla 3.1.6) stage
Coperta luminosità media in scala di grigi sotto 70 su 255 uguale
Spostata immagine ridotta a 320 px e sfocata; spostata se più dell'8% dei pixel cambia di oltre 25 livelli rispetto allo snapshot precedente SIFT: spostata se si abbinano meno di 200 punti caratteristici con lo snapshot precedente
Frequenza ogni 5 cicli ogni ciclo
Costo basso alto
Risultato damange (con l'errore di battitura) nello stato della telecamera damage

Perché nessuno dei due è affidabile (dedotto dal codice): - Coperta = buio. Una scena notturna, o il passaggio agli infrarossi, sotto la soglia risulta coperta. Una copertura chiara (foglio, vernice bianca) o una luce puntata non risultano coperte. - Il confronto è con lo snapshot precedente, non con un riferimento. - Uno spostamento risulta "spostato" per un solo controllo, poi la nuova inquadratura diventa il riferimento e il segnale sparisce. - Uno spostamento lento non si vede mai. - main misura qualsiasi cambiamento, non lo spostamento: un camion parcheggiato, un cambio di luce, pioggia o neve oltre l'8% dell'immagine risultano "spostata". - stage (SIFT) su scene povere di dettagli (notte, nebbia, muro) trova pochi punti anche senza spostamento, quindi segnala spostamenti falsi. In più costa molta CPU. - Nessuna conferma: un singolo controllo cambia il risultato. - In main il riferimento si azzera ogni volta che lo snapshot manca (telecamera offline, detector lento).

Cosa offrono già detector, telecamere e DVR

Chi Rilevamento di manomissione proprio Come arriverebbe a noi
GANZ AI Box V2.0 sì: tra gli eventi di sistema c'è tamper detection, insieme a video loss / recovery (scheda tecnica GANZ AI Box V2.0, giugno 2023). Il metodo e le impostazioni non sono descritti con le stesse azioni di rete degli allarmi (Event Action → HTTP), verso un indirizzo del box. Oggi il box non ha un endpoint per questo evento
Frigate no: nella documentazione non risulta un rilevamento di manomissione (da confermare sulla 0.17) —
Telecamere molti modelli hanno un rilevamento proprio (video tampering, scene change, lente coperta o sfocata) eventi ONVIF o notifiche HTTP, diversi per marca e modello; il parco non è censito per modello
DVR/NVR molti modelli hanno allarmi di manomissione e perdita del video come sopra, diversi per marca; collegabile alla proposta DVR (P10)

In sintesi: per i box GANZ conviene usare il rilevamento del GANZ e non duplicarlo; per i box Frigate il rilevamento va fatto sul box, perché Frigate non lo offre e telecamere e DVR del parco sono troppo diversi per una soluzione uniforme.

Cosa succede al risultato: nessuno lo usa

Dove Cosa fa con il risultato
Midserver v2 e v3 lo riceve dentro lo stato dell'impianto, ma nessun codice lo legge: nessun evento, nessuna notifica
Pannello MDM ha solo l'interruttore check_camera_damage; non mostra il risultato
qAlarm (backend e frontend) nessun riferimento; non esiste un tipo di evento per la manomissione
monitoring (Prometheus, Loki) nessuna metrica o regola sulle telecamere
stage: controlli di salute il controllo della telecamera usa la salute riportata dal wrapper (online, flusso), non l'anti-manomissione
Sistema vecchio, Lambda qalarm esistono i tipi camera_moved (23) e camera_covered (24), notificati su Telegram (lambdaFunctionsPython/qalarm/video.py), ma nessun produttore nei repository analizzati
Sistema di Michele modulo alarm/tlcmoved, segnalato dal suo README come non funzionante

Oggi gli operatori non vengono avvisati. Da chiarire con Michele se qualche sistema produce gli eventi 23/24 (domanda A23).

La strada per gli eventi esiste già (linea stage)

  • Sul box: il sistema di eventi della stage (app/services/event_manager/) manda gli eventi via MQTT al midserver. Il formato è lo stesso di qAlarm: tipo (MALFUNCTION, SECURITY, ALARM, LIFECYCLE, FUNCTION_RECOVERED), sottocategoria, dati e file allegati. Le stesse categorie sono in qAlarm2_be (event_entity.py) e qAlarm2_fe (events.ts).
  • Telecamera guasta: viaggia già così. Il controllo di salute della telecamera genera un MALFUNCTION/failure con il componente e, quando torna, FUNCTION_RECOVERED. Il midserver v3 lo inoltra sullo stream event_stream letto da qAlarm (da confermare in laboratorio).
  • Per la manomissione mancano:
  • una sottocategoria propria, per esempio tamper sotto SECURITY, con i dati camera, tipo (coperta, spostata, sfocata, accecata, congelata), immagine attuale e di riferimento;
  • il suo riconoscimento in qAlarm (backend e frontend);
  • la regola sulla priorità, decisa con la centrale.

Soluzione ideale

Una manomissione vera arriva all'operatore in qAlarm entro pochi minuti, con le immagini; i falsi allarmi sono rari; il costo sul box resta trascurabile.

Rilevamento sul box

Ogni ciclo (120 s), per ogni telecamera online, sullo snapshot che il detector già fornisce (Frigate latest.jpg, nessuna connessione in più verso la telecamera), ridotto a 320 px e in scala di grigi:

Caso Misura (economica) Segnale
Coperta o accecata contrasto (deviazione standard) e quantità di bordi, confrontati con il riferimento contrasto e bordi quasi a zero, qualunque sia la luminosità: scuro, bianco o luce puntata
Sfocata o spruzzata nitidezza (varianza del Laplaciano) rispetto al riferimento nitidezza molto più bassa con luminosità simile
Spostata o ruotata quanti bordi del riferimento sono ancora al loro posto (mappa dei bordi con una piccola tolleranza) e spostamento stimato con correlazione di fase gran parte dei bordi fissi spariti o traslati. Se il segnale è sospetto, conferma con una corrispondenza di punti (ORB) solo in quel caso
Immagine congelata impronta dello snapshot identica per più cicli, con la telecamera "online" flusso fermo o sostituito
Segnale perso stato online dal detector già gestito come guasto della telecamera
  • Riferimenti, non snapshot precedente:
  • un riferimento per il giorno e uno per la notte (infrarossi, riconoscibile perché l'immagine è senza colore);
  • salvati alla configurazione della telecamera;
  • aggiornati lentamente solo quando lo stato è normale da tempo, mai durante un sospetto;
  • rifatti dal pannello dopo uno spostamento voluto.
  • Bordi e contrasto invece della luminosità: reggono i cambi di luce e il passaggio giorno/notte. Gli oggetti in movimento cambiano pochi bordi fissi, mentre una telecamera girata li cambia quasi tutti.
  • Conferma: sospetto per 3 cicli consecutivi (circa 6 minuti) prima dell'evento, 3 cicli normali prima del ripristino; nessuna decisione nei minuti del passaggio agli infrarossi.
  • Costo: le misure lavorano su immagini 320×180 e richiedono pochi millisecondi per telecamera; ORB parte solo quando c'è un sospetto. Il costo principale resta lo snapshot, che con Frigate non tocca la telecamera. Sincronizzazione in parallelo con limite, come in main.
  • Box GANZ: si usa l'evento tamper detection del GANZ (e video loss), configurato in Event Action → HTTP verso un endpoint del box che lo trasforma nello stesso evento SECURITY/tamper. Il rilevamento del box resta per i box Frigate. Prima va provato in laboratorio cosa rileva davvero il GANZ (coperta, spostata, sfocata) e con quanti falsi allarmi.
  • In futuro: dove telecamera o DVR hanno un rilevamento proprio affidabile, lo stesso endpoint può riceverne gli eventi.

Tipi di manomissione negli eventi (proposta)

Il tipo resta un dato dell'evento, utile per tarare i falsi allarmi e capire la causa. La procedura dell'operatore si basa su due classi:

Classe Tipi Cosa suggerisce Priorità proposta
Visione impedita coperta, accecata, spruzzata sabotaggio, spesso prima di un'intrusione alta; altissima con impianto inserito
Inquadratura degradata spostata, sfocata, congelata urto, vento, guasto o sabotaggio media: verifica e manutenzione
  • Il tipo non_specificato serve per gli eventi che non lo dicono (probabilmente quello del GANZ).
  • La perdita del segnale resta il guasto "telecamera offline", che esiste già.
  • Priorità e procedura vanno confermate con la centrale (A24).

Dati necessari (non un censimento)

  • Per partire non serve un censimento dei modelli: il rilevamento sul box usa solo gli snapshot del detector, e il GANZ si prova in laboratorio.
  • Per tarare le soglie serve una raccolta di snapshot reali (giorno, notte, pioggia, veicoli) da alcuni impianti scelti per tipo di scena. I dati ci sono già: Frigate conserva le registrazioni, e il censimento delle telecamere (--campioni-ore 3,14,22) ne estrae un fotogramma per telecamera alle ore scelte (procedura). Sono immagini dei locali dei clienti: conservate con accesso limitato e cancellate dopo la taratura.
  • Il censimento delle telecamere si fa una volta sola, in MDM-037 (modello, sessioni, flusso secondario, DVR). Comprende anche se telecamera e DVR hanno un rilevamento di manomissione proprio e se il tamper detection del GANZ è attivo. Serve per collegare in futuro quei rilevamenti, non per partire.

Eventi e interfacce

  1. Evento sul box: SECURITY/tamper con tipo, classe, immagine attuale e di riferimento quando la manomissione è confermata; FUNCTION_RECOVERED (o equivalente) quando torna normale; stato anche in /status (campo damage).
  2. Midserver v3: inoltro come gli altri eventi; nessuna logica nuova se il formato resta lo stesso.
  3. qAlarm: sottocategoria riconosciuta, le due immagini affiancate, priorità più alta con l'impianto inserito; procedura dell'operatore decisa con la centrale.
  4. Pannello: stato per telecamera e comando "rifai il riferimento".
  5. Monitoraggio: conteggio delle manomissioni per impianto come metrica del midserver, utile per trovare le telecamere che generano falsi allarmi.

Implementazione sulla linea stage (29/10/2026)

Su mdm_installation, branch stage, commit f427d22e (29/10/2026). Non ancora provata su un box.

  • Modulo: app/services/camera_manager/tamper/, chiamato da CameraManager.check_tamper() una volta per ciclo di monitoraggio, se check_camera_damage è attivo. Snapshot in parallelo, al massimo 8 alla volta, ridotti a 320 px; vale per Frigate e per GANZ.
  • Riferimenti: uno per il giorno e uno per la notte.
  • La modalità cambia solo dopo 2 snapshot concordi.
  • Il riferimento è la struttura stabile della scena: i bordi presenti nella maggior parte delle inquadrature normali, più contrasto, densità dei bordi e nitidezza medi. Si aggiorna a ogni ciclo normale. Ombre, persone e veicoli non entrano nella struttura.
  • "Spostata" e "sfocata" si valutano dopo 5 cicli normali; una traslazione si riconosce da subito.
  • Un nuovo riferimento si avvia solo da un'immagine con dettaglio e coerente con l'altra modalità (nessuna traslazione, struttura al suo posto).
  • Regole:
  • coperta/accecata: contrasto e bordi spariti rispetto al riferimento, qualunque sia la luminosità;
  • sfocata: nitidezza crollata (sotto il 10% del riferimento), oppure sotto il 20% con meno bordi;
  • spostata: traslazione affidabile (correlazione di fase), oppure bordi spariti nella maggior parte di una griglia 4×4;
  • congelata: stesso snapshot per 5 cicli.

Conferma dopo 3 cicli dello stesso tipo e ripristino dopo 3 cicli normali. Le soglie sono in TamperThresholds. - Stato: damage di ogni telecamera vale {"type", "class", "since"} quando la manomissione è confermata. - Eventi: MALFUNCTION sul componente <telecamera>/tamper, con tipo, classe, misure, immagine attuale e di riferimento; FUNCTION_RECOVERED al ripristino. Usa le categorie che qAlarm conosce già. La sottocategoria SECURITY/tamper resta da decidere (A24). - Percorso fino all'operatore (29/10, verificato nel codice e nei test, non ancora in laboratorio): prima della correzione il midserver v3 e qAlarm accettavano l'evento ma scartavano component_type e details nella validazione, e il frontend mostrava solo "MALFUNCTION - failure", senza telecamera né tipo. Correzioni (modifiche locali non committate): - mdm_mid_server (refactor/v1): MalfunctionData in app/core/mqtt/event_model.py conserva component_type e details; test in tests/features/event/test_process_incoming_event.py; - qAlarm2_be (development): MalfunctionData in event_entity.py (evento Socket.IO EVENT) e MDMEventDataEntity/MDMEventDataDTO (cronologia) conservano componente, tipo e dettagli; test in tests/unit/test_event_details.py; - qAlarm2_fe (main): src/features/events/utils/eventLabel.ts mostra "Manomissione telecamera X: coperta/accecata/sfocata/spostata/immagine bloccata" e "Manomissione rientrata: telecamera X", in rosso per la classe "visione impedita"; gli altri guasti mostrano il componente. Usata nella lista eventi e nelle notifiche; test in tests/event-label.test.mjs. - Le due immagini viaggiano come gli altri allegati (S3 tramite il midserver) e sono già mostrate dalla lista eventi. - Dataset e valutazione: uv run mdm tamper collect|synth|evaluate. - collect legge in sola lettura i fotogrammi dalle registrazioni di Frigate. - synth costruisce sequenze etichettate con manomissioni simulate e disturbi innocui (luce, rumore, oggetti fino al 35% dell'immagine, pioggia). - evaluate misura rilevamenti, tipo, ritardo, ripristino e falsi allarmi. - Valutazione su dati generati (4 insiemi da 15 telecamere, 360 sequenze): tutte le manomissioni rilevate con il tipo giusto, conferma 2 cicli dopo il primo fotogramma manomesso (6 per "congelata"), nessun falso allarme. - Valutazione su dati reali (29/10, 131 fotogrammi da 17 telecamere di 5 box Frigate, presi dalle registrazioni degli ultimi 3 giorni; 84 sequenze):

Tipo Rilevate Tipo giusto Falsi allarmi
normale — — 0 su 14
coperta 14/14 14 0
accecata 14/14 14 0
sfocata 13/14 13 0
spostata 12/14 12 1
congelata 14/14 14 0

I casi mancati sono di una scena notturna quasi senza struttura e di sequenze in cui giorno e notte si alternano a ogni fotogramma. La prova è più dura della realtà: i fotogrammi sono a ore di distanza (non 2 minuti) e vengono da momenti con movimento. - Cosa ha cambiato la valutazione reale: la prima versione (riferimento = un fotogramma) dava falsi "spostata" su 4 sequenze normali su 14. Da qui il riferimento come struttura stabile, la regola della sfocatura sulla nitidezza e la conferma per tipo. - Limiti noti: - una rotazione o una sfocatura che avviene proprio alla prima notte, prima che esista un riferimento notturno, si vede solo il mattino dopo (tra giorno e notte si controlla solo la traslazione); - nelle scene quasi senza struttura (notte buia, pochi bordi) sfocatura e rotazione non si riconoscono: il rilevatore lo segnala come "dettaglio insufficiente"; - il primo riferimento dopo l'installazione viene preso così com'è; - manca il comando dal pannello per rifare il riferimento (TamperDetector.reset_reference() esiste).

Cosa dobbiamo fare

# Passo Ticket
1 Chiedere a Michele se oggi arrivano avvisi di telecamera coperta/spostata e da dove (eventi 23/24), e se il tamper detection del GANZ è mai stato configurato A23, M-12
1b Sul GANZ di laboratorio: attivare tamper detection e video loss con un'azione HTTP verso il box; provare telecamera coperta, girata, sfocata, di notte MDM-082
2 Nella stage: sincronizzazione in parallelo con limite e niente SIFT, cioè disattivare il controllo attuale finché non c'è il nuovo, invece di portare l'algoritmo di main che va comunque sostituito MDM-030
3 Box GANZ: endpoint che riceve l'evento di manomissione del GANZ. Box Frigate: nuovo rilevamento (riferimenti giorno/notte, bordi e contrasto, nitidezza, congelamento, conferma su 3 cicli), con una raccolta di snapshot reali per tarare le soglie MDM-082
4 Prova in laboratorio: telecamera coperta (scura e chiara), accecata, sfocata, girata, di notte con infrarossi, con un veicolo che passa e uno che si ferma MDM-082
5 Evento SECURITY/tamper con immagini: box, midserver v3, qAlarm (backend e frontend), pannello MDM-082
6 Decidere con la centrale priorità, notifica a impianto disinserito, procedura A24

Altre funzioni del box con lo stesso problema (risultato che non arriva a nessuno): funzioni-del-box.md.