顔の黄金比などを判定するのに使う比率を用いて
美人具合をランキングづけしてみました。

優勝者は誰でしょうか?

解説動画はこちら




さてやり方ですが
opencvを使って顔のポイントを取り
各パーツの長さを測ります。
そこから比率を出していきます。

顔のポイントを見るコードはこんな感じになります。
(要opencv,dlibのインストール)
import os
import time
import numpy as np
import cv2
import dlib
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as patches
%matplotlib inline

detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("../shape_predictor_68_face_landmarks.dat")

image_path = 'img/hashikan.png'
image = cv2.imread(image_path, cv2.IMREAD_COLOR)
rects = detector(image, 1)
for rect in rects:
    landmarks = np.matrix([[p.x, p.y] for p in predictor(image, rect).parts()])
fig, ax = plt.subplots(figsize=(16,9))
frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
for d in rects:
    parts = predictor(frame, d).parts()
    ax.scatter([point.x for point in parts], [point.y for point in parts])
    for k, point in enumerate(parts):
        ax.text(point.x, point.y, k)
    ax.imshow(frame)
    plt.tick_params(labelbottom=False,labelleft=False,labelright=False,labeltop=False)
    plt.show()

1000nenn

顔は68ポイントの点を取ることができます。

今回のランキングは下記の方々10人の写真で行いました。
橋本環奈 さん
有村架純 さん 
石原さとみ さん 
新垣結衣 さん
広瀬すず さん 
波瑠 さん
綾瀬はるか さん
浜辺美波 さん
桜井日奈子 さん
沢尻エリカ様


顔のパーツから比率を出すコードはこちら
def fwhrs(parts):
    height = parts[8].y - parts[21].y
    width = parts[16].x - parts[0].x
    fwhr = width/height
    return round(fwhr, 3)

def heights(parts):
    top_height = parts[33].y - parts[21].y
    bottom_height = parts[8].y - parts[33].y
    return top_height,bottom_height

def widths(parts):
    left_face = parts[16].x - parts[45].x
    left_eye = parts[45].x - parts[42].x
    middle_face = parts[42].x - parts[39].x
    right_eye = parts[39].x - parts[36].x
    right_face =parts[36].x - parts[0].x
    return left_face, left_eye, middle_face, right_eye,right_face

def mouses(parts):
    top_mouse = parts[57].y - parts[33].y
    bottom_mouse = parts[8].y - parts[57].y
    return top_mouse, bottom_mouse

df = pd.DataFrame()
for file_name in os.listdir('img'):
    frame = cv2.imread('img/'+file_name)
    dets = detector(frame[:, :, ::-1])
    if len(dets) > 0:
        parts = predictor(frame, dets[0]).parts()
        fwhr = fwhrs(parts)  
        top_height,bottom_height = heights(parts)
        left_face, left_eye, middle_face, right_eye,right_face = widths(parts)
        top_mouse, bottom_mouse = mouses(parts)
        t_dict = {'name':[file_name.replace('.png','')],
                  'fwhr':[fwhr],
                  'top_height':[top_height],
                  'bottom_height':[bottom_height],
                  'left_face':[left_face],
                  'left_eye':[left_eye],
                  'middle_face':[middle_face],
                  'right_eye':[right_eye],
                  'right_face':[right_face],
                  'top_mouse':[top_mouse],
                  'bottom_mouse':[bottom_mouse]}
        tmp = pd.DataFrame(t_dict)
        df = pd.concat([df,tmp])

df['Height-Ratio'] = df['top_height']/df['bottom_height']
df['Width-Ratio1'] = df['left_face']/df['middle_face']
df['Width-Ratio2'] = df['left_eye']/df['middle_face']
df['Width-Ratio3'] = df['right_eye']/df['middle_face']
df['Width-Ratio4'] = df['right_face']/df['middle_face']
df['Mouse-Ratio'] = df['top_mouse']/df['bottom_mouse']

ratios = ['Height-Ratio','Width-Ratio1','Width-Ratio2','Width-Ratio3','Width-Ratio4','Mouse-Ratio','fwhr']
df[ratios].mean()

difs = list(df[ratios].mean())
df['DIFF1'] = abs(difs[0] - df['Height-Ratio'] )
df['DIFF2'] = abs(difs[1]- df['Width-Ratio1'] )
df['DIFF3'] = abs(difs[2]- df['Width-Ratio2'] )
df['DIFF4'] = abs(difs[3]- df['Width-Ratio3'] )
df['DIFF5'] = abs(difs[4]- df['Width-Ratio4'] )
df['DIFF6'] = abs(difs[5]- df['Mouse-Ratio'] )
df['DIFF7'] = abs(difs[6] - df['fwhr'] )
df['total']   = df['DIFF1'] + df['DIFF2'] + df['DIFF3'] + df['DIFF4'] + df['DIFF5'] + df['DIFF6'] + df['DIFF7']

columns = ['name','total','fwhr','Height-Ratio','Width-Ratio1','Width-Ratio2','Width-Ratio3','Width-Ratio4','Mouse-Ratio']
df[columns].sort_values('total')

顔のパーツの比率を出しまして
10人の平均を出しました。

その平均との乖離が一番少ない人が
美人だろうということでランキング付けしています。

結果は
nametotal
sawarizi0.274496
haru0.475337
gakky0.545178
ishiharasatomi0.561904
hashikan0.56951
arimura0.660029
sakurai0.728494
hamabeminami0.847156
ayase0.918146
hirose1.051217

ということで
意外な結果が出ました!!!

この10人の平均に一番近かったのは
この方
sawarizi

思いっきしファイル名間違えたのですが
各パーツの整い方が一番平均に近いということで
選ばれました。

今さらですが、このやり方だと
単純に平均に近いかどうかだけなので
美人かどうかの判断にはならないですよねーー

やはり各パーツの比率とかをもっと細かくとって
機械学習で絶対的な美人度を点数化する奴を
作らないとアレですよね。

絶対美人は誰なんだろうか
日本人全員にアンケートとってみたい!!