メモ > 技術 > プログラミング言語: Python > TensorFlow+Keras(ディープラーニング)で画像を判定
TensorFlow+Keras(ディープラーニング)で画像を判定
■FlickrのAPIを使う
$ sudo pip3 install flickrapi
■Flickrから学習用の画像を取得する
# Flickrで写真を検索して、ダウンロードする
from flickrapi import FlickrAPI
from urllib.request import urlretrieve
from pprint import pprint
import os, time, sys
# APIキーとシークレットの指定
key = '取得したAPIキー'
secret = '取得したシークレットキー'
wait_time = 1 # 待機秒数(1以上を推奨)
# キーワードとディレクトリ名を指定してダウンロード
def main():
go_download('ショートケーキ', 'cake')
go_download('サラダ', 'salad')
go_download('麻婆豆腐', 'tofu')
# Flickr APIで写真を検索
def go_download(keyword, dir):
# 画像の保存パスを決定
savedir = './images/' + dir
if not os.path.exists(savedir):
os.mkdir(savedir)
# APIを使ってダウンロード
flickr = FlickrAPI(key, secret, format='parsed-json')
res = flickr.photos.search(
text = keyword, # 検索語
per_page = 300, # 取得件数
media = 'photos', # 写真を検索
sort = 'relevance', # 検索語の関連順に並べる
safe_search = 1, # セーフサーチ
extras = 'url_q, license')
# 検索結果を確認
photos = res['photos']
pprint(photos)
try:
# 1枚ずつ画像をダウンロード
for i, photo in enumerate(photos['photo']):
url_q = photo['url_q']
filepath = savedir + '/' + photo['id'] + '.jpg'
if os.path.exists(filepath): continue
print(str(i + 1) + ':download=', url_q)
urlretrieve(url_q, filepath)
time.sleep(wait_time)
except:
import traceback
traceback.print_exc()
if __name__ == '__main__':
main()
実行すると images 内に画像が300個ずつ保存される。
ただし関係ない画像も保存されるので、手動で100個まで絞り込む。
絞り込んだ画像は cleaned_images に保存するものとする。
■画像Numpy形式に変換する
# 画像ファイルを読んでNumpy形式に変換
import numpy as np
from PIL import Image
import os, glob, random
outfile = "photos.npz" # 保存ファイル名
max_photo = 100 # 利用する写真の枚数
photo_size = 32 # 画像サイズ
x = [] # 画像データ
y = [] # ラベルデータ
def main():
# 各画像のフォルダを読む
glob_files("./cleaned_images/cake", 0)
glob_files("./cleaned_images/salad", 1)
glob_files("./cleaned_images/tofu", 2)
# ファイルへ保存
np.savez(outfile, x=x, y=y)
print("保存しました:" + outfile, len(x))
# path以下の画像を読み込む
def glob_files(path, label):
files = glob.glob(path + "/*.jpg")
random.shuffle(files)
# 各ファイルを処理
num = 0
for f in files:
if num >= max_photo: break
num += 1
# 画像ファイルを読む
img = Image.open(f)
img = img.convert("RGB") # 色空間をRGBに
img = img.resize((photo_size, photo_size)) # サイズ変更
img = np.asarray(img)
x.append(img)
y.append(label)
if __name__ == '__main__':
main()
実行すると cleaned_images 内の画像をもとに photos.npz に保存される。
import numpy as np
import matplotlib.pyplot as plt
# 写真データを読み込み
photos = np.load('photos.npz');
x = photos['x']
y = photos['y']
# 開始インデックス
idx = 0
#idx = 100
#idx = 200
# pyplotで出力
plt.figure(figsize=(10, 10))
for i in range(25) :
plt.subplot(5, 5, i + 1)
plt.title(y[i + idx])
plt.imshow(x[i + idx])
plt.show()
# 画像に保存する
plt.savefig("output.png")
実行すると photos.npz の内容をもとに output.png に画像が表示される。
idx が 0 ならショートケーキの画像、100 にするとサラダの画像、200 にすると麻婆豆腐の画像が表示される。
■画像をもとに学習する
import cnn_model
import keras
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
# 入力と出力を指定
im_rows = 32 # 画像の縦ピクセルサイズ
im_cols = 32 # 画像の横ピクセルサイズ
im_color = 3 # 画像の色空間
in_shape = (im_rows, im_cols, im_color)
nb_classes = 3
# 写真データを読み込み
photos = np.load('photos.npz')
x = photos['x']
y = photos['y']
# 読み込んだデータをの三次元配列に変換
x = x.reshape(-1, im_rows, im_cols, im_color)
x = x.astype('float32') / 255
# ラベルデータをone-hotベクトルに直す
y = keras.utils.np_utils.to_categorical(y.astype('int32'), nb_classes)
# 学習用とテスト用に分ける
x_train, x_test, y_train, y_test = train_test_split(
x, y, train_size=0.8)
# CNNモデルを取得
model = cnn_model.get_model(in_shape, nb_classes)
# 学習を実行
hist = model.fit(x_train, y_train,
batch_size=32,
epochs=20,
verbose=1,
validation_data=(x_test, y_test))
# モデルを評価
score = model.evaluate(x_test, y_test, verbose=1)
print('正解率=', score[1], 'loss=', score[0])
# 学習結果を保存
model.save_weights('photos.h5')
正解率= 0.800000011920929 loss= 0.5151064833005269
正解率を上げるため、回転と反転を行って水増し学習する。
300枚での学習だったが5760枚での学習となる。
ただし学習にかかる時間は何倍にもなる。
# CNNでMNISTの分類問題に挑戦
import cnn_model
import keras
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
import cv2
# 入力と出力を指定
im_rows = 32 # 画像の縦ピクセルサイズ
im_cols = 32 # 画像の横ピクセルサイズ
im_color = 3 # 画像の色空間
in_shape = (im_rows, im_cols, im_color)
nb_classes = 3
# 写真データを読み込み
photos = np.load('photos.npz')
x = photos['x']
y = photos['y']
# 読み込んだデータをの三次元配列に変換
x = x.reshape(-1, im_rows, im_cols, im_color)
x = x.astype('float32') / 255
# ラベルデータをone-hotベクトルに直す
y = keras.utils.np_utils.to_categorical(y.astype('int32'), nb_classes)
# 学習用とテスト用に分ける
x_train, x_test, y_train, y_test = train_test_split(
x, y, train_size=0.8)
# 学習用データを水増しする
x_new = []
y_new = []
for i, xi in enumerate(x_train):
yi = y_train[i]
for ang in range(-30, 30, 5):
# 回転させる
center = (16, 16) # 回転の中心点
mtx = cv2.getRotationMatrix2D(center, ang, 1.0)
xi2 = cv2.warpAffine(xi, mtx, (32, 32))
x_new.append(xi2)
y_new.append(yi)
# さらに左右反転させる
xi3 = cv2.flip(xi2, 1)
x_new.append(xi3)
y_new.append(yi)
# 水増しした画像を学習用に置き換える
print('水増し前=', len(y_train))
x_train = np.array(x_new)
y_train = np.array(y_new)
print('水増し後=', len(y_train))
# CNNモデルを取得
model = cnn_model.get_model(in_shape, nb_classes)
# 学習を実行
hist = model.fit(x_train, y_train,
batch_size=64,
epochs=20,
verbose=1,
validation_data=(x_test, y_test))
# モデルを評価
score = model.evaluate(x_test, y_test, verbose=1)
print('正解率=', score[1], 'loss=', score[0])
# 学習結果を保存
model.save_weights('photos2.h5')
正解率= 0.9666666388511658 loss= 0.4047759254773458
■オリジナル画像をもとに判定する
import cnn_model
import keras
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
im_rows = 32 # 画像の縦ピクセルサイズ
im_cols = 32 # 画像の横ピクセルサイズ
im_color = 3 # 画像の色空間
in_shape = (im_rows, im_cols, im_color)
nb_classes = 3
LABELS = ["ショートケーキ", "サラダ", "麻婆豆腐"]
CALORIES = [344, 81, 256]
# 保存したCNNモデルを読み込む
model = cnn_model.get_model(in_shape, nb_classes)
model.load_weights('photos2.h5')
def check_photo(path):
# 画像を読み込む
img = Image.open(path)
img = img.convert("RGB") # 色空間をRGBに
img = img.resize((im_cols, im_rows)) # サイズ変更
#plt.imshow(img)
#plt.show()
# データに変換
x = np.asarray(img)
x = x.reshape(-1, im_rows, im_cols, im_color)
x = x / 255
# 予測
pre = model.predict([x])[0]
idx = pre.argmax()
per = int(pre[idx] * 100)
return (idx, per)
def check_photo_str(path):
idx, per = check_photo(path)
# 答えを表示
print(path, "は", LABELS[idx], "で、カロリーは", CALORIES[idx],"kcalです。")
print("可能性は", per, "%です。")
if __name__ == '__main__':
check_photo_str('test_cake.jpg')
check_photo_str('test_salad.jpg')
check_photo_str('test_tofu.jpg')
test_cake.jpg は ショートケーキ で、カロリーは 344 kcalです。
可能性は 100 %です。
test_salad.jpg は サラダ で、カロリーは 81 kcalです。
可能性は 100 %です。
test_tofu.jpg は 麻婆豆腐 で、カロリーは 256 kcalです。
可能性は 97 %です。