blexin

Consulenza
Sviluppo e
Formazione IT


Blog

Evolvi la tua azienda

Come utilizzare in Typescript le librerie AI di Azure Cognitive Services, Amazon Rekognition e Google Vision

Riconoscimento delle immagini nel Cloud

Martedì 5 Febbraio 2019

Durante una delle academy Azure tenute per Overnet Education, il nostro partner per la formazione, abbiamo affrontato il tema del riconoscimento delle immagini, che ha riscosso molto interesse tra gli studenti. Ho pensato quindi potesse essere interessante un confronto su questo tema tra i vari Cloud provider, utilizzando Node.js e Typescript.

Introduzione

Tutti i principali fornitori di servizi cloud offrono strumenti per l'analisi di immagini basati sull'intelligenza artificiale. È sufficiente fornire un'immagine (anche un video nel caso di Amazon Rekognition) e il servizio può identificare oggetti, persone, testo, scene e attività, oltre che rilevare eventuali contenuti non appropriati.

In questo articolo utilizzeremo gli SDK Javascript di Amazon, Azure e Google in un progetto Node scritto in Typescript, che analizzerà immagini caricate localmente. Ciascuno dei cloud si differenzia per la filosofia di utilizzo, i servizi offerti e per il tipo di output restituito. Per questo motivo ci limiteremo ad un'analisi che è possibile svolgere su tutti e tre gli SDK.

Per Amazon, il prerequisito principale è quello di creare un account AWS e un utente IAM. Una guida completa che illustra l'intera procedura è disponibile a questo link. Se non avete ancora creato un account sul cloud Amazon, potete accedere mediante il Piano gratuito di AWS.

Per Azure, la procedura per creare un account gratuito di Computer Vision è disponibile a questo indirizzo. Se non avete ancora creato un account sul cloud Azure, potete accedere mediante un Piano iniziale gratuito.

La documentazione per iniziare a lavorare con Google Vision sul cloud Google è disponibile a questo indirizzo

Setup del progetto Node.js

Accedere ai servizi di AI vuol dire eseguire chiamate a servizi REST. Lo faremo utilizzando Node.js, la piattaforma naturale per gestire questo tipo di problema. Creiamo dunque un progetto Node.js da zero; ad esso aggiungeremo, successivamente, tutte le dipendenze necessarie.

I prerequisiti da installare sono i seguenti:

  1. Node.js da https://nodejs.org (nella versione LTS)
  2. Typescript con il comando npm install -g typescript

Per fare questo, apriamo la linea di comando in una cartella vuota e digitiamo in essa il comando npm init. Rispondiamo alle domande del wizard e, alla fine del processo, troveremo nella cartella il file package.json contenente tutte le impostazioni necessarie. In genere, nella stessa cartella, eseguo il comando git init per inizializzare git e git remote add per configurare la repository remota del codice.

Occorre ora modificare il file package.json. Anzitutto, nella sezione scripts, inseriamo una sezione “build”: “tsc” per indicare che useremo il compilatore typescript nella fase di build.

Ci occorrono poi alcune dipendenze per la fase di sviluppo:

  • npm install @types/node –save-dev ossia le definizioni dei tipi per Typescript di Nod;.
  • npm install ts-node --save-dev che useremo per compilare ed eseguire il codice in node;
  • npm install typescript --save-dev;
  • npm install tslint --save-dev ossia un linter per il linguaggio Typescript.

Poiché stiamo usando Typescript, è necessario aggiungere un altro file di configurazione chiamato tsconfig.json nella root directory. Ci limitiamo ad usare solo lo stretto necessario, ossia le opzioni di compilazione e il percorso dei file sorgenti (con estensione .ts).

Aggiungiamo ora la configurazione necessaria per poter eseguire il debug del codice all’interno di Visual Studio Code. È possibile creare manualmente questa configurazione, aggiungendo un folder .vscode e un file chiamato launch.json a questa cartella

In questo file, runtimeArgs serve ad indicare a Node che ts-node deve essere caricato prima di eseguire effettivamente il codice.

Verifichiamo che la configurazione funzioni, scrivendo del codice in index.ts e lanciando in VS Code una sessione di debug

Terminiamo la configurazione con l’installazione di:

  • npm install aws-sdk –save, grazie alla quale saremo in grado di usare le funzioni di Rekognition;
  • npm install @google-cloud/vision, per l'SDk di Google;
  • npm install --save request request-promise, per eseguire direttamente chiamate REST (nel caso di Azure);
  • npm install --save-dev @types/request, per avere in Typescript i tipi di request;
  • npm install --save-dev @types/request-promise, per avere in Typescript i tipi di request-promise.

Configurazione ed utilizzo di Azure Computer Vision

Dal portale di Azure è possibile estrarre l'endpoint del servizio e le due chiavi segrete. Per comodità d'uso le possiamo inserire in un file di configurazione

export let config = {
    azureVisionConfig: {
        azureEndPoint: "https://westeurope.api.cognitive.microsoft.com/vision/v1.0/",
        azureKey1: "yourkey1",
        azureKey2: "yourkey2",
    },
};

Dalla documentazione ufficiale delle API, risulta che le chiamate http da eseguire sono del tipo

Serve quindi una maniera efficiente per costruire la querystring con la quale interroghiamo il servizio. Creiamo un'interfaccia per costruire i parametri della request

La documentazione mostra anche il json restituito da una request. Possiamo convertire questo risultato in un'interfaccia per tipizzare la response.

export interface ICategory {
    name: string;
    score: number;
}
export interface IImageType {
    clipArtType: number;
    lineDrawingType: number;
}
export interface ITag {
    name: string;
    confidence: number;
}
export interface ICaption {
    text: string;
    confidence: number;
}
export interface IDescription {
    tags: string[];
    captions: ICaption[];
}
export interface IMetadata {
    width: number;
    height: number;
    format: string;
}
export interface IAzureCognitiveServiceResponse {
    categories: ICategory[];
    imageType: IImageType;
    tags: ITag[];
    description: IDescription;
    faces: any[];
    requestId: string;
    metadata: IMetadata;
}

Creiamo quindi un unico metodo in una classe che prenderà in ingresso il percorso di un file locale e un oggetto di tipo AzureRequestParameters, che restituirà una IAzureCognitiveServiceResponse. La libreria request-promise ci consente di creare un metodo async che restituisca una Promise tipizzata.

Un esempio semplice di chiamata è il seguente:

const responseAzure1 = helperAzure.AnalyzeImage("../images/laurie.jpg", new AzureRequestParameters({
    language: "en",
    visualFeatures: ["Faces", "ImageType"],
})).then((data: IAzureCognitiveServiceResponse) => {
    data.faces.forEach( (face) => {
        console.log(face);
    });
});

L'immagine passata in input è la seguente:

Ecco il risultato dell'analisi:

Basta modificare leggermente la richiesta in maniera tale da provare ad identificare una celebrità nell'immagine:

La risposta è che con un livello di confidenza superiore al 99%, nella foto appare l'attore Hugh Laurie.

Consideriamo ad esempio la seguente immagine:

I seguenti parametri di ricerca forniscono in output una descrizione degli elementi nella foto.

Configurazione ed utilizzo di Amazon Rekognition

Dal portale di Amazon è possibile estrarre la Access Key e la Secret Access Key che verranno usate per convalidare le chiamate alla libreria Rekognition. La documentazione completa è disponibile al seguente indirizzo.

È possibile installare sul proprio computer queste credenziali in un file disponibile globalmente per tutti i nostri progetti, oppure creare nel singolo progetto un file contenente le chiavi. Per quest’articolo useremo quest'ultima opzione.

Creiamo una classe Typescript (chiamata AWSRekognition) che contenga il codice di autenticazione (attenzione ad indicare l’IdentityPoolID come richiesto dall documentazione di Rekognition) e tutti i metodi per analizzare le immagini.

import * as AWS from "aws-sdk";

export class AWSRekognition {

    private rekognition: AWS.Rekognition;

    constructor() {
        AWS.config.region = "eu-west-1"; // Region
        AWS.config.credentials = new AWS.CognitoIdentityCredentials({
          IdentityPoolId: "eu-west-YOURPOOLID",
        });
        AWS.config.loadFromPath("./src/configuration/credentials.json");
        this.rekognition = new AWS.Rekognition();
    }
}

Il primo metodo che aggiungiamo a questa classe analizza un file immagine locale e prova ad estrarre, mediante Rekognition, informazioni sui volti eventualmente contenuti in essa. Anzitutto leggiamo il file e convertiamolo in una stringa in base64. Dopodiché convertiamo questa stringa in un Buffer, ossia un array di interi. Rekognition accetta questo formato per le immagini in input. Creiamo una funzione helper che esegue questa conversione.

import * as fs from "fs";

export function readImage(path: string): Buffer {
   const fileData = fs.readFileSync(path).toString("base64");
   return new Buffer(fileData, "base64");
}

Il metodo che utilizza Rekognition è invece il seguente:

public DetectFacesOnLocalImage(filePath: string):
            Promise<PromiseResult<AWS.Rekognition.DetectFacesResponse, AWS.AWSError>> {
            const buffer = readImage(filePath);
            const params = {
                Attributes: [
                    "ALL",
                ],
                Image: {
                Bytes: buffer,
                },
            };
            return this.rekognition.detectFaces(params).promise();
}

Il metodo restituisce una Promise tipizzata che possiamo attendere in index.ts. Le informazioni passate in output sono estremamente dettagliate. L'età massima stimata è uguale a 52 anni.

import {AWSRekognition} from "./aws-rekognition";

const helperAWS: AWSRekognition = new AWSRekognition();
const response = helperAWS.DetectFacesOnLocalImage("./images/laurie.jpg").then(
    (data) => {
        console.log(data.FaceDetails[0].AgeRange.High);
}).catch((err) => {
    console.error(err);
});

Lo stesso metodo può essere semplificato se l'immagine è stata caricata in un bucket S3. L'immagine mostra nella console web AWS il contenuto di un bucket chiamato immaginisalvatore. Al metodo detectFaces basta passare il nome del bucket e la chiave dell'immagine.

public DetectFacesOnS3Images(bucketName: string, key: string):
       Promise<PromiseResult<AWS.Rekognition.DetectFacesResponse, AWS.AWSError>> {
    const params = {
        Attributes: [
            "ALL",
        ],
        Image: {
            S3Object: {
            Bucket: bucketName,
            Name: key,
            },
        },
    };
    return this.rekognition.detectFaces(params).promise();
}

Possiamo provare a chiedere a Rekognition se riesce ad associare il volto a una celebrità

public DetectCelebritiesOnS3Image(bucketName: string, key: string) {
    const params = {
        Image: {
            S3Object: {
                Bucket: bucketName,
                Name: key,
            },
        },
    };
    return this.rekognition.recognizeCelebrities(params).promise();
const response = helperAWS.DetectCelebritiesOnS3Image("immaginisalvatore", "laurie.jpg").then(
    (data) => {
        console.log(data.CelebrityFaces[0].Name + " " + data.CelebrityFaces[0].MatchConfidence);
}).catch((err) => {
    console.error(err);
});

La risposta è molto chiara: l'immagine mostra l'attore Hugh Laurie con una confidenza del 99,99%.

Ecco invece il metodo che possiamo invocare per analizzare l'immagine della tavola imbandita per cena:

public DetectLabelsOnS3Image(bucketName: string, key: string):
    Promise<PromiseResult<AWS.Rekognition.DetectLabelsResponse, AWS.AWSError>> {
    const params = {
        Image: {
            S3Object: {
                Bucket: bucketName,
                Name: key,
            },
        },
    };
    return this.rekognition.detectLabels(params).promise();
}

Gli elementi identificati sono i seguenti:

const response = helperAWS.DetectLabelsOnS3Image("immaginisalvatore", "cena.jpg").then(
    (data) => {
        data.Labels.forEach((label) => {
            console.log(label.Name + ": " + label.Confidence);
        });
}).catch((err) => {
    console.error(err);
});

Configurazione ed utilizzo di Google Vision

Le API di Google Vision richiedono:

  • la creazione di un progetto Google Cloud;
  • l'abilitazione nel progetto delle API di Vision;
  • la creazione di un file json contenente le chiavi di sicurezza e il projectId.

Purtroppo, non esistono ancora i file per la definizione dei tipi nell'SDK che abbiamo installato in fase di setup. La classe che utilizza le API è la seguente:

Il codice per analizzare l'immagine di Hugh Laurie è il seguente:

Il risultato è il seguente (compreso il dettaglio sugli elementi del volto):

La chiamata per analizzare la tavola per la cena è:

E questo è un dettaglio dell'output.

Conclusioni

Abbiamo testato codice Typescript su Node.js che utilizza gli SDK Javascript di AI forniti dai principali Cloud provider. La semplicità d'uso e la ricchezza di metodi e informazioni restituite lo rende uno strumento dalle potenzialità infinite. Ad esempio, in un applicativo client Angular (o React) o in uno scenario serverless, in cui l'esecuzione di una funzione nel cloud venga triggerata dal caricamento di un'immagine su uno storage, e tale funzione utilizzi l'AI per moderare l'immagine stessa. Ricordiamo, infine, che altrettanto ricca è l'offerta cloud per analizzare testi nelle immagini.

Trovate il codice qui: https://github.com/sorrentmutie/image-recognition

Autore

Servizi

Evolvi la tua azienda