[番外編]機械学習プログラミング 日向坂46で顔認識してみた。@Python (1)
(番外編)日向坂46で顔認識
最近流行のAI(機械学習)でプログラムを書いてみました。
といいますか、仕事で顔認識処理を開発することになったので、事前学習ということで休暇中に作成してみたものです。
尚、この一連の記事ではOracleは全く登場しません。
いろいろ調べてみたところ、下記の記事が人気のようでしたので、これらの記事を参考に日向坂46メンバーの顔認識処理を作成してみることにしました。
TensorFlowによるももクロメンバー顔認識(前編・中編・後編)
前編:https://qiita.com/kenmaz/items/4b60ea00b159b3e00100
中編:https://qiita.com/kenmaz/items/ef0a1308582fe0fc5ea1
後編:https://qiita.com/kenmaz/items/8a9c382af3f8397710e9
機械学習で乃木坂46を顏分類してみた
「//aidemy.hatenablog.com/entry/2017/12/17/214715」
機械学習で乃木坂46メンバーの誰に似ているかを判定する簡易Webアプリケーションを作った一連の流れ
https://qiita.com/yu8muraka3/items/81360c48a9c067c2ccdb
何故「日向坂46で顔認識」なのか
前述のサイトを眺めながら、「ももクロ」、「乃木坂46」ときたので、私は当初「欅坂46」で作成しようと思っていました。
そんな折、ふと訪れたココイチで日向坂46のキャンペーンをやっておりました(尚、このキャンペーンは2019年12月26日で終了しております)。
私は特にアイドル好きでもない普通のオッサンなので、よくありがちな「誰が誰だかさっぱりわからない・・・」という状況に陥りました。
ここまでであればさしたる問題ではなかったのですが、事態はより深刻でした。。。
・・・全員同じ顔に見えてしまう。
いくらオッサンとはいえ、これではヤバい!と思い、日向坂46をAIで顔画像認識してみることにしました。
顔画像認識処理の流れ
画像の収集からモデルの作成、認識用の画面作成まで以下の流れで書いてみたいと思います。
- 画像ダウンロード処理
- 顔検知抽出処理
- データ増幅処理
- 学習・モデル生成処理
- 顔認識処理+画面
機械学習を行うにあたっては、まず大量の学習データが必要になります。
今回でしたら日向坂46メンバーの顔画像が必要になります。
Pythonを使用し、インターネット上から画像を収集するところから始まります。
収集した画像は、そのままでは学習データとしては使用できません。
今回は顔認識を行うのが目的ですが、収集した画像は必ずしも顔画像とは限りません。
そこで、2.の処理で収集した画像ファイルから顔検知を行い、顔だけを抽出した画像を作成します。
機械学習は学習データが多いに越したことはありません。そこで学習データを擬似的に増やします。これが3.の記事になります。
ここまで、前半はほぼスクレイピングと画像の加工です。
ある意味、AIにおけるデータ準備の重要性を表しているとも言えるかもしれません。
終盤の「4. 学習・モデル生成処理」まで来ると、ようやくAIらしくなってきます。
ここでようやく収集・加工した画像を学習させます。
そして最後の「5.顔認識処理」にたどりつきます。
前提条件
Pythonのバージョンは3.6.9で動作確認を行っています。
OSはUbuntu18.04。
Pythonのモジュールのバージョンは以下のとおりです。
特に、kerasとnumpyはバージョンの依存性があるようで、numpy1.16.3以降では正常に動作しませんでした。
beautifulsoup4 (4.8.2)
html5lib (1.0.1)
Keras (2.3.1)
Keras-Applications (1.0.8)
Keras-Preprocessing (1.1.0)
lxml (4.4.2)
matplotlib (3.1.2)
numpy (1.16.2)
opencv-python (4.1.2.30)
pandas (0.25.3)
requests (2.18.4)
scipy (1.4.1)
tensorboard (1.14.0)
tensorflow (1.14.0)
tensorflow-estimator (1.14.0)
tensorflow-gpu (1.14.0)
urllib3 (1.22)
GPUはなくても大丈夫なはずです。
それでは早速、画像収集のためのスクレイピング処理を記述してみましょう。
1.画像ダウンロード処理
まずモデルを作成するための画像データの収集です。
こちらは、下記の記事を参考にしました。
<参考サイト> PythonでGoogleの画像を大量スクレープする
https://qiita.com/Jixjia/items/881c03c50c6f07b0b6ab
Googleの画像検索結果から画像のURLを取得し、オリジナルサイトの画像を直接取得します。
Googleの画像取得API(「Google Custom Search API」)を使用する必要はありませんし、検索回数の制限もありません。
1回の検索結果で出力されたURL情報を取得しているだけなので、Googleへの負荷もそれほど大きくはないはず(とはいえ、限度があると思いますので、良識の範囲内で使用しましょう)
ダウンロードのみのプログラムを独立させているのは、ここでダウンロードした画像を目視で確認する必要があるからです。
Google検索で上位になった画像が、必ずしも当人の画像とは限りません。また、画像の形式やサイズが、このあとの処理で使用するには不適切かもしれませんし、別サイトの同一画像が複数枚検出されているかもしれません。
そういった機械学習に不都合な画像は、手動で除外する必要があります。

#========================================
# 日向坂46メンバーの顔認識
# 1.画像ダウンロード処理
#========================================
import os
import sys
import bs4
import urllib
import argparse
import json
import html5lib
# 引数解析
def arg_parse():
parser = argparse.ArgumentParser(description='Options for scraping Google images')
parser.add_argument('-s', '--search', default='日向坂46', type=str, help='search term')
parser.add_argument('-n', '--num_images', default=10, type=int, help='num of images to scrape')
parser.add_argument('-o', '--directory', default='<DEFAULT_SAVE_DIRECTORY>', type=str, help='output directory')
return parser.parse_args()
# 画像のスクレイピング
# 引数1. imageinfo: 画像情報(URL, Type)
# 引数2. max_images: 取得画像数
# 引数3. save_directory: 保存先ディレクトリ名
def scraping(imageinfo, max_images, save_directory):
for idx, (img, Type) in enumerate(imageinfo[0:max_images]):
try:
# 画像タイプが設定されていなければjpgとして扱う
Type = Type if len(Type) > 0 else 'jpg'
print("Downloading image {} ({}), type is {}".format(idx, img, Type))
# 画像のURLに直接アクセスしてダウンロード
raw_img = urllib.request.urlopen(img).read()
# ファイルに保存
f = open(os.path.join(save_directory , "img_"+str(idx)+"."+Type), 'wb')
f.write(raw_img)
f.close()
except Exception as e:
print ("could not load : "+img)
print (e)
# 画像情報(オリジナルURLと画像タイプ)を取得
# 引数1. soup: 画像情報検索結果bs4オブジェクト
def file_url(soup):
# 配列初期化
imageinfo=[]
for imgelem in soup.find_all("div",{"class":"rg_meta"}):
# ou: オリジナル画像のURL
# ity: 画像タイプ(jpg, png等)
link , Type =json.loads(imgelem.text)["ou"] ,json.loads(imgelem.text)["ity"]
if ('facebook.com' not in link) and ('jpg' in Type or 'png' in Type):
imageinfo.append((link,Type))
return imageinfo
# 画像検索結果URLのスクレイピング
# 引数1. query: 検索文字列
def get_soup(query):
url="https://www.google.co.jp/search?q="+urllib.parse.quote(query)+"&source=lnms&tbm=isch"
header={'User-Agent':"Mozilla/5.0 (Windows NT 8.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.5357.134 Safari/597.45"}
return bs4.BeautifulSoup(urllib.request.urlopen(urllib.request.Request(url,headers=header)),'html5lib')
def main():
args = arg_parse()
# 複数のキーワードを"+"で繋げる
query = '+'.join(args.search.split())
max_images = args.num_images
# 保存先ディレクトリの下に、検索キーワードを+で連結してフォルダを作成する
save_directory = args.directory + '/' + query
if not os.path.exists(save_directory):
os.makedirs(save_directory)
# スクレイピングで画像情報を取得
soup = get_soup(query)
# 画像のオリジナルURLと画像タイプを取得
scraping(file_url(soup), max_images, save_directory)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
sys.exit()
起動時の引数として以下の3つを指定できるようにしています。
-s: Google画像検索に渡す検索キーワード、複数の場合は""で括る
-n: ダウンロードする画像の数。常識の範囲内で。初期値は10にしています。
-o: 画像の保存先。カレントディレクトリ以下のサブディレクトリ名を指定します。
コマンドの発行例は以下の通りです。
python3 facerecg1_imagedl.py -s “日向坂46 小坂菜緒" -n 50 -o images
プログラム的に特筆すべきポイントは、、、あまりありません。
一つ挙げると、検索文字列のエンコードには注意です。今回は恐らく日本語を使用すると思いますので。URL全体ではなく検索文字列部分のみをエンコードするのがポイントです。
下記のサイトを参考に解決しました。
URLの日本語エンコーディング(パーセントエンコーディングについて)
https://note.nkmk.me/python-urllib-parse-quote-unquote/
次回は、顔部分抽出処理~データ増幅処理を行います。
最近のコメント