В этом примере показано, как к разработке и реализации отделимый фильтр изображений, который использует меньше аппаратных ресурсов, чем традиционный 2D фильтр изображений.
Традиционные фильтры изображений работают с областью пикселей и вычисляют получившееся значение одного центрального пикселя с помощью двумерного ядра фильтра, которое является матрицей, которая представляет коэффициенты фильтра. Каждый коэффициент умножается с его соответствующим пикселем, и результат суммирован, чтобы сформировать значение. Область затем перемещена на один пиксель, и следующее значение вычисляется.
Отделимый фильтр прост в концепции: если двумерное ядро фильтра может быть включено в горизонтальную компоненту и вертикальную компоненту, то каждое направление может быть вычислено отдельно с помощью одномерных фильтров. Эта факторизация может только быть сделана для определенных типов ядер фильтра. Эти ядра называются отделимые, поскольку части могут быть разделены. Решение, какие ядра отделимы и которое не является, является легкой линейной алгеброй использования в MATLAB. Математически, два 1D фильтра применяют операцию свертки, чтобы равняться исходному 2-D ядру фильтра, но отделимая реализация фильтра часто сохраняет аппаратные ресурсы.
Систему SeparableFilterHDL.slx показывают ниже. Подсистема SeptFiltHDL содержит отделимый фильтр, и также реализацию блока Image Filter эквивалентного 2D ядра как ссылка.
modelname = 'SeparableFilterHDL'; open_system(modelname); set_param(modelname,'SampleTimeColors','on'); set_param(modelname,'SimulationCommand','Update'); set_param(modelname,'Open','on'); set(allchild(0),'Visible','off');
Запустите путем решения того, что цель фильтра будет и вычислит ядро. Этот пример использует Гауссов фильтр размера 5x5 со стандартным отклонением 0,75. Этот фильтр имеет эффект размытия на изображениях и часто используется, чтобы удалить шум и маленькие детали перед другими операциями фильтрации, такими как обнаружение ребра. Заметьте, что Гауссово ядро фильтра циркулярное симметричный о центре.
Hg = fspecial('gaussian',[5,5],0.75)
Hg = 0.0002 0.0033 0.0081 0.0033 0.0002 0.0033 0.0479 0.1164 0.0479 0.0033 0.0081 0.1164 0.2831 0.1164 0.0081 0.0033 0.0479 0.1164 0.0479 0.0033 0.0002 0.0033 0.0081 0.0033 0.0002
Чтобы проверять, отделимо ли ядро, вычислите его ранг, который является оценкой количества линейно независимых строк или столбцов в ядре. Если rank
возвращается 1, затем строки и столбцы связаны линейно, и ядро может быть разделено на свои горизонтальные и вертикальные компоненты.
rankHg = rank(Hg)
rankHg = 1
Чтобы разделить ядро, используйте svd
функция, чтобы выполнить сингулярное разложение. svd
функция возвращает три матрицы, [U,S,V]
, таким образом, что U*S*V'
возвращает исходное ядро, Hg
. Поскольку ядро является рангом 1, S
содержит только один ненулевой элемент. Компоненты разделенного фильтра являются первым столбцом каждого U
и V
, и разделение сингулярного значения между этими двумя векторами. Чтобы разделить сингулярное значение, умножьте оба вектора с квадратным корнем из S
. Необходимо изменить V
так, чтобы Hh
горизонталь, или строка, вектор.
Для получения дополнительной информации об отделимости фильтра обратитесь к ссылкам в нижней части этого примера.
[U,S,V]=svd(Hg) Hv=abs(U(:,1)*sqrt(S(1,1))) Hh=abs(V(:,1)'*sqrt(S(1,1)))
U = -0.0247 -0.8445 -0.5309 0.0665 -0.0000 -0.3552 0.3274 -0.5123 -0.0648 0.7071 -0.8640 -0.2416 0.4345 0.0796 0.0000 -0.3552 0.3274 -0.5123 -0.0648 -0.7071 -0.0247 -0.1190 0.0663 -0.9904 -0.0000 S = 0.3793 0 0 0 0 0 0.0000 0 0 0 0 0 0.0000 0 0 0 0 0 0.0000 0 0 0 0 0 0.0000 V = -0.0247 0.1147 0.9846 -0.1298 0.0000 -0.3552 0.5987 -0.0646 0.1062 0.7071 -0.8640 -0.4906 0.0208 -0.1114 -0.0000 -0.3552 0.5987 -0.0646 0.1062 -0.7071 -0.0247 -0.1714 0.1478 0.9737 0.0000 Hv = 0.0152 0.2188 0.5321 0.2188 0.0152 Hh = 0.0152 0.2188 0.5321 0.2188 0.0152
Можно проверять работу путем восстановления исходного ядра от этих учтенных частей и видеть, являются ли они тем же самым к в точности с плавающей точкой. Вычислите ядро проверки, Hc
, и сравните его с исходным Hg
использование допуска.
Hc = Hv * Hh; equalTest = all(Hc(:)-Hg(:) < 5*max(eps(Hg(:))))
equalTest = logical 1
Этот результат доказывает тот Hv
и Hh
может использоваться, чтобы воссоздать исходное ядро фильтра.
Для генерации HDL-кода необходимо установить коэффициенты фильтра на типы данных с фиксированной точкой. При выборе фиксированных точек необходимо рассмотреть то, что происходит с отделимостью, когда вы квантуете ядро.
Во-первых, квантуйте целое ядро к номинальному типу данных. Этот пример использует 10-битный номер фиксированной точки. Позвольте Fixed-Point Tool выбрать лучшую дробную длину, чтобы представлять значения ядра. Сделайте то же преобразование для векторов горизонтальной и вертикальной компоненты.
Hgfi = fi(Hg,0,10); Hvfi = fi(Hv,0,10); Hhfi = fi(Hh,0,10);
В этом случае, лучшая точность 10-битный ответ для Hg
имеет 11 дробных битов, в то время как Hv
и Hh
используйте только 10 дробных битов. Этот результат целесообразен начиная с Hv
и Hh
умножаются и добавляются вместе, чтобы сделать исходное ядро.
Теперь необходимо проверять, является ли квантованное ядро все еще рангом 1. Начиная с rank
и svd
функции не принимают фиксированные точки, необходимо преобразовать назад в, удваивается. Эта операция не квантует результаты, пока фиксированная точка меньше, чем 53 бита, который является эффективным размером мантиссы, удваивается.
rankDouble = rank(double(Hgfi))
rankDouble = 3
Этот результат показывает, что квантование может иметь сильное воздействие на отделимости: поскольку ранг больше не равняется 1, квантованный фильтр, кажется, не отделим. Для этого конкретного ядра фильтра вы могли экспериментировать с квантованным размером слова и обнаружить, что 51 бит точности необходим для функции ранга, чтобы возвратиться 1 после квантования. На самом деле этот результат чрезмерно консервативен из-за квантования почти нулевых значений в rank
функция.
Вместо того, чтобы расширить фиксированную точку до 51 бита, добавьте аргумент допуска в rank
функционируйте, чтобы ограничить эффекты квантования.
rankDouble2048 = rank(double(Hgfi),1/2048)
rankDouble2048 = 1
Этот результат показывает, что квантованное ядро является рангом 1 в 11-битном дробном допуске. Так, 11-битные разделенные коэффициенты приемлемы, в конце концов.
Другое беспокойство квантования - обеспечивает ли фильтр фиксированной точки плоскую полевую яркость. Коэффициенты фильтра должны суммировать к 1,0, чтобы обеспечить уровни яркости. Для нормированного Гауссова фильтра, такого как этот пример, содействующая сумма всегда 1.0, но эта сумма может быть отодвинута от 1,0 квантованием фиксированной точки. Может быть очень важно в обработке видеоданных и обработке изображений обеспечить точно 1.0, в зависимости от приложения. Если вы воображаете цепь фильтров, каждый из которых повышает средний уровень приблизительно на 1%, то совокупная ошибка может быть большой.
sumHg = sum( Hg(:) ) sumHgfi = sum( Hgfi(:) )
sumHg = 1.0000 sumHgfi = 1 DataTypeMode: Fixed-point: binary point scaling Signedness: Unsigned WordLength: 15 FractionLength: 11
В этом случае, суммы Hg
с двойной точностью и фиксированная точка
Hgfi
действительно 1.0. Если поддержание уровней яркости к абсолютным пределам важно в вашем приложении, то вам придется вручную настроить содействующие значения фильтра, чтобы обеспечить сумму 1.
Наконец, проверяйте, что комбинация квантованных фильтров компонента все еще выдерживает сравнение с квантованным ядром. По умолчанию, fi
функционируйте использует полную точность на арифметическом выражении. Используйте конвергентное округление, поскольку существуют некоторые содействующие значения очень около округляющегося предела.
Hcfi = fi(Hvfi * Hhfi,0,10,'fimath',fimath('RoundingMethod','Convergent')); equalTest = all( Hcfi(:)==Hgfi(:) )
equalTest = logical 1
Этот результат подтверждает, что фиксированная точка, разделенные коэффициенты достигают того же фильтра как 2D Гауссово ядро.
Чтобы видеть отделимую реализацию фильтра, откройте Отделимую подсистему Фильтра, которая является в подсистеме SepFiltHDL.
open_system([modelname '/SepFiltHDL/Separable Filter'],'force');
Эта подсистема выбирает вертикальные и горизонтальные векторы пикселей для фильтрации и выполняет работу фильтра.
Line Buffer выходные параметры столбец пикселей для каждого временного шага фильтра. Буфер Линии также заполняет ребра изображения. Этот Дополнительный метод использования модели: Constant
, со значением 0. shiftEnable выходной сигнал обычно используется, чтобы управлять горизонтальным сдвиговым регистром, чтобы скомпилировать 2D пиксельное ядро. Однако для отделимого фильтра, вы хотите работать в каждом направлении отдельно. Эта модель использует столбец выходного пикселя для вертикального фильтра и использует сигнал shiftEnable позже, чтобы создать горизонтальный пиксельный вектор.
Разделенная горизонталь и вертикальные фильтры симметричны, таким образом, модель использует предварительный сумматор, чтобы сократить количество множителей еще больше. После сумматора блок Gain умножает столбец пикселей Hv
вектор. Параметр Усиления устанавливается на Hv
и типом данных параметров является fixdt(0,10)
. Получившимся выходным типом в полной точности является ufix18_En10
. Затем блок Sum завершает вертикальный фильтр. Блок Sum сконфигурирован в режиме максимальной точности. Выход является скаляром ufix21_En10
ввод.
Существует много опций конвейеризации, которые вы могли выбрать, но поскольку этот проект прост, ручная конвейеризация быстра и легка. Добавление задержек 2 циклов до и после множителей Усиления гарантирует хорошую скорость, когда синтезируется для FPGA. Задержка 3 циклов после Суммы позволяет, чтобы он был достаточно конвейерным также. Модель балансирует эти задержки на шине pixelcontrol и сигнале shiftEnable прежде, чем идти в фильтр измерения по горизонтали.
Лучший способ создать сдвиговый регистр ширины ядра состоит в том, чтобы использовать блок Tapped Delay, который переключает скаляр на нижний регистр и выводит значения регистра как вектор. Для хороших результатов синтеза HDL используйте блок Tapped Delay в активированной подсистеме с Синхронным блоком маркера.
Выход Коснувшейся подсистемы Задержки является вектором 5 горизонтальных пикселей, готовых к горизонтальной компоненте отделимого фильтра. Модель реализует подобное симметричное, предварительно добавляют и блок Gain, на этот раз с Hh
как параметр. Затем блок Sum и подобная конвейеризация завершают горизонтальный фильтр. Отфильтрованное пиксельное значение финала находится в типе данных полной точности ufix34_En20
.
Много раз в обработке изображений требуется сделать полную точность или по крайней мере арифметические операции высокой точности, но затем возвратиться к исходному пиксельному типу входных данных для выхода. Эта подсистема возвращается к исходному пиксельному типу при помощи набора блока Data Type Conversion к uint8
, с Nearest
округление и насыщение.
Блоки Vision HDL Toolbox обеспечивают выходные данные, чтобы обнулить, когда выход не допустим, как обозначено в шине pixelcontrol выход. В то время как не строго требуемый, это поведение делает тестирование и отладку намного легче. Чтобы выполнить это поведение, модель использует блок switch с набором блока Constant к 0.
Отделимое 5x5 реализация фильтра использует 3 множителя в вертикальном направлении и 3 множителя в горизонтальном направлении для в общей сложности 6 множителей. Традиционный фильтр изображений обычно требует 25 множителей для 5x5 ядро. Однако блок Image Filter использует в своих интересах любую симметрию в ядре. В этом примере ядро имеет симметрию с 4 путями и с 8 путями, таким образом, Фильтр Изображений только использует 5 множителей. В целом существуют сбережения во множителях при реализовании отделимого фильтра, но в этом случае 2D реализация подобна.
Отделимый фильтр использует 4 полусумматора в каждом направлении, 2 для предварительно добавления плюс 2 в Сумме, для в общей сложности 8. Фильтр Изображений требует, чтобы 14 общих количеств сумматоров, с 10 предварительно добавили сумматоры и 4 итоговых сумматора. Таким образом, существует существенное сохранение в сумматорах.
Фильтр Изображений требует 25 регистров для сдвигового регистра, в то время как отделимый фильтр использует только 5 регистров для сдвигового регистра. Каждый сумматор также требует регистра трубопровода так, чтобы был 8 для отделимого случая и 14 для традиционного случая. Количество множителя конвейерно обрабатывает шкалы регистров в зависимости от количества множителей.
Отделимый фильтр использует меньше сумматоров и регистров, чем 2D фильтр. Количество множителей подобно между двумя фильтрами только потому, что 2D реализация оптимизирует симметричные коэффициенты.
Получившиеся изображения от симуляции отделимого фильтра и ссылочного Фильтра Изображений очень похожи. Используя настройки фиксированной точки в этом примере, различии между отделимым фильтром и ссылочным фильтром никогда не превышает один бит. Этим различием является различие на 0,1% или больше, чем PSNR на 54 дБ между отфильтрованными изображениями в целом.
Проверять и сгенерировать HDL-код сослались в этом примере, у вас должна быть лицензия HDL Coder™.
Чтобы сгенерировать HDL-код, используйте следующую команду.
makehdl('SeparableFilterHDL/SepFiltHDL')
Чтобы сгенерировать испытательный стенд, используйте следующую команду. Обратите внимание на то, что генерация испытательного стенда занимает много времени из-за большого размера данных. Уменьшайте время симуляции прежде, чем сгенерировать испытательный стенд.
makehdltb('SeparableFilterHDL/SepFiltHDL')
Часть этой модели, которую можно реализовать на FPGA, является частью между Системой координат К Пикселям и Пикселями, Чтобы Структурировать блоки. Подсистема SepFiltHDL включает и отделимый алгоритм и традиционную 2D реализацию в целях сравнения.
Теперь, когда у вас есть HDL-код, можно симулировать его в симуляторе HDL. Автоматически сгенерированный испытательный стенд позволяет вам доказывать, что симуляция Simulink и симуляция HDL соответствуют.
Можно также синтезировать сгенерированный HDL-код в инструменте синтеза FPGA, таком как Xilinx Vivado. В FPGA Virtex-7 (xc7v585tffg1157-1), создание фильтра достигает тактовой частоты более чем 250 МГц.
Отчет использования показывает, что отделимый фильтр использует меньше ресурсов, чем традиционный фильтр изображений. Различие в использовании ресурса мало из-за оптимизации симметрии, примененной блоком Image Filter.
Фильтр в этом примере сконфигурирован для Гауссовой фильтрации, но другие типы фильтров также отделимы, включая некоторых, которые очень полезны. Средний фильтр, который имеет ядро с коэффициентами, которые являются всем 1/N
, всегда отделимо.
Hm = ones(3)./9 rank(Hm)
Hm = 0.1111 0.1111 0.1111 0.1111 0.1111 0.1111 0.1111 0.1111 0.1111 ans = 1
Или ядро обнаружения ребра Sobel:
Hs = [1 0 -1; 2 0 -2; 1 0 -1] rank(Hs)
Hs = 1 0 -1 2 0 -2 1 0 -1 ans = 1
Или ядра градиента как это:
Hgrad = [1 2 3; 2 4 6; 3 6 9] rank(Hgrad)
Hgrad = 1 2 3 2 4 6 3 6 9 ans = 1
Отделимость может также быть применена к фильтрам, которые не используют, умножаются - добавляют, такие как морфологические фильтры, где оператор является min или максимум.
Вы использовали линейную алгебру, чтобы определить, отделимо ли ядро фильтра или нет, и если это, вы изучили, как разделить компоненты с помощью svd
функция.
Вы исследовали эффекты квантования фиксированной точки и узнали, что важно работать с точными значениями при вычислении ранга и сингулярных значений. Вы также узнали о важности поддержания усиления DC. Наконец вы изучили, почему отделимые фильтры могут быть реализованы более эффективно и как вычислить сбережения.
[1] Eddins, S. "Отделимая свертка". Стив на Обработке изображений (4 октября 2006).
[2] Eddins, S. "Отделимая свертка: Часть 2 дюйма. Стив на Обработке изображений (28 ноября 2006).