aa develop

開発と成長

HOG特徴量と主成分分析を用いてAV女優の顔の類以度を可視化する

AV女優の顔写真から求まるHOG特徴量を主成分分析を用いて二次元に落とし込むことで顔の類以度を可視化した.画像数は10000枚ぐらい.結果画像は以下のリンク先. 結果画像(かなりファイルサイズが大きいので注意, ブラウザでは見られないかも)

下の画像はそれを画像数を1000枚ぐらいに落としたもの. f:id:aa_debdeb:20160714111302j:plain 結果として,図の左側と右側で顔の傾きが違うぐらいには分類できているが,顔の細かいディテールまでは考慮できていない.

以下,解説.

まず,使う画像を次のサイトから取得するために,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

参考