Ionic + Cloud Vision de Google
Tutorial para crear una app con Ionic que usa la API de Google Cloud Vision al estilo "Not Hot Dog".
En está ocasión estaremos viendo como crear una imitación de la aplicación “Not hot dog app” que aparece en la famosa serie silycon valley, por si acaso no sabes cual es la aplicación te dejo un link para que le des un vistaso.
Para hacer posible la aplicación utilizaremos Ionic Framework y el Api de Google Cloud Vision, por lo tanto lo primero que debemos hacer es instalar Ionic e iniciar un nuevo proyecto, lo conseguimos de la siguiente manera (infiero que tienes instalado Node.js y npm)
$ npm i ionic -g && ionic start hotDogoNo blank
El primer comando instalara ionic de manera global en el sistema, el segundo iniciara un nuevo proyecto de ionic llamado hotDogoNo y con la plantilla blank, acá pueden ver más al respecto.
Ahora de momento iremos a la pagina de Cloud Vision y activamos la Api y guardamos el Api Key.
Abrimos el proyecto de ionic en nuestro editor favorito, y nos centraremos en la carpeta /src/ y ahora especificamente en el archivo /src/app/app.modules.ts donde importaremos el modulo http de angular con el que realizaremos las peticiones y también el modulo de la cámara para poder tomar la foto desde el celular y colocamos los módulos dentro del array de imports y providers respectivamente.
Para instalar el modulo de la cámara
$ ionic cordova plugin add cordova-plugin-camera
$ npm install --save @ionic-native/camera
import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { HttpModule } from '@angular/http'; //HttpModule
import { Camera } from '@ionic-native/camera'; //Camara
import { MyApp } from './app.component';
@NgModule({
declarations: [
MyApp
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
HttpModule //Array de imporst
],
bootstrap: [IonicApp],
entryComponents: [
MyApp
],
providers: [
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler},
Camera // Array de providers
]
})
export class AppModule {}
Por el momento es todo en este archivo.
Ahora vamos al archivo /src/ages/inicio/inicio.ts
Yo he eliminado la pagina “home” que viene por defecto y he creado la pagina “inicio”
En la pagina de inicio escribiremos el código necesario para la lógica de la aplicación, o sea, aquí capturamos la imagen y la enviamos a Cloud Vision para luego trabajar con el resultado que nos retorna, pero veamos y analicemos el código
Aquí pueden ver como es la estructura para enviar la petición a Cloud Vision
import { SplashScreen } from '@ionic-native/splash-screen';
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Http } from '@angular/http';
import { LoadingController } from 'ionic-angular/components/loading/loading-controller';
import { Camera, CameraOptions } from '@ionic-native/camera';
import { ToastController } from 'ionic-angular/components/toast/toast-controller';
// imports necesarios
@IonicPage({
//Lazy loading
name: "inicio"
})
@Component({
selector: 'page-inicio',
templateUrl: 'inicio.html',
})
export class InicioPage {
//Variables utilizadas en la aplicación
//Apikey de google cloud vision
googleCloudVisionAPIKey = "TUAPIKEY";
//Para obtener las respuestas de google cloud vision
labels: any[] = [];
//Para dar vista previa a la imgen
imagen: any = null;
//Respuesta de google cloud vison
resultado: any = null;
//variable de control de si es o no es hotdog
es: boolean = false;
constructor(public navCtrl: NavController, public navParams: NavParams, public splashScreen: SplashScreen, public http: Http, public loader: LoadingController, private camera: Camera, public toast: ToastController) {
//Objetos necesarios, necesarios tambien agregarlos en el app.module.ts
}
ionViewDidLoad() {
//Para ocultar el splash de ionic
this.splashScreen.hide();
}
//Funcion para hacer la petición a google cloud vision, estructura necesaria para la petición segun la documentación
getLabels(base64) {
const body = {
"requests": [
{
"image": {
"content": base64
},
"features": [
{
"type": "LABEL_DETECTION"
}
]
}
]
}
//Retornar la respuesta
return this.http.post(`https://vision.googleapis.com/v1/images:annotate?key=${this.googleCloudVisionAPIKey}`, body)
}
//Funcion para abrir la camara y procesar la imagen
tomarFoto() {
//Crear loader
let loader = this.loader.create({
content: 'Ejecutando analisis...'
});
//Mostrar loader
loader.present();
//Opciones para abrir la camara
const opciones: CameraOptions = {
//Calidad de la imagen
quality: 100,
//Alto de la imagen
targetHeight: 500,
//Ancho de la imagen
targetWidth: 500,
//Tip de respuesta (base64 en este caso)
destinationTyp-e: this.camera.DestinationType.DATA_URL,
//Tipo png
encodingType: this.camera.EncodingType.PNG,
mediaType: this.camera.MediaType.PICTURE,
//Abrir desde la camara (se puede tambien desde la galeria)
sourceType: this.camera.PictureSourceType.CAMERA
}
//Abirmos la camara pasando las opciones antes estipuladas
this.camera.getPicture(opciones).then((img) => {
this.labels = [];
this.es = false;
//Hacemos la petición a google cloud vision
this.getLabels(img).subscribe((resultados) => {
//Hacemos la variable imagen igual a la imagen obtenida por la camara para mostrar la vista previa
this.imagen = img;
//Obtenemos los resultados que nos da google
this.resultado = resultados.json().responses;
//Recorremos las etiquetas de la respuesta con map()
this.resultado[0].labelAnnotations.map(obj => {
//Guardamos las etiquetas en la variable labels
this.labels.push(obj.description);
//Si algunas de las etiquetas es "hot dog" entonces es un hot dog
if (obj.description == "hot dog") this.es = true;
});
//Quitamos el loader
loader.dismiss();
}, err => {
//Por si acaso ocurre un error
loader.dismiss();
this.mostrarToast(err.message, 5000);
});
}, err => {
//Por si acaso ocurre un error
loader.dismiss();
this.mostrarToast(err.message, 5000);
});
}
//Funcion para mostrar mensaje de error recibe mensaje de error y la duración de el mensaje
mostrarToast(mensaje: string, duracion: number) {
this.toast.create({
message: mensaje,
duration: duracion
}).present();
}
}
El anterior es el código de toda la lógica de la aplicación, bastante optimízable por cierto pero por cuestiones del tutorial lo escribí así.
Ahora en el archivo /src/ages/inicio/inicio.html tendremos los siguiente
<ion-header>
<!-- Color rojo de el navbar -->
<ion-navbar color="danger">
<ion-title>¿Hog dog o no?</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-row margin>
<ion-card>
<!-- Si la imagen existe, mostramos su vista previa -->
<img *ngIf="imagen" [src]="'data:image/png;base64,' + imagen" />
</ion-card>
</ion-row>
<!-- Si existe ya un resultado, mostraremos lo siguiente -->
<ion-col *ngIf="resultado">
<!-- Si la variable "es" esta en true, mostramos que es un hotdog, de lo contrario pues mostramos que no es hotdog -->
<button *ngIf="es" color="secondary" ion-button full>
¡Es un Hotdog!
</button>
<button *ngIf="!es" color="danger" ion-button full>
¡No es Hotdog!
</button>
<!-- Si no es hotdog mostramos las etiquetas de lo que posiblemente está en la imagen -->
<div *ngIf="!es">
<h3>Posiblemente sea</h3>
<!-- Recorremos la variable labels -->
<ion-chip color="secondary" *ngFor="let label of labels">
<!-- Mostramos una por una -->
<ion-label>{{ label }}</ion-label>
</ion-chip>
</div>
</ion-col>
<!-- Boton flotante que ejecuta la funcion tomarFoto() -->
<ion-fab bottom right>
<button color="danger" ion-fab (click)="tomarFoto()">
<ion-icon name="camera"></ion-icon>
</button>
</ion-fab>
</ion-content>
Y ahora veamos un poquito la aplicación funcionando
Mis disculpas por la alerta molestosa que sale al grabar el mi celular xD
Cualquier duda o sugerencia estoy en las redes como @JoralmoPro
Nos vemos en linea
