[Из песочницы] Задача классификации глазами школьника: определение наличия автомобиля на парковке по кадрам с камеры видеонаблюдения

Здравствуйте, я школьник 11 классов, интересуюсь программированием, около-IT тематикой.

Пишу данный пост с целью поделиться своим проектом, занявшим 10 часов моей жизни на выходных и выполненным с целью понять возможности современных методов анализа данных. Публикация может рассматриваться как пример удачной реализации для людей, несведущих в этой области знания, а так же как просьба указать мои ошибки для людей, соответственно, сведущих.
Дано: видео-поток с камеры видеонаблюдения, на котором имеется фрагмент 100×50 пикселей с изображением конкретно парковочного места, на котором может присутствовать или отсутствовать лишь конкретный автомобиль.

Изображение с камеры
ljtwgnucovy2ul-dnd55glqukvq.png


Изображение парковочного места
4qhligevi8kiky3szfrtz9ncgjw.png


Задача: определить наличие или отсутствие автомобиля на парковочном месте.

Получение изображения с камеры


Я использую библиотеку openCV для получения и предобработки изображения.
Следующий код я использовал для построения датасета, который использую для обучения нейросети: я фотографирую ежечасно парковочное место, и после получения 60 фотографий вручную разделяю их на фото с машиной и без неё.

dataminer.py
import cv2 as cv
import numpy as np
import time

cap = cv.VideoCapture()
r = 0
while r <=100:
    cap.open('http://**.**.***.***:***/*****CG?container=mjpeg&stream=main') #URL-адрес видеопотока
    hasFrame, frame = cap.read()#Чтение кадра из потока
    frame = frame[100:200, 300:750]
    box = [0,335,100,385] 
    quantframe = frame[box[0]:box[2], box[1]:box[3]]#Сохранение в отдельную переменную части кадра с изображением машины
    r+=1
    cv.imwrite(str(r)+'.png',quantframe) #Сохранение изображения машины в файл
    print('saved')
    cap.release()
    time.sleep(3600)
    key = cv.waitKey(1)
    if key & 0xFF == ord('q'):
        cv.destroyAllWindows()
        break



Обработка изображения


Я посчитал верным решением обучать нейросеть не на исходных изображениях, а на изображениях с контурами автомобиля, найденными посредством функции cv2.findcontours (…).

Вот код, преобразующий исходное изображение в изображение контуров:

Нахождение контуров
def contoursfinder(image):
  img = image.copy()
  srcimg = img.copy()
  hsv_min = np.array((0, 0, 0), np.uint8)
  hsv_max = np.array((255, 255, 60), np.uint8) #Поскольку контуры находятся по различиям в цвете между частями картинки, необходимо подобрать параметры, исходя из цветовой гаммы картинки
  hsv = cv.cvtColor( img, cv.COLOR_BGR2HSV )
  thresh = cv.inRange( hsv, hsv_min, hsv_max )
  contours, _  = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
  img = np.zeros((100,50,3), np.uint8)
  cv.drawContours(img, contours, -1, (255,255,255), 1, cv.LINE_AA)
  return cv.cvtColor(img, cv.COLOR_BGR2GRAY)



Результат работы функции:

Скрытый текст
jarupv7ez7gl_0aswbydbo-7gfa.png


Нейронная сеть


Я использовал библиотеку tensorflow (keras).

Архитектура сети списана с примера с интернетов: для меня неочевидно объяснение, почему именно так. Если знающие люди расскажут или подскажут где почитать, почему эта архитектура эффективна или почему какая-та другая будет эффективнее, буду безмерно благодарен.
Модель нейросети: последовательна, состоит из двух плотных скрытых слоев в 256 и 128 нейронов и входного, выходного слоев.

Код
model = keras.Sequential([
    keras.layers.Flatten(input_shape=(100, 50)),
    keras.layers.Dense(256, activation=tf.nn.relu),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(2, activation=tf.nn.softmax)
])
model.compile(optimizer=tf.train.AdamOptimizer(), 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model.fit(trainingimagesarr, trainingimagesarrlabel, epochs=1, callbacks=[tbCallBack])



Перед обучением вся numpy матрица была поделена на 255, дабы давать на вход нейросети числа в диапазоне от 0 до 1.

trainingimagesarr = trainingimagesarr / 255.0
trainingimagesarrlabel = np.array(trainingimagesarrlabel)-1


Теперь я могу вызвав функцию:

def realtest():
  cap = cv.VideoCapture()
  cap.open('http://**.**.***.***:***/*****CG?container=mjpeg&stream=main')
  hasFrame, frame = cap.read()
  frame = frame[100:200, 300:750]
  quantframe = frame[0:100,275:325]
  quantframe = contoursfinder(quantframe)
  return model.predict(np.array([quantframe]))[0][1]>0.60


получить данные о наличии автомобиля на стоянке.

Сильно не пинайте, но чуть-чуть :-)

Спасибо!

© Habrahabr.ru