搜索
bottom↓
回复: 0

【正点原子FPGA连载】第二十八章V7725摄像头Sobel边缘检测--摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南

[复制链接]

出0入234汤圆

发表于 2020-8-15 11:29:44 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-24 10:53 编辑

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
QQ群头像.png
5)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第二十八章V7725摄像头Sobel边缘检测



边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。本次实验将使用对OV7725摄像头采集到的数据进行Sobel边缘检测处理,并且在LCD屏上显示。
282828.1Sobel边沿检测简介
28.2实验任务
28.3硬件设计
28.4软件设计
28.5下载验证



28.1Sobel边缘检测简介
所谓边缘是指其周围像素灰度急剧变化的那些象素的集合,它是图像最基本的特征。边缘存在于目标、背景和区域之间,所以,它是图像分割所依赖的最重要的依据。由于边缘是位置的标志,对灰度的变化不敏感,因此,边缘也是图像匹配的重要的特征。
边缘检测和区域划分是图像分割的两种不同的方法,二者具有相互补充的特点。在边缘检测中,是提取图像中不连续部分的特征,根据闭合的边缘确定区域。而在区 域划分中,是把图像分割成特征相同的区域,区域之间的边界就是边缘。由于边缘检测方法不需要将图像逐个像素地分割,因此更适合大图像的分割。边缘大致可以分为两种,一种是阶跃状边缘,边缘两边像素的灰度值明显不同;另一种为屋顶状边缘,边缘处于灰度值由小到大再到小的变化转折点处。边缘检测的主要工具是边缘检测模板。边缘检测的有很多,典型的有索贝尔算子、普里维特算子、罗伯茨交叉边缘检测等边缘检测技术,在本章设计中采用的是索贝尔算子。
索贝尔算子算法简介:
索贝尔算子(Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。
Sobel卷积因子为:
阿莫论坛发帖领航者专用1749.png

图 28.1.1 Sobel卷积因子

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

图 28.1.2 横向及纵向的图像灰度值计算公式

图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:
阿莫论坛发帖领航者专用1977.png

图 28.1.3 像素的灰度值计算公式

通常,为了提高效率 使用不开平方的近似值,但这样做会损失精度,迫不得已的时候可以如下这样子:
阿莫论坛发帖领航者专用11065.png


图 28.1.4 像素灰度值的近似计算公式

如果梯度G大于某一阀值,则认为该点(x,y)为边缘点。
28.2实验任务
本节实验任务是使用领航者开发板及OV7725摄像头进行图像采集,对采集到的图像进行Sobel边缘检测处理,并通过带有HDMI接口的显示器实时显示。
28.3硬件设计
本次实验的硬件电路、管脚分配与“OV7725摄像头HDMI显示”完全相同,有关这一部分内容请读者参考“OV7725摄像头HDMI显示”实验。PL端的硬件系统框架与“OV7725摄像头HDMI显示”实验基本相似,但不同点在于,在OV7725摄像头采集模块采集了YUV格式的图像数据之后,对数据进行Sobel边缘检测处理,然后再送入Video In to AXI4-Stream IP核。
系统架构如下图所示:
阿莫论坛发帖领航者专用11423.png

图 28.3.1 系统架构框图

我们将Sobel边缘检测处理模块封装成IP核,以便直接在IP Integrator设计画布中进行调用。有关在Vivado中自定义IP核的操作步骤,请参见“自定义IP核”实验。在IP Integrator设计画布中封装后的Sobel边缘检测IP核及其连接如下图所示:
阿莫论坛发帖领航者专用11592.png

图 28.3.2 Sobel边缘检测IP核

可以看到,OV7725摄像头采集模块的输出接口连接到Sobel边缘检测IP核的输入接口,Sobel边缘检测IP核的输出接口连接到Video In to AXI4-Stream IP核的输入接口。
Sobel边缘检测IP核主要包含三个源文件,层次结构如下图所示:
阿莫论坛发帖领航者专用11765.png

图 28.3.3 Sobel边缘检测IP核的层次结构

其中,VIP_sobel_edge_detector.v源文件是该IP核的顶层源文件,用于接收来自摄像头数据采集模块数输出数据,对图像矩阵进行计算,并且输出最终的经过Sobel边沿检测后的像素数据。VIP_matrix_generate_3x3_8bit.v源文件用于生成经过缓存后的图像矩阵,随数据同时输出的还有控制信号。line_shift_RAM_8bit.v源文件调用了两个伪双端口RAM,用于对当前的行数据进行两次缓存,以生成图像矩阵的3列数据,随数据同时输出的还有控制信号。
VIP_sobel_edge_detector.v源文件的代码如下所示:
  1. 1   module VIP_sobel_edge_detector
  2. 2   (
  3. 3       //global clock
  4. 4       input               clk,                       //cmos 像素时钟
  5. 5       input               rst_n,                     //复位
  6. 6   
  7. 7       //准备要进行处理的图像数据
  8. 8       input               per_frame_vsync,
  9. 9       input               per_frame_href,
  10. 10      input               per_frame_clken,
  11. 11      input       [23:0]  per_frame_data,
  12. 12  
  13. 13      //处理完毕的图像数据
  14. 14      output              post_frame_vsync,
  15. 15      output              post_frame_href,
  16. 16      output              post_frame_clken,
  17. 17      output      [23:0]  post_frame_data
  18. 18  
  19. 19  );
  20. 20  
  21. 21  //parameter define
  22. 22  parameter  SOBEL_THRESHOLD = 8'd55;              //sobel边沿监测的阈值
  23. 23  
  24. 24  //wire define
  25. 25  wire  [7:0]   per_img_Y;
  26. 26  wire  [7:0]   matrix_p11;
  27. 27  wire  [7:0]   matrix_p12;
  28. 28  wire  [7:0]   matrix_p13;  //3X3矩阵的输出数据
  29. 29  wire  [7:0]   matrix_p21;
  30. 30  wire  [7:0]   matrix_p22;
  31. 31  wire  [7:0]   matrix_p23;
  32. 32  wire  [7:0]   matrix_p31;
  33. 33  wire  [7:0]   matrix_p32;
  34. 34  wire  [7:0]   matrix_p33;
  35. 35  wire          matrix_frame_vsync;                  //与3X3矩阵的输出数据同步的vsync
  36. 36  wire          matrix_frame_href;                   //与3X3矩阵的输出数据同步的href
  37. 37  wire          matrix_frame_clken;                  //与3X3矩阵的输出数据同步的clken
  38. 38  wire  [10:0]  pixel_sobel_value;
  39. 39  
  40. 40  //reg define
  41. 41  reg  [9:0]  Gx_temp1;
  42. 42  reg  [9:0]  Gx_temp2;
  43. 43  reg  [9:0]  Gx_data;
  44. 44  reg [9:0]   Gy_temp1;
  45. 45  reg [9:0]   Gy_temp2;
  46. 46  reg [9:0]   Gy_data;
  47. 47  reg [20:0]  Gxy_square;
  48. 48  reg pixel_edge_flag;
  49. 49  reg  [4:0]  matrix_frame_vsync_dly;
  50. 50  reg  [4:0]  matrix_frame_href_dly;
  51. 51  reg  [4:0]  matrix_frame_clken_dly;
  52. 52  
  53. 53  //*****************************************************
  54. 54  //**                    main code
  55. 55  //*****************************************************
  56. 56  
  57. 57  //提取出YUV图像数据的高8位,来直接作为图像的灰度值
  58. 58  assign  per_img_Y = { per_frame_data[23:19] , per_frame_data[15:13] } ;
  59. 59  //输出最后的经sebel边沿检测后的图像数据接口
  60. 60  assign  post_frame_vsync = matrix_frame_vsync_dly[4] ;
  61. 61  assign  post_frame_href  = matrix_frame_href_dly[4]  ;
  62. 62  assign  post_frame_clken = matrix_frame_clken_dly[4] ;
  63. 63  assign  post_frame_data  = post_frame_href ? {24{pixel_edge_flag}} : 24'b0 ;
  64. 64  
  65. 65  
  66. 66  //生成 8Bit 3X3 的图像矩阵
  67. 67  VIP_matrix_generate_3x3_8bit   u_VIP_Matrix_Generate_3X3_8Bit
  68. 68  (
  69. 69      .clk                    (clk),
  70. 70      .rst_n                  (rst_n),
  71. 71  
  72. 72      //准备要进行处理的图像数据
  73. 73      .per_frame_vsync        (per_frame_vsync),
  74. 74      .per_frame_href         (per_frame_href),
  75. 75      .per_frame_clken        (per_frame_clken),
  76. 76      .per_img_Y              (per_img_Y),
  77. 77  
  78. 78      //矩阵化后的图像数据和控制信号
  79. 79      .matrix_p11         (matrix_p11),   
  80. 80      .matrix_p12         (matrix_p12),   
  81. 81      .matrix_p13         (matrix_p13),
  82. 82      .matrix_p21         (matrix_p21),   
  83. 83      .matrix_p22         (matrix_p22),   
  84. 84      .matrix_p23         (matrix_p23),
  85. 85      .matrix_p31         (matrix_p31),   
  86. 86      .matrix_p32         (matrix_p32),   
  87. 87      .matrix_p33         (matrix_p33),
  88. 88      .matrix_frame_vsync (matrix_frame_vsync),
  89. 89      .matrix_frame_href  (matrix_frame_href),
  90. 90      .matrix_frame_clken (matrix_frame_clken)
  91. 91  );
  92. 92  
  93. 93  //Sobel Parameter
  94. 94  //         Gx                  Gy                 Pixel
  95. 95  // [   -1  0   +1  ]   [   +1  +2   +1 ]     [   P11  P12   P13 ]
  96. 96  // [   -2  0   +2  ]   [   0   0    0  ]     [   P21  P22   P23 ]
  97. 97  // [   -1  0   +1  ]   [   -1  -2   -1 ]     [   P31  P32   P33 ]
  98. 98  
  99. 99  //步骤一:
  100. 100 //计算 Gx * Pixel 的绝对值
  101. 101 always@(posedge clk or negedge rst_n) begin
  102. 102     if(!rst_n) begin
  103. 103         Gx_temp1 <= 0;
  104. 104         Gx_temp2 <= 0;
  105. 105         Gx_data <= 0;
  106. 106     end
  107. 107     else begin
  108. 108         Gx_temp1 <= matrix_p13 + (matrix_p23 << 1) + matrix_p33;
  109. 109         Gx_temp2 <= matrix_p11 + (matrix_p21 << 1) + matrix_p31;
  110. 110         Gx_data <= (Gx_temp1 >= Gx_temp2) ? (Gx_temp1 - Gx_temp2) : (Gx_temp2 - Gx_temp1) ;
  111. 111     end
  112. 112 end
  113. 113
  114. 114
  115. 115 //步骤二:
  116. 116 //计算 Gy * Pixel 的绝对值
  117. 117 always@(posedge clk or negedge rst_n) begin
  118. 118     if(!rst_n) begin
  119. 119         Gy_temp1 <= 0;
  120. 120         Gy_temp2 <= 0;
  121. 121         Gy_data <= 0;
  122. 122     end
  123. 123     else begin
  124. 124         Gy_temp1 <= matrix_p11 + (matrix_p12 << 1) + matrix_p13;
  125. 125         Gy_temp2 <= matrix_p31 + (matrix_p32 << 1) + matrix_p33;
  126. 126         Gy_data <= (Gy_temp1 >= Gy_temp2) ? Gy_temp1 - Gy_temp2 : Gy_temp2 - Gy_temp1;
  127. 127     end
  128. 128 end
  129. 129
  130. 130
  131. 131 //步骤三:
  132. 132 //计算 Gx^2 + Gy^2
  133. 133 always@(posedge clk or negedge rst_n) begin
  134. 134     if(!rst_n)
  135. 135         Gxy_square <= 0;
  136. 136     else
  137. 137         Gxy_square <= Gx_data * Gx_data + Gy_data * Gy_data;
  138. 138 end
  139. 139
  140. 140 //步骤四:
  141. 141 //计算 (Gx^2 + Gy^2)^0.5
  142. 142 cordic_0  u_SQRT (
  143. 143   .aclk                    ( clk               ),  //  输入时钟
  144. 144   .s_axis_cartesian_tvalid ( 1'b1              ),  //  输入数据有效信号
  145. 145   .s_axis_cartesian_tdata  ( Gxy_square        ),  //  输入数据
  146. 146   .m_axis_dout_tvalid      (                   ),  //  输出数据有效信号
  147. 147   .m_axis_dout_tdata       ( pixel_sobel_value )   //  输出数据
  148. 148 );
  149. 149
  150. 150 //步骤五:
  151. 151 //与sobel边缘检测阈值进行比较,
  152. 152 //以判定该像素点是否为边缘
  153. 153 always@(posedge clk or negedge rst_n)
  154. 154 begin
  155. 155     if(!rst_n)
  156. 156         pixel_edge_flag <= 1'b0;
  157. 157     else if(pixel_sobel_value >= SOBEL_THRESHOLD)
  158. 158         pixel_edge_flag <= 1'b1;  //该像素点是边缘
  159. 159     else
  160. 160         pixel_edge_flag <= 1'b0;  //该像素点不是边缘
  161. 161 end
  162. 162
  163. 163 //对图像矩阵数据的计算共耗时5个周期,
  164. 164 //所以要将控制信号延时5个周期
  165. 165 always@(posedge clk or negedge rst_n) begin
  166. 166     if(!rst_n) begin
  167. 167         matrix_frame_vsync_dly <= 0;
  168. 168         matrix_frame_href_dly  <= 0;
  169. 169         matrix_frame_clken_dly <= 0;
  170. 170     end
  171. 171     else begin
  172. 172         matrix_frame_vsync_dly <= { matrix_frame_vsync_dly[3:0] , matrix_frame_vsync };
  173. 173         matrix_frame_href_dly  <= { matrix_frame_href_dly[3:0]  , matrix_frame_href  };
  174. 174         matrix_frame_clken_dly <= { matrix_frame_clken_dly[3:0] , matrix_frame_clken };
  175. 175     end
  176. 176 end
  177. 177
  178. 178 endmodule
复制代码

代码中的第67到91行例化了VIP_matrix_generate_3x3_8bit模块,用于生成3X3的图像矩阵。有关3x3矩阵的生成,我们在基于“基于OV5640的中值滤波实验”中有过介绍,本章节就不再赘述,有需要的朋友可以参考“基于OV5640的中值滤波实验”相关部分。接下来的第101-178行就是依次按照步骤,来计算出最后的经过Sobel边沿检测后的图像数据。
第101到112行的always语句是步骤一,用于计算出Pixel像素矩阵与X方向的算子矩阵Gx之间的乘积。这里要注意的是,由于在步骤三中要对X方向和Y方向上的乘积进行平方运算,所以在这里我们计算X方向的乘积时,只需计算出乘积的绝对值即可(因为一个数的平方与其相反数的平方相等)。算子矩阵Gx中带有负数,所以我们只需比较正数列相乘得到的数值和负数列相乘得到的数值的大小,然后取差值,就得到了Pixel像素矩阵与X方向的算子矩阵Gx之间的乘积,如代码第110行所示。
步骤二的原理与步骤一相同,只不过其计算的是Pixel像素矩阵与Y方向的算子矩阵Gy之间的乘积,如代码第117到128行。这里就不在赘述。
步骤三用于计算在步骤一和步骤二中计算出的乘积的平方和,如代码第133到138行所示。
步骤四用于对平方和进行开根号计算。如代码第142到148行所示。注意,在这里我们调用了CORDIC IP核,该IP核是一个数字信号处理IP核,主要用于完成复杂的数学运算。我们要将其配置为所需的平方根计算,其配置参数如下图所示:
阿莫论坛发帖领航者专用18775.png

图 28.3.4 CORDIC IP核的配置参数

第二个页面“AXI4 Stream Options”保持默认即可。
步骤五就是最后的阈值判断。把在步骤四中计算出的数值与我们定义的Sobel边缘检测阈值进行比较。如果大于等于该阈值,则认为该像素点是边缘点;否则,则认为该像素点不是边缘点。如代码第153到161行所示。
最后,根据判断结果来对当前像素点的颜色数据进行赋值。如果该像素点是边缘点,则该像素点显示为白色,否则显示为黑色。如代码第60行所示。
当然,由于像素点的颜色数据的计算共耗时5个周期,所以也要对行场同步等控制信号延时5个周期,以与数据同步。如代码第165到176行的always块所示。
由于数据的缓存和读取需要一定的延迟,所以两个伪双端口块RAM各自的读写端口的输入地址也需要进行延迟打拍处理,以与数据进行同步,如代码第49行的always语句所述。
连线后的Block Design如下图所示:
阿莫论坛发帖领航者专用19204.png

图 28.3.5 整体Block Design框图

接下来验证当前设计。验证完成后弹出对话框提示没有错误或者关键警告,点击“OK”。如果验证结果报出错误或者警告,则需要重新检查设计。
为工程添加的约束文件与“OV7725摄像头HDMI显示”完全相同,有关这一部分内容请读者参考“OV7725摄像头HDMI显示”实验。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“ Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择File > Launch SDK,启动SDK软件。
28.4软件设计
PS端软件的设计与“OV7725摄像头HDMI显示”几乎相同。唯一的不同之处在于,在之前的“OV7725摄像头HDMI显示”实验中,我们将OV7725摄像头的输出数据格式设置为了RGB565;而本次实验我们要将其设置为YUV格式。在PS端软件中的ov7725_init()函数中,设置该格式的语句如下所示:
105     sccb_write_reg8 (0x12, 0x04) ; //COM7 输出VGA YUV格式
有关PS软件的其他部分的更详细介绍,请读者参考“OV7725摄像头HDMI显示”实验。
28.5下载验证
编译完工程之后我们就可以开始下载程序了。将OV7725摄像头模块插在领航者Zynq开发板的“OLED/CAMERA”插座上,并将HDMI电缆一端连接到开发板上的HDMI插座、另一端连接到显示器。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的BIT文件,来对PL进行配置。最后下载软件程序,下载完成后,在下方的SDK Terminal中可以看到应用程序打印的信息,如下图所示:
阿莫论坛发帖领航者专用110140.png

图 28.5.1 串口打印的信息

此时表示ZYNQ成功检测到OV7725摄像头。同时,显示器上显示出OV7725摄像头采集的图像经过Sobel边缘检测处理之后的画面,即只有边缘为白色,非边缘为黑色。说明本次OV7725摄像头HDMI显示Sobel边缘检测的实验在领航者ZYQN开发板上验证成功,如下图所示:
阿莫论坛发帖领航者专用110314.png

图 28.5.2 实验结果

另外,读者可以尝试修改VIP_sobel_edge_detector IP核中的sobel边沿监测的阈值,默认是55,如下图所示:
阿莫论坛发帖领航者专用110414.png

图 28.5.3 sobel边沿监测的阈值

然后重新综合Block Design、生成比特流、并重新编译整个PS软件,将设计下载到ZYNQ,以观察不同阈值下的边沿监测的不同效果。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 13:31

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表