👨‍🏫

InsightFaceとFastAPIで顔検出サーバを作ってみた

2021/07/27に公開

目次

初めに

昨今では色々な顔検出ライブラリがOSS(オープンソースソフトウェア)として公開されており、手軽に画像から顔検出、顔認証などを行うことができます。
今回は、比較的新しいアルゴリズムが実装されている「InsightFace」を使った顔検出、顔器官点検出、顔属性取得を試してみました。
また、以前から気になっていた「FastAPI」を使って、ウェブAPI化してみました。

環境

環境は以下の通りです。GPUは使用しておらず、CPUのみで実行(推論)できます。

  • ハードウェア: MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
    • OS: macOS Big Sur 11.4
    • CPU: 2.3 GHz Quad-Core Intel Core i7
    • メモリ: 32 GB 3733 MHz LPDDR4X
  • ソフトウェア:
    • Docker Desktop for Mac: 3.5.1
    • Docker Compose: 1.29.2
  • 主なPythonパッケージ:
    • insightface: 0.4
    • onnxruntime: 1.8.1
    • fastapi: 0.66.0
$ sw_vers
ProductName:	macOS
ProductVersion:	11.4
BuildVersion:	20F71

$ docker version
Client:
 Cloud integration: 1.0.17
 Version:           20.10.7
 API version:       1.41
 Go version:        go1.16.4
 Git commit:        f0df350
 Built:             Wed Jun  2 11:56:22 2021
 OS/Arch:           darwin/amd64
 Context:           desktop-linux
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       b0f5bc3
  Built:            Wed Jun  2 11:54:58 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.6
  GitCommit:        d71fcd7d8303cbf684402823e425e9dd2e99285d
 runc:
  Version:          1.0.0-rc95
  GitCommit:        b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

$ docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.9.0
OpenSSL version: OpenSSL 1.1.1h  22 Sep 2020

ソースコード

ソースコード一式はGitHubの以下のリポジトリに置いています。
本記事執筆時のタグは20210727cです。

https://github.com/nayutaya/202107-face-detector

ビルド

顔検出サーバはDockerを使って実装されているため、Dockerイメージをビルド、起動するだけで試すことができます。
ソースコードの取得、ビルド、起動、停止の例は以下の通りです。

# リポジトリを取得
git clone https://github.com/nayutaya/202107-face-detector.git
cd 202107-face-detector
# タグをチェックアウト
git checkout 20210727c
# Dockerイメージをビルドする
docker-compose build
# Dockerコンテナをバックグラウンドで起動する
docker-compose up -d

# (試したあとに)Dockerコンテナを停止する
docker-compose down

メインのコード

メインのコードはdetector-insightface/src/main.pyに記載されています。

InsightFaceの使い方はとても簡単で、主要なコードは3行程度です。

import insightface

face_analysis = insightface.app.FaceAnalysis()
face_analysis.prepare(ctx_id=0, det_size=(640, 640))
faces = face_analysis.get(image)

detector-insightface/src/main.pyの内容を以下に示します。
現時点では、顔認証に必要な顔特徴量(face.embedding)の処理は実装できていません。

import datetime
import hashlib

import fastapi
import fastapi.middleware.cors
import insightface
import numpy as np
import onnxruntime
import PIL.Image

SERVICE = {
    "name": "detector-insightface",
    "version": "0.1.0",
    "libraries": {
        "insightface": insightface.__version__,
        "onnxruntime": onnxruntime.__version__,
    },
}

app = fastapi.FastAPI()
app.add_middleware(
    fastapi.middleware.cors.CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

face_analysis = insightface.app.FaceAnalysis()
face_analysis.prepare(ctx_id=0, det_size=(640, 640))


@app.get("/")
async def get_root():
    return {
        "service": SERVICE,
        "time": int(datetime.datetime.now().timestamp() * 1000),
    }


@app.post("/detect")
async def post_detect(file: fastapi.UploadFile = fastapi.File(...)):
    assert file.content_type == "image/jpeg"
    image = PIL.Image.open(file.file).convert("RGB")
    image = np.array(image)
    image = image[:, :, [2, 1, 0]]  # RGB to BGR

    faces = face_analysis.get(image)

    file.file.seek(0)
    sha1_hash = hashlib.sha1(file.file.read()).hexdigest()
    file_size = file.file.tell()

    return {
        "service": SERVICE,
        "time": int(datetime.datetime.now().timestamp() * 1000),
        "request": {
            "file": {"name": file.filename, "size": file_size, "sha1": sha1_hash}
        },
        "response": {
            "width": image.shape[1],
            "height": image.shape[0],
            "numberOfFaces": len(faces),
            "faces": [
                {
                    "score": face.det_score.astype(float),
                    "boundingBox": {
                        "x1": face.bbox[0].astype(float),
                        "y1": face.bbox[1].astype(float),
                        "x2": face.bbox[2].astype(float),
                        "y2": face.bbox[3].astype(float),
                    },
                    "keyPoints": [
                        {"x": xy[0].astype(float), "y": xy[1].astype(float)}
                        for xy in face.kps
                    ],
                    "landmarks": {
                        "3d_68": [
                            {
                                "x": xyz[0].astype(float),
                                "y": xyz[1].astype(float),
                                "z": xyz[2].astype(float),
                            }
                            for xyz in face.landmark_3d_68
                        ],
                        "2d_106": [
                            {"x": xy[0].astype(float), "y": xy[1].astype(float)}
                            for xy in face.landmark_2d_106
                        ],
                    },
                    "attributes": {"sex": face.sex, "age": face.age},
                    # TODO: face.embedding
                }
                for face in faces
            ],
        },
    }

requirements.txtについての補足

Dockerfile内にはrequirements1.txtからrequirements3.txtまでの3つのファイルを使ったPythonパッケージのインストール処理が行われています。
本来は1つのrequirements.txtで済ませたい所ですが、insightfaceパッケージのインストール時に、依存ライブラリのインストールが完了していないとエラーになるため、requirements1.txtrequirements2.txtに分割しています。
また、InsightFaceの初回利用時にはモデルのダウンロードが行われますが、この処理にはそれなりの時間を要するため、開発時にパッケージ構成を変更する度に行いたくありません。
そのため、FastAPIなどのウェブ関係のパッケージはrequirements3.txtとして分割しています。

参考に、Dockerfileの内容を以下に示します。

FROM ubuntu:20.04
RUN apt-get update \
  && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
    build-essential \
    ca-certificates \
    libopenblas-base \
    libopencv-dev \
    pandoc \
    python3-dev \
    python3-pip \
    python3-setuptools \
  && rm --recursive --force /var/lib/apt/lists/*
RUN python3 -m pip install --upgrade pip setuptools
WORKDIR /opt/app
COPY requirements1.txt ./
RUN python3 -m pip install --requirement requirements1.txt
COPY requirements2.txt ./
RUN python3 -m pip install --requirement requirements2.txt
COPY src/get_model.py ./src/
RUN ./src/get_model.py
COPY requirements3.txt ./
RUN python3 -m pip install --requirement requirements3.txt
COPY src/ ./src/
CMD ["uvicorn", "--host=0.0.0.0", "--app-dir=src", "main:app"]

curlを使った動作確認

リポジトリには動作確認用の画像ファイル(43730.jpgぱくたそから取得しました)が含まれているため、新たに画像を用意することなく動作を確認することができます。

curlコマンドによる実行例と実行結果(整形しています)は以下の通りです。ちゃんと顔を検出できていますね。
顔の位置を示すバウンディングボックスの他、2D/3Dの顔器官点、顔属性(女性、23歳)などの情報も取得できています。

$ curl -X POST \
  --header "Content-Type: multipart/form-data" \
  --form "file=@43730.jpg;type=image/jpeg" \
  http://localhost:8000/detect
{
  "service": {
    "name": "detector-insightface",
    "version": "0.1.0",
    "libraries": {
      "insightface": "0.4",
      "onnxruntime": "1.8.1"
    }
  },
  "time": 1627311484978,
  "request": {
    "file": {
      "name": "43730.jpg",
      "size": 44866,
      "sha1": "cebb764b4b965bba6c6cc7c5bd77a11a9f71d562"
    }
  },
  "response": {
    "width": 800,
    "height": 533,
    "numberOfFaces": 1,
    "faces": [
      {
        "score": 0.8679838180541992,
        "boundingBox": {
          "x1": 401.4267883300781,
          "y1": 74.14395904541016,
          "x2": 525.7346801757812,
          "y2": 232.2003631591797
        },
        "keyPoints": [
          {
            "x": 449.41046142578125,
            "y": 141.31069946289062
          },
          ...
        ],
        "landmarks": {
          "3d_68": [
            {
              "x": 391.0307312011719,
              "y": 142.33551025390625,
              "z": 59.646602630615234
            },
            ...
          ],
          "2d_106": [
            {
              "x": 493.8816833496094,
              "y": 230.3689422607422
            },
            ...
          ]
        },
        "attributes": {
          "sex": "F",
          "age": 23
        }
      }
    ]
  }
}

ウェブブラウザを使った動作確認

FastAPIを利用しているため、Swagger UIを使い、ウェブブラウザ上から動作確認を行うこともできます。

手順は以下の通りです。

  1. Dockerイメージを起動する。
  2. ウェブブラウザで http://localhost:8000/docs#/default/post_detect_detect_post を開く。
  3. 「Try it out」ボタンを押下する。
  4. fileフィールドの「Choose File」ボタンを押下し、画像ファイルを選択する。
  5. 「Execute」ボタンを押下する。

実行例を以下に示します。

終わりに

InsightFaceを使うことで、とても簡単に顔検出、顔器官点検出、顔属性取得を行うことができました。
なお、InsightFace自体は商用利用可能ですが、利用しているモデルによって商用利用可/不可が異なりますので、モデルのライセンスにはご注意ください。

結果を数値として示されても分かりづらいので、次は結果の可視化を行ってみたいと思っています。

InsightFaceの顔検出結果をNext.jsで可視化してみた』に続く。

Discussion