naritoブログ

【お知らせ】
新ブログができました。今後そちらで更新し、このサイトは更新されません(ウェブサイト自体は残しておきます)
このブログの内容に関してコメントしたい場合は、新ブログのフリースペースに書き込んでください

このブログの内容を新ブログに移行中です。このブログで見つからない記事は、新ブログにありま

画像認識で、訓練画像を読み込む

約651日前 2017年2月28日7:11
プログラミング関連
画像認識
画像認識の類を行う場合、訓練画像等を用意しそれを読み込ませる必要があります。
「ゼロから作るDeep Learning」という書籍では、MNISTデータセットのダウンロードから画像データのnumpy配列への変換までをサポートする便利なPythonスクリプトが提供されていました。
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/dataset/mnist.py

上記のコードは、gzのMNISTデータセットからデータを読み込んでいました。
今回は、gzではなくフォルダに画像があるケースで、それを読み込ませれるようにします。

「mnist_png」というディレクトリがあり...


「testing」と「training」の2つのディレクトリ。名前のとおり、訓練とテストですね。


中には更に、0~9までのディレクトリです。


ディレクトリ名の数字は、中に入っている画像が表している数字そのものです。つまり、0ディレクトリならば中にある画像は全て0です。


元のmnist.pyを参考に作成しています。
loaddata.py

import os
import pickle
import numpy as np
from PIL import Image


class LoadMnistImage:

def __init__(self):
"""初期化処理"""

self.train_img = None
self.train_label = None
self.test_img = None
self.test_label = None

def get_data(self, save_file,
normalize=False, flatten=False, one_hot_label=True):
"""既に作成したpickleファイルからデータを読み込む(なければ作成する)"""

if not os.path.exists(save_file):
self.create_data(save_file)

with open(save_file, 'rb') as f:
dataset = pickle.load(f)

self.train_img = dataset['train_img']
self.train_label = dataset['train_label']
self.test_img = dataset['test_img']
self.test_label = dataset['test_label']

if flatten:
self.to_flatten()

if normalize:
self.to_normalize()

if not one_hot_label:
self.no_hot_label()

def to_flatten(self):
"""画像を1次元にする (28, 28) →(784,)"""

self.train_img = self.train_img.reshape(len(self.train_img), 784)
self.test_img = self.test_img.reshape(len(self.test_img), 784)

def to_normalize(self):
"""画像のピクセル値を0.0~1.0に正規化する"""

self.train_img = self.train_img.astype(np.float32) / 255.0
self.test_img = self.test_img.astype(np.float32) / 255.0

def no_hot_label(self):
"""one_hot表現をなくす"""

self.train_label = self.train_label.argmax(axis=1)
self.test_label = self.test_label.argmax(axis=1)

def create_data(self, save_file):
"""画像・ラベルを読み込み、pickleとして作成・保存する"""

train_img, train_label, test_img, test_label = [], [], [], []

# traningディレクトリはの画像はtrain_img, train_labelへ
# testingディレクトリはtest_img, test_labelへ
kinds = [
['training', train_img, train_label],
['testing', test_img, test_label],
]
for kind in kinds:

# このiはディレクトリ名であると同時に、正解ラベル名
for i in range(10):

# mnist_png/training/0 等のパス
dir_path = os.path.join('mnist_png', kind[0], str(i))

# [mnist_png/training/0/1.png, mnist_png/training/0/2.png]等のリスト
file_paths = [os.path.join(dir_path, file)
for file in os.listdir(dir_path)]

# [[画像のnumpy配列], [画像のnumpy配列], ...])のような2次元配列
images = [np.asarray(Image.open(path)).reshape(1, 28, 28) for path in file_paths]
kind[1].extend(images)

# ディレクトリ名のiが、そのまま正解ラベル名なので、1を入れる
label = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
label[i] = 1
labels = [label for x in file_paths]
kind[2].extend(labels)

dataset = {
'train_img': np.array(train_img), # (60000, 28, 28)
'train_label': np.array(train_label), # (60000, 10)
'test_img': np.array(test_img), # (10000, 28, 28)
'test_label': np.array(test_label), # (10000, 10)
}

with open(save_file, 'wb') as f:
pickle.dump(dataset, f, -1)


このような感じで使います。

mnist = LoadMnistImage()
mnist.get_data('save.pkl')
x_train = mnist.train_img
t_train = mnist.train_label
x_test = mnist.test_img
t_test = mnist.test_label



インスタンス化した後に呼び出すメソッドです。
save_fileはセーブした、又はセーブしたいpickleファイルの名前を
normalizeは正規化、flattenは1次元配列にするか、one_hot_labelはone_hot表現にするかのフラグです。
デフォルトではone_hot表現だけするようにしています。

def get_data(self, save_file,
normalize=False, flatten=False, one_hot_label=True):
"""既に作成したpickleファイルからデータを読み込む(なければ作成する)"""



まだpickleが作られていなければ、create_dataで作成します。
pickleがあるか、もしくは作った後にdataset変数としてそれを読み込みます。
インスタンスのtrain_img等の属性にそれらを格納しておきます。
normalize、flattenがTrueならば、そうなるように処理をします。

if not os.path.exists(save_file):
self.create_data(save_file)

with open(save_file, 'rb') as f:
dataset = pickle.load(f)

self.train_img = dataset['train_img']
self.train_label = dataset['train_label']
self.test_img = dataset['test_img']
self.test_label = dataset['test_label']

if flatten:
self.to_flatten()

if normalize:
self.to_normalize()

if not one_hot_label:
self.no_hot_label()


reshapeで1次元に変更しています。今回はMNIST画像なので、28*28の784と入れています。

def to_flatten(self):
"""画像を1次元にする (28, 28) →(784,)"""

self.train_img = self.train_img.reshape(len(self.train_img), 784)
self.test_img = self.test_img.reshape(len(self.test_img), 784)


これは正規化処理です。255で割るだけです。

def to_normalize(self):
"""画像のピクセル値を0.0~1.0に正規化する"""

self.train_img = self.train_img.astype(np.float32) / 255.0
self.test_img = self.test_img.astype(np.float32) / 255.0


今回はデータ作成時にone_hot表現になるようにしていますが、このメソッドでそれを変更できます。

def no_hot_label(self):
"""one_hot表現をなくす"""

self.train_label = self.train_label.argmax(axis=1)
self.test_label = self.test_label.argmax(axis=1)


上記の3つは、それぞれ逆のことをするメソッドも欲しいですね。

pickleの作成メソッドです。

def create_data(self, save_file):
"""画像・ラベルを読み込み、pickleとして作成・保存する"""

train_img, train_label, test_img, test_label = [], [], [], []

# traningディレクトリはの画像はtrain_img, train_labelへ
# testingディレクトリはtest_img, test_labelへ
kinds = [
['training', train_img, train_label],
['testing', test_img, test_label],
]
for kind in kinds:

# このiはディレクトリ名であると同時に、正解ラベル名
for i in range(10):

# mnist_png/training/0 等のパス
dir_path = os.path.join('mnist_png', kind[0], str(i))

# [mnist_png/training/0/1.png, mnist_png/training/0/2.png]等のリスト
file_paths = [os.path.join(dir_path, file)
for file in os.listdir(dir_path)]

# [[画像のnumpy配列], [画像のnumpy配列], ...])のような2次元配列
images = [np.asarray(Image.open(path)).reshape(1, 28, 28) for path in file_paths]
kind[1].extend(images)

# ディレクトリ名のiが、そのまま正解ラベル名なので、1を入れる
label = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
label[i] = 1
labels = [label for x in file_paths]
kind[2].extend(labels)

dataset = {
'train_img': np.array(train_img), # (60000, 28, 28)
'train_label': np.array(train_label), # (60000, 10)
'test_img': np.array(test_img), # (10000, 28, 28)
'test_label': np.array(test_label), # (10000, 10)
}

with open(save_file, 'wb') as f:
pickle.dump(dataset, f, -1)