※こちらはPythonデータ分析勉強会#02の発表資料です。
前回は、YOLOv3でパトライトの監視を行いました。
今回は、YOLOv3で「将棋駒」を認識させます。
そして、用意する画像は一枚だけという、無謀な挑戦をしてみます。
前回から、内容がガラリと変わっておりますが、まずはそのモチベーションを説明します。
#なぜ、用意した画像は一枚なのか?
よくディープラーニングのサービスなんかを見ていると、用意する画像は1000枚とか
平気で書いてあります。
しかし、個人事業主や中小企業は、そんなに労力をかけて画像は用意できませんし、
1000枚用意しても、バリエーション豊かな画像じゃないと、意味がないことが多くあります。
用意する画像が一枚だけなら、労力は最小限ですし、バリエーションも気にしなくて良いです。
従って、「用意する画像が一枚」で精度が出るなら、世の中のディープラーニングの
サービスがより使いやすくなる可能性があります。
今回はその検証を行います。
今回はその方法を模索します。
#なぜ、将棋駒なのか?
- 肖像権を気にすることなく、リアルな画像が撮れるから
- 駒の形が似ており、誤検知が起きやすい?から
個人的に将棋が好きだから
個人的な趣味はさておき、将棋駒をターゲットにすると、YOLOの検証がしやすくなります。
ただし、作業時間を考え、今回は「歩」と「金」だけを認識させることにしました。
本当はパトライトでやりたかったのですが、リアルな画像が用意できないので断念しました。
#用意した画像はこれだ!
こちらは、100均で買った将棋駒です。
10年以上前に購入したため、かなり 汚い 深みのある風合いになっています。
今回はこの写真一本で勝負します!
どうなることやら・・・
#学習の準備
※YOLOのインストールやアノテーション作業のやり方、学習の実行などは前回の記事を参考にしてください。
用意した画像は1枚でも、学習データが1枚では少なすぎます。
そこで、ディープラーニングでお得意のData Augmentation(水増し)を行います。
今回は、PCA Color Augmentation(以下、PCACA)という強力な手法を使います。
そして、YOLOのモデルは、小さいtinyモデルを使います。
その理由は、将来的にラズパイやスマホで動かしたいからです。
#Data Augmentation
今回は、通常のData Augmentationに加え、光の加減が変わっても認識できるようにPCACAを使います。
コードは@koshian2さんのを拝借しています。
import matplotlib.pyplot as plt
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
import cv2
import numpy as np
import os
def pca_color_augmentation_modify(image_array_input):
assert image_array_input.ndim == 3 and image_array_input.shape[2] == 3
# assert image_array_input.dtype == np.uint8
img = image_array_input.reshape(-1, 3).astype(np.float32)
# 分散を計算
ch_var = np.var(img, axis=0)
# 分散の合計が3になるようにスケーリング
scaling_factor = np.sqrt(3.0 / sum(ch_var))
# 平均で引いてスケーリング
img = (img - np.mean(img, axis=0)) * scaling_factor
cov = np.cov(img, rowvar=False)
lambd_eigen_value, p_eigen_vector = np.linalg.eig(cov)
rand = np.random.randn(3) * 0.1
delta = np.dot(p_eigen_vector, rand*lambd_eigen_value)
delta = (delta * 255.0).astype(np.int32)[np.newaxis, np.newaxis, :]
img_out = np.clip(image_array_input + delta, 0, 255).astype(np.uint8)
return img_out
img_path = 'picture/original02.jpg'
save_path = 'picture/'
# 画像ファイルをPIL形式でオープン
img = image.load_img(img_path)
# PIL形式をnumpyのndarray形式に変換
x = image.img_to_array(img)
# (height, width, 3) -> (1, height, width, 3)
x = x.reshape((1,) + x.shape)
datagen = ImageDataGenerator(
rotation_range=5,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0,
zoom_range=[0.9,1.1],
horizontal_flip=False,
vertical_flip=False)
max_img_num = 10
NO = 1
for d in datagen.flow(x, batch_size=1):
# このあと画像を表示するためにndarrayをPIL形式に変換して保存する
temp = image.img_to_array(d[0])
temp = pca_color_augmentation_modify(temp)
cv2.imwrite(save_path + "Aug_{0}.jpg".format(NO), np.asarray(temp)[..., ::-1])
# datagen.flowは無限ループするため必要な枚数取得できたらループを抜ける
if (NO % max_img_num) == 0:
print("finish")
break
NO += 1
まずは、用意した画像と180度回転させた画像2つを用意します。
将棋では、相手の駒は上下逆さになるため、180度回転させた画像も用意しています。
(matplotlibで描画すると、格子上の筋が入った画像になりますが、
実際の学習データに筋は入っておりません。)
そして、ベーシックな拡大/縮小と回転、水平・垂直移動させます。
ここまでは前回と同じです。
そして、今回の目玉PCACAで明るさを変えて、光の加減が変わっても認識できるようにします。
ここまでくると、バリエーション豊かな写真が出てきます。
今回はこれらの水増し画像を400枚用意しました。
あとは、水増しされた画像でひたすらアノテーション作業をします。
#結果
結果の前に、用意した画像を再掲します。
この画像に対し、明るさを変えたり、駒の配置を変えたりして認識精度を見てみます。
歩は5枚中4枚を認識していますが、金は認識せず。。。
ただ、明らかに元画像より暗いのに、歩を認識できたのはPCACAのおかげといえるでしょう。
次に照明を強くした画像で試します。
こちらは、残念な結果に・・・
陰影が強いのか、歩もあまり認識できませんでした。
次に、詰将棋の画像で試します。
こちらは、伊藤看寿作「煙詰め」一部を抜粋しています。
詰将棋なので、上下逆さの相手駒があるわけですが、相手駒は正確に認識できず・・・
相手駒の「銀」を「金」と認識している点は惜しい!といったところでしょうか。
学習させた「金」には「将」という文字が入っており、銀にも入っているので、誤認識してしまったと思われます。
ただ、「と金」を「歩」と認識しなかった点は評価できます。
「と金」と「歩」の駒のサイズは全く同じで、誤認識してしまうかなぁ・・と思っていました。
ところが、杞憂でした。YOLOは、ちゃんと文字(色)を見ていることが分かりました。
最後に、動画で試してみます。
ご覧のとおり、散々な結果に・・・
金は認識しないし、「歩」を「金」と誤認識してしまいます。
全体に点数を付けるとすれば、20点くらいでしょうか。
精度アップさせる方法は、「まとめ」で書きましたので、参考になれば幸いです。
##失敗作
実は、このチャレンジ一回やり直しています。
最初は、以下の画像を用意しました。
しかし、学習させても「金」は認識しないし、「玉」を「金」と認識したり、
「歩」の認識をやたら連発することがありました。
失敗の理由は以下のとおりです。
- YOLOは一枚の画像を49等分に分割して、分割した画像で学習させています。しかし、用意した画像では、駒が密集しすぎて、分割した画像に隣の駒が映り込んでしまい、隣の駒も学習してしまったと思われます。
- 一枚の画像で「歩」が9枚、「金」が2枚と**不均衡なため、「金」を軽視する傾向にあります。**機械学習でよくある、「不均衡なデータで学習したら、多数派しか学習しない」というやつです。
##失敗から学ぶ
ということで、以下のポイントを意識して画像を撮り直しました。
- 駒をまばらに置く。
- 「歩」を2枚、「金」を2枚にして均衡な画像にする。
そして、撮り直したのが今回用意した画像です。
#まとめ
- PCACAを使えば、用意した画像から光の加減が変わっても、ある程度物体検出ができる。PCACA凄い!
- 正直、「用意した画像は1枚」はやめた方が良い。せめて10枚くらいのバリエーション豊かな写真を用意した方が無難です。
- ただ、普通にやると必要な画像が1000枚だったのが、PCACAを使うと10枚になる可能性があると思われます。
- 本稿を書き終えた後に、Random Erasing Data Augmentationを行えば、さらに物体検出の精度が上がるという記事を見つけました。今度やるときは使ってみます。
次回はラズパイでYOLOを動かします。