2015-04-08 107 views
2

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

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

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

+0

每个BlockRAM字中使用了多少位?你使用RGB还是黑白?您也可以使用外部存储器来存储图像(QDR-SSRAM或DDR-SDRAM),并将图像的一部分复制到本地快速BlockRAM中。该解决方案可扩展至FullHD及以上版本。 – Paebbels

+0

请注意,[Sobel](http://en.wikipedia.org/wiki/Sobel_operator)是一个可分离的过滤器,因此您可以将过滤作为两个一维卷积进行。您可以分别处理图像的行和列,而不是在二维邻域中加载9个像素。 – dhanushka

+0

该照片是每像素12位,我使用RGB –

回答

2

一个简单的解决方案是将图像的四分之一存储在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矩阵。管道填​​充时间较长,但一旦完成,每个时钟脉冲都有一个新的矩阵。

+1

此代码使用**寄存器**来存储图像行(至少在Xilinx上),它确实应该修改为使用BRAM。因为如果你需要更大的图像或更多的线条在滤镜中,它会变得非常可怕。 –

+1

是的,理论上这个代码使用寄存器,但使用Quartus,您可以指定使用FPGA存储器模块(如M9K等),而不是寄存器。 –

+0

看起来像这样会推断出2组块ram,其中之前,之后和之间有3个寄存器,所有这些都被配置为模拟一个长移位寄存器。这也适用于这个问题,因为它只使用块内存的2 *宽*(比特/像素)比特。这意味着你可以大幅度提高你的最大分辨率。 – QuantumRipple

1

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

对于这个问题的细节,虽然你不能一次存储整个图像,但是对于BRAM来说,Nicolas Roudel的方法要便宜得多,所以无论你发送结果如何,除非你能够反压你的数据资源。这对你的应用程序来说可能并不重要。

当你试图用极宽,但相当浅的(1k深)来做这样的事情时,公羊会使用更多的块RAM(甚至开始推断分布式RAM)。当读数不遵循特定模式(您的情况中的模式是它们都是连续的和相邻的位置)时,内存不能被分段。保持高效BRAM使用的最佳策略通常是通过使用与您的正常时钟相位对齐的2x时钟为它们提供时钟,从本地双端口块RAM制作四端口RAM,从而允许您每写1个时钟进行一次写操作和3次读取周期。