FYI: Questo articolo è disponibile in lingua inglese su Medium.
Il tema dell’Artificial Intelligence (IA) e del Machine Learning (ML) sta entrando in modo dirompente in ogni settore e trasformando il modo in cui i consumatori usufruiscono di alcuni servizi e prodotti. Google, una delle aziende leader in questo campo, ha messo a disposizione degli sviluppatori vari tool di sviluppo in grado di abilitare nuove esperienze ed infinite possibilità.
Attraverso questo articolo, andremo ad analizzare l’API Cloud Vision e vedremo come integrarla nelle nostre app.
Cloud Vision API è un particolare tipo di API che permette agli sviluppatori di analizzare il contenuto delle immagini, sfruttando modelli di machine learning già consolidati e in continua evoluzione, il tutto in una singola API Rest. Grazie a queste API possiamo avere informazioni contestualizzate su una data immagine e riuscire a classificare rapidamente le immagini in varie categorie e sottocategorie, raggiungendo un livello di dettaglio dell’informazione sempre più profondo.
Prendiamo come esempio la seguente immagine:
Le Vision API permettono di individuare il contenuto di un’immagine con molta precisione. In questo caso, le API danno informazioni dettagliate sul tipo di animale (cane), nella maggior parte dei casi anche sulla razza (beagle) e sul contesto e sfondo (montagne).
Oltre alla classificazione delle immagini, queste API hanno varie funzionalità:
Nell’esempio pratico, vedremo in azione due di queste funzionalità: Rilevamento Etichette e Optical Character Recognition.
Ora che abbiamo le informazioni di base sulle Vision API vediamo come sfruttarle su Android. Creeremo un progetto di esempio che ci permetterà di selezionare un’immagine dalla galleria e ricevere informazioni su di essa sfruttando queste API.
Let’s get into action!
Per utilizzare queste API dobbiamo prima abilitarle dalla Google Cloud Developer Console, per farlo bastano alcuni passi:
keytool -exportcert -keystore path-del-vostro-keystore -list -v
);applicationId
. (Nel mio caso è com.lpirro.cloudvision
);Fatto, configurate le API su Google Cloud Console siamo pronti.
Let’s start coding.
Creiamo un nuovo progetto su Android Studio e ricordiamo che il package name deve corrispondere con quello inserito nel progetto nella Google Cloud Developer Console. Una volta creato il progetto, apriamo il build.gradle ed inseriamo le dependency per utilizzare le API.
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" implementation 'com.android.support:appcompat-v7:27.0.1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' // Google Cloud Vision API implementation 'com.google.android.gms:play-services-base:11.8.0' implementation 'com.google.android.gms:play-services-auth:11.8.0' implementation 'com.google.apis:google-api-services-vision:v1-rev16-1.22.0' implementation ('com.google.api-client:google-api-client-android:1.22.0') { exclude module: 'httpclient' } implementation ('com.google.http-client:google-http-client-gson:1.20.0') { exclude module: 'httpclient' } }
A questo punto apriamo l’ AndroidManifest.xml ed aggiungiamo i permessi necessari per effettuare le chiamate di rete e per ricevere le informazioni sugli account (necessario per l’OAuth request). Inseriamo quindi i permessi:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lpirro.cloudvision"> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.INTERNET"/> ... </manifest>
Fatto questo, possiamo creare l’activity che ci permetterà di selezionare un’immagine della galleria e chiamare i servizi di Cloud Vision per ottenere le informazioni sull’immagine.
Il layout della nostra activity sarà molto banale, avrà una ImageView
per mostrare l’immagine selezionata dalla galleria, due TextView
per mostrare i risultati e un Button
per selezionare l’immagine da scansionare.
E il relativo layout XML:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="16dp" android:paddingLeft="16dp" android:paddingRight="16dp" android:paddingTop="16dp" tools:context=".MainActivity"> <TextView android:id="@+id/selected_image_txt" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:textStyle="bold" android:textSize="20sp" android:gravity="center" android:text="Selected Image "/> <ImageView android:id="@+id/selected_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/selected_image_txt" android:layout_centerHorizontal="true" android:layout_marginTop="10dp"/> <TextView android:id="@+id/labels" android:text="Labels:" android:textStyle="bold" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/selected_image" android:layout_marginTop="10dp"/> <TextView android:id="@+id/tv_label_results" android:text="-" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/labels" android:layout_marginTop="10dp"/> <TextView android:id="@+id/texts" android:text="Texts:" android:textStyle="bold" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/tv_label_results" android:layout_marginTop="10dp"/> <TextView android:id="@+id/tv_texts_results" android:text="-" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/texts" android:layout_marginTop="10dp"/> <Button android:id="@+id/select_image_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Choose an Image"/> </RelativeLayout>
Definito il layout possiamo passare all’Activity. In questo esempio, per le chiamate di rete utilizzeremo la libreria Google API Client per Java e, dato che eseguiremo una OAuth request, prima di tutto dobbiamo ottenere un auth token da Google. Quindi, definiamo prima una classe che ci permetterà di ottenere un OAuth token.
Nota: Per semplicità utilizzeremo un AsyncTask per eseguire questa ed altre operazioni di rete, ma nel caso si utilizzi queste API per un progetto reale, prendi in considerazione l’idea di utilizzare Retrofit, magari in accoppiata con RxJava.
public class GetOAuthToken extends AsyncTask<Void, Void, Void> { Activity mActivity; Account mAccount; int mRequestCode; String mScope; GetOAuthToken(Activity activity, Account account, String scope, int requestCode) { this.mActivity = activity; this.mScope = scope; this.mAccount = account; this.mRequestCode = requestCode; } @Override protected Void doInBackground(Void... params) { try { String token = fetchToken(); if (token != null) { ((MainActivity)mActivity).onTokenReceived(token); } } catch (IOException e) { e.printStackTrace(); } return null; } protected String fetchToken() throws IOException { String accessToken; try { accessToken = GoogleAuthUtil.getToken(mActivity, mAccount, mScope); GoogleAuthUtil.clearToken (mActivity, accessToken); accessToken = GoogleAuthUtil.getToken(mActivity, mAccount, mScope); return accessToken; } catch (UserRecoverableAuthException userRecoverableException) { mActivity.startActivityForResult(userRecoverableException.getIntent(), mRequestCode); } catch (GoogleAuthException fatalException) { fatalException.printStackTrace(); } return null; } }
A questo punto abbiamo tutto il necessario per chiamare le API Cloud Vision dalla nostra app e ricevere i risultati desiderati:
protected BatchAnnotateImagesResponse doInBackground(Object... params) { try { GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); HttpTransport httpTransport = AndroidHttp.newCompatibleTransport(); JsonFactory jsonFactory = GsonFactory.getDefaultInstance(); Vision.Builder builder = new Vision.Builder (httpTransport, jsonFactory, credential); Vision vision = builder.build(); List<Feature> featureList = new ArrayList<>(); Feature labelDetection = new Feature(); labelDetection.setType("LABEL_DETECTION"); labelDetection.setMaxResults(10); featureList.add(labelDetection); Feature textDetection = new Feature(); textDetection.setType("TEXT_DETECTION"); textDetection.setMaxResults(10); featureList.add(textDetection); List<AnnotateImageRequest> imageList = new ArrayList<>(); AnnotateImageRequest annotateImageRequest = new AnnotateImageRequest(); Image base64EncodedImage = getBase64EncodedJpeg(bitmap); annotateImageRequest.setImage(base64EncodedImage); annotateImageRequest.setFeatures(featureList); imageList.add(annotateImageRequest); BatchAnnotateImagesRequest batchAnnotateImagesRequest = new BatchAnnotateImagesRequest(); batchAnnotateImagesRequest.setRequests(imageList); Vision.Images.Annotate annotateRequest = vision.images().annotate(batchAnnotateImagesRequest); annotateRequest.setDisableGZipContent(true); Log.d(TAG, "Sending request to Google Cloud"); BatchAnnotateImagesResponse response = annotateRequest.execute(); return response; } catch (GoogleJsonResponseException e) { Log.e(TAG, "Request error: " + e.getContent()); } catch (IOException e) { Log.d(TAG, "Request error: " + e.getMessage()); } return null; }
con il metodo setType
andiamo a definire il tipo di funzionalità che andremo ad utilizzare, nel nostro caso avremo quindi LABEL_DETECTION e TEXT_DETECTION
. L’immagine da scansionare va passata nel formato Base64. Fatto questo, una volta ricevuti i risultati, vengono passati al metodo getDetectedText()
che non fa altro che formattare la stringa per filtrare le informazioni e visualizzarle finalmente sulla UI.
That’s it.
Cloud Vision API rappresenta un ulteriore passo in avanti per Google, il cui obiettivo è quello di abbassare le barriere all’ingresso e ampliare sempre più la community di developer in ambito Artificial Intelligence e di Machine Learning, due forti propulsori della Digital Transformation.
Attraverso la creazione di servizi moderni di Machine Learning implementati su modelli già pre-istruiti, come ad esempio le API che svolgono determinati compiti, Google sta offrendo nuove opportunità alle aziende per poter rendere le applicazioni ancora più scalabili e smart.
Anche se l’impegno nel rendere accessibile l’AI a tutti è ancora un inizio, il trend dell’Image Recognition ha le carte in regola per ampliarsi. Mentre Facebook, per esempio, sta già sfruttando questa tecnologia per suggerire di taggare una persona in una foto quando viene rilevato il volto, Google invece la utilizza per organizzare le foto dello smartphone in automatico, assegnando dei tag per facilitare ricerca e organizzazione. In Google Photos, infatti, si può trovare velocemente tutte le foto associate a un tag: cercando “foto al mare”, ad esempio, verranno proposte tutte le foto scattate al mare.
Il riconoscimento di immagini sta quindi diventando un must have da integrare nelle applicazioni per aiutare l’utente a leggere qualsiasi testo di un’immagine, da una fattura a un elenco di un esercizio commerciale in modo sempre più rapido e preciso.
Per approfondire puoi scaricare il sorgente di questo progetto di esempio nella repository su GitHub.