Il problema di partenza: quale rimozione sfondo è davvero fattibile gratis?

La richiesta iniziale era ampia: un tool per rimuovere sfondi e, se possibile, anche oggetti indesiderati dalle foto. Le due cose hanno difficoltà molto diverse. Rimuovere oggetti richiede inpainting — ricostruire in modo plausibile l'area cancellata — un compito che nel 2026 è ancora dominio di modelli generativi molto pesanti, poco pratici da far girare nel browser con buona qualità su un dispositivo medio. Rimuovere lo sfondo, invece, è un problema di segmentazione: separare un soggetto da uno sfondo, un compito per cui esistono modelli molto più leggeri, eseguibili interamente via WebAssembly senza alcun server.

Ho quindi deciso di partire solo dalla rimozione sfondo, scartando per ora l'idea della rimozione oggetti. La domanda successiva è stata: quale libreria usare per eseguire un modello di segmentazione interamente lato client, mantenendo il modello a costo zero già seguito per il resto del sito?

La trappola della licenza AGPL

La prima libreria che sembrava perfetta per lo scopo si è rivelata distribuita con licenza AGPL, una licenza copyleft forte: gratuita da usare, ma con un vincolo importante per chi la incorpora in un servizio web pubblico non open source. In sintesi, l'AGPL può obbligare a rilasciare il codice sorgente completo del progetto che la incorpora, sotto la stessa licenza — un vincolo non da poco per un sito che non è open source. "Gratis per l'utente finale" e "sicuro da integrare in un prodotto commerciale closed-source" sono due cose diverse, ed è meglio scoprirlo prima di scrivere codice, non dopo il deploy.

⚠️

Prima di integrare qualsiasi libreria IA "gratuita" in un progetto commerciale vale sempre la pena controllare la licenza esatta, non solo il prezzo. AGPL, GPL e altre licenze copyleft forti sono compatibili con l'uso personale o interno, ma rischiose per un prodotto pubblico closed-source.

Ho quindi cercato un'alternativa con licenza permissiva (Apache-2.0 o MIT), basata su Transformers.js — la libreria di Hugging Face per eseguire modelli di machine learning nel browser sopra ONNX Runtime Web — invece del wrapper proprietario scartato. Stesso principio (modello ONNX scaricato una volta dal CDN, poi cachato dal browser), licenza pulita.

Un modello CNN leggero, non un transformer pesante

Nella scelta del modello ho seguito una lezione utile per chiunque lavori con IA nel browser: l'architettura conta più della dimensione del file. Un modello basato su transformer, anche se qualitativamente ottimo e con licenza permissiva, può andare in crash per esaurimento di memoria durante l'inferenza WASM su molte macchine — l'attenzione dei transformer consuma memoria che cresce rapidamente col numero di "patch" dell'immagine, e alla risoluzione tipica di una foto i tensori intermedi diventano enormi. Un modello CNN più piccolo, con un'architettura pensata per la segmentazione (nel mio caso della famiglia IS-Net), gira invece in modo molto più affidabile in WASM.

Il modello scelto come default è quindi un modello CNN leggero con licenza Apache-2.0, che scarica pochi MB e produce in output un'immagine RGBA con un canale alpha di trasparenza.

LivelloScelta
RuntimeTransformers.js sopra ONNX Runtime Web (WASM)
Modello di defaultCNN leggero, licenza Apache-2.0, ottimizzato per persone
Modello opzionaleTransformer più pesante, licenza MIT, più generico
Elaborazione100% locale, nessun upload dell'immagine
CacheBrowser + service worker, download una tantum

Il "matte grezzo": perché resta un alone sui bordi

Il modello lascia spesso un alone di colore residuo nell'area trasparente, perché l'output è una matte alpha morbida invece di una maschera binaria pulita: pixel di sfondo possono avere valori alpha residui del 5-15% invece di 0%. La soluzione non richiede un altro modello, ma un semplice post-processing della curva alpha: valori bassi forzati a trasparenza piena, valori alti forzati a opacità piena, con una transizione smoothstep nel mezzo per preservare i bordi morbidi (capelli, dettagli fini). Su un'immagine di test, questo approccio ha reso oltre il 60% dei pixel completamente trasparenti e oltre il 35% completamente opachi, lasciando solo una piccola frazione in una fascia intermedia — un risultato pulito.

Il limite reale: un modello specializzato non è un modello generico

I primi test con immagini reali hanno mostrato risultati deludenti su due casi: un'illustrazione piatta (dove il modello ha rimosso solo il margine attorno alla figura, lasciando tutto il disegno opaco) e una foto scattata in un ambiente poco illuminato e affollato (dove il modello ha praticamente cancellato anche il soggetto principale). Per isolare la causa, ho aggiunto temporaneamente un terzo pannello diagnostico che mostrava la maschera grezza del modello prima di qualsiasi pulizia alpha, confrontandola con il risultato finale.

Il confronto ha chiarito subito la causa: la maschera grezza e il risultato finale erano praticamente identici, quindi il problema non era nel mio post-processing, ma a monte, nel modello stesso. Verificando la scheda tecnica, il modello di default risultava addestrato specificamente su un dataset di segmentazione di persone — non un modello generico per qualsiasi soggetto. Su un ritratto ben illuminato e inquadrato da vicino il risultato è infatti pulito, capelli inclusi; su scene complesse o del tutto diverse dal dataset di addestramento (illustrazioni, foto scure, inquadrature affollate) la qualità scende in modo prevedibile.

💡

Lezione generale: un modello "leggero e affidabile in WASM" spesso lo è proprio perché addestrato su un dominio ristretto. Prima di giudicare un modello "scarso", vale la pena controllare su cosa è stato effettivamente addestrato — il problema può essere di dominio, non di qualità.

Un modello superiore, opzionale, non di default

Invece di sostituire il modello leggero con uno più pesante e generico per tutti — pagando in tempo di caricamento anche chi ha un caso d'uso semplice — ho aggiunto un secondo modello, un transformer con licenza MIT più adatto a scene generiche, disponibile solo su richiesta: dopo il primo risultato compare un pulsante "Prova il modello superiore" che rielabora la stessa immagine con il modello più pesante, senza sovrascrivere il risultato precedente finché l'elaborazione non è completata con successo. Se il modello più pesante fallisce per memoria insufficiente, il primo risultato resta intatto e l'utente vede un messaggio d'errore chiaro invece di un tool rotto.

Questo compromesso — leggero di default, pesante su richiesta esplicita — evita di penalizzare la maggioranza degli utenti con un download importante che non useranno mai.

Il bug serio: il thread principale si blocca durante il calcolo

Passando al modello più pesante è emerso un problema molto più serio di una qualità mediocre: durante l'elaborazione l'intera pagina smetteva di rispondere — non solo il tool, ma anche i link della navigazione in alto. La causa è che, per default, il motore ONNX Runtime Web esegue il calcolo WASM sul thread principale del browser, lo stesso che gestisce clic, scroll e rendering: durante un'inferenza pesante quel thread resta occupato e tutta l'interfaccia si congela.

Il primo tentativo di correzione — attivare un'opzione interna della libreria pensata per delegare il calcolo a un thread separato — non ha risolto il problema, probabilmente per un timing di inizializzazione delicato. La soluzione affidabile è stata scrivere e controllare direttamente un vero Web Worker dedicato: un thread separato che carica la libreria, scarica il modello ed esegue tutta l'inferenza, comunicando con la pagina principale solo tramite messaggi (l'immagine in ingresso, il progresso, il risultato finale). In questo modo il thread principale non fa mai calcolo pesante per costruzione, non per una configurazione sperata.

⚠️

Se un'elaborazione IA lato client blocca l'intera interfaccia e non solo il componente che la usa, il sospetto numero uno è che il calcolo giri sul thread principale. Un flag di configurazione può non bastare: un Web Worker dedicato, scritto esplicitamente, è la soluzione più affidabile.

Gestire un'attesa reale di due minuti

Anche risolto il blocco dell'interfaccia, restava un problema di percezione: il modello più pesante impiega circa due minuti a elaborare un'immagine su hardware comune, e la libreria non espone un vero avanzamento percentuale durante il calcolo (solo durante il download del modello). Una barra "piena e ferma" per due minuti comunica un tool rotto, non un tool lento.

La soluzione finale combina due elementi: una barra che avanza in modo stimato su circa due minuti e mezzo, con il conteggio dei secondi trascorsi sotto, e — se l'elaborazione supera quel tempo stimato — un passaggio automatico a un'animazione indeterminata (una striscia che scorre continuamente), segnale universale di "sto ancora lavorando" senza fingere una percentuale che il modello non fornisce. Un timeout di sicurezza più ampio sblocca comunque l'interfaccia se qualcosa va davvero storto, permettendo di riprovare.

Cosa mi porto a casa da questo progetto

La lezione più utile non riguarda un singolo bug, ma un ordine di priorità: verificare la licenza prima della qualità, verificare il dominio di addestramento di un modello prima di giudicarne i risultati, e non fidarsi di un flag di libreria per una proprietà critica come "non bloccare il thread principale" — se è critico, va verificato con un test reale, non assunto. Gestire le aspettative in pagina (un avviso su quando il tool funziona meglio) si è rivelato utile quanto il codice stesso: un limite noto e comunicato chiaramente è molto meno frustrante di un risultato inspiegabile.

Se ti interessa il resto del cluster immagini/PDF costruito con lo stesso principio di elaborazione client-side, ne ho parlato anche a proposito della redazione reale dei PDF con PDF.js e pdf-lib. E se il tema è il costo reale di aggiungere l'IA a un tool gratuito, ho fatto i conti in dettaglio nell'articolo sul riassunto PDF con Gemini Flash-Lite.

Domande frequenti

È sicuro rimuovere lo sfondo di una foto online senza caricarla su un server?

Sì, se lo strumento esegue il modello IA interamente nel browser tramite WebAssembly. Il modello viene scaricato una volta sola e poi cachato, ma l'immagine caricata non lascia mai il dispositivo: nessun upload verso alcun server.

Perché non tutte le librerie open source per l'IA nel browser sono utilizzabili in un progetto commerciale?

Alcune sono distribuite con licenze copyleft forti come la AGPL, che possono obbligare a rilasciare il codice sorgente completo del progetto che le incorpora. Licenze permissive come Apache-2.0 o MIT non impongono questo vincolo.

Perché un modello IA nel browser può bloccare l'intera pagina?

Per default il calcolo WASM avviene sul thread principale, lo stesso che gestisce clic e scroll. La soluzione affidabile è un Web Worker dedicato, un thread separato che lascia libera l'interfaccia per costruzione.

Perché il ritaglio dello sfondo a volte lascia un alone sui bordi?

Il modello produce una matte alpha morbida, non una maschera binaria netta. Un post-processing con soglie e transizione smoothstep elimina l'alone residuo senza bisogno di un modello più pesante.

Perché uno strumento di rimozione sfondo funziona bene su una foto e male su un'altra?

Molti modelli leggeri sono addestrati su un dominio specifico, ad esempio principalmente foto di persone. Su scene molto diverse dal dataset di addestramento la qualità scende in modo prevedibile, non per un bug.