使用Sobel边缘检测法在HDL中实现灰度图像处理(第一部分)
1.1 边缘检测算法介绍
所谓边缘是指其周围像素灰度急剧变化的那些象素的集合,它是图像最基本的特征。边缘存在于目标、背景和区域之间,所以,它是图像分割所依赖的最重要的依据。由于边缘是位置的标志,对灰度的变化不敏感, ,因此,边缘也是图像匹配的重要的特征。
边缘检测和区域划分是图像分割的两种不同的方法,二者具有相互补充的特点。在边缘检测中,是提取图像中不连续部分的特征,根据闭合的边缘确定区域。而在区域划分中,是把图像分割成特征相同的区域,区域之间的边界就是边缘。由于边缘检测方法不需要将图像逐个像素地分割,因此更适合大图像的分割。边缘大致可以分为两种,一种是阶跃状边缘,边缘两边像素的灰度值明显不同;另一种为屋顶状边缘,边缘处于灰度值由小到大再到小的变化转折点处。边缘检测的主要工具是边缘检测模板。
边缘检测的有很多,典型的有索贝尔算子,普里维特算子,罗伯茨交叉边缘检测等边缘检测技术,在 Matlab 中有现成的 IPT 函数,提供边缘检测,如下:
Sobel 边缘检测:
PlayBoy
subplot(1,3,1)
imshow(IMG1);
title('原图像');
IMG1 = rgb2gray(IMG1);
[m,n] = size(IMG1); %用 Sobel 微分算子进行边缘检测
IMG2 = edge(IMG1,'sobel');
subplot(1,3,2);
imshow(IMG2);
title('Sobel 边缘检测得到的图像');
图8‑1Matlab中Sobel算法
但效果不佳,灵活性也不高,同时性能太低了啊!
1.2 Sobel 边缘检测算法研究
所谓的Sobel算法,就是将得到像素点乘以一个3*3矩阵(Sobel算子)得到一个该点的灰度矢量值或者其法矢量值。
所以整个算法的核心就是Sobel算子,下面简单介绍下该算子。
索贝尔算子(Sobel operator)主要用作边缘检测, 在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。
Sobel 卷积因子为:
图8‑2 Sobel卷积因子
该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以 A 代表原始图像, Gx 及 Gy 分别代表经横向及纵向边缘检测的图像灰度值,其公式如下:
图8‑3公式
图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:
图8‑4公式
通常,为了提高效率使用不开平方的近似值,但这样做会损失精度,迫不得已的时候可以如下这样子:
图8‑5公式
如果梯度 G 大于某一阀值 则认为该点(x,y)为边缘点。
if(temp3 > THRESHOLD)
IMG_Sobel(i,j) = 0;%Black
else
IMG_Sobel(i,j) = 255;%White
end
然后可用以下公式计算梯度方向(当然只要检测边缘,则不用计算方向):
图8‑6公式
通过以上分析,可以知道,横向及纵向边缘检测的图像灰度值(Gx、Gy)是必须获取得,但是该点灰度的大小时可以通过准确的计算或者近似计算获取,所以接下来针对该问题,简单介绍两个方式的实现。
1.3 Sobel 边缘检测算法的 HDL 实现第一、二步
FPGA 中针对以上矩阵进行算法移植。由于直接计算会因为负值而得到错误的结果,用补码表示比较繁琐,需要用到 unsigned 以及 signed 类型,不适合 FPGA的运算。
前面 Sobel 算子的实现, 为了实现 FPGA 的加速运算, 发挥并行流水线的特性,可以划分为 4 个步骤, 解析与实现分别如下:
(1) 计算计算 Gx与 Gy与模板每行的乘积;
(2) 求得 3*3 模板运算后的 Gx、 Gy;
(3) 求得 Gx^2+Gy^2 的结果, 及 Gx 与 Gy 的平方和;
(4) 求得 Gx^2+Gy^2 的平方根。
或者
(1) 计算计算 Gx与 Gy与模板每行的乘积;
(2) 求得 3*3 模板运算后的 Gx、 Gy;
(3) 求得|Gx|+|Gy| 的结果。
其中(1)(2)步分析如下:
图8‑7计算Gx、Gy运算
懂一点矩阵运算的就知道,Gx方向算子乘以像素值,等于矩阵中每一个值乘以像素值后相加,所以可以将上诉方法利用上图中方式进行运算,其中中间的零值可以不计算。具体看下代码:
代码8‑1 3*3 模板运算后的 Gx、 Gy
1. //Add you arithmetic here 2. //---------------------------------------------------------------------------- 3. //---------------------------------------------------------------------------- 4. //---------------------------------------------------------------------------- 5. //------------------------------------------- 6. //Sobel Parameter 7. // Gx Gy Pixel 8. // [ -1 0 +1 ] [ +1 +2 +1 ] [ P11 P12 P13 ] 9. // [ -2 0 +2 ] [ 0 0 0 ] [ P21 P22 P23 ] 10. // [ -1 0 +1 ] [ -1 -2 -1 ] [ P31 P32 P33 ] 11. 12. // localparam P11 = 8'd15, P12 = 8'd94, P13 = 8'd136; 13. // localparam P21 = 8'd31, P22 = 8'd127, P23 = 8'd231; 14. // localparam P31 = 8'd44, P32 = 8'd181, P33 = 8'd249; 15. //Caculate horizontal Grade with |abs| 16. //Step 1-2 17. reg [9:0] Gx_temp1; //postive result 18. reg [9:0] Gx_temp2; //negetive result 19. reg [9:0] Gx_data; //Horizontal grade data 20. always@(posedge clk or negedge rst_n) 21. begin 22. if(!rst_n) 23. begin 24. Gx_temp1 <= 0; 25. Gx_temp2 <= 0; 26. Gx_data <= 0; 27. end 28. else 29. begin 30. Gx_temp1 <= matrix_p13 + (matrix_p23 << 1) + matrix_p33; //postive result 31. Gx_temp2 <= matrix_p11 + (matrix_p21 << 1) + matrix_p31; //negetive result 32. Gx_data <= (Gx_temp1 >= Gx_temp2) ? Gx_temp1 - Gx_temp2 : Gx_temp2 - Gx_temp1; 33. end 34. end 35. 36. //--------------------------------------- 37. //Caculate vertical Grade with |abs| 38. //Step 1-2 39. reg [9:0] Gy_temp1; //postive result 40. reg [9:0] Gy_temp2; //negetive result 41. reg [9:0] Gy_data; //Vertical grade data 42. always@(posedge clk or negedge rst_n) 43. begin 44. if(!rst_n) 45. begin 46. Gy_temp1 <= 0; 47. Gy_temp2 <= 0; 48. Gy_data <= 0; 49. end 50. else 51. begin 52. Gy_temp1 <= matrix_p11 + (matrix_p12 << 1) + matrix_p13; //postive result 53. Gy_temp2 <= matrix_p31 + (matrix_p32 << 1) + matrix_p33; //negetive result 54. Gy_data <= (Gy_temp1 >= Gy_temp2) ? Gy_temp1 - Gy_temp2 : Gy_temp2 - Gy_temp1; 55. end 56. end |
---|
前面两步其实很好理解,主要第三步(32行、54行),这一步主要原因就是FPGA无法进行负数运算,所以将上两步计算结果进行取绝对值,实际上这不影响最后得结果,因为后面无论进行平方操作或者取绝对值操作结果都是相同的。
推荐阅读
-
[姿势估计] 实践记录:使用 Dlib 和 mediapipe 进行人脸姿势估计 - 本文重点介绍方法 2):方法 1:基于深度学习的方法:。 基于深度学习的方法:基于深度学习的方法利用深度学习模型,如卷积神经网络(CNN)或递归神经网络(RNN),直接从人脸图像中学习姿势估计。这些方法能够学习更复杂的特征表征,并在大规模数据集上取得优异的性能。方法二:基于二维校准信息估计三维姿态信息(计算机视觉 PnP 问题)。 特征点定位:人脸姿态估计的第一步是通过特征点定位来检测和定位人脸的关键点,如眼睛、鼻子和嘴巴。这些关键点提供了人脸的局部结构信息,可用于后续的姿势估计。 旋转表示:常见的旋转表示方法包括欧拉角和旋转矩阵。欧拉角通过三个旋转角度(通常是俯仰、偏航和滚动)描述头部的旋转姿态。旋转矩阵是一个 3x3 矩阵,表示头部从一个坐标系到另一个坐标系的变换。 三维模型重建:根据特征点的定位结果,三维人脸模型可用于姿势估计。通过将人脸的二维图像映射到三维模型上,可以估算出人脸的旋转和平移信息。这就需要建立人脸的三维模型,然后通过优化方法将模型与特征点对齐,从而获得姿势估计结果。 特征点定位 特征点定位是用于检测人脸关键部位的五官基础部分,还有其他更多的特征点表示方法,大家可以参考我上一篇文章中介绍的特征点检测方案实践:人脸校正二次定位操作来解决人脸校正的问题,客户在检测关键点的代码上略有修改,坐标转换部分客户见上图 def get_face_info(image). img_copy = image.copy image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = face_detection.process(image) # 在图像上绘制人脸检测注释。 image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) box_info, facial = None, None if results.detections: for detection in results. for detection in results.detections: mp_drawing.Drawing.detection = 无 mp_drawing.draw_detection(image, detection) 面部 = detection.location_data.relative_keypoints 返回面部 在上述代码中,返回的数据是五官(6 个关键点的坐标),这是用 mediapipe 库实现的,下面我们可以尝试用另一个库:dlib 来实现。 使用 dlib 使用 Dlib 库在 Python 中实现人脸关键点检测的步骤如下: 确保已安装 Dlib 库,可使用以下命令: pip install dlib 导入必要的库: 加载 Dlib 的人脸检测器和关键点检测器模型: 读取图像并将其灰度化: 使用人脸检测器检测图像中的人脸: 对检测到的人脸进行遍历,并使用关键点检测器检测人脸关键点: 显示绘制了关键点的图像: 以下代码将参数 landmarks_part 添加到要返回的关键点坐标中。
-
使用Sobel边缘检测法在HDL中实现灰度图像处理(第一部分)