Tutorial sul rilevamento facciale


Obiettivi

In questo esempio, utilizzerai l'API Google Vision per rilevare i volti in un'immagine. Per dimostrare che i volti sono stati rilevati correttamente, utilizza questi dati per tracciare un riquadro intorno a ciascuno.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

  • Cloud Vision

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Prima di iniziare

  1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  2. Nella console di Google Cloud Console, nella pagina del selettore dei progetti, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  3. Verifica che la fatturazione sia attivata per il tuo progetto Google Cloud. Scopri come verificare se la fatturazione è abilitata per un progetto.

  4. Attiva l'API Google Cloud Vision.

    Abilita l'API

  5. Nella console di Google Cloud Console, nella pagina del selettore dei progetti, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  6. Verifica che la fatturazione sia attivata per il tuo progetto Google Cloud. Scopri come verificare se la fatturazione è abilitata per un progetto.

  7. Attiva l'API Google Cloud Vision.

    Abilita l'API

  8. Configura l'ambiente per l'utilizzo delle Credenziali predefinite dell'applicazione.
  9. Configura attività e strumenti specifici per le diverse lingue:

    C#

    Java

    • Installa Java.
    • Riferimento API.
    • Scarica e installa il sistema di compilazione Apache Maven. Maven assicurerà che la libreria client dell'API di Google e le librerie client dell'API Vision siano installate al momento della creazione del progetto, perché le abbiamo incluse in pom.xml.

      <dependency>
        <groupId>com.google.apis</groupId>
        <artifactId>google-api-services-vision</artifactId>
        <version>v1-rev20230519-2.0.0</version>
      </dependency>
      <dependency>
        <groupId>com.google.auth</groupId>
        <artifactId>google-auth-library-oauth2-http</artifactId>
      </dependency>
      <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
      </dependency>
      <dependency>
        <groupId>com.google.http-client</groupId>
        <artifactId>google-http-client-jackson2</artifactId>
      </dependency>
      ...

    Node.js

    • Installare la libreria client di Google
    • Installare node.js.
    • Riferimento API.
    • Installare npm e node-canvas. Il codice campione include un elemento package.json per installare tutte le dipendenze utilizzando il comando: npm install. Tieni presente che la tela di nodo-canvas presenta ulteriori dipendenze che potrebbero essere necessarie per l'installazione. Per ulteriori informazioni, consulta il documento di installazione di nodi-canvas.

      {
        "name": "nodejs-docs-samples-vision",
        "private": true,
        "license": "Apache-2.0",
        "author": "Google LLC",
        "engines": {
          "node": ">=12.0.0"
        },
        "files": [
          "*.js"
        ],
        "scripts": {
          "test": "c8 mocha system-test --timeout 600000"
        },
        "dependencies": {
          "@google-cloud/vision": "^3.1.2",
          "natural": "^6.2.0",
          "pureimage": "^0.3.17",
          "redis": "^4.6.5",
          "yargs": "^17.7.1"
        },
        "devDependencies": {
          "@google-cloud/storage": "^6.9.3",
          "@types/uuid": "^9.0.1",
          "@types/yargs": "^17.0.22",
          "c8": "^7.13.0",
          "chai": "^4.3.7",
          "mocha": "^10.2.0",
          "uuid": "^9.0.0"
        }
      }
      

    PHP

    Python

    Ruby

Crea l'oggetto di servizio

Per accedere alle API di Google utilizzando gli SDK client ufficiali, crei un oggetto di servizio basato sul documento di rilevamento dell'API, che descrive l'API all'SDK. Dovrai recuperarlo dal servizio di rilevamento dell'API Vision, utilizzando le tue credenziali:

Java

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.vision.v1.Vision;
import com.google.api.services.vision.v1.VisionScopes;
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.FaceAnnotation;
import com.google.api.services.vision.v1.model.Feature;
import com.google.api.services.vision.v1.model.Image;
import com.google.api.services.vision.v1.model.Vertex;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.List;
import javax.imageio.ImageIO;
/** Connects to the Vision API using Application Default Credentials. */
public static Vision getVisionService() throws IOException, GeneralSecurityException {
  GoogleCredentials credential =
      GoogleCredentials.getApplicationDefault().createScoped(VisionScopes.all());
  JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
  return new Vision.Builder(
          GoogleNetHttpTransport.newTrustedTransport(),
          jsonFactory,
          new HttpCredentialsAdapter(credential))
      .setApplicationName(APPLICATION_NAME)
      .build();
}

Node.js

// By default, the client will authenticate using the service account file
// specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use
// the project specified by the GCLOUD_PROJECT environment variable. See
// https://googlecloudplatform.github.io/gcloud-node/#/docs/google-cloud/latest/guides/authentication
const vision = require('@google-cloud/vision');
// Creates a client
const client = new vision.ImageAnnotatorClient();

const fs = require('fs');

Python

from google.cloud import vision
from PIL import Image, ImageDraw
client = vision.ImageAnnotatorClient()

Invia una richiesta di rilevamento dei volti

Per creare una richiesta all'API Vision, consulta prima la documentazione relativa all'API. In questo caso, chiederai alla risorsa images di annotate la tua immagine. Una richiesta a questa API assume la forma di un oggetto con un elenco requests. Ogni voce di questo elenco contiene due informazioni:

  • I dati dell'immagine codificata in base64
  • Un elenco di funzionalità su cui vuoi annotare l'immagine.

Per questo esempio, dovrai solo richiedere l'annotazione FACE_DETECTION su un'immagine e restituire la parte pertinente della risposta:

Java

/** Gets up to {@code maxResults} faces for an image stored at {@code path}. */
public List<FaceAnnotation> detectFaces(Path path, int maxResults) throws IOException {
  byte[] data = Files.readAllBytes(path);

  AnnotateImageRequest request =
      new AnnotateImageRequest()
          .setImage(new Image().encodeContent(data))
          .setFeatures(
              ImmutableList.of(
                  new Feature().setType("FACE_DETECTION").setMaxResults(maxResults)));
  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);

  BatchAnnotateImagesResponse batchResponse = annotate.execute();
  assert batchResponse.getResponses().size() == 1;
  AnnotateImageResponse response = batchResponse.getResponses().get(0);
  if (response.getFaceAnnotations() == null) {
    throw new IOException(
        response.getError() != null
            ? response.getError().getMessage()
            : "Unknown error getting image annotations");
  }
  return response.getFaceAnnotations();
}

Node.js

async function detectFaces(inputFile) {
  // Make a call to the Vision API to detect the faces
  const request = {image: {source: {filename: inputFile}}};
  const results = await client.faceDetection(request);
  const faces = results[0].faceAnnotations;
  const numFaces = faces.length;
  console.log(`Found ${numFaces} face${numFaces === 1 ? '' : 's'}.`);
  return faces;
}

Python

def detect_face(face_file, max_results=4):
    """Uses the Vision API to detect faces in the given file.

    Args:
        face_file: A file-like object containing an image with faces.

    Returns:
        An array of Face objects with information about the picture.
    """
    client = vision.ImageAnnotatorClient()

    content = face_file.read()
    image = vision.Image(content=content)

    return client.face_detection(
        image=image, max_results=max_results
    ).face_annotations

Elabora la risposta

Complimenti, hai rilevato i volti nell'immagine! La risposta alla nostra richiesta di annotazione relativa al volto include una serie di metadati relativi ai volti rilevati, che includono le coordinate di un poligono che comprendono il volto. A questo punto, tuttavia, si tratta solo di un elenco di numeri. Usiamolo per verificare che hai effettivamente trovato i volti nella tua immagine. Disegnamo i poligoni su una copia dell'immagine, utilizzando le coordinate restituite dall'API Vision:

Java

/** Reads image {@code inputPath} and writes {@code outputPath} with {@code faces} outlined. */
private static void writeWithFaces(Path inputPath, Path outputPath, List<FaceAnnotation> faces)
    throws IOException {
  BufferedImage img = ImageIO.read(inputPath.toFile());
  annotateWithFaces(img, faces);
  ImageIO.write(img, "jpg", outputPath.toFile());
}

/** Annotates an image {@code img} with a polygon around each face in {@code faces}. */
public static void annotateWithFaces(BufferedImage img, List<FaceAnnotation> faces) {
  for (FaceAnnotation face : faces) {
    annotateWithFace(img, face);
  }
}

/** Annotates an image {@code img} with a polygon defined by {@code face}. */
private static void annotateWithFace(BufferedImage img, FaceAnnotation face) {
  Graphics2D gfx = img.createGraphics();
  Polygon poly = new Polygon();
  for (Vertex vertex : face.getFdBoundingPoly().getVertices()) {
    poly.addPoint(vertex.getX(), vertex.getY());
  }
  gfx.setStroke(new BasicStroke(5));
  gfx.setColor(new Color(0x00ff00));
  gfx.draw(poly);
}

Node.js

Per attingere alle immagini, utilizziamo la libreria node-canvas.

async function highlightFaces(inputFile, faces, outputFile, PImage) {
  // Open the original image
  const stream = fs.createReadStream(inputFile);
  let promise;
  if (inputFile.match(/\.jpg$/)) {
    promise = PImage.decodeJPEGFromStream(stream);
  } else if (inputFile.match(/\.png$/)) {
    promise = PImage.decodePNGFromStream(stream);
  } else {
    throw new Error(`Unknown filename extension ${inputFile}`);
  }
  const img = await promise;
  const context = img.getContext('2d');
  context.drawImage(img, 0, 0, img.width, img.height, 0, 0);

  // Now draw boxes around all the faces
  context.strokeStyle = 'rgba(0,255,0,0.8)';
  context.lineWidth = '5';

  faces.forEach(face => {
    context.beginPath();
    let origX = 0;
    let origY = 0;
    face.boundingPoly.vertices.forEach((bounds, i) => {
      if (i === 0) {
        origX = bounds.x;
        origY = bounds.y;
        context.moveTo(bounds.x, bounds.y);
      } else {
        context.lineTo(bounds.x, bounds.y);
      }
    });
    context.lineTo(origX, origY);
    context.stroke();
  });

  // Write the result to a file
  console.log(`Writing to file ${outputFile}`);
  const writeStream = fs.createWriteStream(outputFile);
  await PImage.encodePNGToStream(img, writeStream);
}

Python

def highlight_faces(image, faces, output_filename):
    """Draws a polygon around the faces, then saves to output_filename.

    Args:
      image: a file containing the image with the faces.
      faces: a list of faces found in the file. This should be in the format
          returned by the Vision API.
      output_filename: the name of the image file to be created, where the
          faces have polygons drawn around them.
    """
    im = Image.open(image)
    draw = ImageDraw.Draw(im)
    # Sepecify the font-family and the font-size
    for face in faces:
        box = [(vertex.x, vertex.y) for vertex in face.bounding_poly.vertices]
        draw.line(box + [box[0]], width=5, fill="#00ff00")
        # Place the confidence value/score of the detected faces above the
        # detection box in the output image
        draw.text(
            (
                (face.bounding_poly.vertices)[0].x,
                (face.bounding_poly.vertices)[0].y - 30,
            ),
            str(format(face.detection_confidence, ".3f")) + "%",
            fill="#FF0000",
        )
    im.save(output_filename)

Riassumendo

Java

/** Annotates an image using the Vision API. */
public static void main(String[] args) throws IOException, GeneralSecurityException {
  if (args.length != 2) {
    System.err.println("Usage:");
    System.err.printf(
        "\tjava %s inputImagePath outputImagePath\n", FaceDetectApp.class.getCanonicalName());
    System.exit(1);
  }
  Path inputPath = Paths.get(args[0]);
  Path outputPath = Paths.get(args[1]);
  if (!outputPath.toString().toLowerCase().endsWith(".jpg")) {
    System.err.println("outputImagePath must have the file extension 'jpg'.");
    System.exit(1);
  }

  FaceDetectApp app = new FaceDetectApp(getVisionService());
  List<FaceAnnotation> faces = app.detectFaces(inputPath, MAX_RESULTS);
  System.out.printf("Found %d face%s\n", faces.size(), faces.size() == 1 ? "" : "s");
  System.out.printf("Writing to file %s\n", outputPath);
  app.writeWithFaces(inputPath, outputPath, faces);
}
...

Per creare ed eseguire l'esempio, esegui i comandi seguenti dalla directory del codice campione:

mvn clean compile assembly:single
java -cp target/vision-face-detection-1.0-SNAPSHOT-jar-with-dependencies.jar \
    com.google.cloud.vision.samples.facedetect.FaceDetectApp \
    data/face.jpg \
    output.jpg

Node.js

async function main(inputFile, outputFile) {
  const PImage = require('pureimage');
  outputFile = outputFile || 'out.png';
  const faces = await detectFaces(inputFile);
  console.log('Highlighting...');
  await highlightFaces(inputFile, faces, outputFile, PImage);
  console.log('Finished!');
}

Per eseguire l'esempio, esegui questo comando dalla directory del codice campione:

node faceDetection resources/face.png

Python

def main(input_filename, output_filename, max_results):
    with open(input_filename, "rb") as image:
        faces = detect_face(image, max_results)
        print(
            "Found {} face{}".format(
                len(faces), "" if len(faces) == 1 else "s"
            )
        )

        print(f"Writing to file {output_filename}")
        # Reset the file pointer, so we can read the file again
        image.seek(0)
        highlight_faces(image, faces, output_filename)

Esegui la pulizia

Per evitare che al tuo Account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.