你的位置:EETOP 赛灵思(Xilinx) 社区 >> >> 全部 >> 详细内容

FPGA图像处理系列——实现窗处理

发布者:jackzhang 时间:2015-09-06 13:48:37

窗处理是图像处理中常见的一种处理,它的思想是对于图像矩阵,通过一个固定大小(例如3*3)的小矩阵对图像进行运算操作。常用的窗处理包括 Sobel边缘检测,形态学操作,模糊滤波,高斯滤波等。在基于PC的图像处理领域,可以方便的实现窗处理操作。比如,在opencv库中可以自己随意构 建窗口大小,然后调用相关的函数实现窗处理。

FPGA是一种可定制的逻辑电路,它拥有并行的结构特征,在设计上能实现硬件并行和流水线技术,可以实现算法的加速,而且性价比较高。本文即根据Sobel算法的理论,结合FPGA的结构特征,在FPGA上设计并尝试实现了Sobel窗处理算法方案。

Sobel边缘检测的思想是:该算子包含两组3 x 3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度查分近似值。如果以A代表原始图像,Gx及Gy分别代表经纵向向及横向边缘检测的图像,其公式如下(来源于百度图片):


对于FPGA实现Sobel,首先,是检测窗的实现。这种窗扩展了点运算,以一个局部邻域或窗口内像素值的函数运算结果作为输出:

Q[x,y] = f(I[x,y],...,I[x+△x,y+△y]),  (△x,△y)∈W

其中,W是I[x,y]为中心的窗口。以3×3大小的窗口为例,如图所示:


Sobel算子所需的窗口大小为3×3。在设计过程中,窗口应当满足以下要求:

1.能同时对窗口中所有的元素进行并行操作。

2.窗口采用流水的形式遍历整个图像。

设计的思路是:因为窗口在遍历图像过程中,每一个像素都会被窗口多次 使用,因此需要通过缓存来存储像素,使得它们能在后续的窗口位置被重复利用。在FPGA中,可以设计3个单口行缓存(linebuffer),借助状态机 和列计数值实现这种窗口,采用流水处理的方法,实现加速计算。如图为窗口结构:


其中状态信号用环形计数器来实现(计数值0-2),其驱动时钟为每一行第一个像素的像素时钟。窗口的行0由3个移位寄存器组成;行1——行2由状态机决定和行缓存的对应关系。窗口的控制逻辑如表1所示:

表1 窗口的控制逻辑

输入行

状态

行缓存0

行缓存1

行缓存2

输出行

0

0

(行2)

(行1)

(空)

1

1

行1

(行2)

(空)

2

2

行2

行1

1

3

0

行2

行1

2

4

1

行1

行2

3

5

2

行2

行1

4

6

0

行2

行1

5

 

……

……

……

……

……

  (注:行x对应窗口结构中的行x)

  对于窗口操作,存在的一个问题是边界部分无法得到处理。不过,本系统的窗口比较小,那么边界像素的输出可以不计算。这样输出的图像比输入图像减小了1行和1列,并不会影响图像显示效果。

具体实现思路:

1.窗口模块:

 3个行缓存,每个大小为640×8 bit;

 状态机,状态信号(0-2),以每一行第一个像素时钟作为驱动状态信号的时钟;

 3个移位寄存器,每个大小为8 bit,作为检测窗口的第一行

窗口的实现,需要建立一个像素时钟驱使的always块,根据窗口结构和表1的控制逻辑,构造窗口;

2.边缘判断模块:

    通过窗口可在像素点P处得到以其为中心,周围8个点的像素值。根据Sobel算子结构,进行计算。设置一个阈值,当计算后的值大于此阈值时,判定此像素为边缘。


关键部分代码(verilog):

  1. always@(posedge PCLK)begin//产生行时钟,作为状态机驱动时钟  
  2. begin  
  3.      if(VtcHCnt==0)  
  4.        Line_CLK <= 1;  
  5.      else  
  6.        Line_CLK <= 0;  
  7. end  
  8. end  
  9.   
  10. reg[1:0] state;  
  11. initial state = 0;  
  12.   
  13. always@(posedge Line_CLK)begin//产生状态计数器  
  14.  if(VtcVCnt==1)  
  15.  state <= 2'b00;  
  16.  else begin  
  17.    if(state == 2'b10)  
  18.        state <= 2'b00;  
  19.     else   
  20.        state <= state + 1;  
  21.     end  
  22. end   
  23. //定义窗口第0行的三个元素  
  24. reg[7:0]S_Window0;  
  25. reg[7:0]S_Window1;  
  26. reg[7:0]S_Window2;  
  27.   
  28.   
  29. //定义行缓存  
  30. reg[7:0]LineBuffer0[639:0];  
  31. reg[7:0]LineBuffer1[639:0];  
  32. reg[7:0]LineBuffer2[639:0];  
  33. always@(posedge PCLK)begin  
  34.    case(state)  
  35.      2'b00: begin  
  36.         <span style="white-space:pre">    </span>LineBuffer0[VtcHCnt] <= GRAY;  
  37.          LineBuffer1[VtcHCnt] <= LineBuffer1[VtcHCnt];  
  38.          LineBuffer2[VtcHCnt] <= LineBuffer2[VtcHCnt];     
  39.          S_Window0 <= GRAY;  
  40.          S_Window1 <= S_Window0;  
  41.              S_Window2 <= S_Window1;  
  42.         if(VtcHCnt>=2&&VtcVCnt>=3)begin  
  43.          if((S_Window0 + S_Window1*2 + S_Window2)>(LineBuffer1[VtcHCnt-2]+LineBuffer1[VtcHCnt-1]*2 +LineBuffer1[VtcHCnt]))  
  44.               DOUT_reg <= ((S_Window0 + S_Window1*2  + S_Window2 -LineBuffer1[VtcHCnt-2]-LineBuffer1[VtcHCnt-1]*2  -LineBuffer1[VtcHCnt]))/4;  
  45.          else  
  46.          DOUT_reg <= (LineBuffer1[VtcHCnt-2]+LineBuffer1[VtcHCnt-1]*2  +LineBuffer1[VtcHCnt]-(S_Window0 + S_Window1*2  + S_Window2))/4;  
  47.         end   
  48.         end   
  49.      2'b01: begin  
  50.              LineBuffer1[VtcHCnt] <= GRAY;  
  51.          LineBuffer0[VtcHCnt] <= LineBuffer0[VtcHCnt];  
  52.          LineBuffer2[VtcHCnt] <= LineBuffer2[VtcHCnt];          
  53.          S_Window0 <= GRAY;  
  54.              S_Window1 <= S_Window0;  
  55.              S_Window2 <= S_Window1;  
  56.        if(VtcHCnt>=2&&VtcVCnt>=3)begin  
  57.      if((S_Window0 + S_Window1 *2 + S_Window2)>(LineBuffer2[VtcHCnt-2]+LineBuffer2[VtcHCnt-1]*2  +LineBuffer2[VtcHCnt]))  
  58.          DOUT_reg <= ((S_Window0 + S_Window1*2  + S_Window2 -LineBuffer2[VtcHCnt-2]-LineBuffer2[VtcHCnt-1] *2 -LineBuffer2[VtcHCnt]))/4;  
  59.          else  
  60.          DOUT_reg <= (LineBuffer2[VtcHCnt-2]+LineBuffer2[VtcHCnt-1] *2 +LineBuffer2[VtcHCnt]-(S_Window0 + S_Window1*2  + S_Window2))/4;    
  61.         end          
  62.         end  
  63.      2'b10: begin  
  64.         <span style="white-space:pre">    </span> LineBuffer2[VtcHCnt] <= GRAY;     
  65.          LineBuffer0[VtcHCnt] <= LineBuffer0[VtcHCnt];  
  66.          LineBuffer1[VtcHCnt] <= LineBuffer1[VtcHCnt];     
  67.          S_Window0 <= GRAY;  
  68.         S_Window1 <= S_Window0;  
  69.         S_Window2 <= S_Window1;  
  70.         if(VtcHCnt>=2&&VtcVCnt>=3)begin  
  71.          if((S_Window0 + S_Window1*2  + S_Window2)>(LineBuffer0[VtcHCnt-2]+LineBuffer0[VtcHCnt-1] *2 +LineBuffer0[VtcHCnt]))  
  72.          DOUT_reg <= ((S_Window0 + S_Window1*2  + S_Window2 -LineBuffer0[VtcHCnt-2]-LineBuffer0[VtcHCnt-1]*2  -LineBuffer0[VtcHCnt]))/4;  
  73.          else  
  74.          DOUT_reg <= (LineBuffer0[VtcHCnt-2]+LineBuffer0[VtcHCnt-1]*2  +LineBuffer0[VtcHCnt]-(S_Window0 + S_Window1*2  + S_Window2))/4;  
  75.         end  
  76.         end  
  77.     default:    begin  
  78.            DOUT_reg <= DOUT_reg;  
  79.              LineBuffer0[VtcHCnt] <= LineBuffer0[VtcHCnt];  
  80.             LineBuffer1[VtcHCnt] <= LineBuffer1[VtcHCnt];      
  81.             LineBuffer2[VtcHCnt] <= LineBuffer2[VtcHCnt];      
  82.              end  
  83.     endcase  
  84.   
  85. end   






最新课程

  • 深入浅出玩儿转FPGA

    本视频基于Xilinx公司的Artix-7FPGA器件以及各种丰富的入门和进阶外设,提供了一些典型的工程实例,帮助读者从FPGA基础知识、逻辑设计概念

  • 从零开始大战FPGA基础篇

    本课程为“从零开始大战FPGA”系列课程的基础篇。课程通俗易懂、逻辑性强、示例丰富,课程中尤其强调在设计过程中对“时序”和“逻辑”的把控,以及硬件描述语言与硬件电路相对应的“

  • Verilog基础及典型数字

    课程中首先会给大家讲解在企业中一般数字电路从算法到流片这整个过程中会涉及到哪些流程,都分别使用什么工具,以及其中每个流程都分别做了