顔交換の実装方法をみる
この記事はmob Advent Calendar 24日目の記事です。
顔交換ができるカメラアプリなどが巷にありますよね。どのような実装をしているんだろうって気になったので調べてみることにしました。
参考コードを読んでみる
roopという、 Stable Diffusion の文脈などで出てくる顔交換ができるソフトウェアがあります。
このコードを読んでどのような流れで実装しているのかを見ていきます。
重要なコードは下記のファイルになります。
重要な部分を抜粋するとこのようになります。一部わかりやすいようにコードを改変しています。
def pre_check() -> bool:
download_directory_path = resolve_relative_path('../models')
conditional_download(download_directory_path, ['https://huggingface.co/CountFloyd/deepfake/resolve/main/inswapper_128.onnx'])
return True
def get_face_swapper() -> Any:
global FACE_SWAPPER
with THREAD_LOCK:
if FACE_SWAPPER is None:
model_path = resolve_relative_path('../models/inswapper_128.onnx')
FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.execution_providers)
return FACE_SWAPPER
def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
return get_face_swapper().get(temp_frame, target_face, source_face, paste_back=True)
def process_image(source_path: str, target_path: str, output_path: str) -> None:
source_face = get_one_face(cv2.imread(source_path))
target_frame = cv2.imread(target_path)
reference_face = None if roop.globals.many_faces else get_one_face(target_frame, roop.globals.reference_face_position)
result = swap_face(source_face, reference_face, target_frame)
cv2.imwrite(output_path, result)
順番的には pre_check が呼ばれて、その後 process_image が呼ばれる。
process_image の中で swap_face が、 swap_face の中で get_face_swapper が呼ばれています。
pre_check
pre_check
関数では inswapper_128.onnx
というモデルの存在確認をしています。なければダウンロードします。
process_image
process_image関数内では、次のようなことをやっています。
- 置き換えたいソースになる顔を取得
- 置き換えたい画像を OpenCV で読み込む
- 置き換えたい顔を画像から取得
- swap_face(顔の入れ替え関数) を呼びだす
- 顔を入れ替えた結果を書き出す
swap_face
get_face_swapper
を呼び出して、顔入れ替えモデルに置き換えたい顔、置き換えられる顔、置き換える画像を渡します。
get_face_swapper
inswapper_128.onnx
モデルを返します。
と、このような流れで実装していることがわかります。
ミニマムなコードを作ってみる
上記のコードからミニマムで試せるコードを作ってみるとこんなコードになりました。
from typing import Any
import cv2
import insightface
import numpy
from insightface.app.common import Face
Face = Face
Frame = numpy.ndarray[Any, Any]
FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l')
FACE_ANALYSER.prepare(ctx_id=0)
FACE_SWAPPER = insightface.model_zoo.get_model('inswapper_128.onnx')
def swap_face(source_path: str, target_path: str, output_path: str) -> None:
source_face = FACE_ANALYSER.get(cv2.imread(source_path))[0]
target_frame = cv2.imread(target_path)
reference_face = FACE_ANALYSER.get(target_frame)[0]
result = FACE_SWAPPER.get(target_frame, reference_face, source_face, paste_back=True)
cv2.imwrite(output_path, result)
swap_face("source.jpeg", "target.jpeg", "output.png")
これらの画像を用意して上記のコードを実行してみます。
source.jpeg | target.jpeg |
---|---|
実行したところ下記のコードが生成されました。
しっかり顔が交換されてることが確認できました。 insightface が Python にしかないのでいくつか壁はありそうですが、同様のことをすれば例えばモバイルアプリのカメラアプリなどで同様のことができそうです。
Discussion