TensorFlowで「松本城」にすり寄ってくる自動運転カー作ってみた
前回「TensorFlowで顔めがけて近づいてくる車を作ってみた」のやつは、検出された顔がカバンちゃん似だろうとフェネック似だろうと「そこに顔があるから」と見境なく突っ込んでくるという、まるで登山家の名言を地で行く車でしたが。
今回は対象を識別して、特定のものに接近するやつを作ってみました。いかにも「人工知能」って感じの電子工作です。
いろいろ考えたんですが、既存の教師データを用いるのが簡単だろうということで「TensorFlowで”日本のお城”を識別させてみた: EeePCの軌跡」の時に使ったお城の写真を使うことにしました。
ただ、カメラ映像ってのは明るい/暗い、ノイズの有無がある ので、既存の教師データから明るい画像、暗めの画像、ノイズ付き画像などを作って画像を増やしてみました。
そんな画像を作り出すコードは以下の記事を参考に
こういうのを作りました。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import sys
import os
# ヒストグラム均一化
def equalizeHistRGB(src):
RGB = cv2.split(src)
Blue = RGB[0]
Green = RGB[1]
Red = RGB[2]
for i in range(3):
cv2.equalizeHist(RGB[i])
img_hist = cv2.merge([RGB[0],RGB[1], RGB[2]])
return img_hist
# ガウシアンノイズ
def addGaussianNoise(src):
row,col,ch= src.shape
mean = 0
var = 0.1
sigma = 15
gauss = np.random.normal(mean,sigma,(row,col,ch))
gauss = gauss.reshape(row,col,ch)
noisy = src + gauss
return noisy
# salt&pepperノイズ
def addSaltPepperNoise(src):
row,col,ch = src.shape
s_vs_p = 0.5
amount = 0.004
out = src.copy()
# Salt mode
num_salt = np.ceil(amount * src.size * s_vs_p)
coords = [np.random.randint(0, i-1 , int(num_salt))
for i in src.shape]
out[coords[:-1]] = (255,255,255)
# Pepper mode
num_pepper = np.ceil(amount* src.size * (1. - s_vs_p))
coords = [np.random.randint(0, i-1 , int(num_pepper))
for i in src.shape]
out[coords[:-1]] = (0,0,0)
return out
if __name__ == '__main__':
# ルックアップテーブルの生成
min_table = 50
max_table = 205
diff_table = max_table - min_table
gamma1 = 0.75
gamma2 = 1.5
LUT_HC = np.arange(256, dtype = 'uint8' )
LUT_LC = np.arange(256, dtype = 'uint8' )
LUT_G1 = np.arange(256, dtype = 'uint8' )
LUT_G2 = np.arange(256, dtype = 'uint8' )
LUTs = []
# 平滑化用
average_square = (10,10)
# ハイコントラストLUT作成
for i in range(0, min_table):
LUT_HC[i] = 0
for i in range(min_table, max_table):
LUT_HC[i] = 255 * (i - min_table) / diff_table
for i in range(max_table, 255):
LUT_HC[i] = 255
# その他LUT作成
for i in range(256):
LUT_LC[i] = min_table + i * (diff_table) / 255
LUT_G1[i] = 255 * pow(float(i) / 255, 1.0 / gamma1)
LUT_G2[i] = 255 * pow(float(i) / 255, 1.0 / gamma2)
LUTs.append(LUT_HC)
LUTs.append(LUT_LC)
LUTs.append(LUT_G1)
LUTs.append(LUT_G2)
# 画像の読み込み
file_list = os.listdir('./jpg/')
for file in file_list:
img_src = cv2.imread('./jpg/' + file)
trans_img = []
trans_img.append(img_src)
# LUT変換
for i, LUT in enumerate(LUTs):
trans_img.append( cv2.LUT(img_src, LUT))
# 平滑化
#trans_img.append(cv2.blur(img_src, average_square))
# ヒストグラム均一化
trans_img.append(equalizeHistRGB(img_src))
# ノイズ付加
trans_img.append(addGaussianNoise(img_src))
trans_img.append(addSaltPepperNoise(img_src))
# 反転
#flip_img = []
#for img in trans_img:
# flip_img.append(cv2.flip(img, 1))
#trans_img.extend(flip_img)
# 保存
if not os.path.exists("trans_images"):
os.mkdir("trans_images")
base = os.path.splitext(file)[0] + "_"
img_src.astype(np.float64)
for i, img in enumerate(trans_img):
cv2.imwrite("trans_images/" + base + str(i) + ".jpg" ,img)
これを使うと明暗画像やノイズの入った画像、ピンボケ気味の画像、それらを反転した画像が作られ、結果1枚の画像が18枚に増えます(ただし、このコードでは「反転」をコメントアウトしてるため9枚 理由は後述)。
つまり、当初 全383枚だったお城の写真が一気に6894枚に!
となるはずだったんですが、ここで問題発生!!
なんとうちのGeForce GTX 1050がメモリオーバーを起こしちゃったようで、妙なエラーをはいて動かなくなってしまいました。
画像を3700枚程度まで減らすと動いたため、メモリ不足だと推測しています。
やっぱり安いボードはこういうところがボトルネックになりますね。やはり8GB搭載した1080あたりが欲しい・・・
とりあえず「反転」をコメントアウトし、枚数を9倍(3447枚)に抑えておきました。
これを使ってTensorFlowコードで学習。
また、カメラ映像の中から「お城」部分を切り出す判別器は以下の記事のとおり作成。
TensorFlowで”日本のお城”を識別させてみた: EeePCの軌跡
これで準備完了。
続いて、目標となるお城の写真を準備。
A4で6枚入るように印刷し切り取り、百均で買ってきた札立てに張り付けて作成。
このブログ、百均に頼ってばかりです。セリア、ダイソーに足を向けて寝られませんね。
この写真のうち「松本城」を目標に接近するようプログラムしました。
なぜ「松本城」なのか?ですが、私の苗字とは関係なく、ただ単にこの中で最も識別率が高かったからという理由だけです。
ちなみに私の作った学習器はどういうわけか「迷ったら岡崎城!」と思うようにしているらしく、岡崎城の誤認識率が異常に高いです。
・・・てことで、岡崎城にすり寄るマシーンだけは作れそうにありません。家康公ゆかりのいいお城なんですけどねぇ。
写真を並べて(ここでは左から2番目)
「松本城めがけて走るカー」を置き、いざスタート!
たどたどしく動きながら
だんだんと「松本城」に接近
20cmほどのところで予定通り停止。成功です!
今度は一番左に配置して再びGo!!
最終的にはご覧のとおり、たどり着いてくれました。
よく似てる写真(熊本城)もあるんですが、全く見向きもしませんね。「松本城」一筋です。
なお、松本城の写真だけを引きはがすと
ついてきます。けなげ~。
なんだか「松本城」が死ぬほど好きなワンちゃんのような感じ。なぜか愛着がわきますね。
丸いつぶらな瞳がよりかわいさを誘います・・・が、あれは目ではなくて超音波センサーなんですけどね。
なお、彼の”眼”(カメラ)にはこう見えてます。
たまに誤認識、検出ミスはありますが、大体の写真を識別していることがわかりますね。
(この画像では右から2番目に目標)
20cm付近ではこの大きさになります。もう彼の眼には「松本城」だけしか見えない状態。めろめろです。
動画も載せておきます。実際にはこんな感じの動き。
徐々にほふく前進して迫っていくような慎重な動きですが、確実に目標に向かってます。
ようやく今回で”人工知能”ぽいものを作ることができました。
もっとも、これが一体何の役に立つのか!?といわれると何とも言えませんが・・・
ただ、この工作の製作目的は”デモンストレーション”なので、個人的には満足のいく出来でしょうか。
そういえば、前回書き忘れましたが、Arduinoとモーターの間には「DRV8835」というモータードライブがついてます。
ここで使われてる電子部品は、Raspberry PiとPiカメラ、Arduino UNO互換ボード、DRV8835、タミヤのロボットリモコンキット、超音波センサーHC-SR04、乾電池とモバイルバッテリー。
そんなに派手な部品は使用しておりませんが、人工知能と結びつけばこんなものも作れるってことで。
なお、これだけみると動作は一見完璧なようですが、実は夜になるとうまく動作してくれないという問題を抱えてます。
部屋の明かりが悪いのか、カメラセンサーがダサいのかわかりませんが、写真がちょっと暗くなるとすべて”岡崎城”に見えてしまうようです。
そこでこういうものを使って
照らしてやるとようやく「松本城」に気付いてくれました。
なんだか光で誘導しているように見えますが、ほかのお城の写真を照らしても見向きもしないので、やはり彼は「松本城」一筋です。
ちょっと面倒さに負けてコード・回路図を作っておりませんが、もし需要あればアップします。要望はコメントまで。
ラズベリーパイ3 (Raspberry Pi 3B made in JAPAN) 技適対応 日本製 5.1V/2.5A ラズベリー財団公式アダプタ 【本体 アダプタ セット品】 |
« スマホ向け機械学習に使える「TensorFlow Lite」発表 | トップページ | 2.45インチの超小型スマホ”Jelly”が技適取得し8月に国内販売開始! »
「Raspberry Pi・Arduino・電子工作」カテゴリの記事
- 名古屋 大須へ行ってきました(2024.04.28)
- Raspberry Pi 5用電源購入(2024.04.19)
- Interface 2024年5月号はRaspberry Pi 5特集(2024.03.26)
- Raspberry Pi 5とPCがつながらなかった理由は「プライバシーセパレーター機能」のせいでした(2024.03.12)
- Raspberry Pi 5に日本語LLM(ELYZA-Japanese-Llama-2-7b-fast-Instruct)を入れてみた(2024.03.10)
« スマホ向け機械学習に使える「TensorFlow Lite」発表 | トップページ | 2.45インチの超小型スマホ”Jelly”が技適取得し8月に国内販売開始! »
コメント