戻る
■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 %です。