[手]撕滑动窗口滤波器

来源:OpenFPGA

图像处理中最常用的技术之一是使用滑动窗口滤波器,该滤波器在图像上滑动一个 nxn 矩阵并对中心像素进行操作。

1.png

对像素执行的操作可以从简单的delta函数到更复杂的操作,例如边缘检测或边缘增强。

2.png

实际上我比较喜欢使用一些高级综合工具进行算法设计,例如HLS和 Matlab Simulink。对于图像处理的算法设计,它们会自动构建这些滤波器基础的滑动窗口函数,所以这些工具会加速图像处理算法的设计和开发。

但是作为工程师,我们在利用这些高级工具的同时,也需要能够理解底层的概念和原理。当我们使用更小、资源更受限的设备时,能够从头开始设计和实现相关算法。

滑动窗口滤波器的关键要素之一是其能够滑动 n x n 个像素。这意味着,如果我们要实现 3x3 滑动窗口过滤器,我们需要能够缓冲至少两行像素的内容,这样我们便能够处理在窗口上滑动的滤波器。

本文中,我们将了解如何创建一个简单的 3x3 窗口。此滑动窗口的输出是窗口的 9 个像素值。然后,我们就可以进行例如中值滤波、边缘增强等后续算法操作。

由于使用Xilinx芯片较多,所以我们的开发基于AXIS 接口。

滑动窗口的一个常见问题是如何处理图像边缘周围的像素数据。为了确保框架具有我们需要处理的正确大小,最简单的方法之一是用零填充。

首先,我们定义将要使用行缓冲区。

-- Line buffer type
type t_line_buffer is array (0 to g_IMG_WIDTH-1) of std_logic_vector(g_TDATA_WIDTH-1 downto 0); 

-- Line buffers to store two rows of pixels signal s_line1, s_line2 : t_line_buffer;

设置好了行缓冲区后,下一步就是提供“滑动”功能。

由于我们要使用 AXI Streaming接口,模块会通过下游“背压”。如果有效信号被置位,并且USER信号指示新帧的开始,则内部计数器将被复位。

-- Shift window registers
s_r13 <= s_r12;
s_r12 <= s_r11;
s_r11 <= i_s_axis_tdata;              
s_r23 <= s_r22;
s_r22 <= s_r21;
s_r21 <= s_line1(s_pixel_count);              
s_r33 <= s_r32;
s_r32 <= s_r31;
s_r31 <= s_line2(s_pixel_count);

如果像素是某一行的最后一个像素,则重置该行的像素数。

if i_s_axis_tlast = '1' then
 s_pixel_count <= 0;
     if s_row_count = g_IMG_HEIGHT-1 then
      s_row_count <= 0;
     else
       s_row_count <= s_row_count + 1;
     end if;
else
 s_pixel_count <= s_pixel_count + 1;
end if;

最后一个是处理边缘情况,当顶部或边缘时像素填充 0x00。

s_233 <= s_r22;

if s_row_count = 0 then
  s_w11 <= (others => '0');
  s_w12 <= (others => '0');
  s_w13 <= (others => '0');
else
  s_w11 <= s_r11 when s_pixel_count /= 0 else (others => '0');                      
  s_w12 <= s_r12;
  s_w13 <= s_r13 when s_pixel_count /= g_IMG_WIDTH-1 else (others => '0');                end if;

  -- Middle row padding
  s_w21 <= s_r21 when s_pixel_count /= 0 else (others => '0');
  s_w23 <= s_r23 when s_pixel_count /= g_IMG_WIDTH-1 else (others => '0'); 
   -- Bottom row padding

if s_row_count = g_IMG_HEIGHT-1 then
   s_w31 <= (others => '0');
   s_w32 <= (others => '0');
   s_w33 <= (others => '0');
else
   s_w31 <= s_r31 when s_pixel_count /= 0 else (others => '0');
   s_w32 <= s_r32;
   s_w33 <= s_r33 when s_pixel_count/= g_IMG_WIDTH-1 else (others => '0');
 end if;

编写 RTL 后,下一步就是进行仿真,尤其测试边缘情况。

3.png

该模块的输出是 3x3 网格中的 9 个像素,接下来就是进行图像处理操作,例如中值滤波或者边缘增强等操作,我们将在后续的文章进行讲解。

本文项目地址:https://github.com/ATaylorCEngFIET/MZ581 







最新文章

最新文章