Bootstrap

FPGA图像处理之Sobel边缘检测


一、什么是图像边缘检测?

  边缘检测是计算机视觉和图像处理中的一个重要任务,常用于提取图像中的结构信息。边缘检测的目的是识别图像中强度变化显著的区域,这些区域通常对应于物体的边界。在前面《FPGA图像处理之局部阈值二值化》中我们知道,在机器识别中,计算机并不关心图像色彩和饱和度,它们大多数都只关心图像的轮廓以及结构特征。因此,图像的边缘检测也是非常常见的一种处理,常用的边缘检测算法包括:Sobel算子,Canny算子,拉普拉斯算子等等。

二、Sobel算子理论

  图像的边缘一般是灰度变化比较明显的点;Sobel是离散的微分算子,用来对图像水平和垂直方向进行求导,图像灰度变化越大,一阶导数就越大。Sobel算子就是利用这个原理。

  • 水平方向:这是通过用奇数大小的核 G x Gx Gx卷积图像数据:

在这里插入图片描述

  • 垂直方向:这是通过用奇数大小的核 G y Gy Gy卷积图像数据:

在这里插入图片描述

  • 计算梯度:

在这里插入图片描述
  得到梯度后再与设定的阈值做比较,判断当前是轮廓还是边缘。以下是matlab实现sobel边缘检测的代码:

clc;
close all;
% 读取图像
input_image_path = '...........jpg'; % 输入图像的路径
image = imread(input_image_path);

% 将图像转换为灰度图像(如果是彩色图像)
if size(image, 3) == 3
    gray_image = rgb2gray(image);
else
    gray_image = image;
end

% 使用 Sobel 算子进行边缘检测
edges = edge(gray_image, 'Sobel',0.05);

% 显示原始图像和边缘检测结果
figure;
subplot(1, 2, 1);
imshow(gray_image);
title('Gray Image');

subplot(1, 2, 2);
imshow(edges);
title('Sobel Edge Detection');

在这里插入图片描述
  可以看到,sobel边缘检测能很好的识别出图像边缘,设定的阈值不一样,图像显示的边缘也不一样,下面修改阈值范围:

clc;
close all;
% 读取图像
input_image_path = 'bulit.jpg'; % 输入图像的路径
image = imread(input_image_path);

% 将图像转换为灰度图像(如果是彩色图像)
if size(image, 3) == 3
    gray_image = rgb2gray(image);
else
    gray_image = image;
end

% 使用 Sobel 算子进行边缘检测
edges = edge(gray_image, 'Sobel',0.2);

% 显示原始图像和边缘检测结果
figure;
subplot(1, 2, 1);
imshow(gray_image);
title('Gray Image');

subplot(1, 2, 2);
imshow(edges);
title('Sobel Edge Detection');

在这里插入图片描述
  可以看到显示出的轮廓明显程度也发生了变化。

三、Verilog实现Sobel算子

  首先还是需要生成3*3的滑动窗口,直接调用我们前面几篇文章的模板就行,我们主要来看Sobel算法模块:

            [p11,p12,p13]   [-1,0,1]
  Gx_data = [p21,p22,p23] * [-2,0,2] = (p13+2*p23+p33) - (p11+2*p21+p31)
            [p31,p32,p33]   [-1,0,1]

            [p11,p12,p13]   [-1,-2,-1]
  Gy_data = [p21,p22,p23] * [ 0, 0, 0] = (p31+2*p32+p33) - (p11+2*p12+p13)
            [p31,p32,p33]   [ 1, 2, 1]
  
  G_data = sqrt(Gx_data^2 + Gy_data^2)

  算法核心也非常简单,就是按照Sobel算子窗口求和运算后,再计算其平方根。平方根可以使用cordic IP核,具体配置如下:

在这里插入图片描述

  其它的配置默认即可,调用如下:

square_data u_square_data (
  .aclk(sys_clk),                                        // input wire aclk
  .aresetn(!sys_rst),                                  // input wire aresetn
  .s_axis_cartesian_tvalid(1'b1),                   // input wire s_axis_cartesian_tvalid
  .s_axis_cartesian_tdata({3'd0,G_square_data}),    // input wire [23 : 0] s_axis_cartesian_tdata
  .m_axis_dout_tvalid(),            // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(m_axis_dout_tdata)              // output wire [15 : 0] m_axis_dout_tdata
);

  我们打开仿真,输入一张灰度图像:

在这里插入图片描述
  我们先设置阈值为200(因为开方后的数据位宽为11位,所以阈值范围为0-2048),开始仿真:

img_sobel_edge#(
    .IMG_WIDTH         ( `IMG_WIDTH ),
    .IMG_HEIGHT        ( `IMG_HEIGHT ),
    .THRESH            (200) 
)u_img_sobel_edge(
    .sys_clk           ( clk           ),
    .sys_rst           ( reset           ),
    .i_img_data        ( img_data_i        ),
    .i_img_data_valid  ( valid_i           ),
    .o_img_data        ( img_data_o        ),
    .o_img_data_valid  ( valid_o           )
);

在这里插入图片描述
  仿真初始化完成,我们接着看生成后的图像:

在这里插入图片描述
  可以看到我们生成的图像边缘和最开始的matlab生成的一致,我们修改阈值为400观察一下图像:

在这里插入图片描述
  可以看到阈值大小不一样,显示出来的画面效果也不一致,因此找到一个合适的阈值大小是整个需求的关键。

;