HOG特徴量と主成分分析を用いてAV女優の顔の類以度を可視化する
AV女優の顔写真から求まるHOG特徴量を主成分分析を用いて二次元に落とし込むことで顔の類以度を可視化した.画像数は10000枚ぐらい.結果画像は以下のリンク先. 結果画像(かなりファイルサイズが大きいので注意, ブラウザでは見られないかも)
下の画像はそれを画像数を1000枚ぐらいに落としたもの. 結果として,図の左側と右側で顔の傾きが違うぐらいには分類できているが,顔の細かいディテールまでは考慮できていない.
以下,解説.
まず,使う画像を次のサイトから取得するために,pythonのBeautifulSoupを用いてクローラーを作る. AV女優情報 - DMM.R18
crawler.py
#! -*- coding: utf-8 -*-
import os
import re
import csv
import time
import urllib2
from bs4 import BeautifulSoup
url_template = "http://actress.dmm.co.jp/-/list/=/keyword=%s/page=%d/"
header_template = re.compile(u"[0-9]+人 - 全(\d+)ページ中 [0-9]+ページ目")
def download_image(image_url, local_image_path):
"""
画像をダウンロードする関数
"""
print "downloading " + image_url
local_image_file = open(local_image_path, "wb")
image_data = urllib2.urlopen(image_url, "html.parser").read()
local_image_file.write(image_data)
local_image_file.close();
time.sleep(10.0) # 10秒待機
if __name__ == "__main__":
hiragana = ["a", "i", "u", "e", "o",
"ka", "ki", "ku", "ke", "ko",
"sa", "si", "su", "se", "so",
"ta", "ti", "tu", "te", "to",
"na", "ni", "ne", "no",
"ha", "hi", "hu", "he", "ho",
"ma", "mi", "mu", "me", "mo",
"ya", "yu", "yo",
"ra", "ri", "ru", "re", "ro",
"wa"]
# CSVを作成する
image_csv = open("./image.csv", "w")
csv_writer = csv.writer(image_csv)
for h in hiragana:
current_page = 1
max_page = 10000
while current_page <= max_page:
# gets html
url = url_template % (h, current_page)
html = urllib2.urlopen(url).read()
time.sleep(10.0);
soup = BeautifulSoup(html, "html.parser")
if current_page == 1: # 現在の先頭文字に対する最大ページ数を取得する
td_header = soup.find_all("td", attrs = {"class": "header"})[1] # 2つ目の
td_header.find("br").extract() # タグ内にある
タグを削除する(しないとタグ内の文字を取得できない)
td_header_string = td_header.string
match = header_template.match(td_header_string)
max_page = int(match.groups(1)[0])
# 画像urlのリストを作成する
td_pics = soup.find_all("td", attrs = {"class": "pic"})
for td_pic in td_pics:
image_url = td_pic.find("img").get("src")
image_name =os.path.basename(image_url)
actress_name = td_pic.find("img").get("alt").encode("utf-8")
local_image_path = "./images/" + image_name
csv_writer.writerow([image_name, image_url, local_image_path, actress_name])
current_page += 1
image_csv.close()
# 画像をダウンロードする
image_csv = open("./image.csv", "r")
csv_reader = csv.reader(image_csv)
for row in csv_reader:
image_url = row[1]
local_image_path = row[2]
download_image(image_url, local_image_path)
image_csv.close()
次に,取得した画像からOpenCVのHOGDescriptorを用いてHOG特徴量を算出し,それをscikit-learnの主成分分析により2次元に落としこむ.
hog_pca.py
#! -*- coding: utf-8 -*-
import cv2
import numpy as np
import csv
from sklearn import decomposition
if __name__ == "__main__":
descriptors_list = []
image_csv = open("./image.csv", "r")
csv_reader = csv.reader(image_csv)
for row in csv_reader:
image_name = row[0]
image_url = row[1]
local_image_path = row[2]
actress_name = row[3]
# HOG特徴量を求める
print "analyzing " + image_name
image = cv2.imread(local_image_path)
image = image[0:64, 0:64]
gray_image = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY)
hog = cv2.HOGDescriptor("./hog.xml")
descriptors = hog.compute(gray_image)
descriptors_list.append(descriptors.flatten())
image_csv.close()
# 主成分分析で二次元データにする
pca = decomposition.PCA(n_components = 2)
pca.fit(descriptors_list)
pca_descriptors_list = pca.transform(descriptors_list)
# CSVに記録する
pca_csv = open("./hog_pca.csv", "w")
csv_writer = csv.writer(pca_csv)
image_csv = open("./image.csv", "r")
csv_reader = csv.reader(image_csv)
for idx, row in enumerate(csv_reader):
csv_writer.writerow([row[0], row[1], row[2], row[3],
pca_descriptors_list[idx][0],
pca_descriptors_list[idx][1]])
pca_csv.close()
image_csv.close()
最後にPILライブラリを用いて,顔画像を二次元データを基にそれぞれの位置に貼り付けることで,類以度の可視化画像を作成する.
visualizer.py
#! -*- coding: utf-8 -*-
import csv
import random
from PIL import Image
if __name__ == "__main__":
hog_pca_image = Image.new("RGB", (30000, 30000), (255, 255, 255))
pca_csv = open("./hog_pca.csv", "r")
csv_reader = csv.reader(pca_csv)
for row in csv_reader:
image_name = row[0]
image_url = row[1]
local_image_path = row[2]
actress_name = row[3]
v1 = float(row[4])
v2 = float(row[5])
image = Image.open(local_image_path, "r")
# the range of v is [-2.52784, 2.66311]
position = (int(v1 * 5000) + hog_pca_image.size[0] / 2,
int(v2 * 5000) + hog_pca_image.size[1] / 2)
hog_pca_image.paste(image, position)
# hog_pca_image.show()
hog_pca_image.save("results/all.jpg")
# hog_pca_image = Image.new("RGB", (6000, 6000), (255, 255, 255))
#
# pca_csv = open("./hog_pca.csv", "r")
# csv_reader = csv.reader(pca_csv)
# for row in csv_reader:
# if random.random() < 0.1:
# image_name = row[0]
# image_url = row[1]
# local_image_path = row[2]
# actress_name = row[3]
# v1 = float(row[4])
# v2 = float(row[5])
#
# image = Image.open(local_image_path, "r")
# # the range of v is [-2.52784, 2.66311]
# position = (int(v1 * 1000) + hog_pca_image.size[0] / 2,
# int(v2 * 1000) + hog_pca_image.size[1] / 2)
# hog_pca_image.paste(image, position)
#
#
# # hog_pca_image.show()
# hog_pca_image.save("results/small.jpg")
プロジェクトはこちら.
aa-debdeb/HOG_PCA_AV
参考
- PythonとBeautiful Soupでスクレイピング - Qiita
- anoron2012w.pdf
- python - OpenCV HOGDescriptor.compute error - Stack Overflow
- how initialize python opencv HOGDescriptor with parameters [on hold]
- Pythonで主成分分析 - old school magic
- sklearn.decomposition.PCA — scikit-learn 0.17 documentation