Loading...
MicrosoftServizi CognitiviUnity

Servizi Cognitivi e Unity

Come utilizzare i servizi cognitivi offerti da Azure in Unity

Riguardo i Cognitive Services

Una delle tipologie per rendere le applicazioni più intelligenti e smart sono sicuramente i Servizi Cognitivi offerti da Microsoft Azure. Le funzionalità offerte sono molteplici: da algoritmi per l’elaborazione di immagini e video alla ricerca di informazioni sul web. Il tutto a portata di mano utilizzando delle semplici chiamate REST e ricevendo come risposta un file JSON. Per approfondire l’importanza di queste funzionalità invito a leggere il seguente articolo.

Requisiti

I requisiti fondamentali sono i seguenti:

  • Unity (versione attuale 2018.3.4)
  • Visual Studio (versione attuale stabile 2017)
  • Sottoscrizione Azure

Descrizione esempio

Tra i diversi servizi offerti, di seguito sarà utilizzata la visione artificiale e in particolar modo l’analisi di un’immagine per descrivere cosa vi è rappresentato all’interno. Un esempio è presente qui.
Nel progetto è costruita una scena Unity al cui interno è presente un bottone che, presa un’immagine dagli asset, chiamerà l’API e riceverà la relativa risposta in formato JSON. Una volta rielaborato il contenuto sarà stampato a video.

Il codice sorgente dell’esempio è scaricabile da qui.

Tutorial

Configurazione Azure

Una volta aver effettuato l’accesso sul proprio portale di Azure è necessario creare una nuova risorsa del tipo Computer Vision

É importante notare che è possibile utilizzare la risorsa in modo gratuito se non si superano più di 5000 chiamate al servizio. Di questa risorsa sarà
necessario avere l’endpoint e una delle due chiavi di accesso fornite. Entrambe le informazioni sono presenti nell’overview della risorsa.

La configurazione di Azure è terminata qui. Niente di più facile!

Configurazione Unity

Una volta aver creato una nuova scena è sufficiente creare due nuovi oggetti al suo interno:

  • Un bottone (Button) per chiamare l’API
  • Un campo text (Response) per stampare il risultato

A questo punto è necessario creare uno script che gestisca l’invio della richiesta e la relativa risposta. Lo script in questione sarà CallApi.cs.
Leggendo la documentazione riguardo Computer Vision API è possibile notare che esistono diversi modi per inviare una richiesta di analisi. Quella utilizzata nell’esempio sarà la seguente:

    WWW www = new WWW(resourceURL, bytesImage, header);

La risposta della chiamata sarà catturata subito dopo nel seguente modo:

        yield return www;

Una breve descrizione a riguardo:

  • resourceURL è una stringa che rappresenta l’indirizzo dell’endpoint della risorsa precedentemente creata con parametri di richiesta. In particolare è necessario aggiungere la seguente sottostringa all’endpoint “v2.0/analyze?details=Landmarks&language=en
  • bytesImage è un Array di bytes che rappresenta l’immagine da analizzare.
  • header è l’intestazione della chiamata. E’ un dizionario in cui è specificata la chiave di accesso alla risorsa e il tipo di file inviato.

L’immagine da analizzare sarà la seguente

scaricabile da qui

É importante notare che la seguente immagine dovrà essere salvata nel seguente modo foto.jpeg e inserita in una cartella denominata StreamingAssets. Questo perché durante l’esecuzione dell’applicazione sarà possibile accedere a questa cartella e caricarne gli assets presenti. Per caricare la foto e trasformarla in sequenza di byte usiamo le seguenti righe di codice:

string imageName = Path.Combine(Application.streamingAssetsPath, "foto.jpeg"); 
byte[] bytesImage = UnityEngine.Windows.File.ReadAllBytes(imageName);

Per quanto riguarda invece header esso contiene tutte le informazioni per la connessione:

    var headers = new Dictionary<string, string>() {
        { "Ocp-Apim-Subscription-Key", visionKey },
        { "Content-Type", "application/octet-stream" }
    };

header è un dizionario con due chiavi.

  • Ocp-Apim-Subscription-key ha come valore visionKey che è una stringa contenente la chiave della risorsa creata su Azure.
  • Content-Type descrive il tipo di struttura del file inviato

Tutto è pronto per chiamare collegare la chiamata al bottone. A tal proposito creare una funzione nello script callApi.cs di nome Analyze e di tipo void.

public void Analyze()
{
    StartCoroutine(GetInfo());
}

Questa funzione esegue una Coroutine della funzione GetInfo() che è descritta nel seguente modo

IEnumerator GetInfo()
{
       imageName=Path.Combine(Application.streamingAssetsPath,"foto.jpeg"); 
byte[] bytesImage = UnityEngine.Windows.File.ReadAllBytes(imageName);

var headers = new Dictionary<string, string>() {
            { "Ocp-Apim-Subscription-Key", visionKey },
            { "Content-Type", "application/octet-stream" }
        };

WWW www = new WWW(resourceURL, bytesImage, headers);
yield return www;

}

www quindi è la variabile che contiene la risposta JSON del server. Se stampiamo il risultato usando Debug.Log(www.text) è possibile notare nella console di Unity la seguente risposta

{
  "categories": [
    {
      "name": "building_",
      "score": 0.640625,
      "detail": {
        "landmarks": [
          {
            "name": "Colosseum",
            "confidence": 0.9998661279678345
          }
        ]
      }
    },
    {
      "name": "building_corner",
      "score": 0.1875,
      "detail": {
        "landmarks": [
          {
            "name": "Colosseum",
            "confidence": 0.9998661279678345
          }
        ]
      }
    }
  ],
  "requestId": "641984d6-2032-4733-823e-fabde37ef15b",
  "metadata": {
    "width": 4827,
    "height": 2833,
    "format": "Jpeg"
  }
}

Breve descrizione ai file JSON

I file JSON sono un ottimo meccanismo per lo scambio di informazioni tra client e server. La struttura come si capisce è molto semplice e intuitiva. Per trasformare il JSON in una forma visiva più gradevole come quello presentato è possibile utilizzare diversi tool tra cui codebeautify.

La struttura di un file JSON la seguente:

chiave: valore

É importante notare che la risposta può essere di diversi tipi come stringa, numero, Array, Array di oggetti etc.

Nell’esempio è possibile identificare tre chiavi

  • categories
  • requestiId
  • metadata

La grande potenza del .NET è il fatto che offre una libreria per gestire i file JSON come oggetti. La libreria in questione è JSON.NET – Newtonsoft ed possibile scaricarla per Unity da qui.

L’obiettivo è quello di creare una classe C# che simuli la struttura del file JSON. A tal proposito è presente un tool che svolge questo compito: json2csharp.

Creiamo quindi la classe JSONApi che mappa il file JSON in un oggetto.

public class JSONApi : MonoBehaviour
{
    public List<Category> categories { get; set; }
    public string requestId { get; set; }
    public Metadata metadata { get; set; }

    public class Landmark
    {
        public string name { get; set; }
        public double confidence { get; set; }
    }

    public class Detail
    {
        public List<Landmark> landmarks { get; set; }
    }

    public class Category
    {
        public string name { get; set; }
        public double score { get; set; }
        public Detail detail { get; set; }
    }

    public class Metadata
    {
        public int width { get; set; }
        public int height { get; set; }
        public string format { get; set; }
    }

}

JSON.NET offre delle funzioni per deserializzare un file JSON nell’oggetto creato. La funzione in considerazione è:

JSONApi answer=JsonConvert.DeserializeObject<JSONApi>(www.text);

A questo punto possiamo navigare all’interno dell’oggetto answer per pescare l’informazione interessata. In particolar modo è interessante il campo name di landmarks della categoria rilevata con maggiore score.

La descrizione del problema contiene già la risposta. Per ottenere questa informazione è sufficiente navigare l’oggetto nel seguente modo:

answer.categories[0].detail.landmarks[0].name.ToString()

A questo punto è possibile stampare a video il risultato.

Note finali

É importante notare che i file JSON differiscono in base al servizio richiesto e come tale è necessario creare diverse classi di mappatura per i diversi servizi proposti.

L’esempio descritto è un esempio a puro scopo illustrativo. Come tale infatti non tiene conto di problematiche inerenti a richieste diverse ma dello stesso servizio.

KEEP CALM AND PUT IT ON AZURE!