APIs Google Cloud : Vision et Translate

Beaucoup d’entre vous se sont demandé si mon projet avait été abandonné. En effet, cela fait près d’un an que je n’ai pas écris de nouvel article sur ce blog. Je vous rassure, le projet n’est pas enterré, mais j’ai eu d’autres contraintes durant ces derniers mois. Et j’ai mis de côté, dans un coin de ma tête, pas mal d’idées que je compte bien tester.

Certains m’ont demandé de parler un peu d’intelligence artificielle. Etant donné que c’est un domaine très vaste, très compliqué et que je n’ai pas les compétences pour parler de la théorie, je préfère, au travers de cet article, vous présenter une API qui utilise une intelligence artificielle (par machine learning) : Google Cloud Vision. Cette API permet d’analyser le contenu d’images pour en extraire diverses informations avec des résultats assez bluffants. Voici ses possibilités :

  • la reconnaissance d’image qui permet de déterminer le contenu pertinent présent dans l’image. En quelque sorte, “Que représente cette photo ?”
  • la détection de visages qui permet de repérer les visages présents dans une image, de localiser les différents points du visage (bouche, nez, yeux, …), son inclinaison ainsi que la reconnaissance de sentiments (la personne est-elle joyeuse, triste, surprise, …).
  • la reconnaissance de caractères qui permet d’extraire le contenu textuel d’une image et de déterminer la langue dans lequel il est écrit
  • la détection de contenus inappropriés : nudité, sexe, …, le SafeSearch de Google.

Je vais vous montrer, au travers d’un petit programme JAVA de reconnaissance d’images, comment utiliser cette API (ainsi que Translate qui va permettre de traduire les résultats renvoyés). Voici en vidéo le résultat que l’on va obtenir, la synthèse vocale en moins :



Tout d’abord, pour utiliser ces API, il faut avoir un compte Google Developer et demander de tester l’API Vision (“Try it free“) sur cette page (un numéro de carte bancaire est demandé pour vérifier votre identité). Dans le tableau de bord de Google Cloud Platform, il faut créer un projet puis, dans le gestionnaire d’API, il faut activer les APIs Cloud Vision et Translate. Enfin, dans l’onglet Identifiants, il faut créer une clé API de type Serveur. Mettez de côté le nom de votre projet et la clé API qui serviront dans le programme.

Voici les dépendances Maven nécessaires au fonctionnement de ce programme JAVA :

	<dependency>
		<groupId>com.google.apis</groupId>
		<artifactId>google-api-services-vision</artifactId>
		<version>v1-rev10-1.21.0</version>
	</dependency>
	<dependency>
		<groupId>com.google.apis</groupId>
		<artifactId>google-api-services-translate</artifactId>
		<version>v2-rev44-1.21.0</version>
	</dependency>
	<dependency>
		<artifactId>core-tool</artifactId>
		<groupId>org.openimaj.tools</groupId>
		<version>1.3.1</version>
	</dependency>

OpenIMAJ n’est nécessaire que pour l’affichage de l’image.

En quelques mots, le programme va aller chercher une image (en 800 par 600) aléatoirement sur le site unsplash.it, appeler le service de reconnaissance qui va renvoyer l’annotation la plus pertinente concernant l’image (en anglais), puis appeler le service de traduction pour récupérer l’annotation précédente en français, et ceci, indéfiniment.
Et maintenant, le programme commenté :

import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

import org.openimaj.image.DisplayUtilities;
import org.openimaj.image.ImageUtilities;
import org.openimaj.image.MBFImage;
import org.openimaj.image.colour.RGBColour;
import org.openimaj.image.typography.general.GeneralFont;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.translate.Translate;
import com.google.api.services.translate.Translate.Translations;
import com.google.api.services.translate.TranslateRequestInitializer;
import com.google.api.services.translate.model.TranslationsListResponse;
import com.google.api.services.vision.v1.Vision;
import com.google.api.services.vision.v1.VisionRequestInitializer;
import com.google.api.services.vision.v1.model.AnnotateImageRequest;
import com.google.api.services.vision.v1.model.AnnotateImageResponse;
import com.google.api.services.vision.v1.model.BatchAnnotateImagesRequest;
import com.google.api.services.vision.v1.model.BatchAnnotateImagesResponse;
import com.google.api.services.vision.v1.model.EntityAnnotation;
import com.google.api.services.vision.v1.model.Feature;
import com.google.api.services.vision.v1.model.Image;
import com.google.common.collect.ImmutableList;

public class GoogleCloudApiTest {

    private static final String APPLICATION_NAME = "NOM_DU_PROJET_GOOGLE";
    private static final String API_KEY = "CLE_API_SERVEUR_GENEREE";
    
    // Utilisation de l'annotation d'image seulement
    private static final String FEATURE_LABEL_DETECTION = "LABEL_DETECTION";

    /** Services */
    private Vision vision;
    private Translate translate;

    public static void main(String[] args) throws IOException, GeneralSecurityException, InterruptedException {
        new GoogleCloudApiTest();

    }

    public GoogleCloudApiTest() throws IOException, GeneralSecurityException, InterruptedException {

        // Initialisation des services
        vision = getVisionService();
        translate = getTranslateService();

        // Fenêtre d'affichage de l'image
        JFrame fenetre = DisplayUtilities.createNamedWindow("Test");
        MBFImage image = null;
        String reponseAnglaise = null;
        String reponseFrancaise = null;
        GeneralFont font = new GeneralFont( "Times New Roman", Font.TRUETYPE_FONT);
        while (true) {
            // Récupération d'une image aléatoire en 800*600
            image = ImageUtilities.readMBF(new URL("https://unsplash.it/800/600/?random"));
            // Affichage de l'image dans la fenêtre
            DisplayUtilities.display(ImageUtilities.createBufferedImage(image), fenetre);
            // Reconnaissance de l'image
            reponseAnglaise = reconnaitre(image);
            if (reponseAnglaise != null) {
                // Traduction de la réponse en français
                reponseFrancaise = traduire(reponseAnglaise);
            } else {
                reponseFrancaise = "Je ne sais pas ce que c'est !";
            }
            // Affichage de l'image et de la réponse
            image.drawText(reponseFrancaise, 30, 520, font, 25, RGBColour.YELLOW);
            DisplayUtilities.display(ImageUtilities.createBufferedImage(image), fenetre);
            // Temps d'attente pour voir le résultat
            Thread.sleep(1500);
        }
    }

    /** Récupération du service Vision */
    public static Vision getVisionService() throws IOException, GeneralSecurityException {
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleClientRequestInitializer visionKeyInitializer = new VisionRequestInitializer(API_KEY);
        return new Vision.Builder(GoogleNetHttpTransport.newTrustedTransport(), jsonFactory, null)
            .setApplicationName(APPLICATION_NAME).setGoogleClientRequestInitializer(visionKeyInitializer)
            .build();
    }

    /** Récupération du service Translate */
    public static Translate getTranslateService() throws IOException, GeneralSecurityException {
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleClientRequestInitializer translateKeyInitializer = new TranslateRequestInitializer(API_KEY);
        return new Translate.Builder(GoogleNetHttpTransport.newTrustedTransport(), jsonFactory, null)
            .setApplicationName(APPLICATION_NAME).setGoogleClientRequestInitializer(translateKeyInitializer)
            .build();
    }

    /** Méthode de reconnaissance d'une image. */
    public String reconnaitre(MBFImage mbfImage) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageUtilities.write(mbfImage, "jpg", baos);
        byte[] imageBytes = baos.toByteArray();
        // Création de la requête
        AnnotateImageRequest request = new AnnotateImageRequest()
        // Encodage de l'image en Base64
        .setImage(new Image().encodeContent(imageBytes))
        // Fonctionnalités souhaitées : ici "reconnaissance d'image" (d'autres sont disponibles : détection de visage, reconnaissance de caractères, ...)
        .setFeatures(ImmutableList.of(
            new Feature()
            .setType(FEATURE_LABEL_DETECTION)
            .setMaxResults(1)));
        Vision.Images.Annotate annotate = vision.images().annotate(new BatchAnnotateImagesRequest().setRequests(ImmutableList.of(request)));
        // Due to a bug: requests to Vision API containing large images fail when GZipped.
        annotate.setDisableGZipContent(true);

        // Appel du service de reconnaissance
        BatchAnnotateImagesResponse batchResponse = annotate.execute();
        if (batchResponse.getResponses() != null && !batchResponse.getResponses().isEmpty()) {
            AnnotateImageResponse response = batchResponse.getResponses().get(0);
            // Si des réponses sont disponibles, on traduit la première réponse
            if (response.getLabelAnnotations() != null && !response.getLabelAnnotations().isEmpty()) {
                EntityAnnotation entity = response.getLabelAnnotations().get(0/*new java.util.Random().nextInt(response.getLabelAnnotations().size())*/);
                return entity.getDescription();

            }
        }
        // Pas de réponse trouvée
        return null;
    }

    /** Méthode de traduction. */
    public String traduire(String motATraduire) throws IOException {
        // Appel du service de traduction en français
        List<String> listeATraduire = new ArrayList<String>();
        listeATraduire.add(motATraduire);
        Translations.List listeTraductions = translate.translations().list(listeATraduire, "fr");
        TranslationsListResponse listeResponse = listeTraductions.execute();
        if (listeResponse.getTranslations() != null && !listeResponse.getTranslations().isEmpty()) {
            // Si des traductions sont disponibles, on retourne la première en remplaçant l'apostrophe qui est encodé
            return listeResponse.getTranslations().get(0).getTranslatedText().replace("&#39;", "'");

        }
        return "Traduction impossible";
    }

}

Nicolas

Développeur JAVA, je suis passionné de robotique depuis quelques années, notamment tout ce qui concerne la partie programmation (vision artificielle, synthèse et reconnaissance vocale, intelligence artificielle, ...).

Un commentaire :

  1. Bonjour,
    Je suis totalement débutant en ce qui concerne le JAVA
    J’aimerais utiliser votre code dans le cadre d’un projet artistique. J’ai donc téléchargé le logiciel Eclipse mais je n’arrive pas à le faire fonctionner. Pourriez vous me donner plus d’informations sur la manière d’exécuter ce code? N’hésitez pas de m’envoyer un mail.
    Merci.

Répondre à Goldenberg Annuler la réponse

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *