Come funziona un hash percettivo

Salva di nuovo una foto e il suo SHA-256 cambia da cima a fondo, pur essendo — a occhio — la stessa identica immagine. È il comportamento corretto di un hash crittografico, ed è esattamente ciò che lo rende inutile per rispondere alla domanda che ti stai facendo: «questa è la stessa foto di quella?»

Il problema, in un esempio

Prendi una fotografia. Aprila, salvala con qualità 80 invece che 95. Il contenuto visivo è indistinguibile, ma i byte sono un'altra cosa: la compressione JPEG ha ricalcolato ogni blocco. E l'hash crittografico, che per progetto amplifica qualsiasi differenza di un bit fino a stravolgere metà del digest, ti dice che i due file non hanno nulla in comune.

Ha ragione. I due file non hanno nulla in comune. Ma tu non stavi chiedendo dei file.

HASH CRITTOGRAFICO — risponde «sono file diversi» foto.jpg foto.jpg ricompressa q80 SHA-256 a72c479e834af13a7ffb7765… 7f82fc11cb2d79f7cdbed898… nessuna relazione ricavabile · l'effetto valanga cancella la somiglianza HASH PERCETTIVO — risponde «è la stessa immagine» foto.jpg foto.jpg ricompressa q80 pHash 1011 0110 1100 1001 … 1011 0110 1110 1000 2 bit diversi su 64 · similarità 96,9%
Stesso contenuto, due file diversi. L'hash crittografico e quello percettivo rispondono a due domande diverse, ed entrambi rispondono correttamente.

L'idea: rovesciare l'effetto valanga

Un hash percettivo è costruito con l'obiettivo opposto a quello di SHA-256. Vuole che a piccole variazioni dell'immagine corrispondano piccole variazioni dell'impronta. Non è più una funzione a senso unico: è una funzione che comprime l'aspetto visivo in poche decine di bit, buttando via tutto ciò che l'occhio non registra — il rumore, i dettagli fini, le microdifferenze di compressione — e conservando ciò che resta.

Il procedimento è sempre lo stesso, qualunque sia l'algoritmo:

  1. Riduci l'immagine a una griglia minuscola, tipicamente 8×8 pixel. Sparisce ogni dettaglio: resta la struttura di luci e ombre.
  2. Converti in scala di grigi. Il colore, per questi algoritmi, è rumore.
  3. Confronta ogni cella con una soglia (la media, il pixel accanto, la mediana dei coefficienti). Il risultato di ogni confronto è un bit: 1 o 0.
  4. Concatena i bit. Sessantaquattro confronti, sessantaquattro bit: è l'impronta percettiva.

Ridimensionare, ricomprimere, cambiare formato: nessuna di queste operazioni sposta significativamente le luci e le ombre della griglia 8×8. Quindi nessuna di queste operazioni cambia più di qualche bit.

Come si legge il risultato: la distanza di Hamming

Due impronte percettive non si confrontano chiedendo «sono uguali?». Si confrontano contando quanti bit differiscono. Quel numero si chiama distanza di Hamming, e ha un significato immediato: quante delle sessantaquattro domande sulla struttura dell'immagine hanno ricevuto risposta diversa.

Probatio la traduce anche in percentuale, con una formula che è esattamente quello che sembra:

similarità = (1 − distanza ÷ bit totali) × 100

Due impronte a 64 bit che differiscono per 2 bit danno 96,9% di similarità. Se ne differiscono 32 — cioè metà, il valore atteso fra due immagini scelte a caso — la similarità è 50%, che è il modo tecnico per dire «nessuna relazione».

50%75%87%95%100% 32 bit16 bit8 bit3 bit0 bit distanza di Hamming su 64 bit immagini scorrelate ritaglio · ritocco · fotogrammi vicini ricompressa · ridimensionata Probatio non emette un verdetto «uguale / diverso»: mostra distanza e percentuale per i tre algoritmi. La soglia la scegli tu, e la dichiari in relazione.
Le fasce sono indicative, non normative: dipendono dall'algoritmo, dalla profondità in bit e dal tipo di immagini. Vanno calibrate sul caso concreto.

Nessun verdetto automatico, ed è un pregio

Molti strumenti sputano un «match / no match» basato su una soglia scelta da qualcun altro. Probatio no: il confronto immagini restituisce, per ciascuno dei tre algoritmi (aHash, dHash, pHash), l'impronta di entrambe le immagini, la distanza di Hamming e la similarità percentuale.

La ragione è processuale, prima ancora che tecnica. Una soglia è una decisione, e in una relazione tecnica le decisioni si dichiarano e si giustificano. Scrivere «lo strumento ha detto match» è indifendibile; scrivere «le due immagini distano 4 bit su 64 su tutti e tre gli algoritmi, valore compatibile con una ricompressione e incompatibile con soggetti diversi» è un'affermazione che regge al controesame.

Profondità: 64, 144 o 256 bit

Probatio calcola le impronte a tre profondità, corrispondenti a griglie 8×8, 12×12 e 16×16.

  • 64 bit è il valore classico, adatto a ritrovare la stessa immagine dopo ricompressione o ridimensionamento. È tollerante, anche troppo su immagini molto simili tra loro.
  • 144 e 256 bit conservano più struttura, distinguono meglio immagini che si assomigliano (per esempio due fotogrammi consecutivi di un video), ma diventano più sensibili a ritagli e rotazioni.

Non esiste la scelta giusta in assoluto. Esiste la scelta giusta rispetto alla domanda: ritrovare un'immagine chiede tolleranza, distinguere due immagini simili chiede risoluzione.

Quello che un hash percettivo non fa

  • Non è una prova di identità. Due immagini con distanza zero sono visivamente indistinguibili alla risoluzione della griglia, non necessariamente identiche. Per l'identità c'è SHA-256.
  • Non è a senso unico. Dall'impronta si ricava una vaghissima idea della distribuzione di luci e ombre. Non trattarla come un dato anonimo.
  • Non resiste a un avversario. Chi conosce l'algoritmo può costruire un'immagine con l'impronta che vuole. Serve a cercare, non a sigillare.
  • Non ragiona sul contenuto. Non sa cosa raffigura l'immagine. Vede una griglia di grigi.

Nel fascicolo convivono benissimo: SHA-256 sigilla il file, l'hash percettivo ritrova l'immagine. La prima domanda è «è stato alterato?». La seconda è «l'abbiamo già vista?». Confonderle è il modo più rapido per rispondere male a entrambe.