FPGA图像处理——老戏新说

    本文摘自个人微信公众号: 似猿非猿的FPGA(gh_821036e8d45d)。

    书接上文(FPGA图像处理中矩阵提取),趁着今天休假,采用SpinalHDL做一个小的demo,看看在SpinalHDL里如何优雅的实现Sobel边缘检测。

Sobel边缘检测

Sobel边缘检测原理教材网上一大堆,核心为卷积处理。

    Sobel卷积因子为:

FPGA图像处理——老戏新说_第1张图片

 

    该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像灰度值,其公式如下:

    图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:

 

    通常,为了提高效率 使用不开平方的近似值:

    最后,当计算出来的值大于某一阈值时即认为为边缘像素点。

    归结起来,Sobel边缘检测分为三大步:卷积计算、灰度计算、阈值比较处理。结合上文实现的bufWindow,在SpinalHDL里实现Sobel边缘检测也就几行代码的事情(如果是写Verilog我还是拒绝的)。

卷积计算

    通过bufWindow,我们可以得到一个3x3的矩阵窗口,拿到结果第一步即是计算卷积,由于卷积因子是带符号的,而在做卷积时又需要考虑位宽扩展的事情,在写Verilog时还是需要小心的设计下的,而在SpinalHDL里,两行代码:

val Gx=(windowbuf.io.dataOut.payload(0)(2).expand.asSInt-^windowbuf.io.dataOut.payload(0)(0).expand.asSInt)+|
      ((windowbuf.io.dataOut.payload(1)(2).expand.asSInt-^windowbuf.io.dataOut.payload(1)(0).expand.asSInt)<<1)+|
      (windowbuf.io.dataOut.payload(2)(2).expand.asSInt-^windowbuf.io.dataOut.payload(2)(0).expand.asSInt)
val Gy=(windowbuf.io.dataOut.payload(0)(0).expand.asSInt-^windowbuf.io.dataOut.payload(2)(0).expand.asSInt)+|
       ((windowbuf.io.dataOut.payload(0)(1).expand.asSInt-^windowbuf.io.dataOut.payload(2)(1).expand.asSInt)<<1)+|
       (windowbuf.io.dataOut.payload(0)(2).expand.asSInt-^windowbuf.io.dataOut.payload(2)(2).expand.asSInt)

    首先将bufWindow输出的窗口矩阵值扩展一位位宽转换为有符号值,然后进行计算卷积。计算卷积运用了两个运算符“-^”,"+|"来处理加减运算时的位宽处理(可参照SpinalHDL手册或本公众号的《SpinalHDL—数据类型:UInt/SIn》)。最终得到Gx、Gy。

灰度计算

灰度计算这里采用近似值,通过取绝对值的方式进行实现,在SpinalHDL里也就一行代码的事情:

sobelResult.payload:= (sobelConv.payload(0).abs+| sobelConv.payload(1).abs).fixTo(cfg.dataWidth-1 downto 0,RoundType.ROUNDUP)

    由于在卷积计算时有扩展位宽,这里计算最后调用fixTo进行高位饱和处理。最终得到位宽与输入保持一致(想想你在Veirlog里实现这一步要做多少事情,少年)。 

阈值比较

    阈值比较就很简单了,比较两个值大小取两个极端:

when(sobelResult.payload>io.thresholdValue){
      io.dataOut.payload:=(default->true)
    }otherwise{
      io.dataOut.payload:=(default->false)
    }

    最终实现Sobel边缘检测代码如下:

case class sobelProc(cfg:lineBufferCfg) extends Component{
  require(cfg.lineNum==3)
  val io=new Bundle{
    val thresholdValue =in UInt(cfg.dataWidth bits)
    val dataIn=slave Flow(UInt(cfg.dataWidth bits))
    val dataOut=master Flow(UInt(cfg.dataWidth bits))
    dataOut.valid.setAsReg().init(False)
    dataOut.payload.setAsReg().init(0)
  }
  noIoPrefix()
  val sobel=new Area{
    val windowbuf=bufWindow(cfg)
    val sobelConv=Reg(Flow(Vec(SInt(),2)))
    val sobelResult=Reg(Flow(UInt(cfg.dataWidth bits)))
    sobelConv.valid.init(False)
    sobelResult.valid.init(False)
    io.dataIn<>windowbuf.io.dataIn
    val Gx=(windowbuf.io.dataOut.payload(0)(2).expand.asSInt-^windowbuf.io.dataOut.payload(0)(0).expand.asSInt)+|
      ((windowbuf.io.dataOut.payload(1)(2).expand.asSInt-^windowbuf.io.dataOut.payload(1)(0).expand.asSInt)<<1)+|
      (windowbuf.io.dataOut.payload(2)(2).expand.asSInt-^windowbuf.io.dataOut.payload(2)(0).expand.asSInt)
    val Gy=(windowbuf.io.dataOut.payload(0)(0).expand.asSInt-^windowbuf.io.dataOut.payload(2)(0).expand.asSInt)+|
           ((windowbuf.io.dataOut.payload(0)(1).expand.asSInt-^windowbuf.io.dataOut.payload(2)(1).expand.asSInt)<<1)+|
           (windowbuf.io.dataOut.payload(0)(2).expand.asSInt-^windowbuf.io.dataOut.payload(2)(2).expand.asSInt)
    sobelConv.valid:=windowbuf.io.dataOut.valid
    sobelConv.payload(0):=Gx
    sobelConv.payload(1):=Gy
    sobelResult.valid:=sobelConv.valid
    sobelResult.payload:= (sobelConv.payload(0).abs+| sobelConv.payload(1).abs).fixTo(cfg.dataWidth-1 downto 0,RoundType.ROUNDUP)
    io.dataOut.valid:=sobelResult.valid
    when(sobelResult.payload>io.thresholdValue){
      io.dataOut.payload:=(default->true)
    }otherwise{
      io.dataOut.payload:=(default->false)
    }
  }
}

    区区不到四十行代码,简洁而优雅,基本上就是描述算法,出错概率应该很小吧!

仿真

    做图像处理的小伙伴想想在做仿真验证时需要怎么搞,matlab生成灰度图像二进制数据放在文件里,然后仿真时再导入,仿真完成后将结果保存到文件里,最后再在matlab里做对比。

    太麻烦。SpinalHDL提供了仿真支持,而SpinalHDL是基于Scala的,可以完美实现整个仿真验证流程:从图片直接获取数据,然后进行仿真验证,仿真结果直接再次生成图片。这里仿真在SpinalHDL里进行完成,从网上拉取一张图片:

FPGA图像处理——老戏新说_第2张图片

    仿真里调用javax.imageio.ImageIO等库读取图像,转换成灰度图:

FPGA图像处理——老戏新说_第3张图片

    随后将数据灌入到测试单元里,得到的数据流直接生成图片:

FPGA图像处理——老戏新说_第4张图片

    对SpinalHDL感兴趣的小伙伴欢迎关注微信公众号:似猿非猿的FPGA(gh_821036e8d45d)

 

你可能感兴趣的:(SpinalHDL,数字图像处理,fpga,数字图像处理)