В этой лабораторной работе реализовано выделение статического фона из видеопоследовательности с использованием Gaussian Mixture Model (GMM) и EM-алгоритма.
Программа:
- загружает набор кадров для каждого видео;
- рассматривает поведение каждого пикселя во времени;
- аппроксимирует распределение значений пикселя смесью нормальных распределений;
- выбирает наиболее стабильную компоненту как фон;
- сравнивает восстановленный фон с эталонным изображением
GT_*.pngпо коэффициенту корреляции Пирсона.
Проект демонстрирует базовый подход к background modeling без использования готовых высокоуровневых реализаций из OpenCV.
- изучить применение смеси гауссовских распределений для задачи выделения фона;
- реализовать EM-алгоритм для оценки параметров смеси;
- научиться выбирать фоновую компоненту на основе статистических характеристик;
- оценить качество результата по сравнению с эталонным фоном.
Для каждого пикселя (i, j) берётся последовательность его значений по всем кадрам:
data = frames[:, i, j]Эта последовательность рассматривается как выборка из смеси нескольких гауссовских распределений.
Каждая компонента смеси описывает одно из возможных состояний пикселя во времени:
- фон;
- пиксель, перекрытый движущимся объектом;
- шумовые или небольшие световые изменения.
В отличие от простого усреднения, GMM позволяет разделять такие состояния и выбирать именно то, которое соответствует стабильному фону.
Во временной последовательности значений пикселя обычно наблюдается следующая картина:
- большую часть времени пиксель принадлежит фону;
- иногда он перекрывается движущимся объектом;
- в некоторых кадрах значение может меняться из-за шума, освещения или теней.
Если использовать простое среднее, движущийся объект может частично «встроиться» в фон.
GMM моделирует распределение значений как смесь нескольких состояний, что позволяет выделить наиболее устойчивое состояние отдельно.
Набор видео для обработки необходимо поместить в директорию ./dataset. Для каждого видео из этой директории:
-
читаются все кадры
.png; -
из изображения используется первый канал:
gray = np.array(img)[:, :, 0]
-
загружается эталонный фон:
GT_<имя_папки>.png
Для каждого пикселя формируется выборка значений яркости по всем кадрам.
На этой выборке обучается смесь из N_COMPONENTS = 5 гауссовских компонент.
Начальные средние выбираются по квантилям:
means = np.quantile(data, np.linspace(0.1, 0.9, N_COMPONENTS))Это делает старт более устойчивым, чем случайная инициализация, поскольку компоненты сразу распределяются по диапазону значений.
Начальные параметры:
-
веса одинаковые:
weights = np.ones(N_COMPONENTS) / N_COMPONENTS
-
дисперсии одинаковые:
variances = np.full(N_COMPONENTS, np.var(data) / N_COMPONENTS)
Для оценки параметров смеси используется EM-алгоритм.
Для каждого наблюдения и каждой компоненты вычисляется правдоподобие:
likelihood[:, k] = (
weights[k] * np.exp(exponent) / np.sqrt(2 * np.pi * var)
)После этого считаются responsibilities — вероятности принадлежности наблюдения каждой компоненте:
responsibilities = likelihood / (
np.sum(likelihood, axis=1, keepdims=True) + 1e-10
)Затем обновляются параметры смеси:
- средние;
- дисперсии;
- веса компонент.
means[k] = np.sum(responsibilities[:, k] * data) / sum_resp[k]
variances[k] = (
np.sum(responsibilities[:, k] * (data - means[k]) ** 2)
/ sum_resp[k]
)
weights[k] = sum_resp[k] / NВ конце веса нормализуются:
weights /= np.sum(weights)На каждой итерации считается логарифм правдоподобия:
log_likelihood = np.sum(np.log(np.sum(likelihood, axis=1) + 1e-10))Если изменение log-likelihood становится меньше порога
CONVERGENCE_THRESH = 1e-5алгоритм завершает работу раньше максимального числа итераций.
После обучения нужно определить, какая компонента соответствует фону.
Используется критерий устойчивости:
stability = weights / np.sqrt(variances + 1e-6)
bg_idx = np.argmax(stability)Фоновая компонента обычно:
- имеет большой вес, потому что встречается чаще остальных;
- имеет малую дисперсию, потому что фон меняется слабо.
Поэтому отношение
weight / sqrt(variance)хорошо выделяет наиболее стабильное состояние пикселя.
Итоговое значение фона:
background[i, j] = np.clip(means[bg_idx], 0, 255).astype(np.uint8)После восстановления фона результат сравнивается с эталонным изображением:
corr, _ = pearsonr(gt.flatten(), background.flatten())Используется коэффициент корреляции Пирсона между эталоном и построенным фоном.
Порог успешности:
THRESHOLD = 0.93Если corr >= THRESHOLD, видео считается обработанным успешно.
Итоговая оценка в коде рассчитывается как:
points = successful + 4 if successful > 0 else 0numpy— численные вычисления;Pillow— загрузка изображений;matplotlib— визуализация результатов;scipy— вычисление корреляции Пирсона.
.
├── main.py
├── requirements.txt
├── README.md
├── background/
Где:
- background/ сожержит сравнительный результат полученного выделенного фона с эталонным, предоставленным в экспериментальном датасете
pip install -r requirements.txtСодержимое requirements.txt:
numpy
Pillow
matplotlib
scipypython main.pyВо время выполнения программа:
- обрабатывает каждую видеопоследовательность;
- выводит коэффициент корреляции;
- отображает эталонный и восстановленный фон;
- считает количество успешных роликов.
THRESHOLD = 0.93
N_COMPONENTS = 5
MAX_ITERS = 5
CONVERGENCE_THRESH = 1e-5
MIN_VARIANCE = 1e-5THRESHOLD— минимальная корреляция для успешной обработки;N_COMPONENTS— число компонент смеси;MAX_ITERS— максимум итераций EM-алгоритма;CONVERGENCE_THRESH— порог остановки по изменению log-likelihood;MIN_VARIANCE— минимальная дисперсия для численной устойчивости.
- высокая вычислительная стоимость из-за попиксельной обработки;
- используется только один цветовой канал;
- число компонент задаётся заранее;
- малого количества итераций может быть недостаточно для сложных сцен;
- корреляция Пирсона не всегда полностью отражает визуальное качество.