Image morphing: come abbiamo spinto Flutter al limite
26.07.2020
Abbiamo sviluppato un'app che crea un'animazione di morph fra due visi, per trovare i limiti di Flutter
Uno dei problemi che generalmente viene in mente quando si parla di prodotti cross-platform è quello delle performance. Questo succede probabilmente perchè fino a qualche anno fa le tecnologie cross-platform non erano production-ready, a causa delle cattive performance.
Ma siamo nel 2020 ora e lo sviluppo cross-platform non è più demonizzato come una volta. Abbiamo sviluppato alcune delle nostre app negli ultimi tre anni utilizzando tecnologie cross-platform (React Native prima e poi Flutter, che usiamo tutt'ora) e le riteniamo valide al livello di applicazioni native.
Abbiamo deciso di provare quando potente potesse essere Flutter. Abbiamo provato a spingerlo ai suoi limiti.
L’idea
Perchè non un progetto per fare un morph fra due facce allora? Ed esportare l’animazione? Sì, questa è stata la nostra idea. Volendo essere un po’ più tecnici l’app dovrebbe:
Prendere due immagini scelte dall’utente;
Riconoscere i volti in esse e dare la possibilità all’utente di selezionare il viso;
Creare un’animazione di morph che trasforma il primo volto nel secondo e viceversa;
Esportare il video.
Realizzazione
Abbiamo usato Firebase Machine Learning per riconoscere i volti nelle immagini. Se in un’immagine c’è più di un volto chiediamo all’utente di scegliere quale usare per l’animazione. Se non viene riconosciuto nessun volto chiediamo all’utente di scegliere un’altra immagine (e di segnalarci se l’immagine selezionata aveva invece un viso).
Dobbiamo ora riconoscere tutti i punti del contorno del volto, della bocca, del naso e degli occhi. Per riuscirci abbiamo usato nuovamente Firebase Machine Learning. Ci sono da fare anche degli aggiustamenti alle immagini nel caso in cui i volti siano a distanze differenti per essere certi che le facce siano sempre nella stessa posizione e alla stessa distanza durante l’animazione.
Abbiamo sviluppato un algoritmo che usa la triangolazione di Delunay per creare una mesh partendo dai punti che ci vengono dati da FML. Ora abbiamo un set di triangoli e nessun triangolo è sopra ad un’altro. Sembra complicato? Lo è: vogliamo parlare dell’argoritmo nel dettaglio in un altro articolo, presto.
Un'immagine di Barack Obama con la mesh
Per completare l’animazione abbiamo usato il metodo di Flutter drawVertices. Il metodo ha bisogno di tre parametri:
vertices: tutti i punti della mesh ordinati in base ai triangoli ottenuti usando la triangolazione di Delunay;
blendMode: non vogliamo nessun effetto nelle immagini, quindi lasciamo questo parametro al valore di default (srcOver);
paint: è l’immagine a cui vogliamo applicare il morph;
A questo punto interpoliamo i punti della prima immagine con quelli della seconda immagine. Cambiamo anche l’opacità dello sfondo per rendere perfetta l’animazione.
E’ il momento di esportare l’animazione: sfortunatamente Flutter lavora in single thread e siccome dobbiamo esportare ogni singolo frame dell’animazione, ci potrebbe impiegare un po’ di tempo. Questo è l’unico limite che abbiamo riscontrato sviluppando questo progetto. Non sarebbe accaduto se Flutter avesse supportato le operazioni multi-thread.
Ironicamente, l’esportazione è la parte più lenta di tutto il processo. Anche l’animazione di morphing è più veloce, nonostante richieda molta computazione.
Una volta completata l’esportazione, tutti i frame vengono codificati usando ffmpeg for Flutter. Questo era l’ultimo passaggio per completare la nostra app, ed ora l’animazione è pronta per essere condivisa nei social!
Osservazioni
Ce l’abbiamo fatta! Dopo molti test possiamo finalmente dire che non ci sono problemi di performance a discapito di tutte le operazioni che l’app deve fare per funzionare.
Abbiamo creato un complesso algoritmo di morphing usando solo Flutter senza dover fare aggiustamenti a livello nativo.
Ci abbiamo messo un po’, per due motivi principali:
Mancanza di documentazione per l’animazione di morphing. Nessuno aveva fatto una cosa simile fin’ora, specialmente con Dart, per cui non siamo stati riusciti a trovare aiuto online. Abbiamo raggiunto il nostro obiettivo utilizzando una libreria grafica chiamata Skia, ma ancora l’integrazione con Flutter non era perfetta e ben documentata;
Operazioni multi-thread non supportate da Flutter, che ci ha costretti ad aggirare questa limitazione rallentando il processo di sviluppo e alcune operazioni dell’app.
Mentre il primo problema non è del tutto collegato a Flutter, il secondo lo era certamente. Ma va bene. Abbiamo iniziato questo progetto per spingere Flutter ai suoi limiti e ne abbiamo trovato uno.
Pubblicheremo presto l’algoritmo, nel frattempo anche tu puoi provare l’app che abbiamo sviluppato, Face To Face — Image Morphing, scaricandola dal Play Store o dall’App Store!
Se l'articolo ti è piaciuto, iscriviti alla nostra newsletter!