FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

基于FPGA的实时图像边缘检测:Sobel与Canny对比2026版

二牛学FPGA二牛学FPGA
技术分享
7小时前
0
0
4

Quick Start

  • 下载并安装 Vivado 2024.2(或更高版本),确保包含 Vitis 和 ModelSim/QuestaSim 仿真支持。
  • 新建 Vivado 工程,选择器件 xc7z020clg484-1(Zynq-7020 开发板典型型号)。
  • 创建顶层模块 top_edge_detection,例化 Sobel 和 Canny 两个子模块。
  • 编写 Testbench,输入 640×480 灰度图像(8-bit 像素,BMP 格式转为 COE 文件),时钟 25 MHz(VGA 像素时钟)。
  • 运行行为仿真,观察 Sobel 输出(单比特边缘标志)和 Canny 输出(双阈值边缘标志)。
  • 综合后查看资源利用率(LUT/FF/DSP/BRAM)和最大工作频率(Fmax)。
  • 上板验证:通过 VGA 或 HDMI 输出边缘图像,对比 Sobel 与 Canny 的噪声抑制效果。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Zynq-7020 (xc7z020clg484-1)主流 SoC FPGA,含 ARM Cortex-A9 双核Artix-7 (xc7a35t) 或 Spartan-7
EDA 版本Vivado 2024.2支持 SystemVerilog、IP Integrator、HLS 2024.2Vivado 2023.1 或 2025.1
仿真器QuestaSim 2024.2 (Vivado 内置)支持 VHDL/Verilog 混合仿真,速度快Vivado Simulator (xsim) 或 ModelSim
时钟/复位25 MHz 像素时钟,异步复位低有效640×480@60Hz 标准 VGA 时钟50 MHz 或 100 MHz(需降频或 FIFO 缓存)
接口依赖VGA (RGB444) 或 HDMI (TMDS)输出边缘图像到显示器UART 或以太网(传输像素数据)
约束文件top.xdc时钟周期 40 ns (25 MHz),IO 标准 LVCMOS33自动推导(不推荐)

目标与验收标准

  • 功能点:Sobel 模块输出单比特边缘标志(1 表示边缘),Canny 模块输出双阈值边缘标志(0=非边缘,1=弱边缘,2=强边缘)。
  • 性能指标:Sobel 延迟 ≤ 3 行(含行缓存),Canny 延迟 ≤ 5 行(含高斯滤波、梯度计算、NMS、双阈值)。
  • 资源上限:Sobel 使用 LUT ≤ 800,FF ≤ 600,BRAM ≤ 2 个(18K);Canny 使用 LUT ≤ 2500,FF ≤ 1800,BRAM ≤ 6 个。
  • Fmax:≥ 100 MHz(实际像素时钟 25 MHz,留裕量)。
  • 验收方式:仿真波形中 Sobel 输出与 MATLAB 参考结果逐像素匹配(误差 < 5%);上板后 VGA 显示边缘清晰,无断裂或噪声。

实施步骤

1. 工程结构与顶层模块

// top_edge_detection.sv
module top_edge_detection (
    input  logic       clk_25m,      // 像素时钟 25 MHz
    input  logic       rst_n,        // 异步复位,低有效
    input  logic [7:0] pixel_in,     // 灰度像素输入(0-255)
    input  logic       vsync,        // 场同步
    input  logic       hsync,        // 行同步
    input  logic       de,           // 数据使能
    output logic [7:0] pixel_out_sobel, // Sobel 边缘输出(0或255)
    output logic [7:0] pixel_out_canny, // Canny 边缘输出(0/128/255)
    output logic       vsync_out,
    output logic       hsync_out,
    output logic       de_out
);

    // 实例化 Sobel 模块
    logic [7:0] sobel_edge;
    sobel_edge_det u_sobel (
        .clk      (clk_25m),
        .rst_n    (rst_n),
        .pixel_in (pixel_in),
        .de       (de),
        .edge_out (sobel_edge)
    );

    // 实例化 Canny 模块
    logic [1:0] canny_edge;  // 0=非边缘, 1=弱边缘, 2=强边缘
    canny_edge_det u_canny (
        .clk      (clk_25m),
        .rst_n    (rst_n),
        .pixel_in (pixel_in),
        .de       (de),
        .edge_out (canny_edge)
    );

    // 输出映射:Sobel 单比特转 8-bit;Canny 双阈值转 8-bit
    assign pixel_out_sobel = sobel_edge ? 8'd255 : 8'd0;
    assign pixel_out_canny = (canny_edge == 2'd2) ? 8'd255 :
                             (canny_edge == 2'd1) ? 8'd128 : 8'd0;

    // 同步延迟输出控制信号(延迟与像素处理流水线匹配)
    delay_line #(.STAGES(5)) u_delay_vsync (.clk(clk_25m), .din(vsync), .dout(vsync_out));
    delay_line #(.STAGES(5)) u_delay_hsync (.clk(clk_25m), .din(hsync), .dout(hsync_out));
    delay_line #(.STAGES(5)) u_delay_de    (.clk(clk_25m), .din(de),    .dout(de_out));

endmodule

逐行说明

  • 第 1 行:模块声明,SystemVerilog 语法,端口列表。
  • 第 2-3 行:时钟和复位。clk_25m 是 25 MHz 像素时钟;rst_n 是异步复位,低电平有效,用于初始化所有寄存器。
  • 第 4 行:pixel_in 是 8-bit 灰度像素值(0-255),来自摄像头或测试图像。
  • 第 5-7 行:视频同步信号 vsync、hsync、de(数据使能),用于 VGA/HDMI 时序控制。
  • 第 8-10 行:Sobel 和 Canny 输出,pixel_out_sobel 是 8-bit(0 或 255),pixel_out_canny 是 8-bit(0、128 或 255)。
  • 第 11-13 行:输出同步信号,经延迟后输出,确保与像素数据对齐。
  • 第 15-20 行:实例化 sobel_edge_det 模块。输入时钟、复位、像素、数据使能;输出边缘标志。
  • 第 22-28 行:实例化 canny_edge_det 模块。canny_edge 是 2-bit 信号(0/1/2),分别表示非边缘、弱边缘、强边缘。
  • 第 30-32 行:将 Sobel 的单比特 edge_out 转换为 8-bit 像素值(0 或 255)。
  • 第 33-35 行:将 Canny 的 2-bit 信号映射为 8-bit 像素:强边缘=255,弱边缘=128,非边缘=0。
  • 第 37-40 行:例化延迟线模块 delay_line,将控制信号延迟 5 个时钟周期,与 Sobel/Canny 的流水线延迟匹配。delay_line 内部用移位寄存器实现。

2. Sobel 边缘检测模块

// sobel_edge_det.sv
module sobel_edge_det (
    input  logic       clk,
    input  logic       rst_n,
    input  logic [7:0] pixel_in,
    input  logic       de,
    output logic       edge_out
);

    // 行缓存:3 行,每行 640 像素(8-bit)
    logic [7:0] line_buf [0:2][0:639];
    logic [7:0] p00, p01, p02, p10, p11, p12, p20, p21, p22;
    logic [10:0] col_cnt;
    logic [10:0] row_cnt;

    // 写指针控制
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            col_cnt &lt;= 0;
            row_cnt &lt;= 0;
        end else if (de) begin
            if (col_cnt == 639) begin
                col_cnt &lt;= 0;
                row_cnt &lt;= row_cnt + 1;
            end else begin
                col_cnt &lt;= col_cnt + 1;
            end
            // 写入行缓存
            line_buf[0][col_cnt] &lt;= pixel_in;
            line_buf[1][col_cnt] &lt;= line_buf[0][col_cnt];
            line_buf[2][col_cnt] &lt;= line_buf[1][col_cnt];
        end
    end

    // 3x3 窗口读取
    always_comb begin
        p00 = line_buf[0][col_cnt]; p01 = line_buf[0][col_cnt+1]; p02 = line_buf[0][col_cnt+2];
        p10 = line_buf[1][col_cnt]; p11 = line_buf[1][col_cnt+1]; p12 = line_buf[1][col_cnt+2];
        p20 = line_buf[2][col_cnt]; p21 = line_buf[2][col_cnt+1]; p22 = line_buf[2][col_cnt+2];
    end

    // 梯度计算:Gx 和 Gy
    logic [10:0] gx, gy;
    always_comb begin
        gx = (p02 + 2*p12 + p22) - (p00 + 2*p10 + p20);
        gy = (p00 + 2*p01 + p02) - (p20 + 2*p21 + p22);
    end

    // 幅度近似:|Gx| + |Gy|
    logic [10:0] mag;
    always_comb begin
        mag = (gx[10] ? (~gx + 1) : gx) + (gy[10] ? (~gy + 1) : gy);
    end

    // 阈值比较(阈值=128)
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            edge_out &lt;= 1'b0;
        else if (de &amp;&amp; (col_cnt &gt;= 2) &amp;&amp; (row_cnt &gt;= 2))
            edge_out &lt;= (mag &gt; 10'd128) ? 1'b1 : 1'b0;
        else
            edge_out &lt;= 1'b0;
    end

endmodule

逐行说明

  • 第 1-7 行:模块声明,输入像素和 de,输出单比特 edge_out。
  • 第 9 行:line_buf 是 3 行 × 640 像素的二维数组,用 BRAM 实现。每个元素 8-bit。
  • 第 10 行:p00~p22 是 3×3 窗口的 9 个像素值。
  • 第 11-12 行:col_cnt 和 row_cnt 用于追踪当前像素坐标(0-639 列,0-479 行)。
  • 第 14-28 行:写指针控制。在 de 有效时递增列计数器,每行结束递增行计数器。同时将 pixel_in 写入 line_buf[0],并将 line_buf[0] 移位到 line_buf[1] 和 line_buf[2]——实现行延迟。
  • 第 30-33 行:组合逻辑读取 3×3 窗口。注意 col_cnt 是当前写入列,窗口中心是 (col_cnt+1, row_cnt+1)。
  • 第 35-39 行:计算 Gx 和 Gy。使用 11-bit 有符号数(10:0)避免溢出。Gx = (右列 - 左列) 的 Sobel 卷积核;Gy = (下行 - 上行)。
  • 第 41-44 行:幅度近似:|Gx| + |Gy|。gx[10] 是符号位,若为 1 则取补码(绝对值)。
  • 第 46-53 行:阈值比较。当 de 有效且列/行计数大于等于 2(即有效窗口形成后),若 mag > 128 则输出 1,否则 0。复位时输出 0。

3. Canny 边缘检测模块(简化版)

// canny_edge_det.sv
module canny_edge_det (
    input  logic       clk,
    input  logic       rst_n,
    input  logic [7:0] pixel_in,
    input  logic       de,
    output logic [1:0] edge_out
);

    // 阶段1:高斯滤波(3x3 均值近似)
    logic [7:0] gauss_out;
    gaussian_filter u_gauss (
        .clk(clk), .rst_n(rst_n), .pixel_in(pixel_in), .de(de), .pixel_out(gauss_out)
    );

    // 阶段2:Sobel 梯度计算(复用 Sobel 模块,但输出幅度和方向)
    logic [10:0] mag;      // 梯度幅度
    logic [2:0]  orient;   // 方向量化(0-7,对应 0°-315°)
    sobel_gradient u_grad (
        .clk(clk), .rst_n(rst_n), .pixel_in(gauss_out), .de(de),
        .mag(mag), .orient(orient)
    );

    // 阶段3:非极大值抑制(NMS)
    logic [10:0] nms_mag;
    nms_3x3 u_nms (
        .clk(clk), .rst_n(rst_n), .mag(mag), .orient(orient), .de(de),
        .nms_out(nms_mag)
    );

    // 阶段4:双阈值检测
    logic [1:0] edge_raw;
    dual_threshold #(.TH_HIGH(180), .TH_LOW(60)) u_thresh (
        .clk(clk), .rst_n(rst_n), .mag_in(nms_mag), .de(de),
        .edge_out(edge_raw)
    );

    // 阶段5:边缘跟踪(滞后阈值,简化版:仅保留强边缘及与强边缘相连的弱边缘)
    edge_tracking u_track (
        .clk(clk), .rst_n(rst_n), .edge_in(edge_raw), .de(de),
        .edge_out(edge_out)
    );

endmodule

逐行说明

  • 第 1-7 行:模块声明,输出 edge_out 为 2-bit(0/1/2)。
  • 第 9-12 行:阶段1——高斯滤波。使用 3×3 均值核(1/9 归一化),减少噪声。例化 gaussian_filter 模块,该模块内部用行缓存和加法树实现。
  • 第 14-19 行:阶段2——Sobel 梯度计算。例化 sobel_gradient 模块,输出幅度 mag(11-bit)和方向 orient(3-bit,量化到 8 个方向)。
  • 第 21-25 行:阶段3——非极大值抑制。例化 nms_3x3 模块,在 3×3 邻域内沿梯度方向比较,只保留局部极大值。
  • 第 27-31 行:阶段4——双阈值检测。TH_HIGH=180,TH_LOW=60。若 nms_mag > 180 则为强边缘(2),若在 60-180 之间则为弱边缘(1),否则为非边缘(0)。
  • 第 33-37 行:阶段5——边缘跟踪。例化 edge_tracking 模块,检查弱边缘是否与强边缘 8 邻域相连,若相连则提升为强边缘,否则抑制。简化版仅做一次连通性判断。

4. 时序与约束

# top.xdc
# 时钟约束
create_clock -name clk_25m -period 40.000 [get_ports clk_25m]

# 输入延迟约束(假设外部器件输出延迟 2 ns)
set_input_delay -clock clk_25m -max 2.000 [get_ports pixel_in]
set_input_delay -clock clk_25m -min 1.000 [get_ports pixel_in]

# 输出延迟约束(VGA 建立时间 1.5 ns)
set_output_delay -clock clk_25m -max 1.500 [get_ports pixel_out_sobel]
set_output_delay -clock clk_25m -max 1.500 [get_ports pixel_out_canny]

# 异步复位约束(false path)
set_false_path -from [get_ports rst_n]

逐行说明

  • 第 2 行:创建 25 MHz 时钟,周期 40 ns。这是所有时序分析的基准。
  • 第 4-5 行:输入延迟约束。pixel_in 来自外部 ADC 或摄像头,假设最大延迟 2 ns,最小 1 ns。确保 Vivado 正确分析输入建立/保持时间。
  • 第 7-8 行:输出延迟约束。VGA 接收器要求数据在时钟上升沿前 1.5 ns 稳定。
  • 第 10 行:将异步复位端口设为 false path,避免 Vivado 分析其到寄存器的时序(因为复位是异步的,且通常不要求时序收敛)。

5. 验证与仿真脚本

# run_sim.tcl
# 启动仿真并添加波形
restart
add wave -position end sim:/top_edge_detection/*
add wave -position end sim:/top_edge_detection/u_sobel/*
add wave -position end sim:/top_edge_detection/u_canny/*

# 加载测试图像(COE 文件)
mem load -infile {test_image.coe} -format hex /top_edge_detection/u_testbench/image_mem

# 运行 800 行(640*480 + 消隐区)
run 800000 ns

逐行说明

  • 第 2 行:重启仿真,清除之前的状态。
  • 第 3-5 行:添加顶层和子模块的所有信号到波形窗口,便于观察。
  • 第 7 行:将 test_image.coe 文件加载到 Testbench 中的图像存储器(image_mem)。COE 文件格式:每行一个 8-bit 十六进制像素值。
  • 第 9 行:运行仿真 800 μs,对应约 800 行像素(每行 640 像素 + 消隐区 ≈ 800 个时钟周期)。

常见坑与排查

  • 行缓存溢出:如果 de 信号不连续(如消隐期),行缓存写入指针可能错位。确保 de 只在有效像素区域为高。
  • 窗口初始化延迟:Sobel 和 Canny 都需要前 2 行像素填充行缓存,因此前 2 行输出为 0。检查 col_cnt 和 row_cnt 的起始条件。
  • 阈值选择不当:Sobel 阈值 128 可能对低对比度图像漏检;Canny 的 TH_HIGH/TH_LOW 比例建议 2:1 到 3:1。通过仿真调整。
  • 时序违例:如果 Fmax 低于 100 MHz,检查关键路径(通常是加法树或比较器)。可插入流水线寄存器。

原理与设计说明

Sobel 与 Canny 的核心差异:Sobel 是简单的梯度阈值法,计算快、资源少,但对噪声敏感,边缘较粗(单像素宽但可能断裂);Canny 是多阶段算法(高斯滤波 → 梯度 → NMS → 双阈值 → 边缘跟踪),边缘更细、连续性好,但资源消耗约 3-5 倍,延迟增加 2 行。

标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/41004.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
93719.37W3.99W3.67W
分享:
成电国芯FPGA赛事课即将上线
基于FPGA的实时图像边缘检测:Sobel与Canny对比实现指南(2026版)
基于FPGA的实时图像边缘检测:Sobel与Canny对比实现指南(2026版)上一篇
跨时钟域同步:异步FIFO深度计算与格雷码实现指南下一篇
跨时钟域同步:异步FIFO深度计算与格雷码实现指南
相关文章
总数:966
FPGA原型验证环境搭建指南:SoC软硬件协同验证实践

FPGA原型验证环境搭建指南:SoC软硬件协同验证实践

随着SoC设计复杂度呈指数级增长,传统的软件仿真与硬件原型验证之间的鸿沟…
技术分享
13天前
0
0
28
0
FPGA状态机编码方式对比设计指南:二进制、格雷码与独热码

FPGA状态机编码方式对比设计指南:二进制、格雷码与独热码

QuickStart在Vivado或Quartus中新建工程,…
技术分享
2天前
0
0
10
0
FPGA试卷+答案+超详细解答

FPGA试卷+答案+超详细解答

某高校FPGA课程考试试卷,考试内容很全面,解答也很详细。
技术分享
1年前
0
0
644
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容