ブログ名

顔認証AI:学習用の実装 前編

はじめに

こんにちは!「顔認証AI:学習用の実装 前編」 を開いていただきありがとうございます!
前回記述した通り、今回は実際にプログラムを組んでいきます。

今回作るもの

本当は機械学習の部分も実装しようと思ったのですが、思ったよりも長くなってしまいそうなので急遽半分に分けて、もう半分を次回に持ち越すことにしました!

本来この回で描こうとしていたのは、

1. 画像データの水増し
2. 増やした画像を学習データとテストデータに振り分け
3. 機械学習の下準備
4. 機械学習
↑こんな感じのものなのですが、半分に分けて 「1. 画像データの水増し」と「2. 増やした画像を学習データとテストデータに振り分け」 について書いていきます!

実装

import os
import cv2
import glob
from scipy import ndimage
from posix import listdir
import shutil
import random

# 加工前の画像ディレクトリのパス
original_dir_path = "./faces/original/"
# 加工後の画像ディレクトリのパス
augumente_faces_dir_path = "./faces/augumente_faces/"
# テストディレクトリのパス
test_dir_path = "./faces/test/"

# 水増しの準備
def data_augumente_pre():
    # 水増しする
    original_dir = os.listdir("./faces/original/")
    for name in original_dir:
        # DS.Storeを取ってきたらcontinue
        if name is ".DS.Store":
            continue

        # 確認用の表示
        print(name + "さんの顔を水増しします。")
        # 水増しする人物の画像データを呼び出す
        faces = listdir(original_dir_path + name)
        # 加工した画像を置く場所を確保
        os.makedirs(augumente_faces_dir_path + name, exist_ok=True)

        # 一つずつ加工していく
        for i in len(faces):
            # 画像読み込み
            face_img = cv2.imread(original_dir_path + name + "/" + faces[i])
            data_augumente(face_img, name)


# 画像を加工して水増しする
def data_augumente(face_img, name):

    rotate_left = -15
    rotate_center = 0
    rotate_right = 15

    # 左回転
    img_rotate_left = ndimage.rotate(face_img, rotate_left)
    cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_left.jpg", img_rotate_left)
    threshold(img_rotate_left, rotate_left, name)
    gaussian_blur(img_rotate_left, rotate_left, name)
    reverse(img_rotate_left, rotate_left, name)

    # 正面
    img_rotate_center = ndimage.rotate(face_img, rotate_center)
    cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_center.jpg", img_rotate_center)
    threshold(img_rotate_center, rotate_center, name)
    gaussian_blur(img_rotate_center, rotate_center, name)
    reverse(img_rotate_center, rotate_center, name)

    # 右回転
    img_rotate_right = ndimage.rotate(face_img, rotate_right)
    cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_rigt.jpg", img_rotate_right)
    threshold(img_rotate_right, rotate_right, name)
    gaussian_blur(img_rotate_right, rotate_right, name)
    reverse(img_rotate_right, rotate_right, name)

# ぼかし処理で水増し
def gaussian_blur(face_img, rotate_value, name):
    # ぼかし処理
    img_filter = cv2.GaussianBlur(face_img, (5, 5), 0)
    # 保存したファイル名
    file_name = "/" + name + "_GaussianBlur_" + rotate_value + ".jpg"
    # 書き込み
    cv2.imwrite(augumente_faces_dir_path + name + file_name, img_filter)

# 閾値処理で水増し
def threshold(face_img, rotate_value, name):
    # 閾値処理
    img_thr = cv2.threshold(face_img, 100, 255, cv2.THRESH_TOZERO)[1]
    # 保存したファイル名
    file_name = "/" + name + "_threshold_" + rotate_value + ".jpg"
    # 書き込み
    cv2.imwrite(augumente_faces_dir_path + name + file_name, img_thr)

# 反転処理で水増し
def reverse(face_img, rotate_value, name):
    # 反転処理
    img_flip = cv2.flip(face_img, 1)
    # 保存したファイル名
    file_name = "/" + name + "_flip_" + rotate_value + ".jpg"
    # 書き込み
    cv2.imwrite(augumente_faces_dir_path + name + file_name, img_flip)


# 画像データ一覧の中からランダムにテストデータに移行
def random_sort():
    random_v = 0.2
    names = os.listdir(augumente_faces_dir_path)
    for name in names:
        # DS.Storeを取ってきたらcontinue
        if name is ".DS.Store":
            continue
        # testディレクトリに人名のディレクトリを作成
        os.mkdir(test_dir_path + name)
        # 水増しした画像データの一覧取得
        imgs = os.listdir(augumente_faces_dir_path + name)
        # imgsの数を元に、その中の2割の番号を取得
        random_numbers = random.sample(range(len(imgs)), k=round(len(imgs)*random_v))

        # ランダムに選んだ番号を元に画像を移動
        for random_number in random_numbers:
            shutil.move(augumente_faces_dir_path + name + "/" + imgs[random_number], test_dir_path + name)

if __name__ == '__main__':
    data_augumente_pre()
    random_sort()

実装の解説

それでは解説していきます。

全体の準備

import os
import cv2
import glob
from scipy import ndimage
from posix import listdir
import shutil
import random

# 加工前の画像ディレクトリのパス
original_dir_path = "./faces/original/"
# 加工後の画像ディレクトリのパス
augumente_faces_dir_path = "./faces/augumente_faces/"
# テストディレクトリのパス
test_dir_path = "./faces/test/"

↑まずは準備。各種インポートと各ディレクトリのパスを変数にしておきます。

水増しの準備

# 水増しの準備
def data_augumente_pre():
    # 水増しする
    original_dir = os.listdir("./faces/original/")
    for name in original_dir:
        # DS.Storeを取ってきたらcontinue
        if name is ".DS.Store":
            continue

        # 確認用の表示
        print(name + "さんの顔を水増しします。")
        # 水増しする人物の画像データを呼び出す
        faces = listdir(original_dir_path + name)
        # 加工した画像を置く場所を確保
        os.makedirs(augumente_faces_dir_path + name, exist_ok=True)

        # 一つずつ加工していく
        for i in len(faces):
            # 画像読み込み
            face_img = cv2.imread(original_dir_path + name + "/" + faces[i])
            rotate(face_img, name)

↑画像を加工して増やす関数。

# 水増しする
    original_dir = os.listdir("./faces/original/")

originalディレクトリに保存してある名前全てに適用したいのでoriginal_dirに登録してある名前の一覧を取っておきます。次のループで取得した名前を一つづつ回していきます。

 # 水増しする人物の画像データを呼び出す
        faces = listdir(original_dir_path + name)
        # 加工した画像を置く場所を確保
        os.makedirs(augumente_faces_dir_path + name, exist_ok=True)

名前を取得できたら、その人物の画像データの一覧を取得します。ついでにtestディレクトリにその人物のテストデータを入れるためのディレクトリを作っておきます。

# 画像読み込み
            face_img = cv2.imread(original_dir_path + name + "/" + faces[i])
            data_augumente(face_img, name)

最後にループの中で画像を一つずつ読み込み、data_augumenteで本格的な加工に入る。

水増し

# 画像を加工して水増しする
def data_augumente(face_img, name):

    rotate_left = -15
    rotate_center = 0
    rotate_right = 15

    # 左回転
    img_rotate_left = ndimage.rotate(face_img, rotate_left)
    cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_left.jpg", img_rotate_left)
    threshold(img_rotate_left, rotate_left, name)
    gaussian_blur(img_rotate_left, rotate_left, name)
    reverse(img_rotate_left, rotate_left, name)

    # 正面
    img_rotate_center = ndimage.rotate(face_img, rotate_center)
    cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_center.jpg", img_rotate_center)
    threshold(img_rotate_center, rotate_center, name)
    gaussian_blur(img_rotate_center, rotate_center, name)
    reverse(img_rotate_center, rotate_center, name)

    # 右回転
    img_rotate_right = ndimage.rotate(face_img, rotate_right)
    cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_rigt.jpg", img_rotate_right)
    threshold(img_rotate_right, rotate_right, name)
    gaussian_blur(img_rotate_right, rotate_right, name)
    reverse(img_rotate_right, rotate_right, name)

↑今回の水増し方法は元画像を左に回転させたもの、正面のもの、右に回転したものを用意し、それぞれの画像に対して、ぼかし処理、閾値処理、反転処理を行なっていきます。

f:id:shizuuuka0202:20191218205949p:plain ↑左から、正面の画像、右回転させた画像、左回転させた画像

ぼかし処理

ぼかし処理とは、ある点を周辺の色で平均化する処理のことを言います。今回はガウシアンぼかしと言う方法を使います。

# ぼかし処理で水増し
def gaussian_blur(face_img, rotate_value, name):
    # ぼかし処理
    img_filter = cv2.GaussianBlur(face_img, (5, 5), 0)
    # 保存したファイル名
    file_name = "/" + name + "_GaussianBlur_" + str(rotate_value) + ".jpg"
    # 書き込み
    cv2.imwrite(augumente_faces_dir_path + name + file_name, img_filter)

f:id:shizuuuka0202:20191218210111p:plain 少し分かりづらいですが、よく見るとぼかしがかかっているのが分かります。

閾値処理

# 閾値処理で水増し
def threshold(face_img, rotate_value, name):
    # 閾値処理
    img_thr = cv2.threshold(face_img, 100, 255, cv2.THRESH_TOZERO)[1]
    # 保存したファイル名
    file_name = "/" + name + "_threshold_" + str(rotate_value) + ".jpg"
    # 書き込み
    cv2.imwrite(augumente_faces_dir_path + name + file_name, img_thr)

f:id:shizuuuka0202:20191218210202p:plain

反転処理

# 反転処理で水増し
def reverse(face_img, rotate_value, name):
    # 反転処理
    img_flip = cv2.flip(face_img, 1)
    # 保存したファイル名
    file_name = "/" + name + "_flip_" + str(rotate_value) + ".jpg"
    # 書き込み
    cv2.imwrite(augumente_faces_dir_path + name + file_name, img_flip)

f:id:shizuuuka0202:20191218210225p:plain

ランダムにテストデータへ

# 画像データ一覧の中からランダムにテストデータに移行
def random_sort():
    random_v = 0.2
    names = os.listdir(augumente_faces_dir_path)

    for name in names:
        # DS.Storeを取ってきたらcontinue
        if name is ".DS_Store":
            continue
        # testディレクトリに人名のディレクトリを作成
        os.mkdir(test_dir_path + name)
        # 水増しした画像データの一覧取得
        imgs = os.listdir(augumente_faces_dir_path + name)
        # imgsの数を元に、その中の2割の番号を取得
        random_numbers = random.sample(range(len(imgs)), k=round(len(imgs)*random_v))

        # ランダムに選んだ番号を元に画像を移動
        for random_number in random_numbers:
            shutil.move(augumente_faces_dir_path + name + "/" + imgs[random_number], test_dir_path + name)

↑今回は加工した画像の中から、2割の画像をテストデータに移すことにします。 はじめに namesに登録してある人名の一覧を取ってきます。

        # testディレクトリに人名のディレクトリを作成
        os.mkdir(test_dir_path + name)
        # 水増しした画像データの一覧取得
        imgs = os.listdir(augumente_faces_dir_path + name)
        # imgsの数を元に、その中の2割の番号を取得
        random_numbers = random.sample(range(len(imgs)), k=round(len(imgs)*random_v))

人名でループを回し、testディレクトリの中にその人物の画像データを入れるためのディレクトリを作成します。その後、水増し後の画像データの総数を上限としてランダムに要素番号を作成します。
例:データ数が20の場合、0~19の中からランダムに4つの数字(20の2割なので)が選ばれ、random_numberに格納されます。中身は[3, 8, 16, 17]のようになります。

        # ランダムに選んだ番号を元に画像を移動
        for random_number in random_numbers:
            shutil.move(augumente_faces_dir_path + name + "/" + imgs[random_number], test_dir_path + name)

前述したランダムな要素番号を持っているデータをtestディレクトリに移します。shutil.move()を使用することで簡単に移動することができます。

最後に

いかがだったでしょうか! 長すぎることもあって、二つに分けさせてもらいました。次回は機械学習の部分を実装していきたいと思うのでお楽しみに!


次の記事へ

前の記事へ 目次に戻る