-
Программа обнаруживает движущиеся в кадре объекты. Программа записывает время когда объект появляется в поле зрения камеры и время когда объект покидает кадр. По завершении записи, предоставляется интерактивная диаграмма со временем всех вхождений объекта в кадр и временем нахождения перед камерой.
-
Данная программа может быть использована для наблюдения за животными, а так же людьми.
-
Программа создана на языке Python с использованием библиотеки OpenCV. Также используется библиотека Bokeh для визуализации конечных данных.
** Программа получает доступ к веб-камере. Первый кадр видео будет играть роль неподвижного фона. Все последующие кадры видео будут сравниваться с первым, чтобы установить есть ли между ними разница.
** Далее в скрипте, сделаем неподвижный фон, а также текущий кадр видео (Gray Frame) черно-белымы. Сохраним неподвижный фон (первый кадр) в переменной, конвертируем его в черно-белое изображение, и циклом while пройдем по текущим кадрам, конвертируя их в черно-белые изображения.
** Далее установим разницу между двумя черно-белыми изображениями: неподвижным фоном и текущим кадром. Это будет (Delta Frame). Светлые пиксели в Delta Frame указывают на обнаружение движения, темные пиксели на отсутствие движения.
- Текущий черно-белый кадр Gray Frame слева и Delta Frame справа.
** Далее в Threshold Frame установим, что если в Delta Frame замечена разница интенсивностью более 100, данные пиксели будут конвертированы в белые пиксели, пиксели ниже порога 100 будут конвертированы в черные пиксели. Так мы получим контуры движущегося объекта.
- Threshold Frame.
** После расчета Threshold Frame в цикле, будем искать контуры белого объекта в текущем фрейме. Циклом for пройдем по всем контурам текущего фрейма, и проверим если площадь обхватываемого контурами объекта больше определенной величины (например, 50 000 пикселей). При соблюдении условия, объект будет считаться движущимся объектом.
** Далее нарисуем прямоугольник вокруг контуров охватывающих объект нужного размера. Прямоугольник будет виден в Color Frame - это цветная версия текущего фрейма.
- Color Frame.
** Далее установим время когда движущийся объект попадал в кадр (Background Frame) и выходил из кадра. Визуализуруем данные, чтобы получить интерактивную диаграмму.
- Интерактивная диаграмма со временем всех вхождений объекта в кадр и временем нахождения перед камерой.
- Всплывающее окно со временем одного вхождения в кадр и продолжительностью нахождения в кадре.
# Метод VideoCapture принимает в качестве аргумента индекс веб-камеры, либо путь до видеофайла.
# VideoCapture(0) аргумент (0) - используется только одна веб-камера.
video = cv2.VideoCapture(0)
# release метод для получения доступа к объекту video.
video.release()
# check - True или False нужен для проверки записи видео.
# frame - первый кадр, заснятый нашей камерой.
check, frame = video.read()
# Конвертация цветного изображения в черно-белое.
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Метод imshow библиотеки cv2 для показа frame объекта (рекурсивно показывает каждый кадр в видео).
# "Color Frame" - название окна.
cv2.imshow("Color Frame", gray)
cv2.waitKey(0) # Окно закроется при нажатии на любую из клавиш.
video.release()
cv2.destroyAllWindows()
a = 1 # Подсчет количества сделанных кадров.
while True:
a = a + 1
check, frame = video.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow("Color Frame", gray)
key = cv2.waitKey(1) # Ожидание 1 миллисекунда.
if key == ord('q'):
break
print(a)
video.release()
cv2.destroyAllWindows()
first_frame = None
# В цикле while создадим условие.
if first_frame is None:
first_frame = gray
continue
# Применение _GaussianBlur_ для текущего изображения делает его размытым. Это уменьшает шумы и повышает
точность вычисления разницы между изображениями.
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# Установка разницу междву первым и текущим кадрами видео.
delta_frame = abs(first_frame - gray)
cv2.imshow("Delta Frame", delta_frame)
thresh_frame = cv2.threshold(delta_frame, 30, 255, cv2.THRESH_BINARY)[1]
# Сглаживание изображения, удаление черных просветов на белых полях.
thresh_frame = cv2.dilate(thresh_frame, None, iterations=2)
cv2.imshow("Threshold Frame", thresh_frame)
(cnts,_) = cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Отфильтровка контуров. Сохраним лишь контуры обрисовывающие объекты свыше 90000 пикселей.
for contour in cnts:
if cv2.contourArea(contour) < 90000:
continue
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0,255,0), 3)
cv2.imshow("Color Frame", frame)
** Необходимо поэкспериментировать с размером контурной области (contourArea). Значения могут разниться от 1 000 до 100 000 пикселей. Цифра зависит от экрана конкретного устройства и от того насколько большой объект мы хотим захватить.
if cv2.contourArea(contour) < 90000:
continue
# На текущий момент скрипт выглядит так:
import cv2, time
first_frame = None
video = cv2.VideoCapture(0)
while True:
check, frame = video.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if first_frame is None:
first_frame = gray
continue
delta_frame = abs(first_frame - gray)
thresh_frame = cv2.threshold(delta_frame, 150, 255, cv2.THRESH_BINARY)[1]
thresh_frame = cv2.dilate(thresh_frame, None, iterations=2)
(cnts,_) = cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in cnts:
if cv2.contourArea(contour) < 90000:
continue
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0,255,0), 3)
cv2.imshow("Gray Frame", gray)
cv2.imshow("Delta Frame", delta_frame)
cv2.imshow("Threshold Frame", thresh_frame)
cv2.imshow("Color Frame", frame)
key = cv2.waitKey(1)
if key == ord('q'):
break
video.release()
cv2.destroyAllWindows()
status = 0
# Далее в цикле, при обнаружении объекта свыше 90000 пикселей статус изменится на 1.
for contour in cnts:
if cv2.contourArea(contour) < 90000:
continue
status = 1
9. Определение места в скрипте когда статус меняется с 0 на 1 (объект появляется в кадре) и с 1 на 0 (объект покидает кадр)
status_list = [None, None]
# В конце цикла for.
status_list.append(status)
# После цикла while.
print(status_list)
# Соответсвенно, статус 0 означает что в камеру не попало движущихся объектов. Статус 1 означает обнаружение движущихся объектов.
if status_list[-1] == 1 and status_list[-2] == 0:
times.append(datetime.now())
if status_list[-1] == 0 and status_list[-2] == 1:
times.append(datetime.now())
# Исключаем ошибку list index out of range.
status_list = [None, None]
# Результаты записи будут сохранены в файл Times.csv
df = pandas.DataFrame(columns=["Start", "End"])
for i in range(0, len(times), 2):
df = df.append({"Start": times[i], "End": times[i+1]}, ignore_index=True)
df.to_csv("Times.csv")
# В случае когда запись ведется длительное время, нужно избежать проблем с памятью из-за размеров списка.
# Для этого в цикле прежде чем проверить последний и предпоследний элементы списка, сделаем следующее:
status_list = status_list[-2:]
# Это сохранит лишь 2 последних элемента списка.
from motion_detector import df
from bokeh.plotting import figure, show, output_file
from bokeh.models import HoverTool, ColumnDataSource
import pandas
cds = ColumnDataSource(df)
p = figure(
x_axis_type = "datetime",
height = 100,
width = 500,
sizing_mode = "scale_both",
title = "Motion graph"
)
# убрать отметки на вертикальной оси
p.yaxis.minor_tick_line_color = None
# убрать горизонтальную сетку
p.yaxis[0].ticker.desired_num_ticks = 1
hover = HoverTool(
tooltips = [
("Start", "@Start{%d/%m/%Y %H:%M:%S}"),
("End", "@End{%d/%m/%Y %H:%M:%S}")
],
formatters = {
"@Start" : "datetime",
"@End" : "datetime"
}
)
p.add_tools(hover)
q = p.quad(
left = "Start",
right = "End",
bottom = 0,
top = 1,
color = "green",
source = cds
)
output_file("Graph.html")
show(p)