Reconocimiento de objetos en JavaScript con Serverless Framework y AWS Rekognition

/ agosto 8, 2019/ Server/ 0 comments


 

Serverless 🔌

CommitStrip Serverless

Los servidores no son más que computadoras que atienden las peticiones de un usuario y le devuelven una respuesta por internet.

Muchas empresas nos ofrecen un alquiler de sus servidores, pero no siempre es lo más conveniente: Tenemos que ocuparnos del sistema operativo, las actualizaciones, instalar los lenguajes de programación, aumentar o disminuir el número de servidores que necesitamos (porque debemos pagar aunque nadie esté usando nuestra aplicación), entre muchas otras complicaciones.

Serverless resuelve todos estos problemas: En vez de pagar por una cantidad de servidores durante un tiempo, le pediremos a nuestros proveedores (Amazon, Google, Microsoft) que nos cobren únicamente por las veces que nuestros usuarios entren a nuestra aplicación. Ahora no debemos preocuparnos por la administración de nuestros servidores, solo por programar.

Serverless Framework ⚡️

Serverless Framework es una herramienta open source para construir e implementar aplicaciones serverless. Tiene soporte para las tecnologías más populares como AWS Lambda, Microsoft Azure, Google Cloud Platform e IBM OpenWhisk. Nos va a ser muy útil para desarrollar nuestra aplicación super rápido y simple.

AWS Rekognition 🔥

AWS Recognition es un servicio basado en Deep Learning (inteligencia artificial). Su trabajo es detectar objetos, personas, celebridades o actividades especiales en el contenido de fotografías y videos. Nuestra aplicación va a utilizar esta herramienta para etiquetar el contenido de nuestras imágenes.

Requisitos para empezar:

  • Node.js y NPM.
  • Una cuenta gratis en AWS: No te preocupes, ¡seguir este tutorial no va a generar cobros a tu cuenta! 😏
  • Cuenta IAM: AWS IAM nos ayuda a administrar los accesos y permisos de usuarios y máquinas. En este caso necesitamos una cuenta para Serverless Framework y AWS Rekognition. Para crear la cuenta puedes seguir esta guía: IAM – Creating AWS Access Keys.

Instalación de Serverless Framework:

Para instalar la herramienta podemos ejecutar el siguiente comando: npm install serverless --global.

Recuerda pasar las credenciales de IAM a la configuración de Serverless Framework:

sls config credentials -p aws --key TOKEN -s PASSWORD

Estructura de Serverless Framework:

Para usar esta herramienta solo necesitamos crear el archivo serverless.yml con los siguientes campos:

  • service: El nombre de nuestra aplicación.
  • provider: Serverless es compatible con Amazon, Google Cloud, Microsoft Azure, entre otros. En este tutorial usaremos AWS, pero tú usa el que te resulte más cómodo.
  • plugins: Podemos darle aún más super poderes a nuestra aplicación usando los plugins de serverless. En este caso usaremos serverless-webpack y serverless-offline, pero puedes ver la lista de plugins disponibles en github.com/serverless/plugins.
  • functions: En esta sección describimos los archivos y eventos que necesita nuestra aplicación para funcionar. Puedes separar tu aplicación en muchas funciones.

Organización del Proyecto:

El objetivo de nuestra aplicación es construir una API a la que podamos pasar la URL de una imagen y nos devuelve información sobre su contenido.

Necesitaremos los siguientes paquetes:

  • express para manejar nuestras rutas (/, /api, /api?img=…)
  • serverless-http para traducir la información de Expressjs a Serverless Framework
  • aws-sdk nos proporciona los métodos necesarios para conectarnos con AWS Rekognition
  • request para convertir la URL de nuestras imágenes en información que AWS Rekognition pueda entender
  • serverless-webpack para traducir nuestro código JavaScript a Serverless Framework
  • webpack
  • webpack-node-externals
  • serverless-offline para probar nuestro código en etapa de desarrollo (por defecto no podemos 😅)

Puedes instalar estos paquetes usando los siguientes comandos:

npm i express serverless-http aws-sdk request -S
npm i webpack webpack-node-externals serverless-webpack serverless-offline -D

Configuración:

Ahora vamos a crear los archivos serverless.yml y webpack.config.js:

# severless.yml
service: EL_NOMBRE_DE_TU_APLICACIÓN

provider: 
  name: aws
  runtime: nodejs8.10
  stage: test
  environment:
    ACCESS_KEY_ID: TU_ACCESS_KEY_ID_DE_IAM
    SECRET_ACCESS_KEY: TU_SECRET_ACCESS_KEY_DE_IAM

plugins:
  - serverless-webpack
  - serverless-offline

functions:
  rekognition:
    handler: api.default
    events:
      - http: ANY /
      - http: 'ANY {proxi+}'
// webpack.config.js
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  entry: slsw.lib.entries,
  output: {
    libraryTarget: 'commonjs2',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js',
  },
  target: 'node',
  externals: [nodeExternals()],
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production'
};

Nuestra aplicación:

El código de nuestra aplicación la vamos a escribir en el archivo api.js, se divide en 5 secciones:

1. Importamos las dependencias:

import serverless from 'serverless-http';
import express from 'express';
import AWS from 'aws-sdk';
import request from 'request';

2. Configuración de expressrequest y AWS Rekognition:

const app = express();
const requests = request.defaults({ encoding: null });
const rek = new AWS.Rekognition({
  region: 'us-west-2',
  accessKeyId: process.env.ACCESS_KEY_ID,
  secretAccessKey: process.env.SECRET_ACCESS_KEY,
});

3. Creamos la función GetImageLabels que nos devuelve la información de nuestras imágenes:

function GetImageLabels(params, func) {
  params = {
    img: params.img || params.image || null,
    maxLabels: params.maxLabels || params.MaxLabels || 10,
    minConfidence: params.minConfidence || params.MinConfidence || 80,
  };

  if (!params.img) return func(new Error('The image param cannot be null.'));
  if (params.img.length <= 8) return func(new Error('The image param length can not be <= 8.'));

  requests.get(params.img, (reqErr, _, body) => {
    if (reqErr) return func(new Error('Error in request: ' + reqErr));

    const rekParams = {
      Image: { Bytes: body },
      MaxLabels: params.maxLabels,
      MinConfidence: params.minConfidence,
    };

    rek.detectLabels(rekParams, (rekErr, data) => {
      if (rekErr) return func(new Error('Error in rek: ' + rekErr));
      return func(null, data);
    });
  });
}

4. Definimos las rutas de nuestra API usando la función GetImageLabels:

app.get('/', (req, res) => {
  GetImageLabels(req.query, (err, data) => {
    if (err) return res.json({ message: err.message, error: true });

    res.json({
      image: req.query.img || req.query.image,
      message: 'Success!',
      data,
    });
  });
});

5. Y por último, exportamos las rutas de expressjs con ayuda de serverless-http:

export default serverless(app);

Nuestro archivo completo quedaría así:

// api.js
import serverless from 'serverless-http';
import express from 'express';
import AWS from 'aws-sdk';
import request from 'request';

const app = express();
const requests = request.defaults({ encoding: null });
const rek = new AWS.Rekognition({
  region: 'us-west-2',
  accessKeyId: process.env.ACCESS_KEY_ID,
  secretAccessKey: process.env.SECRET_ACCESS_KEY,
});

app.get('/', (req, res) => {
  GetImageLabels(req.query, (err, data) => {
    if (err) return res.json({ message: err.message, error: true });

    res.json({
      image: req.query.img || req.query.image,
      message: 'Success!',
      data,
    });
  });
});

function GetImageLabels(params, func) {
  params = {
    img: params.img || params.image || null,
    maxLabels: params.maxLabels || params.MaxLabels || 10,
    minConfidence: params.minConfidence || params.MinConfidence || 80,
  };

  if (!params.img) return func(new Error('The image param cannot be null.'));
  if (params.img.length <= 8) return func(new Error('The image param length can not be <= 8.'));

  requests.get(params.img, (reqErr, _, body) => {
    if (reqErr) return func(new Error('Error in request: ' + reqErr));

    const rekParams = {
      Image: { Bytes: body },
      MaxLabels: params.maxLabels,
      MinConfidence: params.minConfidence,
    };

    rek.detectLabels(rekParams, (rekErr, data) => {
      if (rekErr) return func(new Error('Error in rek: ' + rekErr));
      return func(null, data);
    });
  });
}

export default serverless(app);

Para probar que nuestra aplicación esté funcionando podemos correr el siguiente comando:

sls offline start

Ahora si navegamos a http://localhost:3000/?img=http://www.grupocanton.com/all/imagenes/1/2018/07/16/447131_grande_EPFBUm8C.jpg (con una imagen de ejemplo, por supuesto) obtenemos un resultado en tipo JSON con los objetos que Rekognition detecto en nuestra imagen:

{
	"image": "http://www.grupocanton.com/all/imagenes/1/2018/07/16/447131_grande_EPFBUm8C.jpg",
	"message": "Success!",
	"data": {
		"Labels": [
			{
				"Name": "Human",
				"Confidence": 99.18035125732422
			},
			{
				"Name": "People",
				"Confidence": 99.18035125732422
			},
			{
				"Name": "Person",
				"Confidence": 99.18035125732422
			},
			{
				"Name": "Clothing",
				"Confidence": 91.23234558105469
			},
			{
				"Name": "Coat",
				"Confidence": 91.23234558105469
			},
			{
				"Name": "Overcoat",
				"Confidence": 91.23234558105469
			},
			{
				"Name": "Suit",
				"Confidence": 91.23234558105469
			}
		],
		"OrientationCorrection": "ROTATE_0"
	}
}



Juan David

Deploy en AWS Lambda:

Serverless Framework también se puede encargar de configurar nuestro entorno en AWS para hacer deploy, solo tenemos que ejecutar el siguiente comando dentro de la carpeta de nuestro proyecto:

sls deploy

Conclusiones

¡Felicidades! Nuestra aplicación funciona correctamente y devuelve las etiquetas de nuestras imágenes. Ahora es tu turno: ¿Cómo podrías mejorar esta aplicación? Puedes dejar el link a tu proyecto en los comentarios. 😏😌

Existen muchos otros servicios de AWS que puedes utilizar con Lambda y Serverless Framework.


COMENTA!!!

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.