Vincitore al primo posto:
ReadToMe
Ispirazione
L’ispirazione alla base di ReadToMe nasce dai miei figli. In particolare dai due più giovani, rispettivamente di 3 e 5 anni. Entrambi adorano i libri ma non sono ancora in grado di leggerli da soli. Volevo creare uno strumento che consentisse loro di poterne usufruire anche in assenza di noi genitori.
Cosa fa
ReadToMe sfrutta l’apprendimento approfondito e la visione computerizzata in combinazione con i servizi disponibili in AWS. Se desideri leggere una pagina, ti basterà mostrarla ad AWS DeepLens affinché la videocamera la legga ad alta voce.
Creato da: Alex Schultz
Per ulteriori informazioni su Alex e il progetto ReadToMe, consulta questo post sul blog sul machine learning di AWS.
Come l’ho realizzato
Per quanto il flusso di lavoro risulti semplice in apparenza, ho dovuto superare diverse difficoltà per ottenere il risultato desiderato. Di seguito elenco ogni singolo problema che ho dovuto risolvere.
- Determinare se una pagina debba essere letta necessariamente all’interno dell’inquadratura della videocamera
- Isolare il blocco testuale e ripulire l’immagine utilizzando OpenCV
- Eseguire il riconoscimento ottico dei caratteri (OCR)
- Trasformare il testo in audio
- Riprodurre l’audio attraverso gli altoparlanti
Ciascuno dei passaggi sopra descritti nascondeva problematiche impreviste (al punto tale che non sapevo se sarei riuscito a ultimare il progetto nei tempi previsti).
Determinare se una pagina debba essere letta necessariamente all’interno dell’inquadratura della videocamera
ReadToMe sfrutta l’apprendimento approfondito e la visione computerizzata in combinazione con i servizi disponibili in AWS. Se desideri leggere una pagina, ti basterà mostrarla ad AWS DeepLens affinché la videocamera la legga ad alta voce. Per consentire a DeepLens di operare correttamente dovevo fare in modo che rilevasse la presenza di un qualcosa da leggere all’interno dell’inquadratura. Ed è qui che entra in gioco il modello dell’apprendimento approfondito.
Ho cercato su internet ma non sono riuscito a trovare modelli preimpostati che fossero in grado di classificare le pagine di un libro. Di conseguenza ho cominciato a pensare a come preimpostare un modello utilizzando dati nuovi al fine di classificare tale tipologia di oggetto.
Ho dunque effettuato una rapida ricerca sul web per trovare immagini di libri per bambini e acquisire in tal modo i dati necessari. Pur trovando moltissime immagini di copertine, nessuna delle foto ritraeva soggetti che stringevano in mano un libro in un’inclinazione che mi permettesse di procedere all’impostazione dell’applicazione. Avevo bisogno delle immagini di pagine vere e proprie comprensive di testo. Per fortuna ho quattro bambini che posseggono centinaia di libri adatti alla loro età. E così, una sera ho preso una quarantina di volumi e ho cominciato a scattare varie foto di me stesso mentre li tenevo in mano orientandoli di volta in volta in maniera diversa, utilizzando vari tagli di luce e oscurando alcune parti della pagina inquadrata nella foto.
Durante tale processo mi sono accorto che i blocchi di testo contenuti nei libri variavano considerevolmente a seconda del caso. Talvolta il testo era bianco su sfondo nero, mentre in altre occasioni era l’esatto contrario. Altre volte il testo era colorato ed emergeva da uno sfondo cangiante. Il testo poteva scorrere a piè di pagina oppure su tutta la superficie del libro senza alcun ordine logico. Di conseguenza ho deciso di restringere il cerchio e catturare unicamente le immagini dei libri il cui testo fosse posizionato in modo “pressoché normale”. Ho pensato che se avessi costituito un dataset troppo ampio avrei realizzato un modello incapace di discernere i blocchi testuali da tutto il resto. Col tempo forse potrò sperimentare con altre tipologie di dati ma per questo progetto ho deciso di operare una selezione meno inclusiva.
Dopo aver acquisito i dati ho usato labelImg per generare i file xml Pascal VOC che avrei utilizzato per impostare il modello. A quel punto ho cercato di capire come formattare i miei data nella maniera corretta in modo da impostarli con MXNet. Avendo trovato alcuni esempi utilizzando TensorFlow, ho scelto di percorrere quella strada. Una volta ultimato il progetto avrei potuto comunque tornare indietro e renderlo operativo tramite MXNet qualora avessi avuto ancora tempo a disposizione. Sono riuscito a seguire uno degli esempi trovati su YouTube e ho creato un modello operativo in grado di rilevare il testo contenuto in una pagina.
Eseguire il riconoscimento ottico dei caratteri
È stato sorprendente capire come fosse facile integrare Tesseract all’interno del progetto. Mi è bastato installarlo sul dispositivo e fare lo stesso con il pacchetto python. Dopo aver eseguito tali passaggi, il flusso di lavoro avrebbe richiesto semplicemente una chiamata di funzione. È sufficiente fornire un’immagine da elaborare per poi ottenerne il testo su schermata. All’inizio pensavo di utilizzare una funzione Lambda separata con Tesseract installato per eseguire l’OCR, tuttavia ho deciso di includerla alla fine nella mia funzione Lambda principale poiché più semplice da ridurre nel flusso da e verso AWS. L’OCR effettivo pareva non necessitare di un’eccessiva potenza di calcolo e, a paragone con il percorso avente AWS come punto di origine e di arrivo, il tempo sembrava comparabile. Inoltre, ciò mi permette di lavorare più “al limite”, rendendo l’operazione più efficace ed econimica allo stesso tempo.
L’impiego di Tesseract nascondeva un piccolo inghippo. La libreria è alquanto esigente in fatto di qualità delle immagini. C’è voluto un po’ di tempo per capire come ripulire le immagini al punto tale da ottenere una lettura chiara e pulita. Inoltre, Tesseract richiede che il testo sia disposto quasi totalmente in orizzontale (il che è praticamente impossibile, visto che il progetto è destinato a bambini in età prescolare). Ho utilizzato OpenCV per la maggior parte del processo di pre-elaborazione e, dopo una serie di tentativi, sono riuscito a produrre immagini che riportassero un testo di colore nero o bianco senza particolari sbavature. È stata la chiave per rendere operativo il mio progetto. Alla fine i risultati si sono rivelati migliori di quanto mi aspettassi. Tuttavia, sono sicuro che ci sia ancora spazio per perfezionare l’applicazione.
Trasformare il testo in audio
Questo passaggio è stato il più semplice in assoluto. Dopo essere riuscito a riprodurre l’audio tramite il dispositivo, ho integrato la stessa logica in un’unica funzione di chiamata che induce AWS Polly a generare semplicemente il file audio. Non scrivo mai il file su disco, piuttosto mi limito a convogliare il flusso di byte verso la libreria audio per poi eliminarlo una volta terminata l’esecuzione del file audio. Utilizzo alcuni file mp3 statici che vengono riprodotti all’avvio del servizio Green Grass. Impiego tali file per informare l’utente di turno sulle modalità di utilizzo del dispositivo. Ho preferito non utilizzare Poly per generare le informazioni poiché il messaggio rimane sempre invariato, pertanto ho preferito generarle in anticipo per poi distribuirle con la funzione Lambda. (Adoro la facilità con cui i servizi AWS si integrano con i miei progetti!)
Riprodurre l’audio attraverso gli altoparlanti
Green Grass richiede l’esplicita autorizzazione a utilizzare tutti i componenti hardware a cui può accedere il tuo codice. Uno dei modi con cui puoi effettuare la configurazione è attraverso la sezione delle risorse di gruppo nella console AWS IOT. Una volta completata la configurazione devi trasferire tali impostazioni in DeepLens. Tale operazione genera un file JSON che viene distribuito in una directory greengrass sul dispositivo.
Per abilitare la riproduzione dell’audio tramite la funzione Lambda è necessario aggiungere due risorse. La scheda audio di DeepLens è localizzata nel percorso “/dev/snd/”. Per riprodurre il suono bisogna aggiungere sia “/dev/snd/pcmC0D0p” sia “/dev/snd/controlC0”.
Nota a margine: è necessario aggiungere nuovamente queste risorse ogni volta che si assegna un progetto al dispositivo. Al momento in cui scrivo, Green Grass sovrascrive il file delle risorse con un gruppo .json pre-configurato ogni volta che si assegna un progetto a DeepLens.
Risultati di cui vado fiero
Il risultato finale del progetto mi ha reso molto felice. Nonostante avessi un lavoro a tempo pieno e quattro figli a cui badare, sono riuscito a creare un ottimo strumento e a renderlo discretamente operativo nell’arco di pochi mesi. Ho altre idee per progetti futuri con questo dispositivo e non vedo l’ora di imparare sempre di più al riguardo.
Cosa ho imparato
Prima di immergermi in questo progetto non avevo alcuna esperienza con l’apprendimento approfondito o l’intelligenza artificiale in generale. L’argomento mi ha sempre interessato ma mi dava l’impressione di essere inavvicinabile dai “normali sviluppatori”. Tramite questo processo ho scoperto che è possibile creare progetti di apprendimento approfondito realmente utili senza avere un dottorato in matematica e credo che, unitamente al giusto impegno e a una buona dose di pazienza, chiunque sia in grado di cimentarsi se in possesso di una conoscenza di base in materia.
Il futuro di ReadToMe
Ho alcune idee per migliorare il progetto. Vorrei aggiungere una funzione in grado di integrare la capacità di tradurre il testo che viene letto. (Ho effettuato l’iscrizione anticipata al nuovo servizio di traduzione di Amazon ma non ho ancora ricevuto l’approvazione) Intendo inoltre continuare a perfezionare il mio modello per capire se sia possibile migliorarne la precisione e, al contempo, aumentarne la compatibilità con altri libri.
Infine vorrei migliorare la funzione di ripulitura dell’immagine testuale, che viene convogliata direttamente in Tesseract. Nello specifico sarebbe interessante ruotare o deformare l’immagine prima di inviarla a Tesseract. In tal modo un bambino, anche nel caso in cui non tenesse il libro nel modo corretto, potrebbe leggere il testo contenuto nel libro. La sfocatura dovuta al movimento rappresentava anch’essa un problema che dovevo limitare mediante la pulizia dell’immagine. Se il libro non è tenuto in modo fermo per alcuni secondi, l’immagine risulta troppo poco definita per garantire un corretto funzionamento dell’OCR. Ho letto di varie tecniche utili per risolvere il problema, come l’utilizzo di una ponderazione delle immagini su frame multipli o l’applicazione di una serie di filtri per ammorbidire i pixel. Sono sicuro che sia possibile ottenere un risultato migliore in modo più veloce, tuttavia è difficile lavorare con un dispositivo limitato all’impiego di tali risorse.
Risorse utili
Su internet ho trovato molte risorse online che mi hanno aiutato lungo il percorso. Di seguito ne elenco i link più significativi:
(In ordine sparso)
https://becominghuman.ai/an-introduction-to-the-mxnet-api-part-1-848febdcf8ab
http://gluon.mxnet.io/chapter08_computer-vision/object-detection.html?highlight=ssd
https://github.com/zhreshold/mxnet-ssd
https://pythonprogramming.net/introduction-use-tensorflow-object-detection-api-tutorial/
Desidero ringraziare anche tutti coloro che sono intervenuti nei vari forum rispondendo ai miei quesiti e in particolare il team di AWS DeepLens per avermi soccorso in tantissime occasioni! :)
Creato con
opencv
deeplens
python
polly
tesseract-ocr
lambda
mxnet
tensorflow