見出し画像

自分がイケメンであることを証明せよ。

どうもイケメンです!

こんにちは。突然ですが、私はイケメンです。

どれくらいイケメンかと言うと、道を歩いていたら突然女性に話しかけられて、

「ここの草刈ってくれない?」

とか

電車に乗っていたら女子高生から

「ここの席どうぞ」

と言われるくらいにイケメンです。(ちなみに私は30代です。)

しかし、イケメンというのは非常に抽象的な言葉です。つけ麺や担々麺などと比べても非常に抽象的であると言わざるを得ません。

イケメンは抽象的な言葉なので、流石の私でも、本当にイケメンなんですか?と言われると、悩みます。多くの人からイケメンと言われていれば、自分がイケメンだと納得できるのですが、私はイケメンすぎるので、相手も自明すぎるから言う必要ないな、と思うのか、全然イケメンと言われません。日本人はシャイですね。

私はイケメンですか?

こんな記事を書いていると、私がイケメンかどうか悩んできました。

こんな悩みもイケメンが抽象的だからなのです。イケメンなんて言葉、だれが作ったんですか?責任者でてきてください。あと、私がAmazonのkindleで買ったある本の内容が文字化けしていて、読めなかったので2年前くらいにクレームして対応するとの旨返事をいただいたのですが、今も全然治りません。こちらも責任者でてきてください。

そうこう悩んでいるうちに、私がイケメンといえばでおなじみのITエンジニアであることに気が付きました。エンジニアは何でも数値化して考えます。PMは嘘をつくが、数字は嘘をつきません。

イケメンを数値化しよう。

では、イケメンを数値化しようではありませんか?

まずイケメンの定義を決めます。これは簡単です。イケメンと言えば〇ャニーズです。異論ありますか?異論あったらコメントしてください。〇ャニオタにこいつ〇ャニーズディスってますよってチクります。〇ャニーズでいいですね?

さて、非常に理論的にイケメン=〇ャニーズと定義できました。ならば、〇ャニーズの顔の特徴を定量化しましょう。

pythonのface_recognitionというライブラリを使えば、写真中から顔の領域を算出し、さらに128次元の特徴量を求めることができます。

定量化するにしても、まず〇ャニーズの顔を集めなくてはなりません。〇ャニーズのサイトのプロフィールには、写真が掲載されているので、これを収集しましょう。

〇ャニーズの素晴らしい顔をちびちびスクリーンショットすると、なんだかあっちの方向に目覚めてしまう可能性があります。私はエンジニアですし、スクレイピング(ブラウザ自動化)して写真を取得することにしました。

また、ついでなので、face_recongitonで128次元特徴量も取得してしまいましょう。以下のスクリプトを書きました。私は何をしているのでしょうか?

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import chromedriver_binary
import os
import tempfile
import requests
from PIL import Image
import pickle
import face_recognition
import time
import numpy as np

driver = webdriver.Chrome()

face_save_folder = "faces"
os.makedirs(face_save_folder, exist_ok=True)

none_count_max = 5
none_count = 0

for index in range(1,100):

   #url取得
   url = "https://www.johnnys-net.jp/page?id=profile&artist="+str(index)
   driver.get(url)
   time.sleep(1)

   #ページがあるかどうか確認
   page_h2_value = driver.find_element_by_class_name("h2-artist").text
   if page_h2_value == "None": #ページなし
       print("*page_skip")
       none_count += 1
       if none_count_max < none_count:
           print("*page_end")
           break
       continue
   else:
       none_count = 0
       print("*"+page_h2_value+" found.")

   #メイン処理(画像取得→各種保存)
   profile_element = driver.find_element_by_class_name("profile-list")
   profile_item_elements = profile_element.find_elements_by_class_name("profile-list__item")

   for profile_item_element in profile_item_elements:

       #画像リンク取得
       profile_img_element = profile_item_element.find_element_by_class_name("profile-list__img")

       profile_img_href_elem = profile_img_element.find_element_by_tag_name("img")

       image_link_url = profile_img_href_elem.get_attribute("src")

       #名前取得
       profile_name_en_elem = profile_item_element.find_element_by_class_name("profile-list__name-en")

       name = profile_name_en_elem.text

       #特徴量取得
       #画像リンクからローカル(一時ファイル)へ保存
       res = requests.get(image_link_url)
       fp = tempfile.NamedTemporaryFile(dir='./',delete=False)
       fp.write(res.content)
       fp.close()

       #顔画像の特徴量化
       face_img = face_recognition.load_image_file(fp.name)
       os.unlink(fp.name)
       face_loc_list = face_recognition.face_locations(face_img, model="hog")

       if len(face_loc_list) == 0:
           print("face not found(1).")
           continue

       if len(face_loc_list) > 1:
           print("more one face found(1).")
           continue

       face_enc_list = face_recognition.face_encodings(face_img, face_loc_list)

       #保存
       #顔の切り抜き画像を保存
       save_face_file_name = os.path.join(face_save_folder,name+".jpg")
       top, left, bottom, right = face_loc_list[0]
       crop_img = face_img[top:bottom, right:left]
       pil_img = Image.fromarray(crop_img)
       pil_img.save(save_face_file_name)

       #特徴量保存
       save_feature_file_name = os.path.join(face_save_folder,name)
       #f = open(save_feature_file_name, 'wb')
       #pickle.dump(face_enc_list, f)

       np.save(save_feature_file_name, face_enc_list[0])

このスクリプトは、〇ャニーズのプロフィール画像を収集し、facesフォルダに顔領域を保存、また、顔の特徴量をnumpy配列にした上で.npyファイルとして同フォルダに出力します。

実行すると以下のような感じで出力されます。アイコン表示にすると、イケメンがたくさんでなんとも神々しいフォルダになるのですが、こんなんで〇ャニーズと肖像権で争いたくないので、表示できません。

画像1

顔特徴量(npy)ファイルと顔写真(jpg)ファイルが合わせて214件出力されたので、〇ャニーズ107人の特徴量が取得できたことになります。

〇ャニーズを煮詰める。

このままだと、107人の特徴量がとれただけで、自分と比較できませんので、特徴量の平均を取り、重心を求めます。

以下のスクリプトで、npyファイルに保存しておいた128次元のデータをすべて読み込み、列平均を取って、1つの特徴量ファイルします。

import glob
import numpy as np

files = glob.glob("./faces/*.npy")

npy_array = np.empty([0, 128])

for file in files:
   feature = np.load(file)
   npy_array = np.vstack((npy_array, feature))

mean_npy_array = np.mean(npy_array, axis=0)

np.savetxt('mean_feature.csv', mean_npy_array, delimiter=',')
np.save('mean_feature', mean_npy_array)

出力されたmean_feature.npyが「〇ャニーズの特徴量の平均」になります。つまり、〇ャニーズを煮詰めた特徴量です。強いです。やばいです。

テストする。

この特徴量と、自分の特徴量の差を求めれば自分がどれだけイケメンか証明できるはずです。まあ、結果は自明なので焦らず、この特徴量比較が正しく機能するのかテストしてみます。

これには、イケメンと、個性的な顔の方の写真を用意し、比較して、イケメンの場合は差が小さく、個性的な顔の場合は差が大きく出ればよいです。

〇ャニーズ以外のイケメンは、全裸監督、スプーンにうつらない小栗旬(つまり本物)、昔本当に悪かったチャラ男芸人さん、税金を払うのをうっかり忘れちゃった芸人さんが思いつきますので、これを使います。

個性的な顔のほうは、個性的な顔の人が個性的であるかどうかを私が判断するのはおこがましいのですが、吉本という企業が個性的な顔ランキングなんてのをやっていた気がするので、そこで上位の、〇んこんさん、稲〇さん、悪くない方のチャラ男芸人さん、岩〇さんを使ってみます。

なお、比較のため、以下のスクリプトを書きました。暇なんでしょうか?暇なんでしょうね。

import face_recognition
import argparse

import datetime
import os
from PIL import Image
import numpy as np

def calc_loc_size(loc):

   top, left, bottom, right = loc

   return (bottom - top)*(left - right)

def crop_img_by_locs_and_save(img, loc, save_path):

   top, left, bottom, right = loc
   crop_img = img[top:bottom, right:left]
   pil_img = Image.fromarray(crop_img)
   pil_img.save(save_path)

   return

if __name__ == '__main__':

   parser = argparse.ArgumentParser(description='')

   parser.add_argument('feature', help='基準となる顔特徴量ファイル')
   parser.add_argument('targetface', help='比較対象の顔画像(.jpg,.png)')

   args = parser.parse_args()

   #顔画像読み込み
   target_face = face_recognition.load_image_file(args.targetface)

   #顔位置取得
   #loc = face_recognition.face_locations(img, model="cnn") for GPU
   target_face_loc_list  = face_recognition.face_locations(target_face, model="hog")

   if len(target_face_loc_list) == 0:
       print("入力となる比較対象の顔画像に顔の候補が1つも検出できませんでした。処理を終了します。")

   #対象の決定。顔が複数あった場合、一番領域面積が大きいものを対象とする。
   target_face_loc = None

   if len(target_face_loc_list) == 1:
       target_face_loc = target_face_loc_list[0]
   else:
       target_face_loc = target_face_loc_list[0]
       target_face_loc_size = calc_loc_size(target_face_loc)
       for loc in target_face_loc_list:
           loc_size = calc_loc_size(target_face_loc)
           if target_face_loc_size < loc_size:
               target_face_loc_size = loc_size
               target_face_loc = loc

   #特徴量算出
   target_face_encording = face_recognition.face_encodings(target_face, [target_face_loc])

   #特徴量読み込み
   base_face_encording = np.load(args.feature)

   #特徴量距離の算出
   distances = face_recognition.face_distance(target_face_encording,base_face_encording)
   distance = distances[0]

   #結果出力
   #コマンドライン
   print("The distance of two face is "+str(distance))

テスト結果は以下です。

【イケメンサンプル】

税金をうっかり払い忘れてた芸人さん:0.420

全裸監督:0.437

スプーンにうつらない小栗旬:0.435

昔本当に悪かったチャラ男芸人さん:0.449

【個性的な顔サンプル】

〇んこんさん:0.588

稲〇さん:0.581

悪くない方のチャラ男芸人:0.504

岩〇さん:0.495

以上

イケメンサンプルのほうは0.4弱と差が小さいのに対し、個性的な顔のサンプルのほうは0.5程度~0.6と差が大きいことが分かります。中々いいですね。

私はイケメンで...

では、自分がイケメンであることを証明するときがやってきました。

肝心の自分と〇ャニーズの特徴量を煮詰めたやつを比較してみます。ポチッ

face_distance_by_feature_npy.py mean_feature.npy test_faces\my_face.jpg
The distance of two face is 0.46604342227446854

0.466....

....数字は嘘をつかないが、噓つきは数字を使います。

.....

寝ます。

....まぁ普通かな












この記事が気に入ったらサポートをしてみませんか?