API de Vision

Objetivos

En esta muestra, se usará la API de Vision de Google para detectar rostros en una imagen. A fin de comprobar que los rostros se detectaron de forma correcta, luego usarás esos datos para dibujar un recuadro alrededor de cada rostro.

Costos

En este instructivo, se usa el siguiente componente facturable de Cloud Platform:

  • API de Google Cloud Vision

Usa la calculadora de precios para generar una estimación de los costos según el uso previsto. Los usuarios nuevos de Cloud Platform pueden cumplir los requisitos para una prueba gratuita.

Antes de comenzar

  1. Accede a tu Cuenta de Google.

    Si todavía no tienes una cuenta, regístrate para obtener una nueva.

  2. En la página de selección de proyectos de Cloud Console, selecciona o crea un proyecto de Cloud.

    Ir a la página Selector de proyectos

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  4. Habilita la API Google Cloud Vision.

    Habilita la API

  5. Configura el entorno para usar las credenciales predeterminadas de la aplicación.
  6. Configura tareas y herramientas específicas del lenguaje:

    C#

    Java

    Node.js

    PHP

    Python

    Ruby

Crea el objeto de servicio

Para acceder a las API de Google con los SDK cliente oficiales, crea un objeto de servicio basado en el documento de descubrimiento de las API, que describe la API al SDK. Tendrás que recuperarlo desde el servicio de descubrimiento de la API de Vision con tus credenciales:

C#


using Google.Cloud.Vision.V1;
using System;
using System.Linq;
var client = ImageAnnotatorClient.Create();

Java

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
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 = JacksonFactory.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');

PHP

use Google\Cloud\Vision\V1\ImageAnnotatorClient;
$imageAnnotator = new ImageAnnotatorClient();

Python

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

Ruby

require "google/cloud/vision"
image_annotator = Google::Cloud::Vision.image_annotator

Envía una solicitud de detección de rostro

Si quieres crear una solicitud a la API de Vision, primero debes consultar la documentación de la API. En este caso, le pedirás al recurso images que haga una operación annotate en tu imagen. Una solicitud a esta API adopta la forma de un objeto con una lista de requests. Todos los elementos de esta lista incluyen dos tipos de información:

  • Los datos de la imagen codificados en base64
  • Una lista de características para las que querrías tener anotaciones respecto de la imagen

Para este ejemplo, tan solo solicitarás la anotación de FACE_DETECTION en una imagen, y se mostrarán las partes pertinentes de la respuesta:

C#

var response = client.DetectFaces(Image.FromFile(args[0]));

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

PHP

# annotate the image
// $path = 'path/to/your/image.jpg'
$image = file_get_contents($path);
$response = $imageAnnotator->faceDetection($image);
$faces = $response->getFaceAnnotations();

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 = types.Image(content=content)

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

Ruby

response = image_annotator.face_detection image: path_to_image_file

Procesa la respuesta

Felicitaciones. Detectaste los rostros en tu imagen. La respuesta a nuestra solicitud de anotación de rostros contiene un conjunto de metadatos sobre los rostros detectados, que incluyen coordenadas de un polígono que abarca el rostro. Sin embargo, en este punto, solo es una lista de números. Los usaremos para confirmar que efectivamente encontraste los rostros en tu imagen. Dibujaremos polígonos en una copia de la imagen mediante las coordenadas que muestra la API de Vision:

C#

using (var image = System.Drawing.Image.FromFile(args[0]))
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(image))
{
    var cyanPen = new System.Drawing.Pen(System.Drawing.Color.Cyan, 3);
    foreach (var annotation in response)
    {
        g.DrawPolygon(cyanPen, annotation.BoundingPoly.Vertices.Select(
            (vertex) => new System.Drawing.Point(vertex.X, vertex.Y)).ToArray());
        // ...
    }
    // ...
}

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

Para dibujar en imágenes, usamos la biblioteca 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);
}

PHP

Usamos la extensión de GD para dibujar en imágenes.

# draw box around faces
if ($faces && $outFile) {
    $imageCreateFunc = [
        'png' => 'imagecreatefrompng',
        'gd' => 'imagecreatefromgd',
        'gif' => 'imagecreatefromgif',
        'jpg' => 'imagecreatefromjpeg',
        'jpeg' => 'imagecreatefromjpeg',
    ];
    $imageWriteFunc = [
        'png' => 'imagepng',
        'gd' => 'imagegd',
        'gif' => 'imagegif',
        'jpg' => 'imagejpeg',
        'jpeg' => 'imagejpeg',
    ];

    copy($path, $outFile);
    $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
    if (!array_key_exists($ext, $imageCreateFunc)) {
        throw new \Exception('Unsupported image extension');
    }
    $outputImage = call_user_func($imageCreateFunc[$ext], $outFile);

    foreach ($faces as $face) {
        $vertices = $face->getBoundingPoly()->getVertices();
        if ($vertices) {
            $x1 = $vertices[0]->getX();
            $y1 = $vertices[0]->getY();
            $x2 = $vertices[2]->getX();
            $y2 = $vertices[2]->getY();
            imagerectangle($outputImage, $x1, $y1, $x2, $y2, 0x00ff00);
        }
    }

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)

Ruby

Usamos la gema rmagick para dibujar en imágenes.

require "rmagick"
  response.responses.each do |res|
    res.face_annotations.each do |annotation|
      puts "Face bounds:"
      annotation.bounding_poly.vertices.each do |vertex|
        puts "(#{vertex.x}, #{vertex.y})"
      end

      x1 = annotation.bounding_poly.vertices[0].x.to_i
      y1 = annotation.bounding_poly.vertices[0].y.to_i
      x2 = annotation.bounding_poly.vertices[2].x.to_i
      y2 = annotation.bounding_poly.vertices[2].y.to_i

      photo = Magick::Image.read(path_to_image_file).first
      draw = Magick::Draw.new
      draw.stroke = "green"
      draw.stroke_width 5
      draw.fill_opacity 0
      draw.rectangle x1, y1, x2, y2
      draw.draw photo

      photo.write path_to_output_file
    end
  end

  puts "Output file: #{path_to_output_file}"

Combina todas las opciones

C#

static readonly string s_usage = @"dotnet run image-file

Use the Google Cloud Vision API to detect faces in the image.
Writes an output file called image-file.faces.
";
public static void Main(string[] args)
{
    if (args.Length < 1)
    {
        Console.WriteLine(s_usage);
        return;
    }

    var client = ImageAnnotatorClient.Create();
    var response = client.DetectFaces(Image.FromFile(args[0]));

    int numberOfFacesFound = 0;
    using (var image = System.Drawing.Image.FromFile(args[0]))
    using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(image))
    {
        var cyanPen = new System.Drawing.Pen(System.Drawing.Color.Cyan, 3);
        foreach (var annotation in response)
        {
            g.DrawPolygon(cyanPen, annotation.BoundingPoly.Vertices.Select(
                (vertex) => new System.Drawing.Point(vertex.X, vertex.Y)).ToArray());
            // ...
        }
        // ...
    }
    // ...
}

Para crear la muestra, abre el archivo de la solución dotnet-doc-samples/vision/api/Vision.sln en Visual Studio y compila la solución.

Para ejecutar la muestra, usa lo siguiente:

C:\...\> cd dotnet-docs-samples\vision\api\DetectFaces\bin\Debug
C:\...\bin\Debug> DetectFaces ..\..\..\VisionTest\data\face.png

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

Para compilar y ejecutar la muestra, ejecuta los siguientes comandos desde el directorio del código de muestra:

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

Para ejecutar la muestra, ejecuta el siguiente comando desde el directorio del código de muestra:

node faceDetection resources/face.png

PHP

call_user_func($imageWriteFunc[$ext], $outputImage, $outFile);
printf('Output image written to %s' . PHP_EOL, $outFile);

Para ejecutar la muestra, ejecuta el siguiente comando desde el directorio del código de muestra:

composer install
php vision.php face test/data/face.png output-image.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('Writing to file {}'.format(output_filename))
        # Reset the file pointer, so we can read the file again
        image.seek(0)
        highlight_faces(image, faces, output_filename)

Ruby

if $PROGRAM_NAME == __FILE__
  if ARGV.size == 2
    draw_box_around_faces path_to_image_file:  ARGV.shift,
                          path_to_output_file: ARGV.shift
  else
    puts <<~USAGE
      Usage: ruby draw_box_around_faces.rb [input-file] [output-file]
       Example:
        ruby draw_box_around_faces.rb images/face.png output-image.png
    USAGE
  end
end

Para ejecutar la muestra, ejecuta el siguiente comando desde el directorio del código de muestra:

bundle install
bundle exec ruby draw_box_around_faces.rb resources/face_no_surprise.png output-image.png

Imagen de entrada Imagen de salida

Realiza una limpieza

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform por los recursos que usaste en este instructivo:

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a la página Administrar recursos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar .
  3. En el cuadro de diálogo, escribe el ID del proyecto y haz clic en Cerrar para borrar el proyecto.