FPGA图像处理中二值算子的一些妙用

在讲FPGA编程之前,先要介绍一个我用C-sharp写的图像处理小软件。代码已经上传到这个Git Hub库中。

https://github.com/becomequantum/icon-default.png?t=N7T8https://github.com/becomequantum/

FPGA图像处理中二值算子的一些妙用_哔哩哔哩_bilibili开源了一个C#写的图像处理小软件,讲了二值图像算子的一些作用,比如去噪,简单形状识别等等。icon-default.png?t=N7T8https://www.bilibili.com/video/BV1WY411L7Bd后面还有很多图,请到视频中去看。联系请用B站私信

大脑视觉皮层运作机理简介,CNN其实不像它_哔哩哔哩_bilibili

FPGA图像处理中二值算子的一些妙用_第1张图片

如上图所示,这个软件实现了一些基本的图像处理方法。开源这个软件并不是要讲如何用C-sharp写图像处理代码,因为这些图像处理的C-sharp代码都比较简单,有兴趣的自己看一下就可以了。我主要是想讲如何用FPGA、写Verilog代码来实现这个软件中的一些图像处理功能。比如直方图数据统计、RGB颜色模式转HSL、 灰度图像的Canny边缘检测(也就是图像卷积)、二值图像的膨胀腐蚀、连通域识别等。要写Verilog实现这些功能则必须要有测试数据来验证功能实现的是否正确,C-Sharp代码的作用就是为了帮助调试和测试Verilog代码。

.

如果你掌握了一些技巧,写Verilog实现上述图像处理算法其实并不困难,代码也不长、不复杂。即使要用实时流水线的方式实现多层图像卷积也不在话下。其实卷积的代码也就是多重循环,连个if else都没有,是很容易实现的,根本不需要HLS高层次综合。所以我到现在都不会用HLS。

下面将先简单介绍一下此软件的一些功能,再来讲一下、类似膨胀腐蚀的二值图像处理算法在实际中的一些应用。

“刷空图”这个功能可以生成指定大小和颜色值的一副图片,然后可以被复制或保存。

“直方图”可以画出一副彩色图像的RGB直方图和HSL直方图,右边是H值做为横轴,S值做为纵轴的二维“直方图”。

彩色图像“转灰度”后,再点“Canny”可以得到边缘检测之后的图像。

如果有一副以某种单一颜色为背景的图像,我们可以通过先设置HSL上下限值,再点“标背景”来实现抠掉背景。按住鼠标左键在图像背景上划动时,软件会自动统计轨迹点的HSL上下限。可能需要多采样几次才能找出合适的范围。把背景标成某个颜色值后还可以点“变透明”把该颜色变为透明。

这里用颜色上下限抠背景的方法用的是HSL颜色模式,而不是用RGB。这是因为用HSL模式实现这个功能要简单很多,这也是为啥会有HSL颜色模式的原因之一。所以用FPGA实现RGB转HSL的算法是有实用价值的,因为在工业领域这种背景抠图也很常用。要用这种方法那就得整一个单一颜色的背景,但有背景在带来抠图简单的同时也可能会带来一些问题,比如背景弄脏了就会影响效果。

因此在少数条件允许的应用场景下,是不是可以考虑用黑色空洞做为背景呢?也就是在背景位置整一个“黑空盒子”,渣掉进去了也看不见。此时背景就几乎是纯黑色的。这时可能有人就会问了,那要是前景物体也是黑的,岂不是就和黑背景区分不开了?

看上面这张图,这几块煤在白色背景下看起来挺黑的,但放到纯黑背景下后又发现它不是那么黑。仍然可以用“标背景”方法把背景区分开来。因为前景即使是黑色物体,在光照下也必然会比背景亮,只要这个物体表面没有涂吸光率能达99.995%的黑涂料。

标背景的颜色上下限找到后,点“二值化”可以把图像二值化,如上图所示,黑色为背景,白色为前景。再点“递归连通”就会对二值图像进行连通域识别,结果如上图。框下面的第一个数只是编号,第二个数是这个连通域拥有的总点数。

对二值图像还可以进行“取边缘”操作,如上图所示,取边缘之后也可以进行连通域识别。

“神经网络”功能页面中还有一个查看Mnist手写数字的功能,如上图所示。还可以设置只显示某个数。其实为了学习神经网络,我还写了用C-sharp读取Onnx模型数据,并进行推理的代码。不过功能还很简陋,只支持LeNet5这样较老的简单模型,除了学习之外,也没啥实际用处,所以这个功能就没有放在这次开源的代码里。等粉丝多了,有人感兴趣时再放。

“方波”、“三角波”等是多年前写的用来学习傅里叶变换的代码,点选展开的波形分量可以看到部分波形叠加起来的效果。FPGA厂家一般都会提供FFT的IP核,并不需要自己写Verilog代码去实现。

该软件的基本功能就简单介绍到这,下面讲一下二值图像算子的一些作用。“阈胀蚀”实现了一个算子大小和阈值可调的膨胀腐蚀功能,其实就是实现了一个二值卷积功能,算子里所有的权重都是1,负的阈值相当于偏置。所以计算时只需要把图像上算子范围里的所有点的值加起来,再减去阈值,看最后结果是否大于零。大于零该点最终结果就是1,否则就是0。

当算子直径为3,阈值为1时,就等价于膨胀,如上图所示。阈值为算子面积9时就等价于腐蚀。如果阈值在中间又有啥效果呢?从上图中可以看出,中间左边密度较大的一片点还留下了一些,右边密度较小的消失了,而且分散的点被聚集到了一坨。那大家想想这样的计算效果可能会有啥实际用处呢?右下角的正方形原本周围有些噪点,处理后噪点没有了,但形状也变了。这是因为这个方块很小,如果是大的形状,用较小算子去噪之后对原形状的改变是很小的。所以这也算是有边缘平滑和去噪效果。再看个例子,设置合适的算子大小和阈值处理带噪点的字效果是上图这样的。

再来看看上面这幅图(去视频中看),这是在网上找的一幅显微细胞图。细胞大多是细长的,和周围的区别是细胞中间的部分比较黑。那现在如果有数一下这里面的细胞大概有多少个的需求,这该如何用算法实现呢?估算小目标的个数貌似也是个常见的应用需求。

因为细胞的颜色比较黑,可以先用“标背景”功能试着标记一下细胞。当亮度值上限设为125时结果如上图所示。此时细胞基本都被标记上了,但不是细胞的地方也有很多噪点。得到这个结果后点“回到原图”,再点“二值化”,再点一下“点运算”反个色,就得到了如上图所示的二值化结果。然后把直径设为9,阈值设为40,点“阈胀蚀”就能得到上图所示的去噪后的结果。直径和阈值这两个参数是需要自己调整的,在数个数这个应用场景下,直径一般要设到和目标尺度差不多,阈值则要设到算子面积的一半左右。

我们把这个去噪结果中的白色再标成紫色,然后和原图相减,再把紫色标成红色,就可以把它和最初的标记图做对比了,如上图所示。可以看出去噪后大部分细胞都是被标上了的,细胞之外的噪点也只剩少数几个,效果还是不错的。

再回到刚才去噪后的二值图像,我们对它进行连通域识别就能得出细胞的个数了。结果如上图所示。最下面最大的标号是289,说明图中共有289个连通域,但真实细胞个数肯定要小于这个数,因为里面还有一些小噪点。比如用红框标出的第248个连通域,它就1个点,应该是噪点。那在连通域识别的时候再加上一个最少点数阈值,也就是小于这个点数的连通域不纳入最终结果统计,这样就能又过滤掉一些小噪点,让结果更加准确。

FPGA图像处理中二值算子的一些妙用_第2张图片

​其实最初的这幅细胞图片,也就经过了二值化,去噪,连通域识别这三个步骤就得到了计数结果,如果是染了色的细胞,那还需要加一个RGB转HSL处理才能二值化,也就四个步骤。这几步都能用FPGA实时流水线算法实现,而且所需的资源很少。所以经典的图像处理算法,如果你会用,还是能解决很多实际问题的。

由于二值图像所需存储空间小,所以在FPGA里可以实现较大的,比如直径好几十的二值图像算子。那整这么大的二值算子有啥用呢?上面去噪用的二值算子都是方形的,但这个形状是可以根据需求改的,也就是可以用它来做二值图像的模板匹配,来检测一些局部形状特征什么的。

当算子的大小比待检测目标的尺寸还要大点时,还可以想办法用算子识别目标的形状特征。如果目标太大也可以把图像缩小一半后再用算子检测,毕竟大物体缩小一半后形状特征并不会失真多少。或许有人会想识别形状难道不是连通域识别之后才能做的吗?理论上连通域扫描获得形状边缘点坐标后也可以继续利用这些坐标点信息进行形状识别,但这要用FPGA实现似乎困难了一点,我没想出啥办法是易于纯FPGA实现的,用CPU还是容易实现的。

其实用大算子也可以大概识别目标的一些形状特征,估算目标的大小。当然这里估算出来的大小并没有连通域识别的结果那么精确。这个方法算子里进行的运算和上面的去噪是不同的,但也简单直接,易于FPGA实现,所需资源也不是很多,只比上面的去噪稍微多点。不过“無限次元”这个软件里并没有模拟这个功能,有兴趣的朋友可以自己研究。实现提示就在上面这张图中。

一般简单凸形状可以用这种方法识别特征,弯弯绕的形状就有点困难。算子获取数据后再加一些简单的判断就可以识别大小,粗细,是否对称,圆不圆等简单形状特征,加上SVM,神经网络这些处理或许也能识别更复杂的。还可以对数据进行模板匹配,选出最接近某个标准模板的形状。

由此看来简单的二值算子功能还是挺多的。现在也有人研究二值或三值的神经网络,这样的网络更易于FPGA实现,消耗的资源更少,还是比较值得研究的。

你可能感兴趣的:(fpga开发,图像处理,人工智能)