Anleitung zur Gesichtserkennung

Dienstobjekt erstellen

Für den Zugriff auf Google APIs mithilfe der offiziellen Client-SDKs erstellen Sie ein Dienstobjekt, das auf dem Discovery-Dokument der API basiert. Dieses Dokument enthält eine API-Beschreibung für das SDK. Sie müssen es unter Angabe Ihrer Anmeldedaten vom Discovery-Dienst der Vision API abrufen:

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()

Gesichtserkennungsanfrage senden

Lesen Sie vor dem Erstellen einer Anfrage an die Vision API die API-Dokumentation. In diesem Beispiel fordern Sie die Ressource images auf, Ihr Bild zu annotieren (annotate). Die Anfrage an diese API erfolgt in Form eines Objekts mit der Liste requests. Jedes Element der Liste enthält zwei Informationen:

  • Die Bilddaten mit Base64-Codierung
  • Eine Liste der Elemente, die Sie zu diesem Bild annotieren möchten

Für dieses Beispiel fordern Sie einfach die Annotation FACE_DETECTION für ein Bild an und geben den relevanten Teil der Antwort zurück:

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

Antwort verarbeiten

Herzlichen Glückwunsch, Sie haben die Gesichter im Bild erkannt. Die Antwort auf unsere Anfrage zum Annotieren von Gesichtern umfasst eine Reihe von Metadaten zu den erkannten Gesichtern. Hierzu zählen unter anderem die Koordinaten eines Polygons um jedes Gesicht. Aktuell liegt jedoch nur eine Liste mit Zahlen vor. Bestätigen Sie damit, dass Sie die Gesichter im Bild erkannt haben. Zeichnen Sie mit den von der Vision API zurückgegebenen Koordinaten Polygone auf eine Kopie des Bildes:

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

Sie verwenden die Bibliothek „node-canvas“, um auf Bildern zu zeichnen.

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)

Zusammenfassung

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);
}
...

Erstellen und führen Sie das Beispiel mit dem folgenden Befehl aus dem Verzeichnis mit dem Beispielcode aus:

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!');
}

Führen Sie das Beispiel mit dem folgenden Befehl aus dem Verzeichnis mit dem Beispielcode aus:

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)