-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtext_doc.txt
263 lines (163 loc) · 11.9 KB
/
text_doc.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
Программа обнаруживает движущиеся в кадре объекты. Программа записывает время когда объект
появляется в поле зрения камеры и время когда объект покидает кадр. По завершении записи, предоставляется
интерактивная диаграмма со временем всех вхождений объекта в кадр и временем нахождения перед камерой.
Данная программа может быть использована для наблюдения за животными, а так же людьми.
Программа создана на языке Python с использованием библиотеки OpenCV. Также используется библиотека Bokeh для
визуализации конечных данных.
Принцип работы программы детектора движения:
Программа получает доступ к веб-камере. Первый кадр видео будет играть роль статического фона. Все последующие
кадры видео будут сравниваться с первым, чтобы установить есть ли между ними разница.
Далее в скрипте, сделаем статический фон (первый кадр), а также текущий кадр видео черно-белымы.
Сохраним статический фон (первый кадр) в переменной, конвертируем его в черно-белое изображение, и
циклом while пройдем по текущим кадрам, конвертируя их в черно-белые изображения.
Далее установим разницу между двумя черно-белыми изображениями (Delta Frame). Светлые пиксели в
Delta Frame указывают на наличие движения, темные пиксели на отсутствие движения.
Далее в Threshold Frame установим, что если в Delta Frame замечена разница интенсивностью более 100, данные пиксели будут
конвертированы в белые пиксели, пиксели ниже порога 100 будут конвертированы в черные пиксели. Так мы получим контуры
движущегося объекта.
После расчета Threshold Frame в цикле, будем искать контуры белого объекта в текущем фрейме. Циклом for
пройдем по всем контурам текущего фрейма, и проверим если площадь обхватываемого контурами объекта больше определенной
величины (например, 50 000 пикселей). При соблюдении условия, объект будет считаться движущимся объектом.
Далее нарисуем прямоугольник вокруг контуров охватывающих объект нужного размера. Прямоугольник будет виден в Color Frame - это
цветная версия текущего фрейма. Далее установим время когда движущийся объект попадал в кадр (Background Frame)
и выходил из кадра. Визуализуруем данные, чтобы получить интерактивную диаграмму.
Создание:
1. Создание VideoCapture объекта
# Метод VideoCapture принимает в качестве аргумента индекс веб-камеры, либо путь до видеофайла.
# VideoCapture(0) аргумент (0) - используется только одна веб-камера.
video = cv2.VideoCapture(0)
# release метод для получения доступа к объекту video.
video.release()
2. Создание frame объекта для чтения изображений из VideoCapture объекта
# 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()
3. Создание цикла While для показа видео, вместо показа одного изображения
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()
4. Создадие переменной для хранения первого кадра
first_frame = None
# В цикле while создадим условие.
if first_frame is None:
first_frame = gray
continue
5. Применение GaussianBlur для текущего изображения делает его размытым. Это уменьшает шумы и повышает
точность вычисления разницы между изображениями.
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# Установка разницу междву первым и текущим кадрами видео.
delta_frame = abs(first_frame - gray)
cv2.imshow("Delta Frame", delta_frame)
6. Классификация разницу между Gray 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)
7. Создание контуров белого объекта в thresh_frame
(cnts,_) = cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Отфильтровка контуров. Сохраним лишь контуры обрисовывающие объекты свыше 70000 пикселей.
for contour in cnts:
if cv2.contourArea(contour) < 70000:
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()
Хранение меток времени обнаружения объекта в CSV файле.
8. Изменение статуса с "движется" на "не движется"
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 означает обнаружение движущихся объектов.
10. Установка точного времени когда статус меняется с 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]
11. Pandas и CSV
# Результаты записи будут сохранены в файл 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 последних элемента списка.
12. Визуализация данных с Bokeh (см. файл. plotting.py)
# Полный код программы из plotting.py
from motion_detector import df
from bokeh.plotting import figure, output_file, show
from bokeh.models import HoverTool, ColumnarDataSource
df["Start_string"] = df["Start"].dt.strftime("%Y-%m-%d %H:%M:%S")
df["End_string"] = df["End"].dt.strftime("%Y-%m-%d %H:%M:%S")
cds = ColumnarDataSource(df)
p = figure(x_axis_type='datetime', height=100, width=500, sizing_mode='scale_width', title="Motion Graph")
# убрать отметки на вертикальной оси
p.yaxis.minor_tick_line_color = None
# убрать горизонтальную сетку
p.yaxis[0].ticker.desired_num_ticks=1
hover = HoverTool(tooltips=[("Start", "@Start_string"),("End", "@End_string")])
p.add_tools(hover)
q = p.quad(left="Start", right="End", bottom=0, top=1, color="green", sourse=cds)
output_file("Graph.html")
show(p)