企画趣旨
「結局、顔面偏差値が一番高いアイドルは誰なのか?」
アイドル界隈ではよく耳にする話題です。
アイドルのヲタクに聞いたら、自分の推しているアイドルの名前が返ってくると思います。
たしかにそれは正しいと思います。
でもやっぱり客観的な意見も気になります。
客観的な意見を得るためにアンケートという形もありですが、
せっかく顔認識が発展しているのだからAIから意見をもらいたいなと思いました。
今回はその備忘録です。
私の顔で試したところスコアは100点満点56点でしたが(←ちょっと不服)、
果たしてNo.1アイドルとそのスコアは??
(結果は目次番号15にあります)
全体の流れ
今回の企画は大きく3部構成になっております。
- [前編]画像ファイルのスクレイピング(目次番号1~6まで)
- [中編]Face++のAPIによる判定(目次番号7~11まで)
- [後編]DataFrameのビュー作成と可視化(目次番号12~16まで)
前編の目次
- 1.前編のゴール
- 2.利用する技術
- 3.ホームページから画像をスクレイピング
- 4.取得した情報をDataFrameに格納
- 5.DataFramをCSV出力
- 6.サンプルコード
1.前編のゴール
サイトから複数の画像ファイルをスクレイピングしDataFrameに格納します。
2.利用する技術
- 顔認識(中編で使用します)
- 言語
- Python 38
- 環境
- Windows
- 主なライブラリ
- BeautifulSoup
- Pandas
- BIツール(後編で使用します)
- MotionBoard
3.ホームページから画像をスクレイピング
今回はももいろクローバーZが所属することで有名な**「スターダスト」**という事務所のアイドルをターゲットとします。
所属するアイドルの数は65人です(調査時点)。
スクレイピング対象のホームページは下記です。
https://www.stardust.co.jp/stardustplanet/
このホームページから下記2点を目指します
- 画像ファイルの取得
- 人物名の取得
画像ファイルの取得
まずは画像がどのタグの中に存在するのか確認します。
一番左上の「高城れに」さんでGoogleの検証をしてみます。
これを見ると**img src
**の部分と検討が付くと思います。
ただここで注意点があります。
この**img
**だけだと関係ない画像まで取得されてしまいます。
例えばアイドルの横にあるグループのロゴ。
これも**img
**タグで囲まれてます。
アイドルさんとロゴの区別する点として、**img
**の1つ外側のタグが違います。
・アイドル ⇒ div
・ロゴ ⇒ a
そのため今回は
**「div
の中のimg
」**という指定をしました。
人物名の取得
次に人物名を取得します。
と言ってもタグの指定は画像ファイルとまったく同じで今回のホームページは大丈夫でした。
これは画像ファイルと人物名が同じdiv
の中のimg
に存在したからです。
4.取得した情報をDataFrameに格納
以下のような3つのカラムを持つDataFrameを用意します。
そして取得した情報をフィールドにセットします。
for文を回してこのDataFrameに全員の情報を格納します。
name | image_url | file_path |
---|---|---|
高城れに | http://img.stardust.co.jp/upload/talent/photo/2/238.L.jpg | 保存先のファイルパス |
5.DataFramをCSV出力
2次利用しやすいようにテーブルをCSVファイルとして出力します。
6.サンプルコード
今回の内容をコードに落とすと以下のようになります。
import requests
from bs4 import BeautifulSoup
import os
import pandas as pd
# 取得したファイルを格納するローカルディレクトリ
images_dir = 'YOUR_LOCAL_DIRECTLY'
# 取得したデータフレームを格納するアウトプットディレクトリ
outputfiles_dir = 'YOUR_OUTPUT_DIRECTLY'
# スクレイピング対象のURL
url = 'https://www.stardust.co.jp/stardustplanet/'
headers = {'User-Agent': 'Mozilla/5.0'}
soup = BeautifulSoup(requests.get(url, headers=headers).content, 'lxml')
# 取得した情報を格納する変数
images = []
names = []
files_path = []
# 画像URLの取得
# <div>タグの中の<img>のsrcが画像のURL
for image_url in soup.select('div > img'):
# imagesのリストに取得した画像URLを格納
images.append('http:' + image_url.get('src'))
# 人物名の取得
# <div>タグの中の<img>のaltが人物名
for name in soup.select('div > img'):
# namesのリストに取得した人物名を格納
names.append(name.get('alt'))
for target in images:
# files_pathのリストにローカルディレクトリと画像ファイル名を連結したパスを格納
files_path.append(
images_dir + target.split('/')[-1])
# 画像URLをローカルファイルとして保存する
re = requests.get(target)
with open(images_dir + target.split('/')[-1], 'wb') as f:
f.write(re.content)
# リストの値をテーブルのカラムへの格納
data = {
'name': names,
'image_url': images,
'file_path': files_path
}
# テーブルを定義
df = pd.DataFrame(data)
# テーブルをCSVファイルとして出力する
df.to_csv(
outputfiles_dir + 'storeimages.csv')
print('終了')
以上が[前編]です。
中編の目次
-
- 中編のゴール
-
- Face++とは?
-
- APIを利用する
-
- 判定結果を格納
- 11 サンプルコード
7. 中編のゴール
画像ファイルをFace++を利用して顔面偏差値を判定し、結果をDataFrameへ格納します
8. Face++とは?
ホームページはこちらです。
https://www.faceplusplus.com/
私は最近知りました。
出会いは、「顔認識使ってみたいなー」「けど、有料サービスはちょっとハードル高いなー」
という思いから、**「顔認識 無料」**みたいなググり方。
こちらのサイトがFace++について詳しく勉強になりました。
ありがとうございます。
ひと言でまとめると、
「中国国家でも利用される顔認識システムの会社のサービス」
です。
そして今回の企画のカギ
**「顔面偏差値」として解釈させていただいている「beauty」**というスコアを取得することができます。
Face++のDetect APIを利用します。
公式ドキュメントによる「beauty」の説明は下記。
**「美しさを100点満点で測定した値」**だそうです。
Result of beauty analysis, including the following fields. The value of each field is a floating-point number with 3 decimal places between [0,100]. Higher beauty score indicates the detected face is more beautiful.
9. APIを利用する
ユーザーの登録が必要です。
登録の手順はこちらが参考になりました。
ありがとうございます。
ユーザー登録することでAPI KEYを取得できます。
大まかな流れは下記です。
画像ファイルをbase64 形式でエンコード
↓
パラメーターをセット
ここでAPI KEY やReturnしてほしい属性を設定。
今回は「性別、年齢、beauty」を設定。
↓
リクエスト
↓
JSON形式でレスポンスが返ってくる
10. 判定結果を格納
パラメーターで設定した「性別、年齢、beauty」をリストに入れて、そのリストの値を下記のようなDataFrameに格納します。
beautyは male_scoreとfemale_scoreとして返ってきます。
そのためリストもカラムも2つずつ用意します。
input_file | gender | age | beauty_male | beauty_female |
---|---|---|---|---|
判定する画像ファイル | 性別 | 年齢 | 男性としての顔面偏差値 | 女性としての顔面偏差値 |
11. サンプルコード
今回の内容をコードに落とすと以下のようになります。
import requests
import json
import glob
import base64
import pandas as pd
# face++のURL
url = 'https://api-us.faceplusplus.com/facepp/v3/detect'
# 取得したイメージファイルを格納しているローカルディレクトリ
images = 'YOUR_IMAGE_FILES_DIRECTLY'
# 取得したデータフレームを格納するアウトプットディレクトリ
outputfiles_dir = 'YOUR_OUTPUT_DIRECTLY'
# 取得したJSONデータを格納リスト
gender = []
age = []
beauty_male = []
beauty_female = []
# ファイルパスを変数に格納
file_path = glob.glob(
images)
list_df = pd.DataFrame(
columns=['file_path', 'gender', 'age', 'beauty_male', 'beauty_female'])
# ファイル分繰り返して
for input_file in file_path:
# バイナリファイルを開く。withはclose自動
with open(input_file, 'rb') as f:
img_file = base64.encodebytes(f.read())
# パラメーターをセット
param = {
'api_key': 'YOUR_API_KEY',
'api_secret': 'YOUR_API_SECRET',
'image_base64': img_file,
'return_landmark': 1,
'return_attributes': 'gender,age,beauty'
}
# POSTする
res = requests.post(url, data=param)
# JSONファイルを読み込む
data = json.loads(res.text)
# JSON文字列として整形
data_json = json.dumps(data, indent=4)
# カラムにJSONデータを格納する
for face in data['faces']:
if 'attributes' in face:
gender = face['attributes']['gender']['value']
age = face['attributes']['age']['value']
beauty_male = face['attributes']['beauty']['male_score']
beauty_female = face['attributes']['beauty']['female_score']
#顔認識できない場合はNoneをセット
else:
gender = None
age = None
beauty_male = None
beauty_female = None
# テーブルにレコードを追加
temp_se = pd.Series(
[input_file, gender, age, beauty_male, beauty_female], index=list_df.columns)
list_df = list_df.append(temp_se, ignore_index=True)
# CSVファイルへ出力
list_df.to_csv(
outputfiles_dir+'detectapi.csv')
print('finish')
以上が[中編]です。
さあラスト[後編]です。
後編の目次
-
- 後編のゴール
-
- キー項目の作成
-
- 左外部結合
-
- BIツールで可視化&結果発表
-
- サンプルコード
12. 後編のゴール
2つのDataFrameを結合し、BIツールで可視化します。
13. キー項目の作成
前編と中編でそれぞれ作成したテーブルを見てみましょう。
- 前編で作ったDataFrame:storeimages.csv
name | image_url | file_path |
---|---|---|
高城れに | http://img.stardust.co.jp/upload/talent/photo/2/238.L.jpg | 保存先のファイルパス |
- 中編で作ったDataFrame:detectapi.csv
input_file | gender | age | beauty_male | beauty_female |
---|---|---|---|---|
判定する画像ファイル | 性別 | 年齢 | 男性としての顔面偏差値 | 女性としての顔面偏差値 |
これら2つのDataFrameを1つにします。
2つを結合するためには共通するキー項目を作る必要があります。
今回は、**「画像ファイル名」**をキー項目とします。
それぞれのテーブルからは下記のようにキー項目を作成します。
storeimagesは「image_url」から文字列をsplitします。
detectapiは「input_file」から文字列をsplitします。
14. 左外部結合
こうして「key」というカラムができたら2つのテーブルを結合します。
storeimagesを親としてdetectapiを結合します。
今回はどっちが親でも関係ないです。
storeimageのレコードは要するに画像の一覧です。
このテーブルのレコードは結合落ちしてほしくないので親にしました。
そして結合したあと、
顔面偏差値の降順にソートしました。(これは別に必要ないです。趣味です。
最後に必要なカラムだけに絞った結果が下記です。
name | beauty_female | age |
---|---|---|
人物名 | 顔面偏差値 | 推定年齢 |
15. BIツールで可視化&結果発表
最後は結合して作ったDataFrameを可視化します。
ランキング表形式での可視化が分かりやすいなと思いました。
表形式での可視化はPythonだとちょっと苦手かなという印象が個人的にあります。
そこでMotionBoardというBIツールを使って可視化してみました。
(以上写真はhttps://www.stardust.co.jp/stardustplanet/)
冒頭の通りですが、
ちなみに試しに自分の写真を判定してもらったところ
スコアは56点でした。。
かなしい。
アイドルさんは当たり前ですが顔面偏差値高いです!
以上で今回の企画は終了です。
16. サンプルコード
今回の内容をコードに落とすと以下のようになります。
import numpy as np
import pandas as pd
# storeimages.csv
storeimages_csv = 'YOUR_PATH_storeimages.csv'
# detectapi.csv
detectapi_csv = 'YOUR_PATH_detectapi.csv'
# 取得したデータフレームを格納するアウトプットディレクトリ
outputfiles_dir = 'YOUR_OUTPUT_DIRECTLY'
# テーブルとして読み込む
df_images = pd.read_csv(
storeimages_csv)
# テーブルとして読み込む
df_detectapi = pd.read_csv(
detectapi_csv)
# 2つのテーブルを結合するキー項目を作成。
# イメージファイル名をキー項目とする
df_images['key'] = df_images['image_url'].str.rsplit('/', 1).str[1]
df_detectapi['key'] = df_detectapi['file_path'].str.rsplit('\\', 1).str[1]
# キー項目で結合。
df_view = df_images.merge(df_detectapi, how='left', on='key')
# beautyを降順ソート
df_view = df_view.sort_values(
'beauty_female', ascending=False, na_position='last')
df_view = df_view.loc[:, ['name', 'beauty_female', 'age']]
# CSV出力
df_view.to_csv(
outputfiles_dir+'view.csv')
# view.csvをBIツールで可視化する