3DDFA形式3DMMを用いた感情(表情)移植

はじめに

 Liquid R&Dチームの小倉です。今回は3DDFAを用いて顔画像からその感情の表出であるところの表情を抽出し、それを他人に移植することで間接的ですが感情の移植を試みたいと思います。

f:id:liquid-tech:20210705152441p:plain
感情移植イメージ

手順

 以下の流れで感情(表情)を移植します

  1. 抽出元、移植先から顔3次元情報を抽出、それから顔2次元情報を復元
  2. 抽出元の顔3次元情報の中の表情成分を移植先の顔3次元情報の表情成分に移植
  3. 表情成分が移植された顔3次元情報から顔2次元情報を復元
  4. 移植先の元の顔2次元情報と表情成分移植後に復元された顔2次元情報を使用して移植先の顔画像を変換

顔3次元情報(3DMM)

 今回顔画像からの表情の抽出は、まず顔検出をFaceBoxesで行い、3DDFA_V2 (3DDFAの後継)で検出された顔領域から顔3次元情報を抽出し、その顔3次元情報の中の表情成分を切り取る事で行います。この顔3次元情報は界隈で3DMM(3D Morphable Model)と呼ばれており、以下3DMMと呼んでいきます。

3DDFA形式の3DMM

 顔の3次元情報の一般的な呼称として3DMMという言葉が最初 に出てきたのは20年ほど前ですが、それから各自で様々な形式 が開発、提案されてきました。

 今回使用する3DDFA形式の3DMMは62次元のベクトルで、内訳は以下の様になっています

  • アフィン変換成分(12次元)
  • 形状成分 (40次元)
  • 表情成分 (10次元)

 アフィン変換成分はカメラ座標から顔の中心への変換行列(3x3の回転成分+3x1の移動成分)となっており、形状成分と表情成分はそれぞれ A 3D Face Model for Pose and Illumination Invariant Face RecognitionFaceWarehouse: a 3D Facial Expression Database for Visual Computingを元にした特徴量をPCAで次元削減したものになっています。

f:id:liquid-tech:20210705155519p:plain
3DDFA の元論文での3DMMの紹介

3DMMを見てみる

 実際に顔画像を入力してみて出力された3DMMを見てみます。左から入力画像、アフィン変換成分(p vec)、形状成分(shp_vec)、表情成分 (exp_vec)となっています。一番右の表情成分に注目すると、感情が無い顔ではあまり値が入っておらず、感情が有る顔ではそれなりに値が入っているのがわかります。

 今回はこの表情成分のみを抽出元から移植先にコピーすることで感情の移植を図ります

f:id:liquid-tech:20210705155748p:plain
感情が有る顔の3DMM

f:id:liquid-tech:20210705155733p:plain
感情が無い顔の3DMM

(アフィン変換成分に関しては移動成分を省略しているため、回転成分の9次元のみを表示しています)

顔2次元情報(ランドマーク)

 顔の2次元情報も様々な形式がありますが、今回は現状最も一般的に使われている68点の形式のものを用います。目、鼻、口、輪郭の各点が画像中の2次元座標で表現されています。

f:id:liquid-tech:20210705155856p:plain
顔の各パーツと68点の対応

3DMM(3次元)から68点ランドマーク(2次元)への変換

 3DMMからこの2次元情報(以下ランドマークと呼びます)を計算する際は、生出力である3DMMの形状成分(alpha_shp)、表情成分(alpha_exp), アフィン変換成分(p, offset )から顔を3D再構成した後に該当のランドマークを画像座標に射影して計算されます。また、この際には3DDFA V2がモデル学習時の正規化で使用された平均、偏差などを使用する必要があります。

 vertex = p @ (u_base + w_shp_base @ alpha_shp + w_exp_base @ alpha_exp).reshape(3, -1, order='F') + offset

ランドマークを見てみる

 先程抽出した3DMMをランドマークに変換すると以下の様になります

入力顔画像 ランドマーク ランドマーク重畳後
f:id:liquid-tech:20210705160617p:plain f:id:liquid-tech:20210705160627p:plain f:id:liquid-tech:20210705160641p:plain
f:id:liquid-tech:20210705160705p:plain f:id:liquid-tech:20210705160716p:plain f:id:liquid-tech:20210705160729p:plain

画像変換

 今回はMoving Least Squaresという手法で画像を変換します。詳細は元論文をご参照下さい。実装はこちらをお借りします。

f:id:liquid-tech:20210705160918p:plain
歪められた微笑

感情の移植

 それでは感情を移植してみます。

まず移植先としては、先程から感情が無い顔の例で登場していた弊社CTO大岩をそのまま用います。

f:id:liquid-tech:20210705160952p:plain
感情の無いCTO大岩

そして抽出元としては、感情がわかりやすくてインターネットで探しやすい、顔画像認識界隈でおなじみトランプ元大統領を用います。

f:id:liquid-tech:20210705161024p:plain
感情むき出しのトランプ元大統領

それでは、喜怒哀の感情がむき出しのトランプ元大統領の感情を、感情の無いCTO大岩の顔に移植していきます(楽は喜とかぶっている気がするので今回は省略します)

抽出元

f:id:liquid-tech:20210705161118p:plain
喜んでいるトランプ元大統領

上機嫌です

移植

f:id:liquid-tech:20210705161401p:plain

喜びがちょっと漏れ出ています

抽出元

f:id:liquid-tech:20210705161440p:plain
怒っているトランプ元大統領

トランプ元大統領といえばこの顔

移植

f:id:liquid-tech:20210705161540p:plain

何か言いたげです

抽出元

f:id:liquid-tech:20210705161615p:plain
哀しんでいるトランプ元大統領

珍しく落ち込んでいます

移植

f:id:liquid-tech:20210705161704p:plain

どこか物憂げです

感情の増幅

 さて感情の移植はしてみましたが、移植前の顔にあまりに感情が無いせいか移植後の顔でもまだ感情の物足りなさが否定できません。そこで、感情の無い顔の表情成分がほぼ0だった事から表情成分を増幅して移植してみるとどうなるかを見てみます。使用する画像は移植の時と同じです。

f:id:liquid-tech:20210705161941p:plain

だいぶ口角が上がっています

f:id:liquid-tech:20210705162026p:plain

怒りが抑えきれずに吠えています

f:id:liquid-tech:20210705162109p:plain

今にも泣き出しそうです

おわりに

 今回は3DDFAを用いた感情の移植と増幅を行ってみました。抽出元、移植先共に画像1枚で画像変換時の補完もしていないので、移植後の画像は正直かなり不自然な感じになっていますが、とりあえず3DDFA形式の3DMMの表情成分が果たしている役割の一端は確認できたかと思います。

 Liquid R&Dチームでは、例えば今回使用したFaceBoxes、3DDFAといったOSSの性能検証もこの様に楽しく行っています。そして、感情の無い顔の持ち主として登場したCTO大岩ですが、本当はこの様に顔画像を乱用しても笑顔で許してくれる感情溢れる良い人です。

長めの補足

 ちなみに顔の表情を抽出して他の顔に移植することはみんな興味があるようで結構昔からやられていて、そもそも3DDFA形式3DMMの表情成分のベースとなっているFaceWarehouse: a 3D Facial Expression Database for Visual Computingでも他人間、本人間での表情の移植がやられています。

f:id:liquid-tech:20210705162205p:plain
FaceWarehouse での他人間感情移植

 またGANが登場してからというもの、学習データセットと移植先画像によっては人間の目では生成されたものかどうかの判断がつかないほどに自然な移植が行われるようになっています

f:id:liquid-tech:20210705162254p:plain
StarGANでの各種GANによる感情移植(生成)

さらに学習済みモデル・コードの公開、計算資源、クラウド環境利用の一般化が進み、インターネットさえ繋がれば誰でも簡単に移植が出来るようになっています。以下はsonyさんが提供しているニューラルネットワークライブラリnnablaにて実装された、First Order Motion Model元ソース)のサンプルコードのGoogleColab上のノートブックにて、トランプ元大統領の映像とCTO大岩の画像を入力して実行した結果です。

f:id:liquid-tech:20210705162542p:plain
歯が合成されて口が自然に開いているように見えます

f:id:liquid-tech:20210705162704p:plain
ただ目の感じなど微妙に変わったようにも見えます

 このように、現在はもはやどの画像がオリジナルでどの画像が生成されたものかが人間の目では判断がつかないという時代になってきています。Liquidではこの現状を受け止めながら、真贋判定技術の開発に取り組んでいます。

令和時代の人体錬成

おはこんにちは、Liquid R&Dチームの布目です。

Liquid R&Dチームで現在重点的に取り組んでいることに、eKYCでの顔の真贋判定があります。この記事では真贋判定について簡単に解説します。また真贋判定をより頑強にするための、顔3次元復元+3Dプリンタで人体錬成する取り組みについてまとめました。

真贋判定とは

真贋判定は、本物と偽物を判定するといった意味合いです。顔の真贋判定では、撮影された顔が本物か偽物かを判定することになります。顔の偽物は大雑把に3種類に分けることができます。

  • Photo Attack
    • 偽装したい人の顔写真を紙に印刷して容貌撮影
  • Display Attack
    • 偽装したい人の画像/動画をスマホやディスプレイに表示して容貌撮影
  • Mask Attack
    • 偽装したい人の顔のお面を使って容貌撮影

eKYCでは、例えば不正な手段で入手した他人の顔画像を印刷して、顔撮影を突破しようとすることが想定されます。もし突破されてしまうと、赤の他人で本人確認ができてしまい、銀行口座の開設などができるようになってしまいます。撮影された顔が本物であるか偽物であるか見極めるのはとても大切です。しかしながら、本物は本物と、偽物は偽物と判定する手法はまだまだ発展途上であり多くの研究が行われています。(arxivなどでface anti spoofなどをキーワードに検索するとたくさん論文を読むことができます。)

真贋判定の課題点の一つにデータ集めがあります。上であげたPhoto AttackとDisplay Attackは多大な工数が必要ですが、比較的容易にデータ収集できます。しかしMask Attackはそもそも誰かのお面を作る必要があります。今回は3Dプリンタでお面を作ることを目標に、機材、3次元復元手法について調査しました。最終的には以下のようなサイクルを想定しています!

f:id:liquid-tech:20210705123042p:plain

顔3次元復元について

3つの方向性があります。

  • RGBカメラで複数の角度から撮った数枚の画像から復元
  • RGB-Dカメラで複数の角度から撮った数枚の画像から復元
  • 3Dスキャナで顔形状の復元

RGBカメラとRGB-Dカメラを使ったものを試しました。

RGBカメラ

RGBカメラはスマートフォンデジタルカメラなどに搭載されてるカメラです。いわゆる一般的なカメラですね。

MetaShapeとFaceBuilderというソフトを試しました。

f:id:liquid-tech:20210702123144p:plain f:id:liquid-tech:20210702123154p:plain

MetaShapeはちょっとギザギザした感じ、FaceBuilderはなんとなくスムージングされたモデルを出力しました。

RGB-Dカメラ

RGB-Dカメラは、RGBカメラ同様の画像に追加で深さ(depth)情報を取得できるカメラです。MicrosoftのKincet, Kincet AzureやIntelのReal SenseシリーズなどがRGB-Dカメラにあたります。(AppleのFaceIDでも使われているようです。)

まずはReal Sense d415 + RecFusionというソフトを試しました。

f:id:liquid-tech:20210702123214p:plain f:id:liquid-tech:20210702123240p:plain

ちょっと荒さを感じる結果になりました。

ちょうどこれらを調べている時期にRGB-Dカメラを使ったフォトリアルな3次元復元手法に関する論文が出たのでそちらも試しました。(https://arxiv.org/pdf/2010.05562.pdf) こちらはAzureKinectを使用しています。

f:id:liquid-tech:20210702123300p:plain

なかなかリアルなモデルが出力されて良さそう!でも目がない!となりました。

おわりに

RGB,RGB-Dカメラでの3次元復元を試し、なかなか良いモデル作成が行えることを確認しました。どちらの場合でも言えることですが、高性能なカメラ、正確なカメラキャリブレーションがないとリアルなモデルの作成は難しいです。またリアルなモデルが真贋判定で有利になるのかはまた別に議論が必要な点です。実はちょっとカクカクしているなどの低品質なマスクの方が真贋判定を欺ける可能性も十分に考えられるので、今後も実験を試していきたいところになります。

今回の人体錬成で持って行かれるものはお金と時間だけです!腕と脚は持ってかれないのでとても良心的です!みなさんも気軽に人体錬成しましょう!

Goでスタックトレースを上書きせずにエラーをラップする方法

こんにちは。eKYC開発チームの藤本です。

eKYCのサーバーサイドではpkg/errorsパッケージを使用してGoのスタックトレースを記録しています。スタックトレースは標準のerrorsパッケージではサポートされていませんが、エラー発生時のスタックトレースがわかるとエラーの解決が楽になるので、是非記録しておきたい情報です。

pkg/errorsパッケージを使用してスタックトレースを記録するには、エラーを初期化するときにerrors.New, errors.Errorf関数を使用するか、既存のエラーに対してerrors.WithStack関数を使用します。こうすると、作成されたエラーerrに対してerr.StackTraceメソッドを呼び出すことによりスタックトレースを取得することができ、err.Formatメソッドの出力にもスタックトレースが含まれるようになります。

こうして作成したエラーをラップするとどういう挙動になるでしょうか?

errors.Wrap関数を使用した場合

pkg/errorsには、errors.Wrapというその名の通りにエラーをラップしてくれる関数があるので、これを使用して作成したエラーがどういう挙動をするのかを実際にコードを書いて確認してみます。

コード

package main

import (
    errs "errors"
    "fmt"
    "github.com/pkg/errors"
)

func main() {
    err := errors.New("error")

    we := errors.Wrap(err, "wrap")

    fmt.Println("### Error() ###")
    fmt.Printf("%v\n\n", we.Error())
 
    fmt.Println("### StackTrace() ###")
    if e, ok := we.(interface{ StackTrace() errors.StackTrace }); ok {
        fmt.Printf("%+v\n\n", e.StackTrace())
    }

    fmt.Println("### Formatter ###")
    if e, ok := we.(fmt.Formatter); ok {
        fmt.Printf("%+v\n\n", e)
    }

    fmt.Println("### Unwrap() ###")
    fmt.Printf("%v\n", errs.Is(we, err))
}

出力

### Error() ###
wrap: error

### StackTrace() ###

main.main
    /tmp/sandbox309561315/prog.go:12
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:225
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1371

### Formatter ###
error
main.main
    /tmp/sandbox309561315/prog.go:10
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:225
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1371
wrap
main.main
    /tmp/sandbox309561315/prog.go:12
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:225
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1371

### Unwrap() ###
true

Go Playground

StackTraceメソッドの出力は、errros.Wrap関数を呼び出した時点(12行目)のスタックトレースになってしまい、元々のエラーの発生箇所(10行目)がわからなくなってしまいます。Formatメソッドの出力には、ラップした時点のスタックトレースと元々のスタックトレースが両方出力されるようになります。
標準パッケージのIs関数で元々のエラーとの一致判定を行うことで、作成したエラーがUnwrapメソッドをサポートしていてラップされた状態になっていることも確認しています。(Is関数の仕様)

エラーの原因を特定するためには、元々のエラーの発生箇所を知りたいので、それがわからなくなってしまったり、余分な情報がついて読みづらくなるのはできることなら避けたいところです。

errors.WithMessage関数を使用した場合

命名からは一見わかりませんが、errors.WithMessage関数もエラーをラップしてくれるので、これを使用して作成したエラーの挙動も同様のコードを書いて確認してみます。

コード

package main

import (
    errs "errors"
    "fmt"
    "github.com/pkg/errors"
)

func main() {
    err := errors.New("error")

    me := errors.WithMessage(err, "message")

    fmt.Println("### Error() ###")
    fmt.Printf("%v\n\n", me.Error()) 

    fmt.Println("### StackTrace() ###")
    if e, ok := me.(interface{ StackTrace() errors.StackTrace }); ok {
        fmt.Printf("%+v\n\n", e.StackTrace())
    }

    fmt.Println("### Formatter ###")
    if e, ok := me.(fmt.Formatter); ok {
        fmt.Printf("%+v\n\n", e)
    }

    fmt.Println("### Unwrap() ###")
    fmt.Printf("%v\n", errs.Is(me, err))
}

出力

### Error() ###
message: error

### StackTrace() ###
### Formatter ###
error
main.main
    /tmp/sandbox076104167/prog.go:10
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:225
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1371
message

### Unwrap() ###
true

Go Playground

こちらの出力では、スタックトレースをが上書きされずにラップできていることがわかります。ただし、StackTraceメソッドは使えないため、Formatメソッドの出力結果でスタックトレースを確認する必要があります。

まとめ

エラーをerrors.Wrap関数とerrors.WithMessage関数でラップした場合の挙動を比較してみました。StackTraceメソッドが使えないというデメリットはありますが、errors.WithMessage関数を使うとスタックトレースを上書きせずに簡単にエラーをラップできるので是非使用してみてください。

Liquid が目指すものと技術組織の取り組み

こんにちは、Liquid の CTO の大岩 ( @ooiwa ) です。

Liquid という名前を聞いたことがあるでしょうか? ご存知な方は、生体認証、eKYCという言葉を思い浮かべるかもしれません。僕たちは、LIQUID eKYC というB2B2Cのサービスを作っています。一言でいうと、「従来は窓口に行く必要があった本人確認を、スマートフォンでセルフィー撮影と身分証撮影で済むようにした」サービスです。このブログ開設の日からちょうど2年前にあたる2019年7月1日にリリースされ、金融口座の開設から、携帯電話の契約、C2Cサービスでの本人確認など、幅広く使われています。

それを運営するLiquidは、「認証を空気化し、滑らかな世界をつくる」ことを目指しています。LIQUID eKYC の実現により、このビジョンへ少し近づくことができました。窓口や郵送の必要がなくなり、新しくサービスを利用するまでの時間を短縮できました。

目指すべき世界は更に滑らかなものを想像します。例えば、サービスを利用するまでの時間を短縮できたものの、より待ち時間を少なくできないか。口座作成時のみならずログイン時や端末を変えたときなども、パスワードなしにスムーズに利用ができないか。

f:id:ooiwa:20210630151435p:plain
公式HPでは、我々が抱く未来のイメージを紹介しています。

そこに近づくために、Liquidのエンジニアたち、リサーチャーたちは何に取り組んでいるのか? 第1回テックブログでは、その課題と取り組みの一部を紹介していきます。

LIQUID eKYC を末永く続けるための改善

今日で2周年を迎えたLIQUID eKYCですが、

  • セキュリティルームの構築を含む高いセキュリティ
  • いち早いスマートフォンWEBでの対応
  • リリースから今までの離脱率の改善

他にも多くのことを達成できており、ここまで至れたチームを誇りに思います。

一方で、機能追加と安定性を重視して進んできた結果、運用負荷への対応が後回しになってしまったり、当初の想定を超えた大幅な仕様変更に苦労するところも出てきています。コードベースやパフォーマンスの改善、チームとして取り組めるようなレベルアップと、一歩ずつ先に進んでいくことに取り組んでいます。

そして、このサービスは、金融を含めた様々な業界を支える認証のインフラとして、至極当たり前ですが今後長く存在し続けます。技術の発展に伴い、より楽に使えて、より高セキュリティに成長をし続ける予定です。また僕たちは、eKYCをビジョンの実現のための起点と考えています。本人確認された情報をもとに、様々なサービスが作られていくことを目指しています。データの持ち方のありようや、セキュリティのレベルを離散的に上げられる技術も検討していきます。

f:id:ooiwa:20210630151650p:plain
Miro でのデザイン協議

f:id:ooiwa:20210630151716p:plain
ある日のeKYCバックエンドおしゃべりのメモ

不正検知技術の向上

鍵となる技術の1つが、不正検知技術です。

今のeKYCでは、セルフィーを撮影する際に顔を動かしてもらったり、免許証の斜め面をとってもらったりと、少しユーザーに負担をお願いしています。また、オペレーターの管理画面での確認の時間もかかります。大多数のユーザーには関係ないものの、ごく一部の不正をチェックするためにこのプロセスが存在しています。逆に、不正を検知する技術が上がれば、それぞれの負担を軽減し、今までコストが高くてできなかった用途でも楽に認証ができる可能性があります。

Liquidでは、顔認証、画像処理による偽造顔検知(※1)、端末の使いまわし検知(※2)のあわせ技でこの問題に取り組んでいます。データのあり方の検討から、スマートフォンアプリやWEBでのUIの検討と作成、データの作成、モデルの検証、実データでの評価とループを回しています。

※1 特許出願済 2020-173696 ※2 特許出願済 2020-177094​

f:id:ooiwa:20210630151948p:plain
左から本物(Bonafide)の大岩、ディスプレイ上の大岩、お面の大岩、face swap された大岩

eKYC の次の認証を空気化する新規事業

そして、eKYCで登録された情報をどう活用し、さらなる価値を作り出していくかが、新規事業のチームの課題となります。

eKYCで顔撮影をして口座登録をしたら、ログインや送金時の認証も顔でできないでしょうか。パスワードを忘れても、面倒なやり取りで2,3日待つことなく、安全に即座に復帰ができないでしょうか。登録後のアカウントの引き渡しを防ぐすべはないでしょうか。

  • 法律や、世界の標準化の動きの把握
  • 顔認証や不正検知の技術の精度や振る舞いの見極め
  • 高いセキュリティの担保とユーザーの同意
  • ユーザーや事業者のニーズの開拓

と多くのことを考慮し、さらにこれを高速で検証しなければならず、もう一つスタートアップを作るようなものになっています。

特にここは圧倒的に人が足りないのですが、今中心メンバーとなってくれる方を絶賛募集しています。

おわりに

今回は課題と取り組みの一部を紹介しました。次回からは、様々な情報発信をして、どうこのビジョンに向かっているのか、またどんな人達がいるのかを知ってもらえればと思います。

こんな記事を書く予定です。

  • LIQUID eKYC を支える技術の紹介
  • メンバーが日々学び試している技術の紹介
  • 社内の議論のメタファーとしてよく出る本やアニメの紹介

そんなLiquidでは、一緒に技術のチャレンジをしてくれる仲間を探しています。

  • 目指すべき世界にワクワクする人
  • 技術の取り組みに興味がある人
  • (これからのブログ記事で)中の人に興味を持った人

お話しましょう、ぜひ気軽にこちらからご連絡ください!

liquidinc.asia