В предыдущей статье я рассказал о реализации алгоритма расчёта межкадровой разности с использованием встроенной SDRAM в FPGA. В этой статье я опишу следующий этап разработки прошивки стереокамеры — аппаратную реализацию морфологических преобразований и блока гамма-коррекции.

Морфологические преобразования

Базовые морфологические преобразования — это фундаментальный инструмент в обработке изображений, основанный на теории множеств и используемый для анализа и обработки бинарных и, как в моем случае, изображений в градациях серого.

Преобразования осуществляются с помощью структурного элемента (или ядра), которое скользит по изображению, взаимодействуя с пикселями. Основная цель — изменить структуру объектов: убрать шум, заполнить отверстия, объединить или разделить объекты.

Структурный элемент (Structuring Element) - это небольшая бинарная маска (матрица), которая определяет окрестность для обработки каждого пикселя. Она бывает разной формы (квадрат, круг, крест и т.д.) и размера. В моей реализации используется квадратный структурный элемент размерностью 3x3. Центр структурного элемента совмещается с текущим обрабатываемым пикселем.

Базовые операции

  • Дилатация (расширение)

    • Принцип: увеличение светлых областей

    • Алгоритм: для каждой позиции структурного элемента выбирается максимальное значение из покрываемых пикселей

    • Эффект: светлые области увеличиваются, тёмные уменьшаются, подчёркиваются яркие детали

      Рисунок 1 : Дилатация (Dilation). Источник
      Рисунок 1 : Дилатация (Dilation). Источник
  • Эрозия (сжатие)

    • Принцип: уменьшение светлых областей

    • Алгоритм: для каждой позиции выбирается минимальное значение из покрываемых пикселей

    • Эффект: тёмные области увеличиваются, светлые уменьшаются, подчёркиваются тёмные детали

      Рисунок 2 Эрозия (Erosion). Источник
      Рисунок 2 Эрозия (Erosion). Источник

Производные операции

  • Размыкание (эрозия → дилатация)

    • Удаляет светлые шумы

    • Сохраняет общую интенсивность фона

      Рисунок 3 Размыкание (Opening). Источник
      Рисунок 3 Размыкание (Opening). Источник

      Замыкание (дилатация → эрозия)

    • Удаляет тёмные шумы

    • Заполняет тёмные промежутки в светлых областях

      Рисунок 4 Замыкание (Closing). Источник
      Рисунок 4 Замыкание (Closing). Источник

Реализация 

Для реализации скользящего окна будем использовать два последовательно соединенных сдвиговых регистра с динамической длиной. Длина сдвигового регистра равна длине строки изображения. С учетом текущего входного пикселя на выходе блока регистров получаем три пикселя, которые на исходном изображении находятся на одной вертикальной координате.

Рисунок 5 Реализация фильтра
Рисунок 5 Реализация фильтра

Из этих трёх пикселей с помощью мультиплексора, в зависимости от требуемого алгоритма, выбираем минимум, максимум или среднее значение. Среднее значение используется для медианного фильтра, хотя сам по себе такой фильтр и не является базовым морфологическим преобразованием, его реализация в данном случае не занимает дополнительных ресурсов.

Далее полученное значение поступает в три последовательно соединённых регистра, выходы которых подключены к точно такому же мультиплексору. Таким образом, когда конвейер полностью заполнится полезными данными, на выходе при получении каждого нового входного пикселя будем иметь новый обработанный пиксель, рассчитанный из скользящего окна 3×3.

В такой реализации получаем краевые эффекты в первых строках и первых столбцах кадра. Для решения этой проблемы в будущем можно использовать несколько алгоритмов:

  • заполнение константами (Zero Padding, Constant Padding);

  • растягивание (Replicate/Clamp);

  • зеркальное отражение (Border Reflection).

Для выполнения производных операций размыкания и замыкания требуется два последовательно включённых фильтра. Таким образом, конвейер принял следующий вид

Рисунок 6 Блок фильтров
Рисунок 6 Блок фильтров

Каждый из фильтров управляется независимо, таким образом можно получить любую пару преобразований из комбинации эрозии, дилатации или медианного фильтра. Блок BYPASS полностью исключает фильтры из конвейера обработки данных.

Тестирование

Для тестов возьмём сцену с близко расположенными винтами на тёмном фоне и попробуем с помощью алгоритма blob detection выделить каждый винт на сцене отдельно. Как видно, без фильтрации часто несколько винтов на сцене «сливаются» и определяются как единый объект. Попробуем включить оба фильтра в режим эрозии, это должно визуально «разделить» близко расположенные объекты.

Фильтрация отключена
Фильтрация отключена

Как видно из GIF-файла, результат детекции стал точнее: границы светлых объектов сжались, и между ними появились промежутки, позволяющие алгоритму blob detection с более высокой точностью выделить каждый винт на сцене.

Две последовательные операции Erosion
Две последовательные операции Erosion

Отдельно хочется отметить, что такая аппаратная фильтрация никак не влияет на скорость работы камеры: вся обработка делается «на лету», FPS не деградирует, и ресурсы Arduino высвобождаются для других задач.

Гамма-коррекция(LUT)

Гамма-коррекция — это нелинейная операция, используемая для кодирования и декодирования яркости изображения в фотографии, компьютерной графике и видео. Её главная цель — эффективно использовать ограниченный диапазон данных для того, чтобы сохранить воспринимаемую человеческим глазом яркость.

Человеческий глаз воспринимает яркость нелинейно. Мы более чувствительны к изменениям в темных тонах, чем в светлых. Разницу между 5% и 10% яркости мы заметим легко, а разницу между 85% и 90% — почти не заметим. Сенсоры камер же работают практически линейно: если количество фотонов, попадающих на пиксель, удваивается, то электрический заряд (а затем и цифровое значение) также удваивается. Если бы мы хранили и отображали данные линейно, нам бы пришлось использовать большое количество бит для кодирования теней, которые мы так хорошо различаем, а светлые участки были бы «потрачены впустую». Гамма-коррекция решает эту проблему.

Многие современные сенсоры содержат внутри себя ISP для предварительной обработки изображения, в том числе имеются и модули нелинейного предыскажения значений пикселей. Например, используемый мной сенсор MT9V034 имеет режим ADC Companding Mode, который как раз осуществляет нелинейное сжатие яркости. Принцип работы этого режима изображен на рисунке ниже:

Рисунок 7 MT9V034 АDC Companding Mode
Рисунок 7 MT9V034 АDC Companding Mode

Как видно, нелинейность сжатия аппроксимируется всего 4-мя отрезками. Наличие в прошивке FPGA своего модуля гамма-коррекции на базе LUT позволяет каждому значению входной яркости сопоставить любое значение выходной яркости, что является более гибким подходом и позволяет, например, выделить узкий диапазон значений из входного потока и растянуть его на весь диапазон при отображении или инвертировать значения яркостей.

Реализация LUT довольно проста – значение входного пикселя используется как адрес чтения двухпортовой памяти. Значения из памяти являются выходом блока. По умолчанию память инициализирована линейно – по нулевому адресу располагается значение 0, по адресу 1 – 1 и т. д. Второй порт памяти подключен к шине процессора, процессор производит запись рассчитанной таблицы в память. Диаграмма блока представлена на рисунке:

Рисунок 8 Модуль гамма-коррекции
Рисунок 8 Модуль гамма-коррекции

Результаты тестирования

Рисунок 9 Линейный режим работы блока
Рисунок 9 Линейный режим работы блока
Рисунок 10  Гамма коррекции со параметром гамма = 1/2.2
Рисунок 10 Гамма коррекции со параметром гамма = 1/2.2
Рисунок 11 Инверсия яркости
Рисунок 11 Инверсия яркости

Заключение

Я специально не перегружал данную статью кодом, так как весь проект теперь находится в отрытом доступе на github. Наряду с verilog кодом я так же опубликовал код MCU, схему платы, гербер, и сборочный чертеж. Надеюсь моя работа кому-то окажется полезной, возможно даже кто-то внесет свою лепту в развитие проекта!

Комментарии (0)