Компьютерное зрение решает задачи поиска, отслеживания и классификации объектов в самых разнообразных областях: промышленности, медицине, сфере безопасности. Одно из возможных новых применений компьютерного зрения — выявление атмосферных осадков при помощи видеокамер наружного наблюдения. О том, как можно реализовать такой алгоритм и поговорим в этом посте.
При дожде или снегопаде капли дождя или снежинки оставляют на видеокадрах треки — протяженные линии. Особенно ярко этот эффект проявляется в темное время суток при активации инфракрасной подсветки видеокамер.
Таким образом, если в кадре есть некоторое количество линий, ориентированных преимущественно вертикально, то можно предположить, что есть атмосферные осадки: дождь или снег. Далее подробно рассмотрим алгоритм, реализующий эту идею.
Алгоритм выявления атмосферных осадков
Для начала проведем подготовительную работу: импортируем библиотеки opencv и numpy, инициализируем захват кадров из видеофайла с именем, записанным в переменной fname, и создадим экземпляр класса backSub для исключения фона.
import cv2 as cv import numpy as np cap = cv2.VideoCapture(fname) backSub = cv2.createBackgroundSubtractorMOG2(25, 16, False)
Все дальнейшие действия будем выполнять в цикле, последовательно считывая кадры исходного видеофайла.
while True: ret, im_src = cap.read() if not ret: break
Считанный кадр im_src представляет собой RGB-изображение разрешением 1920×1080 пикселей.
Далее выполним несколько операций обработки исходного видеокадра.
Первая операция — вычитание фона.
fg_mask = backSub.apply(frame)
Результатом этой операции является grayscale-изображение fg_mask
, содержащее маску движущихся объектов.
Теперь выполним фильтрацию на основе морфологической операции открытия с ядром 3×3.
kernel = np.ones((3,3),np.uint8) filtered = cv.morphologyEx(fg_mask, cv.MORPH_OPEN, kernel)
В результате с изображения исчезнут отдельные мелкие артефакты.
Далее с помощью функции findContours выделим контуры.
cnt_all, hierarchy = cv.findContours(filtered,\ cv.RETR_EXTERNAL,\ cv.CHAIN_APPROX_SIMPLE)
Для иллюстрации выполним обводку найденных контуров.
Исключим самые маленькие по площади контуры. В качестве минимальной площади контура выберем значение 16.
min_area = 16 cnt_sel_by_area = [] for cnt in cnt_all: if cv.contourArea(cnt, False) >= min_area: cnt_sel_by_area.append(cnt)
Результат:
Для того, чтобы выбрать контуры, соответствующие протяженным объектам, рассчитаем коэффициент формы — отношение квадрата периметра контура к площади контура с коэффициентом пропорциональности 1 / (4⨯pi). Для круглых контуров этот коэффициент минимален и равен единице. Чем более вытянут контур, тем больше значение этого коэффициента. В качестве порогового значения установим 4, затем выберем контуры с коэффициентом формы, превышающим выбранный порог.
shape_coef_min = 4 cnt_sel_by_shape = [] for cnt in cnt_sel_by_area: peri = cv.arcLength(cnt, True) area = cv.contourArea(cnt) shape_coef = (peri ** 2) / area / (4 * np.pi) if shape_coef >= shape_coef_min: cnt_sel_by_shape.append(cnt)
Визуально оценим результат этого действия:
Переходим к отбору контуров по направлению. Для того, чтобы определить ориентацию контуров относительно вертикали, используем аппроксимацию контуров прямыми линиями с помощью функции fitLine. Среди возвращаемых этой функцией значений есть координаты направляющего вектора прямой vx и vy. Оценить угол между прямой и вертикалью можно через скалярное произведение направляющего вектора прямой и направляющего вектора оси ординат. Значение этого угла может лежать в диапазоне от 0 до 180 градусов. Для удобства приведем это значение в диапазон 0..90 угловых градусов. Установим в качестве порога максимального отклонения значение 30 градусов и выберем контуры, удовлетворяющие этому условию.
max_angle = 30 cnt_sel = [] for cnt in cnt_sel_by_shape: vx, vy, x, y = cv.fitLine(cnt, cv.DIST_L2, 0, 0.01, 0.01).flatten() vect_line = np.array([vx, vy]) vect_y_axis = np.array([0, 1]) dot_product = np.dot(vect_line, vect_y_axis) angle = np.rad2deg(np.arccos(dot_product)) angle = min(angle, 180 - angle) if angle <= max_angle: cnt_sel.append(cnt)
Результат отбора контуров проиллюстрирован на следующем изображении. Здесь зеленым цветом отмечены выбранные контуры, а красным — исключенные.
Выполним последнее действие: рассчитаем количество выбранных контуров
n_cnt_curr = len(cnt_sel)
Для иллюстрации нанесём выбранные контуры на исходный видеокадр.
В результате описанных выше операций для каждого видеокадра будет рассчитано количество выявленных контуров треков капель дождя или снежинок. Для того, чтобы сделать вывод о наличии атмосферных осадков, установим минимальное пороговое значение количества найденных контуров.
decision_thr = 3
Далее можно сравнивать количество контуров в кадре с пороговым значением и делать предположение о наличии осадков. Однако, при таком прямолинейном подходе есть риск получения нескольких изменяющихся прогнозов в секунду. Поэтому имеет смысл выполнить усреднение количества контуров во времени. Для усреднения воспользуемся экспоненциальным сглаживанием с параметром 0,1.
alpha = 0.1 n_cnt_avg = n_cnt_curr * alpha + n_cnt_avg * (1 - alpha) n_cnt_avg_int = int(round(n_cnt_avg))
Далее сравним усредненное значение с пороговым и сделаем вывод о наличии осадков.
is_precipitation = n_cnt_avg_int >= decision_thr concl_dict_ru = {True: 'есть осадки', False: 'нет осадков'} conclusion = concl_dict_ru[is_precipitation]
Проиллюстрируем промежуточные расчеты, динамику количества контуров и гипотезу о наличии осадков.
Видео с демонстрацией работы описанного алгоритма можно здесь или здесь. Кадр из демонстрации приведен ниже.
Код размещен в репозитории автора.
Таким образом, был рассмотрен один из способов определения атмосферных осадков с помощью методов компьютерного зрения. Данный способ имеет две особенности. Первая заключается в том, что наилучшим условием применения является сочетание пониженной освещенности и оснащенности видеокамеры инфракрасной подсветкой. Вторая состоит в наличии ряда гиперпараметров, требующих подбора.
ссылка на оригинал статьи https://habr.com/ru/post/668958/
Добавить комментарий