「金正恩は影武者か!?」FACE01 GRAPHICS ver.1.2.8 顔認証で測定

きっかけ

一年前の2020年7月頃、北朝鮮の最高指導者金正恩の写真が影武者ではないか?と世間で噂され、ちょうどその頃東海顔認証のFACE01 GRAPHICSの新バージョン登場の時期が重なったことから、顔認証によって影武者かどうか判定を試みました。その時の記事は「「金正恩は影武者か!?」何パーセントで本人であるかを表示」からご覧になれます。

2021年になり再び影武者説が噂されました。ニュースでは痩せたのではないかと報道されているようです。「日テレNEWS 【北朝鮮】建国記念日”異例の”パレード 金正恩氏体重減少?2021/09/09

今回再びFACE01 GRAPHICSの新バージョンが登場しましたのでご紹介がてら影武者かどうかを検証してみたいと思います。

方法

金正恩氏の顔画像として下の2枚を登録しました。ゴシップ的に表現するなら「本人」だと思われる写真です。

登録した顔画像

次にその他の人物として99人の芸能人の顔写真を登録しました。金正恩氏とあわせて計100人の登録者となるようにしたわけです。

100人分の登録顔画像

この状態で顔認証を行います。検証対象となる映像は顔が小さいため編集して拡大してますが、拡大した分解像度が落ちていますので良い入力映像データとは言えない状態です。

結論

断定は出来ませんが「本人」だという結果になりました。

下の動画をご覧ください。

出力されたファイル

99%以上で本人であると検出値が出力されました。

顔画像登録されている99人の芸能人はヒットしませんでした。

耳の形の考察・双子での検証例は今回は行いません。前回の「一卵性双生児の比較」「考察」をご覧ください。

FACE01 GRAPHICS ver.1.2.8

今回用いた顔認証アプリケーションはFACE01 GRAPHICS ver.1.2.8です。

FACE01 GRAPHICS ver.1.2.8は多くの更新が行われています。

例えばconfig_FACE01GRAPHICS128.iniのbottom_areaをTrueとすると以下の様なインターフェースに変化します。

呼出し方を含めて1つづつみていきましょう。

まず設定ファイルとなるconfig_FACE01GRAPHICS128.iniの内容は下の様になります。この設定ファイルを変更することで色々な機能を呼出したり消したりすることが出来ます。

[DEFAULT]
; 指定した数値%以上を本人とする
similar_percentage=99.0
; 検出顔画像エンコーディング回数(削除予定・変更非推奨)
jitters=0
; 登録顔画像エンコーディング回数
priset_face_images_jitters=100
; 顔検出面積(0: 80x80px, 1: 40x40px)
upsampling=1
; 深層学習モデルを使い顔検出('hog' or 'cnn')
mode=cnn
; フレームドロップ指定(-1:自動)
frame_skip=-1
; 入力映像データ指定
movie=金正恩_ひとり.mp4
; 入力映像に対して処理対象エリアを指定
set_area=NONE
; 入力映像に対してリサイズする横幅指定
SET_WIDTH=600
; 顔領域にクラシックなな枠を表示
rectangle=False
; 顔領域にモダンな枠を表示
target_rectangle=True
; opencv-pythonによるウィンドウの描画
show_video=False
; 顔領域の自動保存
crop_face_image=True
; 顔領域自動保存の頻度(フレーム数指定)
frequency_crop_image=80
; デフォルト顔画像の描画
default_face_image_draw=False
; 情報を半透明で表示
show_overlay=True
; 顔の下に%を表示
show_percentage=True
; 顔の下に名前を表示
show_name=True
; 入力映像データのプロパティを表示して終了
print_property=False
; 1フレーム処理にかかるミリ秒を標準出力
calculate_time=False
; 複数人検出された時に警告を出す
multiple_faces=False
; ウィンドウ下部に情報を表示
bottom_area=False

次にどうやって呼び出すかご紹介します。FACE01 GRAPHICSは開発者用のソフトウェアですのでプログラムから呼出します。

Pythonから呼び出すコードは以下になります。

import configparser
from concurrent.futures import ThreadPoolExecutor

import cv2
import PySimpleGUI as sg

import FACE01GRAPHICS128 as fg

# configファイル読み込み
conf=configparser.ConfigParser()
conf.read('config_FACE01GRAPHICS128.ini', 'utf-8')

similar_percentage=         float(conf.get('DEFAULT','similar_percentage'))
jitters=                    int(conf.get('DEFAULT','jitters'))
priset_face_images_jitters= int(conf.get('DEFAULT','priset_face_images_jitters'))
upsampling=                 int(conf.get('DEFAULT','upsampling'))
mode=                       conf.get('DEFAULT','mode')
frame_skip=                 int(conf.get('DEFAULT','frame_skip'))
movie=                      conf.get('DEFAULT','movie')
rectangle=                  conf.getboolean('DEFAULT','rectangle')
target_rectangle=           conf.getboolean('DEFAULT','target_rectangle')
show_video=                 conf.getboolean('DEFAULT','show_video')
frequency_crop_image=       int(conf.get('DEFAULT','frequency_crop_image'))
set_area=                   conf.get('DEFAULT','set_area')
print_property=             conf.getboolean('DEFAULT','print_property')
calculate_time=             conf.getboolean('DEFAULT','calculate_time')
SET_WIDTH=                  int(conf.get('DEFAULT','SET_WIDTH'))
default_face_image_draw=    conf.getboolean('DEFAULT', 'default_face_image_draw')
show_overlay=               conf.getboolean('DEFAULT', 'show_overlay')
show_percentage=            conf.getboolean('DEFAULT', 'show_percentage')
crop_face_image=            conf.getboolean('DEFAULT', 'crop_face_image')
show_name=                  conf.getboolean('DEFAULT', 'show_name')
multiple_faces=             conf.getboolean('DEFAULT', 'multiple_faces')
bottom_area=                conf.getboolean('DEFAULT', 'bottom_area')

kaoninshoDir, priset_face_imagesDir=fg.home()

known_face_encodings, known_face_names=fg.load_priset_image(
    kaoninshoDir,
    priset_face_imagesDir, 
    jitters=priset_face_images_jitters, 
)

xs=fg.face_attestation(
    kaoninshoDir, 
    known_face_encodings, 
    known_face_names, 
    similar_percentage=similar_percentage, 
    jitters=jitters, 
    upsampling=upsampling,
    mode=mode, 
    model='small', 
    frame_skip=frame_skip, 
    movie=movie, 
    rectangle=rectangle, 
    target_rectangle=target_rectangle, 
    show_video=show_video,
    frequency_crop_image=frequency_crop_image, 
    set_area=set_area,
    print_property=print_property,
    calculate_time=calculate_time,
    SET_WIDTH=SET_WIDTH,
    default_face_image_draw=default_face_image_draw,
    show_overlay=show_overlay,
    show_percentage=show_percentage,
    crop_face_image=crop_face_image,
    show_name=show_name,
    multiple_faces=multiple_faces,
    bottom_area=bottom_area
)

layout = [
    [sg.Image(filename='', key='cam1', pad=(0,0))],
    [sg.Button('終了', key='terminate', pad=(0,10))]
]

window = sg.Window(
    'window1', layout, alpha_channel=1, margins=(0, 0), 
    no_titlebar=True, grab_anywhere=True,
    location=(350,130), modal=True
)

def multi(x):
    name, pict, date, img, location, percentage_and_symbol = x['name'], x['pict'], x['date'], x['img'], x['location'], x['percentage_and_symbol']
    if not name==None:
        print(name, percentage_and_symbol, location, date)
    return img

pool = ThreadPoolExecutor()

for array_x in xs:
    for x in array_x:
        if x=={}:
            continue

        result = pool.submit(multi, x)
        event, _ = window.read(timeout=1)
        
        if  not result.result() is None:
            imgbytes=cv2.imencode(".png", result.result())[1].tobytes()
            window["cam1"].update(data=imgbytes)
    
        if event=='terminate':
            break
    else:
        continue
    break

window.close()
print('終了します')

まとめ

今回はFACE01 GRAPHICS ver.1.2.8の機能拡張を紹介しつつ、その検証に「金正恩の影武者説」というゴシップ記事を用いました。
結果的には前回と同様「顔認証処理では本人である」と出力されました。本当に本人なのかそれほど整形で似せているのかまでは分かりませんが面白い題材にはなりました。

最後までお読み頂きありがとうございました。