【问题标题】:Image Processing Pipelining in VHDLVHDL 中的图像处理流水线
【发布时间】:2015-04-08 03:06:02
【问题描述】:

我目前正在尝试用 VHDL 开发 Sobel 滤波器。我正在使用存储在 BRAM 中的 640x480 图片。该算法使用图像的 3x3 像素矩阵来处理每个输出像素。我的问题是我目前只知道将图像放入 BRAM 中,其中 BRAM 的每个地址都保存一个像素值。这意味着我每个时钟只能读取一个像素。我的问题是我正在尝试对数据进行管道传输,因此理想情况下,我需要能够每个时钟获取三个像素值(图片的每一行中的一个),因此在我的初始延迟之后,我可以在每个时钟中加载三个新的像素值时钟并在每个时钟上获得一个输出像素。我正在寻找一种方法来做到这一点,但无法弄清楚。

我能想到的解决此问题的唯一方法是将图像放在 3 个 BRAM 中。这样我可以在每个时钟周期读取 3 行的值。但是,没有足够的内存空间来容纳一个足够大的 RAM 来容纳 640x480 图像,更不用说三个了。我可以降低图片尺寸来做到这一点,但我真的想用我目前的 640x480 图像尺寸来做。

任何帮助或指导将不胜感激。

【问题讨论】:

  • 每个 BlockRAM 字使用多少位?您使用 RGB 还是仅使用黑白?您还可以使用外部存储器来存储图像(QDR-SSRAM 或 DDR-SDRAM)并将图像的一部分复制到本地快速 BlockRAM 中。此解决方案可扩展到 FullHD 及更高版本。
  • 请注意,Sobel 是一个可分离的过滤器,因此您可以将过滤作为两个一维卷积执行。您可以分别处理图像的行和列,而不是在二维邻域中加载 9 个像素。
  • 照片是每像素 12 位,我使用的是 RGB

标签: memory image-processing vhdl fpga pipeline


【解决方案1】:

一个简单的解决方案是将图像的 1/4 存储在 4 个单独的存储器中。第一个内存包含每 4 行,每 4 行第二个,从第二行开始,等等。即使你需要 3 行,我也会使用 4,因为 4 均分 480 和每隔一个标准分辨率。此外,找到一个模 4 的二进制数是微不足道的,这是对内存进行排序所需要的。

您可以使用行号的 MSB 来寻址您的 RAM,并使用 LSB 来确定每个 RAM 输出的相对顺序(代码仅用于演示想法,它不能按原样使用......):

address <= line(line'left downto 2) & col; -- Or something more efficent on packing
data0 <= ram0(address);
data1 <= ram1(address);
data2 <= ram2(address);
data3 <= ram3(address);

case line(1 downto 0) is
    when "00" =>
        line0 <= data0;
        line1 <= data1;
        line2 <= data2;
    when "01" =>
        line0 <= data1;
        line1 <= data2;
        line2 <= data3;
    when "10" =>
        line0 <= data2;
        line1 <= data3;
        line2 <= data0;
    when "11" =>
        line0 <= data3;
        line1 <= data0;
        line2 <= data1;
    when others => null;
end case;

【讨论】:

    【解决方案2】:

    几年前我做了一个sobel过滤器。为此,我编写了一个在每个时钟周期提供 9 个像素的管道:

    architecture rtl of matrix_3x3_builder_8b is
    type fifo_t is array (0 to 2*IM_WIDTH + 2) of std_logic_vector(7 downto 0);
    signal fifo_int : fifo_t;
    
    begin    
    
        p0_build_5x5: process(rst_i,clk_i)
        begin
            if( rst_i = '1' )then
                fifo_int <= (others => (others => '0'));
            elsif( rising_edge(clk_i) )then
                 if(data_valid_i = '1')then
                    for i in 1 to 2*IM_WIDTH + 2 loop
                        fifo_int(i) <= fifo_int(i-1);
                    end loop;           
                    fifo_int(0) <= data_i;  
                end if;
            end if;
        end process p0_build_5x5;
    
    data_o1 <= fifo_int(0*IM_WIDTH + 0);
    data_o2 <= fifo_int(0*IM_WIDTH + 1);
    data_o3 <= fifo_int(0*IM_WIDTH + 2);
    data_o4 <= fifo_int(1*IM_WIDTH + 0);
    data_o5 <= fifo_int(1*IM_WIDTH + 1);
    data_o6 <= fifo_int(1*IM_WIDTH + 2);
    data_o7 <= fifo_int(2*IM_WIDTH + 0);
    data_o8 <= fifo_int(2*IM_WIDTH + 1);
    data_o9 <= fifo_int(2*IM_WIDTH + 2);
    
    end rtl;
    

    在这里,您逐个像素地读取图像以构建您的 3x3 矩阵。填充管道的时间较长,但一旦完成,每个时钟脉冲都会有一个新矩阵。

    【讨论】:

    • 这段代码使用寄存器来存储图像线(至少在Xilinx上),它确实应该被修改为使用BRAM。事实上,如果您需要更大的图像或过滤器中的更多行,它会非常容易缩放。
    • 是的,理论上这段代码使用寄存器,但在 Quartus 中,您可以指定使用 FPGA 内存块(如 M9K 等...)而不是寄存器。
    • 看起来这将推断出 2 组块 ram,在之前、之后和之间有 3 个寄存器,全部配置为模拟一个长移位寄存器。这也特别适用于这个问题,因为它只使用块 RAM 的 2*width*(bits/pixel) 位。这意味着您可能会大大提高您的最大分辨率。
    【解决方案3】:

    如果您想继续存储整个图像,那么我会按照 Jonathan Drolet 的建议在四个 ram 之间循环,同时一次写入和读取所有 4 个(将您关心的三个混合到 3 个寄存器中)。 这是有效的,因为您的 ram 将足够深,您仍然能够在 1/4 深度(仍然是 77k 深)处获得完整的 BRAM 利用率,并且您的读取可以被可预测地分段。

    对于这个问题的具体情况,Nicolas Roudel 的方法使用 BRAM 便宜得多,尽管您不能一次存储整个图像,因此无论您在哪里发送结果都无法对您进行反压,除非您可以对数据源进行反压.这对您的应用程序可能很重要,也可能无关紧要。

    当您尝试使用极宽但相当浅(1k 深)的 ram 进行此类操作时,分段将使用更多的块 ram(甚至开始推断分布式 ram)。当读取不遵循特定模式时(在您的情况下,模式是它们都是连续且相邻的位置),则无法对 ram 进行分段。保持高效 BRAM 使用的最佳策略通常是从本机双端口块 ram 构建四端口 ram,方法是使用与正常时钟相位对齐的 2x 时钟为它们计时,允许您每 1x 时钟执行一次写入和 3 次读取循环。

    【讨论】:

      猜你喜欢
      • 2017-07-01
      • 2013-06-11
      • 2013-01-09
      • 1970-01-01
      • 2015-03-03
      • 1970-01-01
      • 2013-07-10
      • 2014-08-04
      • 2019-09-28
      相关资源
      最近更新 更多