FPGA图像处理的Sobel边缘检测

sobel边缘检测
sobel边缘检测原理教材网上一大堆,核心为卷积处理。
sobel卷积因子为:
该算子包含两组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的,可以完美实现整个仿真验证流程:从图片直接获取数据,然后进行仿真验证,仿真结果直接再次生成图片。
原文标题:fpga图像处理——老戏新说
文章出处:【微信公众号:fpga之家】欢迎添加关注!文章转载请注明出处。


不间断电源的三个分类,切换到电池供电
第二十届电感变压器产业链峰会正式启动!
宋慧乔和宋仲基大婚,玩“太阳的后裔”番外篇,科技界的小米和诺基亚专利授权也“大婚”了
医用氧传感器的介绍,为什么RGM需要氧传感器
华为物联网操作系统LiteOS内核教程06-内存管理
FPGA图像处理的Sobel边缘检测
PCB设计过程中进行回流路径分析:高速信号回流路径
中性点接地电阻柜一般是多大尺寸的
指纹识别或成过去式,苹果备将Face ID应用到所有硬件数码产品中
变频器十大品牌排行榜
德州仪器(TI)推出超节能OMAP4470应用处理器
老邢点评:新一轮投资潮悄然而至,VR市场有望复苏
总结常见的电脑蓝屏的原因和解决措施
墨奇M1智能通行一体机为用户提供方便、安全身份认证体验
数据中心各个阶段元宇宙是什么样
PoE解决方案的关键因素及设计需要考虑哪些问题
华强北破解AirPods了吗?华强北AirPods破解iOS16盗版检测
科裕智能科技感应锁918-3-D介绍
捷易科技2023CPSE安博会圆满落幕!
谁是汽车网络安全的主要参与者?