FPGA 图像边缘检测(Canny算子)

1 顶层代码

`timescale 1ns / 1ps
//边缘检测二阶微分算子:canny算子


module image_canny_edge_detect (
    input clk,
    input reset, //复位高电平有效

    input [10:0] img_width,
    input [ 9:0] img_height,
    input [ 7:0] low_threshold,
    input [ 7:0] high_threshold,

    input valid_i,
    input [15:0] rgb_565_i,  // 输入的16位RGB图像数据

    output        valid_o,
    output [15:0] rgb_565_o  // 输出的16位RGB图像数据
);


  //变量声明
  wire valid_gray;
  wire [7:0] img_data_gray;

  wire valid_gf;
  wire [7:0] img_data_gf;

  wire valid_sbl;
  wire [7:0] grad_mag;
  wire [10:0] grad_dx;
  wire [10:0] grad_dy;

  wire valid_nms;
  wire [7:0] img_data_nms;

  wire valid_db;
  wire [7:0] img_data_db;

  wire [23:0] img_data_i, img_data_o;
  assign img_data_i = {rgb_565_i[15:11], 3'b000, rgb_565_i[10:5], 2'b00, rgb_565_i[4:0], 3'b000};
  assign rgb_565_o  = {{img_data_o[23:19], img_data_o[15:10], img_data_o[7:3]}};


  //彩色图像灰度化
  image_rgb2gray u_image_rgb2gray (
      .clk       (clk),
      .reset     (reset),
      .valid_i   (valid_i),
      .img_data_i(img_data_i),
      .valid_o   (valid_gray),
      .img_data_o(img_data_gray)
  );

  ///高斯滤波
  image_gaussian_filter u_image_gaussian_filter (
      .clk       (clk),
      .reset     (reset),
      .img_width (img_width),
      .img_height(img_height),
      .valid_i   (valid_gray),
      .img_data_i(img_data_gray),
      .valid_o   (valid_gf),
      .img_data_o(img_data_gf)
  );

  ///Sobel算子
  image_sobel_edge u_image_sobel_edge (
      .clk       (clk),
      .reset     (reset),
      .img_width (img_width),
      .img_height(img_height),
      .valid_i   (valid_gf),
      .img_data_i(img_data_gf),
      .valid_o   (valid_sbl),
      .grad_mag  (grad_mag),
      .grad_dx   (grad_dx),
      .grad_dy   (grad_dy)
  );

  ///非极大值计算
  non_maximum_suppression u_non_maximum_suppression (
      .clk       (clk),
      .reset     (reset),
      .img_width (img_width),
      .img_height(img_height),
      .valid_i   (valid_sbl),
      .grad_mag  (grad_mag),
      .grad_dx   (grad_dx),
      .grad_dy   (grad_dy),
      .valid_o   (valid_nms),
      .img_data_o(img_data_nms)
  );

  双阈值
  //根据输入的低阈值和高阈值来判断,如果这个像素点大于高阈值,则赋值为255;如果低于低阈值,则赋值为0;
  double_threshold u_double_threshold (
      .clk           (clk),
      .reset         (reset),
      .img_width     (img_width),
      .img_height    (img_height),
      .low_threshold (low_threshold),
      .high_threshold(high_threshold),
      .valid_i       (valid_nms),
      .img_data_i    (img_data_nms),
      .valid_o       (valid_db),
      .img_data_o    (img_data_db)
  );

  assign valid_o = valid_db;
  assign img_data_o = {3{img_data_db}};

endmodule

2 彩色图变灰度图代码

`timescale 1ns / 1ps
//彩色图像灰度化



module image_rgb2gray (
    input clk,
    input reset,

    input valid_i,
    input [23:0] img_data_i,

    output valid_o,
    output [7:0] img_data_o
);

  //常量
  parameter MODE = 0;  //0表示加权平均法,1表示平均法 
  //Y=0.299*R十0.587*G+0.114*B
  parameter C0 = 9'd306;  //0.299*1024;
  parameter C1 = 10'd601;  //0.587*1024;
  parameter C2 = 7'd117;  //0.114*1024;

  //参数声明
  wire [7:0] R, G, B;

  assign {R, G, B} = img_data_i;

  generate
    if (MODE) begin

      reg valid_d1;
      reg [9:0] RGB_avr;

      reg valid_d2;
      reg [16:0] RGB_avr_m;

      reg valid_d3;
      reg [7:0] RGB_new;

      //平均法
      //1/3 * 512 = 171

      always @(posedge clk) begin
        if (reset) begin
          valid_d1 <= 'b0;
          RGB_avr  <= 'b0;
        end else begin
          valid_d1 <= valid_i;
          RGB_avr  <= R + G + B;
        end
      end

      //最大值不可能超过255*3*171 = 17'd130815
      always @(posedge clk) begin
        RGB_avr_m <= RGB_avr * 8'd171;
      end
      always @(posedge clk) begin
        if (reset) begin
          valid_d2 <= 'b0;
        end else begin
          valid_d2 <= valid_d1;
        end
      end

      //最大值不可能超过255
      always @(posedge clk) begin
        if (reset) begin
          valid_d3 <= 'b0;
          RGB_new  <= 'b0;
        end else begin
          valid_d3 <= valid_d2;
          RGB_new  <= RGB_avr_m[16:9];
        end
      end

      assign valid_o = valid_d3;
      assign img_data_o = {3{RGB_new}};

    end else begin

      //加权平均法
      reg valid_d1;
      reg [16:0] Y_R_m;
      reg [17:0] Y_G_m;
      reg [14:0] Y_B_m;

      reg valid_d2;
      reg [17:0] Y_s;//最大值,当RGB都等于255时,(C0 + C1 + C2)*255 = 1024*255;不会出现负数

      reg valid_d3;
      reg [7:0] Y;

      always @(posedge clk) begin
        Y_R_m <= R * C0;
        Y_G_m <= G * C1;
        Y_B_m <= B * C2;
      end

      always @(posedge clk) begin
        if (reset) begin
          valid_d1 <= 0;
        end else begin
          valid_d1 <= valid_i;
        end
      end

      always @(posedge clk) begin
        if (reset) begin
          Y_s <= 0;
          valid_d2 <= 0;
        end else begin
          if (valid_d1) begin
            Y_s <= Y_R_m + Y_G_m + Y_B_m;
          end
          valid_d2 <= valid_d1;
        end
      end

      always @(posedge clk) begin
        if (reset) begin
          Y <= 0;
          valid_d3 <= 0;
        end else begin
          if (valid_d2) begin
            Y <= Y_s[17:10];
          end
          valid_d3 <= valid_d2;
        end
      end

      assign valid_o = valid_d3;
      assign img_data_o = Y;

    end
  endgenerate


endmodule

3 3行缓存代码

`timescale 1ns / 1ps
//FIFO实现3行图像缓存



module image_line_buffer #(
    parameter W = 8
) (
    input wire clk,
    input wire reset,

    input wire [10:0] img_width,
    input wire [ 9:0] img_height,

    input wire valid_i,
    input wire [W-1:0] img_data_i,

    output reg valid_o,
    output reg [W-1:0] prev_line_data_o,
    output reg [W-1:0] cur_line_data_o,
    output reg [W-1:0] next_line_data_o
);

  //常量声明
  localparam N = 3;  //窗口大小

  //变量声明
  genvar i;
  integer j;
  wire [0:0] valid[0:N-1];
  wire [W-1:0] data[0:N-1];

  reg [10:0] out_data_cnt;
  reg [9:0] out_line_cnt;
  reg ch_valid;
  reg [W-1:0] ch_data[0:N-1];

  assign valid[0] = valid_i;
  assign data[0]  = img_data_i;

  //行缓存模块, 只需要缓存N-1个fifo即可
  generate
    for (i = 1; i < N; i = i + 1) begin : lb

      line_buffer #(
          .W(W)
      ) u_line_buffer (
          .clk      (clk),
          .reset    (reset),
          .img_width(img_width),
          .valid_i  (valid[i-1]),
          .data_i   (data[i-1]),
          .valid_o  (valid[i]),
          .data_o   (data[i])
      );
    end
  endgenerate

  //模式1,按照上一行、当前行、下一行输出
  //特殊情况,可根据需求设定,是复制还是给0
  //第1个行缓存,是整个画面的第1行时, 上一行数据不存在,复制第1个行缓存或者直接为0输出
  //第1个行缓存,是整个画面的最后1行时,下一行数据不存在,复制第1个行缓存输出
  always @(posedge clk) begin
    if (reset) begin
      for (j = 0; j < 3; j = j + 1) ch_data[j] <= 0;
    end else if (valid[N-2]) begin
      ch_data[1] <= data[1];
      if (out_line_cnt == 0) begin
        ch_data[2] <= 0;
        ch_data[0] <= data[0];
      end else if (out_line_cnt == img_height - 1) begin
        ch_data[2] <= data[2];
        ch_data[0] <= 0;
      end else begin
        ch_data[2] <= data[2];
        ch_data[0] <= data[0];
      end
    end
  end


  always @(posedge clk) begin
    if (reset) begin
      ch_valid <= 0;
      out_data_cnt <= 0;
      out_line_cnt <= 0;
    end else begin
      ch_valid <= valid[N-2];
      out_data_cnt <= valid[N-2] ? ((out_data_cnt == img_width - 1) ? 0 : out_data_cnt + 1) : out_data_cnt;
      out_line_cnt <= valid[N-2]&&(out_data_cnt == img_width - 1) ? ((out_line_cnt == img_height - 1) ? 0 : out_line_cnt + 1) : out_line_cnt;
    end
  end

  //单路输出
  always @(posedge clk) begin
    if (reset) begin
      valid_o <= 0;
      prev_line_data_o <= 0;
      cur_line_data_o <= 0;
      next_line_data_o <= 0;
    end else begin
      valid_o <= ch_valid;
      prev_line_data_o <= ch_data[2];
      cur_line_data_o <= ch_data[1];
      next_line_data_o <= ch_data[0];
    end
  end


endmodule

4 1行缓存代码

`timescale 1ns / 1ps
//FIFO实现1行图像缓存



module line_buffer #(
    parameter W = 8
) (
    input wire clk,
    input wire reset,

    input wire [10:0] img_width,

    input wire valid_i,
    input wire [W-1:0] data_i,

    output wire valid_o,
    output wire [W-1:0] data_o
);

  //变量声明
  reg [10:0] wr_data_cnt;
  wire rd_en;
  wire [11:0] fifo_data_count_w;

  //写入数据计数
  always @(posedge clk or posedge reset) begin
    if (reset) begin
      wr_data_cnt <= 0;
    end else begin
      wr_data_cnt <= valid_i && (wr_data_cnt < img_width) ? (wr_data_cnt + 1'b1) : wr_data_cnt;
    end
  end

  //assign rd_en = valid_i&&(wr_data_cnt == img_width) ? 1'b1 : 1'b0;
  //等价于
  assign rd_en   = valid_i && (fifo_data_count_w == img_width) ? 1'b1 : 1'b0;
  assign valid_o = rd_en;

  generate
    if (W == 8) begin

      fifo_line_buffer_w8 u_fifo_line_buffer_w8 (
          .clk       (clk),               // input wire clk
          .srst      (reset),             // input wire srst
          .din       (data_i),            // input wire [7 : 0] din
          .wr_en     (valid_i),           // input wire wr_en
          .rd_en     (rd_en),             // input wire rd_en
          .dout      (data_o),            // output wire [7 : 0] dout
          .full      (),                  // output wire full
          .empty     (),                  // output wire empty
          .data_count(fifo_data_count_w)  // output wire [11 : 0] data_count
      );

    end else begin

      fifo_line_buffer u_fifo_line_buffer (
          .clk       (clk),               // input wire clk
          .srst      (reset),             // input wire srst
          .din       (data_i),            // input wire [22 : 0] din
          .wr_en     (valid_i),           // input wire wr_en
          .rd_en     (rd_en),             // input wire rd_en
          .dout      (data_o),            // output wire [22 : 0] dout
          .full      (),                  // output wire full
          .empty     (),                  // output wire empty
          .data_count(fifo_data_count_w)  // output wire [11 : 0] data_count
      );

    end

  endgenerate


endmodule

5 高斯滤波代码

`timescale 1ns / 1ps
//  高斯滤波



module image_gaussian_filter (
    input wire clk,
    input wire reset,

    input wire [10:0] img_width,
    input wire [ 9:0] img_height,

    input wire valid_i,
    input wire [7:0] img_data_i,

    output reg valid_o,
    output reg [7:0] img_data_o
);

  //常量声明
  localparam MODE = 1;  //0表示彩色图像输出,1表示灰度图像输出

  //变量声明
  wire valid;
  wire [7:0] prev_line_data;
  wire [7:0] cur_line_data;
  wire [7:0] next_line_data;
  reg valid_d1;
  reg [7:0] prev_line_data_d1;
  reg [7:0] cur_line_data_d1;
  reg [7:0] next_line_data_d1;

  reg [7:0] prev_line_data_d2;
  reg [7:0] cur_line_data_d2;
  reg [7:0] next_line_data_d2;
  reg [7:0] x_cnt;

  reg valid_s;
  reg [7:0] prev_line_data_d2_s;
  reg [7:0] cur_line_data_d2_s;
  reg [7:0] next_line_data_d2_s;
  reg [7:0] prev_line_data_d1_s;
  reg [7:0] cur_line_data_d1_s;
  reg [7:0] next_line_data_d1_s;
  reg [7:0] prev_line_data_s;
  reg [7:0] cur_line_data_s;
  reg [7:0] next_line_data_s;

  wire [7:0] Y0, Y1, Y2;
  wire [7:0] Y3, Y4, Y5;
  wire [7:0] Y6, Y7, Y8;

  reg valid_s_d1;
  reg [9:0] Y_sum0;
  reg [10:0] Y_sum1;
  reg [9:0] Y_sum2;

  reg valid_s_d2;
  reg [14:0] Y_sum;
  wire [15:0] RGB_sum;
  wire [7:0] gray;

  image_line_buffer u_image_line_buffer (
      .clk             (clk),
      .reset           (reset),
      .img_width       (img_width),
      .img_height      (img_height),
      .valid_i         (valid_i),
      .img_data_i      (img_data_i),
      .valid_o         (valid),
      .prev_line_data_o(prev_line_data),
      .cur_line_data_o (cur_line_data),
      .next_line_data_o(next_line_data)
  );

  always @(posedge clk) begin
    if (reset) begin
      valid_d1 <= 0;
      prev_line_data_d1 <= 0;
      cur_line_data_d1 <= 0;
      next_line_data_d1 <= 0;
      prev_line_data_d2 <= 0;
      cur_line_data_d2 <= 0;
      next_line_data_d2 <= 0;
    end else begin
      valid_d1 <= valid;
      prev_line_data_d1 <= prev_line_data;
      cur_line_data_d1 <= cur_line_data;
      next_line_data_d1 <= next_line_data;
      prev_line_data_d2 <= prev_line_data_d1;
      cur_line_data_d2 <= cur_line_data_d1;
      next_line_data_d2 <= next_line_data_d1;
    end
  end

  //边界数据处理
  always @(posedge clk) begin
    if (reset) begin
      x_cnt <= 0;
    end else begin
      x_cnt <= valid_d1 ? ((x_cnt == img_width - 1) ? 0 : x_cnt + 1) : x_cnt;
    end
  end

  always @(posedge clk) begin
    if (reset) begin
      valid_s <= 0;
      prev_line_data_d2_s <= 0;
      cur_line_data_d2_s <= 0;
      next_line_data_d2_s <= 0;
      prev_line_data_d1_s <= 0;
      cur_line_data_d1_s <= 0;
      next_line_data_d1_s <= 0;
      prev_line_data_s <= 0;
      cur_line_data_s <= 0;
      next_line_data_s <= 0;
    end else begin
      valid_s <= valid_d1;
      prev_line_data_d1_s <= prev_line_data_d1;
      cur_line_data_d1_s <= cur_line_data_d1;
      next_line_data_d1_s <= next_line_data_d1;
      if (x_cnt == 0) begin
        prev_line_data_d2_s <= prev_line_data_d1;
        cur_line_data_d2_s <= cur_line_data_d1;
        next_line_data_d2_s <= next_line_data_d1;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
      if (x_cnt == img_width - 1) begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data_d1;
        cur_line_data_s <= cur_line_data_d1;
        next_line_data_s <= next_line_data_d1;
      end else begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
    end
  end

  assign Y0 = prev_line_data_d2_s;
  assign Y1 = cur_line_data_d2_s;
  assign Y2 = next_line_data_d2_s;
  assign Y3 = prev_line_data_d1_s;
  assign Y4 = cur_line_data_d1_s;
  assign Y5 = next_line_data_d1_s;
  assign Y6 = prev_line_data_s;
  assign Y7 = cur_line_data_s;
  assign Y8 = next_line_data_s;

  //高斯滤波模版
  /************
 [1. 2. 1.]
 [2. 4. 2.]
 [1. 2. 1.]
*************/

  always @(posedge clk) begin
    if (reset) begin
      valid_s_d1 <= 0;
      {Y_sum0, Y_sum1, Y_sum2} <= 0;
    end else if (valid_s) begin
      valid_s_d1 <= 1;
      Y_sum0 <= Y0 + {Y1, 1'b0} + Y2;
      Y_sum1 <= {Y3, 1'b0} + {Y4, 2'b0} + {Y5, 1'b0};
      Y_sum2 <= Y6 + {Y7, 1'b0} + Y8;
    end else valid_s_d1 <= 0;
  end


  //彩色图像 直接求和
  //灰度图像 1/3,扩大4bit,即16/3约等于5 = 4 + 1
  always @(posedge clk) begin
    if (reset) begin
      valid_s_d2 <= 0;
      Y_sum <= 0;
    end else if (valid_s_d1) begin
      valid_s_d2 <= 1;
      Y_sum <= Y_sum0 + Y_sum1 + Y_sum2;
    end else valid_s_d2 <= 0;
  end

  always @(posedge clk) begin
    if (reset) begin
      valid_o <= 0;
      img_data_o <= 0;
    end else if (valid_s_d2) begin
      valid_o <= 1;
      img_data_o <= Y_sum[11:4];
    end else begin
      valid_o <= 0;
    end
  end

endmodule

6 sobel边缘检测代码

`timescale 1ns / 1ps
//边缘检测一阶微分算子:Sobel算子



module image_sobel_edge (
    input wire clk,
    input wire reset,

    input wire [10:0] img_width,
    input wire [ 9:0] img_height,

    input wire valid_i,
    input wire [7:0] img_data_i,

    output reg valid_o,
    output reg [7:0] grad_mag,
    output reg [10:0] grad_dx,
    output reg [10:0] grad_dy
);

  //常量声明
  localparam N = 16;

  //变量声明
  wire valid;
  wire [7:0] prev_line_data;
  wire [7:0] cur_line_data;
  wire [7:0] next_line_data;
  reg valid_d1;
  reg [7:0] prev_line_data_d1;
  reg [7:0] cur_line_data_d1;
  reg [7:0] next_line_data_d1;

  reg [7:0] prev_line_data_d2;
  reg [7:0] cur_line_data_d2;
  reg [7:0] next_line_data_d2;
  reg [10:0] x_cnt;

  reg valid_s;
  reg [7:0] prev_line_data_d2_s;
  reg [7:0] cur_line_data_d2_s;
  reg [7:0] next_line_data_d2_s;
  reg [7:0] prev_line_data_d1_s;
  reg [7:0] cur_line_data_d1_s;
  reg [7:0] next_line_data_d1_s;
  reg [7:0] prev_line_data_s;
  reg [7:0] cur_line_data_s;
  reg [7:0] next_line_data_s;

  wire [7:0] Y0;
  wire [7:0] Y1;
  wire [7:0] Y2;
  wire [7:0] Y3;
  wire [7:0] Y4;
  wire [7:0] Y5;
  wire [7:0] Y6;
  wire [7:0] Y7;
  wire [7:0] Y8;

  reg valid_s_d1;
  wire [9:0] Gx_Y0_a;
  wire [9:0] Gx_Y1_a;
  wire [9:0] Gy_Y0_a;
  wire [9:0] Gy_Y1_a;
  reg Gx_Y_sign;
  reg [9:0] Gx_Y;
  reg Gy_Y_sign;
  reg [9:0] Gy_Y;

  reg valid_s_d2;
  reg Gx_Y_sign_d1;
  reg [19:0] Gx_Y_square;
  reg Gy_Y_sign_d1;
  reg [19:0] Gy_Y_square;
  wire [20:0] Gx_Gy_sum;

  reg [N-1:0] Gx_Y_sign_shift;
  reg [10*N-1:0] Gx_Y_shift;
  reg [N-1:0] Gy_Y_sign_shift;
  reg [10*N-1:0] Gy_Y_shift;

  wire valid_sqr;
  wire [10:0] data_sqr;

  image_line_buffer u_image_line_buffer (
      .clk             (clk),
      .reset           (reset),
      .img_width       (img_width),
      .img_height      (img_height),
      .valid_i         (valid_i),
      .img_data_i      (img_data_i),
      .valid_o         (valid),
      .prev_line_data_o(prev_line_data),
      .cur_line_data_o (cur_line_data),
      .next_line_data_o(next_line_data)
  );

  always @(posedge clk) begin
    if (reset) begin
      valid_d1 <= 0;
      prev_line_data_d1 <= 0;
      cur_line_data_d1 <= 0;
      next_line_data_d1 <= 0;
      prev_line_data_d2 <= 0;
      cur_line_data_d2 <= 0;
      next_line_data_d2 <= 0;
    end else begin
      valid_d1 <= valid;
      prev_line_data_d1 <= prev_line_data;
      cur_line_data_d1 <= cur_line_data;
      next_line_data_d1 <= next_line_data;
      prev_line_data_d2 <= prev_line_data_d1;
      cur_line_data_d2 <= cur_line_data_d1;
      next_line_data_d2 <= next_line_data_d1;
    end
  end

  //边界数据处理
  always @(posedge clk) begin
    if (reset) begin
      x_cnt <= 0;
    end else begin
      x_cnt <= valid_d1 ? ((x_cnt == img_width - 1) ? 0 : x_cnt + 1) : x_cnt;
    end
  end

  always @(posedge clk) begin
    if (reset) begin
      valid_s <= 0;
      prev_line_data_d2_s <= 0;
      cur_line_data_d2_s <= 0;
      next_line_data_d2_s <= 0;
      prev_line_data_d1_s <= 0;
      cur_line_data_d1_s <= 0;
      next_line_data_d1_s <= 0;
      prev_line_data_s <= 0;
      cur_line_data_s <= 0;
      next_line_data_s <= 0;
    end else begin
      valid_s <= valid_d1;
      prev_line_data_d1_s <= prev_line_data_d1;
      cur_line_data_d1_s <= cur_line_data_d1;
      next_line_data_d1_s <= next_line_data_d1;
      if (x_cnt == 0) begin
        prev_line_data_d2_s <= prev_line_data_d1;
        cur_line_data_d2_s <= cur_line_data_d1;
        next_line_data_d2_s <= next_line_data_d1;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
      if (x_cnt == img_width - 1) begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data_d1;
        cur_line_data_s <= cur_line_data_d1;
        next_line_data_s <= next_line_data_d1;
      end else begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
    end
  end

  assign Y0 = prev_line_data_d2_s;
  assign Y1 = cur_line_data_d2_s;
  assign Y2 = next_line_data_d2_s;
  assign Y3 = prev_line_data_d1_s;
  assign Y4 = cur_line_data_d1_s;
  assign Y5 = next_line_data_d1_s;
  assign Y6 = prev_line_data_s;
  assign Y7 = cur_line_data_s;
  assign Y8 = next_line_data_s;

  /Sobey算子
  /************
    [-1.  0.  1]  
    [-2.  0.  2]
Gx= [-1.  0.  1]
    [ 1.  2.  1]
    [ 0.  0.  0]
Gy= [-1. -2. -1]
*************/
  assign Gx_Y0_a = Y0 + {Y3, 1'b0} + Y6;
  assign Gx_Y1_a = Y2 + {Y5, 1'b0} + Y8;

  assign Gy_Y0_a = Y0 + {Y1, 1'b0} + Y2;
  assign Gy_Y1_a = Y6 + {Y7, 1'b0} + Y8;
  always @(posedge clk) begin
    if (reset) begin
      valid_s_d1 <= 0;
      {Gx_Y, Gy_Y} <= 0;
      {Gx_Y_sign, Gy_Y_sign} <= 0;
    end else if (valid_s) begin
      valid_s_d1 <= 1;
      Gx_Y <= (Gx_Y0_a > Gx_Y1_a) ? Gx_Y0_a - Gx_Y1_a : Gx_Y1_a - Gx_Y0_a;
      Gx_Y_sign <= (Gx_Y0_a > Gx_Y1_a) ? 1 : 0;
      Gy_Y <= (Gy_Y0_a > Gy_Y1_a) ? Gy_Y0_a - Gy_Y1_a : Gy_Y1_a - Gy_Y0_a;
      Gy_Y_sign <= (Gy_Y0_a > Gy_Y1_a) ? 1 : 0;
    end else valid_s_d1 <= 0;
  end

  //求平方
  always @(posedge clk) begin
    if (reset) begin
      valid_s_d2  <= 0;
      Gx_Y_square <= 0;
      Gy_Y_square <= 0;
    end else begin
      valid_s_d2  <= valid_s_d1;
      Gx_Y_square <= Gx_Y * Gx_Y;
      Gy_Y_square <= Gy_Y * Gy_Y;
    end
  end
  assign Gx_Gy_sum = Gx_Y_square + Gy_Y_square;

  //平方根
  cordic_square_root u_cordic_square_root (
      .aclk                   (clk),         // input wire aclk
      .s_axis_cartesian_tvalid(valid_s_d2),  // input wire s_axis_cartesian_tvalid
      .s_axis_cartesian_tdata (Gx_Gy_sum),   // input wire [23 : 0] s_axis_cartesian_tdata
      .m_axis_dout_tvalid     (valid_sqr),   // output wire m_axis_dout_tvalid
      .m_axis_dout_tdata      (data_sqr)     // output wire [15 : 0] m_axis_dout_tdata
  );

  always @(posedge clk) begin
    if (reset) begin
      Gx_Y_sign_shift <= 0;
      Gx_Y_shift <= 0;
      Gy_Y_sign_shift <= 0;
      Gy_Y_shift <= 0;
    end else begin
      Gx_Y_sign_shift <= {Gx_Y_sign_shift, Gx_Y_sign};
      Gx_Y_shift <= {Gx_Y_shift, Gx_Y};
      Gy_Y_sign_shift <= {Gy_Y_sign_shift, Gy_Y_sign};
      Gy_Y_shift <= {Gy_Y_shift, Gy_Y};
    end
  end

  always @(posedge clk) begin
    if (reset) begin
      valid_o  <= 0;
      grad_mag <= 0;
      grad_dx  <= 0;
      grad_dy  <= 0;
    end else if (valid_sqr) begin
      valid_o  <= 1;
      grad_mag <= data_sqr;
      grad_dx  <= {Gx_Y_sign_shift[N-1], Gx_Y_shift[N*10-1:(N-1)*10]};
      grad_dy  <= {Gy_Y_sign_shift[N-1], Gy_Y_shift[N*10-1:(N-1)*10]};
    end else begin
      valid_o <= 0;
    end
  end

endmodule

7 非极大值抑制代码

`timescale 1ns / 1ps
//  非极大值抑制



module non_maximum_suppression (
    input clk,
    input reset,

    input wire [10:0] img_width,
    input wire [ 9:0] img_height,

    input valid_i,
    input [7:0] grad_mag,
    input [10:0] grad_dx,
    input [10:0] grad_dy,

    output reg valid_o,
    output reg [7:0] img_data_o
);

  //常量
  localparam N = 24;

  //参数声明
  wire valid;
  wire [7:0] prev_line_data;
  wire [7:0] cur_line_data;
  wire [7:0] next_line_data;
  reg valid_d1;
  reg [7:0] prev_line_data_d1;
  reg [7:0] cur_line_data_d1;
  reg [7:0] next_line_data_d1;

  reg [7:0] prev_line_data_d2;
  reg [7:0] cur_line_data_d2;
  reg [7:0] next_line_data_d2;
  reg [10:0] x_cnt;

  reg valid_s;
  reg [7:0] prev_line_data_d2_s;
  reg [7:0] cur_line_data_d2_s;
  reg [7:0] next_line_data_d2_s;
  reg [7:0] prev_line_data_d1_s;
  reg [7:0] cur_line_data_d1_s;
  reg [7:0] next_line_data_d1_s;
  reg [7:0] prev_line_data_s;
  reg [7:0] cur_line_data_s;
  reg [7:0] next_line_data_s;

  wire [7:0] Y0, Y1, Y2;
  wire [7:0] Y3, Y4, Y5;
  wire [7:0] Y6, Y7, Y8;

  wire grad_valid;
  wire [21:0] grad_cur_line_data;

  reg valid_s_d1;
  reg grad_dx_sign;
  reg [9:0] grad_dx_abs;
  reg grad_dy_sign;
  reg [9:0] grad_dy_abs;

  reg [1:0] mode;
  reg [9:0] dividend, divisor;

  wire div_valid;
  wire [23:0] div_data;

  reg [2*N-1:0] mode_shift;
  wire mode_s;
  reg [72*N-1:0] Y_shift;
  wire [7:0] Y0_s, Y1_s, Y2_s;
  wire [7:0] Y3_s, Y4_s, Y5_s;
  wire [7:0] Y6_s, Y7_s, Y8_s;

  reg div_valid_d1;
  reg [7:0] grad1, grad2, grad3, grad4;
  reg [7:0] weight;
  reg [8:0] one_sub_weight;
  reg [7:0] Y4_s_d1;

  reg div_valid_d2;
  reg [15:0] grad1_m, grad2_m;
  reg [16:0] grad3_m, grad4_m;
  reg [7:0] Y4_s_d2;

  wire [16:0] t1, t2;

  /行视频数据缓存
  image_line_buffer u_image_line_buffer (
      .clk             (clk),
      .reset           (reset),
      .img_width       (img_width),
      .img_height      (img_height),
      .valid_i         (valid_i),
      .img_data_i      (grad_mag),
      .valid_o         (valid),
      .prev_line_data_o(prev_line_data),
      .cur_line_data_o (cur_line_data),
      .next_line_data_o(next_line_data)
  );

  always @(posedge clk) begin
    if (reset) begin
      valid_d1 <= 0;
      prev_line_data_d1 <= 0;
      cur_line_data_d1 <= 0;
      next_line_data_d1 <= 0;
      prev_line_data_d2 <= 0;
      cur_line_data_d2 <= 0;
      next_line_data_d2 <= 0;
    end else begin
      valid_d1 <= valid;
      prev_line_data_d1 <= prev_line_data;
      cur_line_data_d1 <= cur_line_data;
      next_line_data_d1 <= next_line_data;
      prev_line_data_d2 <= prev_line_data_d1;
      cur_line_data_d2 <= cur_line_data_d1;
      next_line_data_d2 <= next_line_data_d1;
    end
  end

  //边界数据处理
  always @(posedge clk) begin
    if (reset) begin
      x_cnt <= 0;
    end else begin
      x_cnt <= valid_d1 ? ((x_cnt == img_width - 1) ? 0 : x_cnt + 1) : x_cnt;
    end
  end

  always @(posedge clk) begin
    if (reset) begin
      valid_s <= 0;
      prev_line_data_d2_s <= 0;
      cur_line_data_d2_s <= 0;
      next_line_data_d2_s <= 0;
      prev_line_data_d1_s <= 0;
      cur_line_data_d1_s <= 0;
      next_line_data_d1_s <= 0;
      prev_line_data_s <= 0;
      cur_line_data_s <= 0;
      next_line_data_s <= 0;
    end else begin
      valid_s <= valid_d1;
      prev_line_data_d1_s <= prev_line_data_d1;
      cur_line_data_d1_s <= cur_line_data_d1;
      next_line_data_d1_s <= next_line_data_d1;
      if (x_cnt == 0) begin
        prev_line_data_d2_s <= prev_line_data_d1;
        cur_line_data_d2_s <= cur_line_data_d1;
        next_line_data_d2_s <= next_line_data_d1;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
      if (x_cnt == img_width - 1) begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data_d1;
        cur_line_data_s <= cur_line_data_d1;
        next_line_data_s <= next_line_data_d1;
      end else begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
    end
  end

  assign Y0 = prev_line_data_d2_s;
  assign Y1 = cur_line_data_d2_s;
  assign Y2 = next_line_data_d2_s;
  assign Y3 = prev_line_data_d1_s;
  assign Y4 = cur_line_data_d1_s;
  assign Y5 = next_line_data_d1_s;
  assign Y6 = prev_line_data_s;
  assign Y7 = cur_line_data_s;
  assign Y8 = next_line_data_s;

  /grad_dx和grad_dy行数据缓存
  image_line_buffer #(
      .W(21)
  ) u_image_grad_line_buffer (
      .clk             (clk),
      .reset           (reset),
      .img_width       (img_width),
      .img_height      (img_height),
      .valid_i         (valid_i),
      .img_data_i      ({grad_dx, grad_dy}),
      .valid_o         (grad_valid),
      .prev_line_data_o(),
      .cur_line_data_o (grad_cur_line_data),
      .next_line_data_o()
  );


  /非极大值限制计算
  //计算grad_dx,grad_dy绝对值
  always @(posedge clk) begin
    if (reset) begin
      {grad_dx_sign, grad_dx_abs} <= 0;
      {grad_dy_sign, grad_dy_abs} <= 0;
    end else begin
      grad_dx_sign <= grad_cur_line_data[21];
      grad_dx_abs  <= grad_cur_line_data[20:11];
      grad_dy_sign <= grad_cur_line_data[10];
      grad_dy_abs  <= grad_cur_line_data[9:0];
    end
  end

  //计算模式
  always @(posedge clk) begin
    if (reset) begin
      mode <= 0;
      {dividend, divisor} <= 0;
    end else begin
      if (grad_dx_abs > grad_dy_abs) begin
        mode <= (grad_dx_sign ^ grad_dy_sign) ? 0 : 1;
        dividend <= grad_dy_abs;
        divisor <= grad_dx_abs;
      end else begin
        mode <= (grad_dx_sign ^ grad_dy_sign) ? 2 : 3;
        dividend <= grad_dx_abs;
        divisor <= grad_dy_abs;
      end
    end
  end

  //除法计算
  div_gen_10x10 u_div_gen_10x10 (
      .aclk                  (clk),               // input wire aclk
      .s_axis_divisor_tvalid (valid_s),           // input wire s_axis_divisor_tvalid
      .s_axis_divisor_tdata  ({6'b0, divisor}),   // input wire [15 : 0] s_axis_divisor_tdata
      .s_axis_dividend_tvalid(valid_s),           // input wire s_axis_dividend_tvalid
      .s_axis_dividend_tdata ({6'b0, dividend}),  // input wire [15 : 0] s_axis_dividend_tdata
      .m_axis_dout_tvalid    (div_valid),         // output wire m_axis_dout_tvalid
      .m_axis_dout_tdata     (div_data)           // output wire [23 : 0] m_axis_dout_tdata
  );

  //同步延时
  always @(posedge clk) begin
    if (reset) begin
      mode_shift <= 0;
      Y_shift <= 0;
    end else begin
      mode_shift <= {mode_shift, mode};
      Y_shift <= {Y_shift, Y0, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8};
    end
  end

  assign mode_s = mode_shift[2*N-1:2*(N-1)];
  assign {Y0_s, Y1_s, Y2_s, Y3_s, Y4_s, Y5_s, Y6_s, Y7_s, Y8_s} = Y_shift[72*N-1:72*(N-1)];

  //计算插值系数、插值数据
  always @(posedge clk) begin
    if (reset) begin
      div_valid_d1 <= 0;
      weight <= 0;
      one_sub_weight <= 0;
      Y4_s_d1 <= 0;
      {grad1, grad2, grad3, grad4} <= 0;
    end else begin
      div_valid_d1 <= div_valid;
      weight <= div_data[7:0];
      one_sub_weight <= 256 - div_data[7:0];
      Y4_s_d1 <= Y4_s;
      case (mode_s)
        0: begin
          grad1 <= Y7_s;
          grad2 <= Y1_s;
          grad3 <= Y8_s;
          grad4 <= Y0_s;
        end
        1: begin
          grad1 <= Y7_s;
          grad2 <= Y1_s;
          grad3 <= Y6_s;
          grad4 <= Y2_s;
        end
        2: begin
          grad1 <= Y5_s;
          grad2 <= Y3_s;
          grad3 <= Y8_s;
          grad4 <= Y0_s;
        end
        3: begin
          grad1 <= Y5_s;
          grad2 <= Y3_s;
          grad3 <= Y6_s;
          grad4 <= Y2_s;
        end
      endcase
    end
  end

  //计算极值t1\t2
  always @(posedge clk) begin
    if (reset) begin
      div_valid_d2 <= 0;
      Y4_s_d2 <= 0;
      {grad1_m, grad2_m, grad3_m, grad4_m} <= 0;
    end else begin
      div_valid_d2 <= div_valid_d1;
      Y4_s_d2 <= Y4_s_d1;
      grad1_m <= grad1 * weight;
      grad2_m <= grad2 * weight;
      grad3_m <= grad3 * one_sub_weight;
      grad4_m <= grad4 * one_sub_weight;
    end
  end

  assign t1 = grad1_m + grad3_m;
  assign t2 = grad2_m + grad4_m;

  //超过极值后,赋值为0
  always @(posedge clk) begin
    if (reset) begin
      valid_o <= 0;
      img_data_o <= 0;
    end else if (div_valid_d2) begin
      valid_o <= 1;
      img_data_o <= ({1'b0, Y4_s_d2} > t1[16:8]) && ({1'b0, Y4_s_d2} > t2[16:8]) ? Y4_s_d2 : 0;
    end else begin
      valid_o <= 0;
    end
  end

endmodule

8 双阈值代码

`timescale 1ns / 1ps
//双阈值



module double_threshold (
    input clk,
    input reset,

    input [10:0] img_width,
    input [ 9:0] img_height,
    input [ 7:0] low_threshold,
    input [ 7:0] high_threshold,

    input valid_i,
    input [7:0] img_data_i,

    output reg valid_o,
    output reg [7:0] img_data_o
);

  //常量声明
  localparam MODE = 1;  //0表示彩色图像输出,1表示灰度图像输出

  //变量声明
  wire valid;
  wire [7:0] prev_line_data;
  wire [7:0] cur_line_data;
  wire [7:0] next_line_data;
  reg valid_d1;
  reg [7:0] prev_line_data_d1;
  reg [7:0] cur_line_data_d1;
  reg [7:0] next_line_data_d1;

  reg [7:0] prev_line_data_d2;
  reg [7:0] cur_line_data_d2;
  reg [7:0] next_line_data_d2;
  reg [7:0] x_cnt;

  reg valid_s;
  reg [7:0] prev_line_data_d2_s;
  reg [7:0] cur_line_data_d2_s;
  reg [7:0] next_line_data_d2_s;
  reg [7:0] prev_line_data_d1_s;
  reg [7:0] cur_line_data_d1_s;
  reg [7:0] next_line_data_d1_s;
  reg [7:0] prev_line_data_s;
  reg [7:0] cur_line_data_s;
  reg [7:0] next_line_data_s;

  wire [7:0] Y0, Y1, Y2;
  wire [7:0] Y3, Y4, Y5;
  wire [7:0] Y6, Y7, Y8;

  image_line_buffer u_image_line_buffer (
      .clk             (clk),
      .reset           (reset),
      .img_width       (img_width),
      .img_height      (img_height),
      .valid_i         (valid_i),
      .img_data_i      (img_data_i),
      .valid_o         (valid),
      .prev_line_data_o(prev_line_data),
      .cur_line_data_o (cur_line_data),
      .next_line_data_o(next_line_data)
  );

  always @(posedge clk) begin
    if (reset) begin
      valid_d1 <= 0;
      prev_line_data_d1 <= 0;
      cur_line_data_d1 <= 0;
      next_line_data_d1 <= 0;
      prev_line_data_d2 <= 0;
      cur_line_data_d2 <= 0;
      next_line_data_d2 <= 0;
    end else begin
      valid_d1 <= valid;
      prev_line_data_d1 <= prev_line_data;
      cur_line_data_d1 <= cur_line_data;
      next_line_data_d1 <= next_line_data;
      prev_line_data_d2 <= prev_line_data_d1;
      cur_line_data_d2 <= cur_line_data_d1;
      next_line_data_d2 <= next_line_data_d1;
    end
  end

  //边界数据处理
  always @(posedge clk) begin
    if (reset) begin
      x_cnt <= 0;
    end else begin
      x_cnt <= valid_d1 ? ((x_cnt == img_width - 1) ? 0 : x_cnt + 1) : x_cnt;
    end
  end

  always @(posedge clk) begin
    if (reset) begin
      valid_s <= 0;
      prev_line_data_d2_s <= 0;
      cur_line_data_d2_s <= 0;
      next_line_data_d2_s <= 0;
      prev_line_data_d1_s <= 0;
      cur_line_data_d1_s <= 0;
      next_line_data_d1_s <= 0;
      prev_line_data_s <= 0;
      cur_line_data_s <= 0;
      next_line_data_s <= 0;
    end else begin
      valid_s <= valid_d1;
      prev_line_data_d1_s <= prev_line_data_d1;
      cur_line_data_d1_s <= cur_line_data_d1;
      next_line_data_d1_s <= next_line_data_d1;
      if (x_cnt == 0) begin
        prev_line_data_d2_s <= prev_line_data_d1;
        cur_line_data_d2_s <= cur_line_data_d1;
        next_line_data_d2_s <= next_line_data_d1;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
      if (x_cnt == img_width - 1) begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data_d1;
        cur_line_data_s <= cur_line_data_d1;
        next_line_data_s <= next_line_data_d1;
      end else begin
        prev_line_data_d2_s <= prev_line_data_d2;
        cur_line_data_d2_s <= cur_line_data_d2;
        next_line_data_d2_s <= next_line_data_d2;
        prev_line_data_s <= prev_line_data;
        cur_line_data_s <= cur_line_data;
        next_line_data_s <= next_line_data;
      end
    end
  end

  assign Y0 = prev_line_data_d2_s;
  assign Y1 = cur_line_data_d2_s;
  assign Y2 = next_line_data_d2_s;
  assign Y3 = prev_line_data_d1_s;
  assign Y4 = cur_line_data_d1_s;
  assign Y5 = next_line_data_d1_s;
  assign Y6 = prev_line_data_s;
  assign Y7 = cur_line_data_s;
  assign Y8 = next_line_data_s;


  always @(posedge clk) begin
    if (reset) begin
      valid_o <= 0;
      img_data_o <= 0;
    end else if (valid_s) begin
      valid_o <= 1;
      if (Y4 < low_threshold) img_data_o <= 0;
      else if((Y0 > high_threshold)||(Y1 > high_threshold)||(Y2 > high_threshold)||
                (Y3 > high_threshold)||(Y4 > high_threshold)||(Y5 > high_threshold)||
                (Y6 > high_threshold)||(Y7 > high_threshold)||(Y8 > high_threshold))
        img_data_o <= 255;
      else img_data_o <= 0;
    end else begin
      valid_o <= 0;
    end
  end

endmodule
相关推荐
stm 学习ing21 分钟前
HDLBits训练6
经验分享·笔记·fpga开发·fpga·eda·verilog hdl·vhdl
szxinmai主板定制专家1 小时前
【NI国产替代】基于国产FPGA+全志T3的全国产16振动+2转速(24bits)高精度终端采集板卡
人工智能·fpga开发
stm 学习ing1 小时前
HDLBits训练4
经验分享·笔记·fpga开发·课程设计·fpga·eda·verilog hdl
cckkppll1 小时前
判断实例化或推断的时机
fpga开发
YangJZ_ByteMaster1 小时前
EndtoEnd Object Detection with Transformers
人工智能·深度学习·目标检测·计算机视觉
博览鸿蒙2 小时前
选择FPGA开发,学历是硬性要求吗?
fpga开发
请站在我身后4 小时前
最新的强大的文生视频模型Pyramid Flow 论文阅读及复现
论文阅读·人工智能·神经网络·计算机视觉·stable diffusion·transformer
伊一大数据&人工智能学习日志5 小时前
OpenCV计算机视觉 02 图片修改 图像运算 边缘填充 阈值处理
人工智能·opencv·计算机视觉
Schwertlilien6 小时前
图像处理-Ch7-小波函数
图像处理
Schwertlilien7 小时前
图像处理-Ch7-图像金字塔和其他变换
图像处理·人工智能