Help us understand the problem. What is going on with this article?

Webカメラとラズパイで収集した駐車場の画像に深層学習を利用して満空情報を識別してみようと思った話。

はじめに

事務所の窓から正面にある駐車場の映像を5分ごとに撮影して動画を作って遊んでいます。

Youtube - 週間 事務所の窓

はじめの頃はそれだけで面白かったのですが、だんだんとつまらくなってきて「何か他にできることないかな〜」って考えていたら表題のようなことを思いついたので取り組んでみました。

また、この結果をWebサイトで常に確認できるようにしてます。

駐車場 状態監視のサンプル
※ 上述のとおり、写真は5分周期で更新されます。

使用するライブラリ

使用するライブラリをインストール。

OpenCV

pip install -U pip
pip install python-opencv

Tensorflow

pip install tensorflow

Keras

pip install keras

準備

まず、以下のフォルダを作成。

├ img
│ ├ 0-多い
│ ├ 1-少ない
│ └ 2-ガラガラ
└ models

次に、img以下のそれぞれのフォルダに分類したい画像を保存。
今回はおよそ1週間分の画像データで、2019枚の画像を使用。

多い

202010111320.jpg
202010110035.jpg

少ない

202010111210.jpg
202010170140.jpg

ガラガラ

202010120740.jpg
202010112230.jpg

学習

フォルダの分類にしたがって画像を学習。

# ライブラリ読込
import glob
import cv2
from matplotlib import pyplot as plt
import numpy as np

import keras
import tensorflow as tf

from sklearn import model_selection
from keras.utils.np_utils import to_categorical
from keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D, Dropout
from keras.models import Sequential

import random

# ラベルデータ作成
labels = []
for i in range(3):
    labels.append("{}-".format(i))

# 画像ファイル数の取得
n = []

for l in labels:
    files = glob.glob("img/{}*/*.jpg".format(l))
    print("{} : {}".format(l, len(files)))

    n.append(len(files))

# 画像ファイル読込
imgX = []
y = []

k = 0

for l in labels:

    print(l)

    files = glob.glob("img/{}*/*.jpg".format(l))
    files.sort()
    print(len(files), end=" -> ")

    # 使用する画像は最少分類区分の1.5倍まで
    j = int(min(n) * 1.5)
    if j > len(files):
        j = len(files)

    files = random.sample(files, j)
    print(len(files))

    i = 0

    for f in files:
        img = cv2.imread(f)
        h, w, c = img.shape
        # 上半分は車と関係ないので画像を加工
        img = img[int(h/2):h, :]
        img = cv2.resize(img, (100, 100))
        imgX.append(img)
        y.append(k)

        print("\r{}".format(i), end="")

        i += 1

    print()
    k += 1

# 画像データを配列データに変換
X = np.array(imgX)
X = X / 255

# 学習・検証用データに分割
test_size = 0.2

X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=test_size, random_state=42)
X_valid, X_test, y_valid, y_test = model_selection.train_test_split(X_test, y_test, test_size=.5, random_state=42)

y_train = to_categorical(y_train)
y_valid = to_categorical(y_valid)
y_test = to_categorical(y_test)

# 学習モデルの作成
input_shape = X[0].shape

model = Sequential()

model.add(Conv2D(
    input_shape=input_shape, filters=64, kernel_size=(5, 5), 
    strides=(1, 1), padding="same", activation='relu'))

model.add(MaxPooling2D(pool_size=(4, 4)))

model.add(Conv2D(
    filters=32, kernel_size=(5, 5), 
    strides=(1, 1), padding="same", activation='relu'))

model.add(Conv2D(
    filters=32, kernel_size=(5, 5), 
    strides=(1, 1), padding="same", activation='relu'))

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(
    filters=16, kernel_size=(5, 5), 
    strides=(1, 1), padding="same", activation='relu'))

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(1024, activation='sigmoid'))
model.add(Dense(2048, activation='sigmoid'))

model.add(Dense(len(labels), activation='softmax'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

# 学習
history = model.fit(
    X_train, y_train, batch_size=400, epochs=200, 
    verbose=1, shuffle=True,
    validation_data=(X_valid, y_valid))

score = model.evaluate(X_test, y_test, batch_size=32, verbose=0)
print('validation loss:{0[0]}\nvalidation accuracy:{0[1]}'.format(score))

# 学習済みモデルの保存
mdlname = "models/mdl_parking_status.h5"
model.save(mdlname)

途中の出力は割愛して、学習結果は以下のとおり。

validation loss:0.15308915078639984
validation accuracy:0.9653465151786804

学習の過程をグラフで確認すると以下のとおり。

loss.png
acc.png

なんとなく学習できてそう。

識別

以下のスクリプトで識別。

# ライブラリ読込
import glob
import cv2
from matplotlib import pyplot as plt
import numpy as np
import requests
import keras

# 学習済みモデルの読込
mdlname = "models/mdl_parking_status.h5"
model = keras.models.load_model(mdlname)

# ラベルデータの作成
labels = []

for lbl in glob.glob("img/*"):
    labels.append(lbl.split("/")[-1])

labels.sort()

# 画像読込
img_url = "https://map.blueomega.jp/parking/img.jpg"
req = requests.get(img_url)
img_org = np.fromstring(req.content, dtype='uint8')
img_org = cv2.imdecode(img_org, 1)

h, w, c = img_org.shape
img = img_org[int(h/2):h, :]
img = cv2.resize(img, (100, 100))
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)

X = np.array([img])
X = X / 255

# 識別
pred = model.predict(X, batch_size=32)
m = np.argmax(pred[0])

# 結果表示
print(pred)
print(labels[m])

取得した画像はこちら。

sample.jpg

識別結果は以下のとおり。

[[9.2753559e-01 7.2361618e-02 1.0272356e-04]]
0-多い

...多分できた!

mix_dvd
ExcelのマクロやWebアプリケーション、iOSアプリを作っています。 また、しまねソフト研究開発センター専門研究員の業務を受託しています。http://www.s-itoc.jp
http://blueomega.jp
s-itoc
しまねソフト研究開発センター(ITOC)はITを活用する企業の支援と研究開発の拠点です。
http://www.s-itoc.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away