얼굴 인식 가이드

목표

이 샘플에서는 Google Cloud Vision API를 사용하여 이미지의 얼굴을 감지합니다. 그런 다음 해당 데이터를 사용하여 각 얼굴 주위에 상자를 그려서 얼굴이 정확히 감지되었는지 검증합니다.

비용

이 가이드는 비용이 청구될 수 있는 다음과 같은 Cloud Platform 구성요소를 사용합니다.

  • Google Cloud Vision API

가격 계산기를 사용하여 예상 사용량을 기준으로 예상 비용을 산출합니다. Cloud Platform 신규 사용자는 무료 평가판을 이용할 수 있습니다.

시작하기 전에

  1. Google 계정에 로그인합니다.

    아직 계정이 없으면 새 계정을 등록하세요.

  2. Google Cloud Platform 프로젝트를 선택하거나 만듭니다.

    리소스 관리 페이지로 이동

  3. Google Cloud Platform 프로젝트에 결제가 사용 설정되어 있는지 확인하세요.

    결제 사용 설정 방법 알아보기

  4. Google Cloud Vision API를 사용 설정합니다.

    API 사용 설정

  5. 애플리케이션 기본 사용자 인증 정보를 사용할 수 있는 환경을 설정합니다.
  6. 다음과 같이 언어별로 작업을 수행하고 도구를 설정합니다.

    C#

    자바

    • 자바 설치
    • API 참조
    • Apache Maven 빌드 시스템을 다운로드하여 설치합니다. pom.xmlGoogle API 클라이언트 라이브러리와 Vision API 클라이언트 라이브러리가 포함되어 있기 때문에 Maven은 프로젝트가 빌드될 때 이러한 라이브러리가 설치되도록 합니다.

      <dependency>
        <groupId>com.google.apis</groupId>
        <artifactId>google-api-services-vision</artifactId>
        <version>v1-rev20190125-1.28.0</version>
      </dependency>
      <dependency>
        <groupId>com.google.api-client</groupId>
        <artifactId>google-api-client</artifactId>
        <version>1.28.0</version>
        <exclusions>
          <exclusion> <!-- exclude an old version of Guava -->
            <groupId>com.google.guava</groupId>
            <artifactId>guava-jdk5</artifactId>
          </exclusion>
        </exclusions>
      </dependency>
      <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>25.1-android</version>
      </dependency>
      ...

    Node.js

    • Google 클라이언트 라이브러리를 설치합니다.
    • node.js를 설치합니다.
    • API 참조
    • npmnode-canvas를 설치합니다. 샘플 코드에는 npm install 명령어를 사용하여 모든 종속 항목을 설치하는 package.json이 포함되어 있습니다. node-canvas의 추가 종속 항목을 설치해야 할 수 있습니다. 자세한 내용은 node-canvas 설치 문서를 참조하세요.

      {
        "name": "nodejs-docs-samples-vision",
        "private": true,
        "license": "Apache-2.0",
        "author": "Google Inc.",
        "engines": {
          "node": ">=8"
        },
        "files": [
          "*.js"
        ],
        "scripts": {
          "test": "mocha system-test --timeout 600000"
        },
        "dependencies": {
          "@google-cloud/automl": "^0.1.3",
          "@google-cloud/vision": "^0.24.0",
          "mathjs": "^5.0.4",
          "natural": "^0.6.1",
          "redis": "^2.8.0",
          "yargs": "^13.0.0",
          "canvas": "^2.0.0"
        },
        "devDependencies": {
          "@google-cloud/storage": "^2.0.0",
          "chai": "^4.2.0",
          "execa": "^1.0.0",
          "mocha": "^6.0.0",
          "uuid": "^3.2.1"
        }
      }
      

    PHP

    Python

    Ruby

서비스 객체 만들기

공식 클라이언트 SDK를 사용하여 Google API에 액세스하려면 API를 SDK에 설명하는 API의 검색 문서를 기반으로 서비스 객체를 만듭니다. 검색 문서는 Vision API의 검색 서비스에서 사용자 인증 정보를 사용하여 가져와야 합니다.

C#


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

자바

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
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.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 {
  GoogleCredential credential =
      GoogleCredential.getApplicationDefault().createScoped(VisionScopes.all());
  JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
  return new Vision.Builder(GoogleNetHttpTransport.newTrustedTransport(), jsonFactory, 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::ImageAnnotator.new

얼굴 감지 요청 보내기

Vision API에 대한 요청을 생성하려면 우선 API 문서를 참조하세요. 여기에서는 images 리소스에 이미지를 annotate하도록 요청합니다. 이 API에 대한 요청은 requests 목록을 갖는 객체의 형태입니다. 이 목록의 각 항목은 두 가지 정보를 포함합니다.

  • base64로 인코딩된 이미지 데이터
  • 해당 이미지에서 주석을 달고 싶은 특징 목록

이 예에서는 이미지 하나에 대한 FACE_DETECTION 주석만 요청하고 응답의 관련 부분만 반환합니다.

C#

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

자바

/**
 * 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

응답 처리

수고하셨습니다. 이미지에서 얼굴이 감지되었습니다. 얼굴 주석 요청의 응답은 얼굴을 둘러싸는 다각형의 좌표를 비롯하여 감지된 얼굴의 여러 가지 메타데이터를 포함합니다. 그러나 아직까지는 숫자의 목록일 뿐입니다. 이 데이터를 사용하여 이미지에서 실제로 얼굴이 발견되었는지 확인해 보려고 합니다. Vision API가 반환한 좌표를 사용하여 이미지 사본에 다각형을 그리겠습니다.

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

자바

/**
 * 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

node-canvas 라이브러리를 사용하여 이미지에 그림을 그립니다.

async function highlightFaces(inputFile, faces, outputFile, Canvas) {
  const {promisify} = require('util');
  const readFile = promisify(fs.readFile);
  const image = await readFile(inputFile);
  const Image = Canvas.Image;
  // Open the original image into a canvas
  const img = new Image();
  img.src = image;
  const canvas = new Canvas.Canvas(img.width, img.height);
  const context = canvas.getContext('2d');
  context.drawImage(img, 0, 0, img.width, img.height);

  // 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.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);
  const pngStream = canvas.pngStream();

  await new Promise((resolve, reject) => {
    pngStream
      .on('data', chunk => writeStream.write(chunk))
      .on('error', reject)
      .on('end', resolve);
  });
}

PHP

GD 확장을 사용하여 이미지에 그림을 그립니다.

# 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

rmagick gem을 사용하여 이미지에 그림을 그립니다.

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

하나로 결합

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

샘플을 빌드하려면 Visual Studio에서 솔루션 파일 dotnet-doc-samples/vision/api/Vision.sln을 열고 솔루션을 빌드합니다.

샘플을 실행하는 방법은 다음과 같습니다.

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

자바

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

샘플을 빌드하고 실행하려면 샘플 코드 디렉토리에서 다음 명령어를 실행합니다.

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 Canvas = require('canvas');
  outputFile = outputFile || 'out.png';
  const faces = await detectFaces(inputFile);
  console.log('Highlighting...');
  await highlightFaces(inputFile, faces, outputFile, Canvas);
  console.log('Finished!');
}

샘플을 실행하려면 샘플 코드 디렉토리에서 다음 명령을 실행합니다.

node faceDetection face.png

PHP

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

샘플을 실행하려면 샘플 코드 디렉토리에서 다음 명령어를 실행합니다.

composer install
php vision.php face images/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 __FILE__ == $PROGRAM_NAME
  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

샘플을 실행하려면 샘플 코드 디렉토리에서 다음 명령을 실행합니다.

bundle install
bundle exec ruby draw_box_around_faces.rb images/face.png output-image.png

입력 이미지 출력 이미지

삭제

이 가이드에서 사용한 리소스 비용이 Google Cloud Platform 계정에 청구되지 않도록 하는 방법은 다음과 같습니다.

  1. GCP Console에서 프로젝트 페이지로 이동합니다.

    프로젝트 페이지로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 다음 종료를 클릭하여 프로젝트를 삭제합니다.
이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Cloud Vision API 문서
도움이 필요하시나요? 지원 페이지를 방문하세요.