InsightFaceとFastAPIで顔検出サーバを作ってみた
目次
- InsightFaceとFastAPIで顔検出サーバを作ってみた(本記事)
- InsightFaceの顔検出結果をNext.jsで可視化してみた
- InsightFaceで顔認証(特徴量抽出、比較)してみた
- InsightFaceをGPUで動かしてみた
- InsightFaceを使って動画から顔検出してみた
- InsightFaceの顔検出結果をブラウザ上で動画にオーバーレイ表示してみた
初めに
昨今では色々な顔検出ライブラリが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
です。
ビルド
顔検出サーバは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.txt
とrequirements2.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を使い、ウェブブラウザ上から動作確認を行うこともできます。
手順は以下の通りです。
- Dockerイメージを起動する。
- ウェブブラウザで http://localhost:8000/docs#/default/post_detect_detect_post を開く。
- 「Try it out」ボタンを押下する。
-
file
フィールドの「Choose File」ボタンを押下し、画像ファイルを選択する。 - 「Execute」ボタンを押下する。
実行例を以下に示します。
終わりに
InsightFaceを使うことで、とても簡単に顔検出、顔器官点検出、顔属性取得を行うことができました。
なお、InsightFace自体は商用利用可能ですが、利用しているモデルによって商用利用可/不可が異なりますので、モデルのライセンスにはご注意ください。
結果を数値として示されても分かりづらいので、次は結果の可視化を行ってみたいと思っています。
Discussion