[番外編]機械学習プログラミング 日向坂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/
次回は、顔部分抽出処理~データ増幅処理を行います。
最近のコメント