Время прочтения: 4 мин.
Получим кадры пустых улиц на примере Токио – одного из самых густонаселённых городов планеты. За основу возьмём раскадровку видео с камеры наблюдения в районе Shinjuku, плотность населения которого составляет более 19 тыс. человек на кв. км.
Для этого используем метод медианного фильтра, реализованный в библиотеке MedianFilter для Go, хорошо зарекомендовавшего себя в задачах обработки огромных объёмов данных и имеющего встроенный параллелизм.
Принцип работы фильтра заключается в устранении шумов и помех на кадрах путём усреднения значений. Видео с камер наблюдения идеально подходит для демонстрации применения медианной фильтрации, так как сама камера недвижима в процессе съёмки, и программе проще идентифицировать все объекты в кадре. Это применяется для удаления не только помех, но и крупных движущихся объектов, например, людей и автомобилей. Если же камера движется, то этот метод не будет идеальным.
Под капотом MedianFilter устроен довольно просто: находит средние значения RGB из заданного массива пикселей:
func medianPixel(pixels []Pixel) Pixel {
…
rMedian := rValues[int(len(rValues)/2)]
gMedian := gValues[int(len(gValues)/2)]
bMedian := bValues[int(len(bValues)/2)]
…
}
Далее medianFilter() создает изображение с каждым пикселем, сгенерированным с помощью предыдущей функции medianPixel(). Она начинает с чтения всех входных кадров и создания объектов изображения для каждого из них. Затем она перебирает все пиксели, сгенерированными с помощью функции medianPixel(), и создает новое изображение с усреднёнными пикселями. Все кадры обрабатываются параллельно благодаря sync.WaitGroup:
func medianFilter(filePaths []string) (*Image, error) {
var images []*Image
for _, filePath := range filePaths {
img, err := newImage(filePath)
if err != nil {
return nil, err
}
images = append(images, img)
}
if len(images) < 5 {
return nil, errors.New("not enough images to perform noise reduction")
}
outputImage := images[0]
heigth := outputImage.Height
width := outputImage.Width
for _, img := range images {
if heigth != img.Height || width != img.Width {
return nil, errors.New("at least one image has a different width or height")
}
}
var wg sync.WaitGroup
for rowIndex := 0; rowIndex < heigth; rowIndex++ {
wg.Add(1)
go (func(rowIndex int) {
for colIndex := 0; colIndex < width; colIndex++ {
var pixels []Pixel
for _, img := range images {
pixels = append(pixels, img.Pixels[rowIndex][colIndex])
}
medPixel := medianPixel(pixels)
outputImage.Pixels[rowIndex][colIndex].set("R", medPixel.R)
outputImage.Pixels[rowIndex][colIndex].set("G", medPixel.G)
outputImage.Pixels[rowIndex][colIndex].set("B", medPixel.B)
}
wg.Done()
})(rowIndex)
}
wg.Wait()
return outputImage, nil
}
Это лишь часть кода, но и в нём необязательно разбираться, так как для запуска обработки достаточно запустить следующий код:
package main
import (
"fmt"
"github.com/koraygocmen/MedianFilter"
"io/ioutil"
)
func main() {
folder := "custom_folder"
files, err := ioutil.ReadDir("./"+folder+"/")
if err != nil {
fmt.Println(err)
}
var imagePath []string
for _, f := range files {
imagePath = append(imagePath, "./"+folder+"/"+f.Name())
}
if err := MedianFilter.RemoveMovingObjs(imagePath, "./"+folder+".jpg"); err != nil {
fmt.Println(err)
}
}
В качестве примера мы взяли около 100 случайных кадров из нескольких минут видео с live-камеры, о которой упоминается выше. Примеры исходных кадров:
В результате обработки получим вот такой кадр:
Аналогичный вариант в ночное время. Было:
Стало:
Как видно, на итоговых кадрах имеются небольшие артефакты в виде «машин-призраков», а также единичных пешеходов. Такие объекты основную часть видео были неподвижны. Это можно исправить путём взятия кадров с большего промежутка времени или настройки самой библиотеки.
Возможности медианной фильтрации не ограничиваются обработкой изображений. Она применяется и для обработки аудио, очищая его от акустических сигналов и шумов окружающей среды, уменьшая вариативность в процессе работы интеллектуальных систем восприятия и анализа речевой информации. Используя MedianFilter можно получить идеальные фотографии достопримечательностей в многолюдных местах. Не говоря о том, что можно сделать с материалом, отснятым в отпуске.
Дальнейший шаг – получение «очищенного» видео.