Cómo Usar Servicios de Aprendizaje Automático de Google Cloud para Android
() translation by (you can also view the original English article)
Las noticias estos días están llenas de palabras de moda tales como automatización, inteligencia artificial y aprendizaje automático. Eso es probablemente por lo que más y más usuarios de teléfonos inteligentes están comenzando a buscar aplicaciones más inteligentes. Como desarrollador regular de aplicaciones Android, sin embargo, probablemente careces de los recursos necesarios para crear tales aplicaciones desde cero.
Afortunadamente, Google recientemente lanzó una plataforma de Aprendizaje Automático en la Nube, que ofrece redes neuronales que han sido pre-entrenadas para realizar una variedad de tareas. La mayoría de sus modelos no solo son altamente precisos, sino también están en constante mejora. ¿Y adivina qué? ¡Puedes usarlos simplemente haciendo unas cuantas llamadas API REST!
En este tutorial te presentará la plataforma de Aprendizaje Automático en la Nube y te mostraré cómo usarla para crear una aplicación Android inteligente que puede reconocer objetos del mundo real y nombrarlos en múltiples lenguajes.
Prerrequisitos
Para sacar lo mejor de este tutorial, todo lo que necesitas es:
- la última versión de Android Studio
- un dispositivo ejecutando Android 4.4 o superior
- y una cuenta de Plataforma Google Cloud
1. Adquiriendo una Llave API
Para poder usar los servicios de aprendizaje automático de Google en tu aplicación Android, necesitas una llave API. Puedes obtener una creando un nuevo proyecto en la consola de Plataforma Google Cloud.
Comienza iniciando sesión en la consola y presionando el botón Crear Nuevo Proyecto. En el diálogo que emerge, dale un nombre significativo al proyecto.



Una vez que el proyecto ha sido creado, ve a Administrador API > Tablero y presiona el botón Habilitar API.
En la siguiente pantalla, bajo el encabezado Aprendizaje Automático Google Cloud, podrás ver todas las diferentes APIs de aprendizaje automático disponibles para ti. En este tutorial, estaremos usando solo las APIs Visión y Traducción.



Para habilitar la API Visión, da clic en su enlace y presiona el botón Habilitar.



De manera similar, para habilitar la API Traducción, da clic sobre su enlace y presiona el botón Habilitar.
Solo necesitarás una llave para usar ambas APIs. Para obtenerla, ve a la pestaña de Credenciales, presiona el botón Crear credenciales, y selecciona llave API.



Ahora deberías ver una ventana emergente conteniendo tu llave API secreta.
2. Creando un Nuevo Proyecto Android
Inicia Android Studio y crea un nuevo proyecto con una actividad vacía. Te sugiero que elijas al menos API nivel 19 para el SDK mínimo soportado.
Aunque no tienes que hacerlo, siempre es una buena idea usar una librería robusta de trabajo en red para comunicar con la plataforma de Aprendizaje Automático Google Cloud. En este tutorial, estaremos usando una de esas librerías llamada Fuel. Agrégala como una dependencia compile
en el archivo build.gradle del módulo de app
:
1 |
compile 'com.github.kittinunf.fuel:fuel-android:1.5.0' |
Presiona Sync Now para actualizar tu proyecto.
Después, nuestra aplicación necesitará el permiso INTERNET
para comunicarse con los servidores de Google. Así pues, agrega la siguiente línea dentro del archivo manifest del proyecto:
1 |
<uses-permission android:name="android.permission.INTERNET"/> |
Por último, agrega tu llave API al archivo values/strings.xml:
1 |
<string name="mykey">ABCDEF12345-abcdef12345-123</string> |
3. Usando la API Visión
La API Visión te ayuda a crear aplicaciones que pueden ver y hacer sentido del entorno del usuario. La detección de rostros, detección de emociones, reconocimiento de caracter óptico, y comentario de imagen son algunas de sus muchas características. Por ahora, nos estaremos concentrando solo en la poderosa característica de comentario de imagen, también llamada detección de etiqueta---la cual, en mi opinión, es muy útil.
Como podrías esperar, la API espera una imagen como una de sus entradas. Así pues, creemos una pantalla en donde el usuario puede capturar imágenes usando la cámara del dispositivo.
Paso 1. Crea un Diseño
El diseño de nuestra pantalla debería tener widget Button
que el usuario puede presionar para tomar una fotografía, un widget ImageView
para mostrar la imagen, y un widget TextView
para mostrar las etiquetas, o comentarios, generados por la API. De manera acorde, agrega el siguiente código al archivo XML de diseño de tu actividad:
1 |
<Button android:text="Take a picture" |
2 |
android:layout_width="match_parent" |
3 |
android:layout_height="wrap_content" |
4 |
android:id="@+id/takePictureButton" |
5 |
android:onClick="takePicture"/> |
6 |
|
7 |
<ImageView
|
8 |
android:layout_width="wrap_content" |
9 |
android:layout_height="wrap_content" |
10 |
android:layout_below="@id/takePictureButton" |
11 |
android:id="@+id/previewImage" |
12 |
android:layout_centerHorizontal="true"/> |
13 |
|
14 |
<TextView
|
15 |
android:layout_width="wrap_content" |
16 |
android:layout_height="wrap_content" |
17 |
android:id="@+id/resultsText" |
18 |
android:layout_below="@id/previewImage"/> |
Nota que hemos asociado un manejador onClick
con el botón. Lo definiremos en el siguiente paso.
Paso 2: Crea un Intent
Creando un nuevo intent con la acción ACTION_IMAGE_CAPTURE
y pasándolo al método startActivityForResult()
, puedes pedir a la aplicación de cámara por defecto del dispositivo del usuario tomar fotografías y pasarlas a tu aplicación. Así pues, agrega el siguiente código a tu clase Activity
:
1 |
public final static int MY_REQUEST_CODE = 1; |
2 |
|
3 |
public void takePicture(View view) { |
4 |
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
5 |
startActivityForResult(intent, MY_REQUEST_CODE); |
6 |
}
|
Para poder recibir las imágenes capturadas por la aplicación de cámara por defecto, debes anular el método onActivityResult()
de tu clase Activity
. Dentro del método, tendrás acceso a un objeto Bundle
conteniendo toda la información de la imagen. Puedes generar la información de la imagen simplemente convirtiéndola a un Bitmap
y pasándola al widget ImageView
.
1 |
@Override
|
2 |
protected void onActivityResult(int requestCode, int resultCode, |
3 |
Intent data) { |
4 |
if(requestCode == MY_REQUEST_CODE && resultCode == RESULT_OK) { |
5 |
|
6 |
// Convert image data to bitmap
|
7 |
Bitmap picture = (Bitmap)data.getExtras().get("data"); |
8 |
|
9 |
// Set the bitmap as the source of the ImageView
|
10 |
((ImageView)findViewById(R.id.previewImage)) |
11 |
.setImageBitmap(picture); |
12 |
|
13 |
// More code goes here
|
14 |
}
|
15 |
}
|
Si ejecutas la aplicación ahora y tomas una foto, verás que el tamaño de la imagen es bastante pequeño. Eso está bien, sin embargo---la API Cloud Vision es muy precisa incluso con miniaturas de imagen.
Paso 3: Codifica la Imagen
La API Visión no puede usar objetos Bitmap
directamente. En su lugar, espera una cadena de texto codificada en Base64 de información comprimida de imagen.
Para comprimir la información de la imagen, puedes usar el método compress()
de la clase Bitmap
. Como sus argumentos, el método espera el formato de compresión a usar, la calidad de salida deseada, y un objeto ByteArrayOutputStream
. El siguiente código comprime el bitmap usando el formato JPEG y también asegura que la calidad de la imagen resultante es lo suficientemente alta:
1 |
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); |
2 |
picture.compress(Bitmap.CompressFormat.JPEG, 90, byteStream); |
Ahora puedes generar la cadena Base64 usando el método encodeToString()
de la clase Base64()
.
1 |
String base64Data = Base64.encodeToString(byteStream.toByteArray(), |
2 |
Base64.URL_SAFE); |
Paso 4: Procesa la Imagen
Después de todo ese arduo trabajo, tienes todo lo que necesitas para interactuar con la API Visión. Comienza creando una cadena que contenga la URL de la API y tu llave API:
1 |
String requestURL = |
2 |
"https://vision.googleapis.com/v1/images:annotate?key=" + |
3 |
getResources().getString(R.string.mykey); |
Para enviar información a la API, debes crear una petición HTTP POST. El cuerpo de la petición debe ser un documento JSON conteniendo la información de la imagen codificada en Base64. Para detección de etiqueta, el documento también debe tener un arreglo features
conteniendo el valor LABEL_DETECTION
. Aquí está el formato del documento JSON.
1 |
{"requests": [{ |
2 |
"image": { |
3 |
"content": <Base64-encoded image data> |
4 |
},
|
5 |
"features": [{ |
6 |
"type": "LABEL_DETECTION" |
7 |
}]
|
8 |
}]}
|
Aunque es posible codificar a mano el documento JSON, crearlo de manera programática es menos propenso al error. El siguiente código te muestra cómo hacerlo usando las clases JSONObject
y JSONArray
:
1 |
// Create an array containing
|
2 |
// the LABEL_DETECTION feature
|
3 |
JSONArray features = new JSONArray(); |
4 |
JSONObject feature = new JSONObject(); |
5 |
feature.put("type", "LABEL_DETECTION"); |
6 |
features.put(feature); |
7 |
|
8 |
// Create an object containing
|
9 |
// the Base64-encoded image data
|
10 |
JSONObject imageContent = new JSONObject(); |
11 |
imageContent.put("content", base64Data); |
12 |
|
13 |
// Put the array and object into a single request
|
14 |
// and then put the request into an array of requests
|
15 |
JSONArray requests = new JSONArray(); |
16 |
JSONObject request = new JSONObject(); |
17 |
request.put("image", imageContent); |
18 |
request.put("features", features); |
19 |
requests.put(request); |
20 |
JSONObject postData = new JSONObject(); |
21 |
postData.put("requests", requests); |
22 |
|
23 |
// Convert the JSON into a
|
24 |
// string
|
25 |
String body = postData.toString(); |
En este punto, podemos usar el método post()
de la clase Fuel
para hacer la petición HTTP POST. Como su único argumento, el método espera la URL de la API. También debes incluir los encabezados content-length
y content-type
en la petición. Para hacerlo, usa el método header()
. De manera similar, para poder agregar el documento JSON al cuerpo de la petición POST, usa el método post()
.
Por último, llamando al método responseString()
y pasando una nueva instancia de la clase Handler
a este, puedes obtener de manera asíncrona la respuesta de la petición como una cadena. De manera acorde, agrega el siguiente código:
1 |
Fuel.post(requestURL) |
2 |
.header( |
3 |
new Pair<String, Object>("content-length", body.length()), |
4 |
new Pair<String, Object>("content-type", "application/json") |
5 |
)
|
6 |
.body(body.getBytes()) |
7 |
.responseString(new Handler<String>() { |
8 |
@Override
|
9 |
public void success(@NotNull Request request, |
10 |
@NotNull Response response, |
11 |
String data) { |
12 |
// More code goes here
|
13 |
}
|
14 |
|
15 |
@Override
|
16 |
public void failure(@NotNull Request request, |
17 |
@NotNull Response response, |
18 |
@NotNull FuelError fuelError) {} |
19 |
});
|
Cuando usas la característica de detección de etiqueta, la API Visión devuelve un documento JSON conteniendo etiquetas. Junto con cada etiqueta, puedes también obtener una puntuación especificando qué tan certera es la etiqueta. El documento luce como esto:
1 |
{"responses":[{ |
2 |
"labelAnnotations": [ |
3 |
{
|
4 |
"mid": "/m/id1", |
5 |
"description": "label1", |
6 |
"score": 0.91 |
7 |
},
|
8 |
{
|
9 |
"mid": "/m/id2", |
10 |
"description": "label2", |
11 |
"score": 0.90 |
12 |
},
|
13 |
...
|
14 |
}]}
|
Por ahora, solo ciclemos a través de todos los objetos en el arreglo labelAnnotations
y agrega el valor de cada llave description
al widget TextView
de nuestro diseño. Aquí está cómo puedes hacer eso dentro del método success()
de la clase Handler
:
1 |
// Access the labelAnnotations arrays
|
2 |
JSONArray labels = new JSONObject(data) |
3 |
.getJSONArray("responses") |
4 |
.getJSONObject(0) |
5 |
.getJSONArray("labelAnnotations"); |
6 |
|
7 |
String results = ""; |
8 |
|
9 |
// Loop through the array and extract the
|
10 |
// description key for each item
|
11 |
for(int i=0;i<labels.length();i++) { |
12 |
results = results + |
13 |
labels.getJSONObject(i).getString("description") + |
14 |
"\n"; |
15 |
}
|
16 |
|
17 |
// Display the annotations inside the TextView
|
18 |
((TextView)findViewById(R.id.resultsText)).setText(results); |
Ahora puedes ejecuta la aplicación, tomar una foto de cualquier objeto cercano, y ver las etiquetas que la API Visión genera para este.



4. Usando la API Traducción
La API de Traducción en la Nube, como sugiere su nombre, puede traducir texto desde y hacia más de 100 lenguajes. Al usarla de manera efectiva, puedes crear aplicaciones inteligentes que puedan comunicarse con tus usuarios en sus propios lenguajes.
En el paso anterior, viste que las etiquetas que nuestra aplicación genera están en inglés. Agreguemos ahora un botón para traducir esas etiquetas a Alemán.
Paso 1: Actualiza el Diseño
Agrega un widget Button
hacia el final del diseño de tu Activity usando el siguiente código:
1 |
<Button
|
2 |
android:layout_width="match_parent" |
3 |
android:layout_height="wrap_content" |
4 |
android:layout_below="@+id/resultsText" |
5 |
android:text="in German" |
6 |
android:onClick="translateToGerman"/> |
Nota que este botón también tiene un manejador de evento onClick
que debe ser definido en tu clase Activity
.
Paso 2: Traduce las Etiquetas
Usando la API de Traducción es mucho más sencillo que usar la API Visión, primariamente porque no necesitas crear un documento JSON para definir tu petición. en su lugar, puedes simplemente pasarle parámetros en una cadena de consulta.
Crea el método translateToGerman()
y, dentro de este, crea una cadena conteniendo la URL de la API de Traducción.
1 |
public void translateToGerman(View view) { |
2 |
String requestURL = |
3 |
"https://translation.googleapis.com/language/translate/v2"; |
4 |
|
5 |
// More code goes here
|
6 |
}
|
Para agregar campos a la cadena de consulta de la URL de arriba, podemos usar una List
de objetos Pair
. Los siguientes campos son importantes:
-
key
, especificando tu llave API secreta -
source
, especificando el lenguaje desde el que quieres traducir -
target
, especificando el lenguaje al que quieres traducir -
q
, especificando el texto que quieres traducir
El siguiente código te muestra cómo configurar la API para traducir de Inglés a Alemán:
1 |
List<Pair<String, String>> params = new ArrayList<>(); |
2 |
// Add API key
|
3 |
params.add(new Pair<String, String>("key", |
4 |
getResources().getString(R.string.mykey))); |
5 |
|
6 |
// Set source and target languages
|
7 |
params.add(new Pair<String, String>("source", "en")); |
8 |
params.add(new Pair<String, String>("target", "de")); |
Debido a que la cadena de consulta puede contener múltiples campos q
, estaremos agregando uno por cada etiqueta que esté presente dentro del widget TextView
dentro de nuestro diseño. Aquí está cómo:
1 |
String[] queries = ((TextView)findViewById(R.id.resultsText)) |
2 |
.getText().toString().split("\n"); |
3 |
|
4 |
for(String query:queries) { |
5 |
params.add(new Pair<String, String>("q", query)); |
6 |
}
|
Finalmente, puedes llamar al método get()
de la clase Fuel
para hacer una petición HTTP GET a la API de traducción. Esta vez también, puedes usar el método responseString()
para obtener la respuesta de manera asíncrona como una cadena de texto.
1 |
Fuel.get(requestURL, params).responseString(new Handler<String>() { |
2 |
@Override
|
3 |
public void success(@NotNull Request request, |
4 |
@NotNull Response response, |
5 |
String data) { |
6 |
// More code here
|
7 |
}
|
8 |
|
9 |
@Override
|
10 |
public void failure(@NotNull Request request, |
11 |
@NotNull Response response, |
12 |
@NotNull FuelError fuelError) { } |
13 |
});
|
La respuesta de la API de Traducción es un documento JSON conteniendo un arreglo de traducciones. Tiene el siguiente formato:
1 |
{ "data": { "translations": [ |
2 |
{
|
3 |
"translatedText": "...." |
4 |
},
|
5 |
{
|
6 |
"translatedText": "...." |
7 |
},
|
8 |
...
|
9 |
] } } |
Por ahora, dentro del método success()
de la clase Handler
, simplemente ciclemos a través del arreglo translations
del documento JSON de arriba, y actualicemos los contenidos del widget TextView
usando los valores asociados con las llaves translatedText
.
1 |
// Access the translations array
|
2 |
JSONArray translations = new JSONObject(data) |
3 |
.getJSONObject("data") |
4 |
.getJSONArray("translations"); |
5 |
|
6 |
// Loop through the array and extract the translatedText
|
7 |
// key for each item
|
8 |
String result = ""; |
9 |
for(int i=0;i<translations.length();i++) { |
10 |
result += translations.getJSONObject(i) |
11 |
.getString("translatedText") + "\n"; |
12 |
}
|
13 |
|
14 |
// Update the contents of the TextView widget
|
15 |
((TextView)findViewById(R.id.resultsText)).setText(result); |
Si ejecutas la aplicación ahora, generas etiquetas para una imagen, y presionas el segundo botón, deberías poder ver las etiquetas en Alemán.



Conclusión
En este tutorial, aprendiste cómo usar las APIs Cloud Vision y Cloud Translation, que son parte de la plataforma de Aprendizaje Automático Google Cloud, en una aplicación Android. Hay muchas más de estas APIs ofrecidas por la plataforma. Puedes aprender más acerca de ellas refiriéndote a la documentación oficial.
Mientras estás aquí, ¡revisa algunos de nuestros otros tutoriales sobre cómo usar servicios de aprendizaje automático y en la nube en tus aplicaciones Android!