基于fpga的sobel边缘检测,部分的代码参考的是野火正点原子的代码和视频。通过matlab将图片转成txt文件,并编写verilog代码将处理好的数据再转成txt文件,同时通过matlab将txt文件再读取成图片。
文章目录
- 前言
- 一、verilog代码
- 1.sobel_edge代码
- 2.tb文件(读写txt文件)
- 注意
前言
一、sobel边缘检测的原理(源自野火)
Sobel算法的核心就是Sobel算子,该算子包含两组3x3的矩阵
对于图像而言,取三行三列的图像数据,将图像数据与对应的算子相乘再相加,得到x方向的GX和y方向的Gy,平方后相加,提取算数平方根,得到Gxy,近似值为GX和Gy绝对值之和,将得到的Gxy与设定的阈值相比较,如果大于阈值则显示黑点,否则显示白点。
Sobel算法在边缘检测中分4步:
- 通过GX和Gy的计算公式,结合FIFO求和算法求取数值。
- 求得GX和Gy绝对值。
- 求出Gxy。
- 将求得的值与设定的阈值相比,大于则为黑色,小于为白。
在经过Sobel运算后,输出的图片相比于输入时的图片会缺少2行2列数据,因为在求GX和Gy时需要使用FIFO求和算法,该算法只有在第2行或第二列数据输入时才开始执行,第0、1行或者第0、1列不会进行求和运算,没有数据输出,所以会缺少2行2列。
求和计算的要点:
完成3行数据的SUM求和,需要调用两个FIFO ip核,当数据开始输入时,将数据的第0行存储到FIFO1中,第一行的数据存入FIFO2中,当数据的第二行的第0个数据输入的同时,读取写入FIFO1中的第0行第0个数据和写入FIFO2中的第一行第一个数据,将三者求和并输出。完成求和的同时,将读取的FIFO2的第一行第0个数据写入FIFO1中,覆盖掉第0行的第0个数据,相当于将第一行数据重新写入FIFO1中,将第二行数据写入FIFO2中, 当第三行数据开始传入时,将1.2.3行进行求和,并继续此步骤。
二、verilog代码
1.sobel_edge代码
代码如下(示例):
module sobel_edge
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data , //输入信号
input wire pi_flag ,
output reg [7:0] po_data , //输出信号
output reg po_flag
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter LENGTH_P = 10'd436 , //图片长度
WIDE_P = 10'd293 ; //图片宽度
parameter THRESHOLD = 8'b000_011_00 ; //比较阈值
parameter BLACK = 8'b0000_0000 , //黑色
WHITE = 8'b1111_1111 ; //白色
//wire define
wire [7:0] data_out1 ; //fifo1数据输出
wire [7:0] data_out2 ; //fifo2数据输出
//reg define
reg [7:0] cnt_h ; //行计数
reg [7:0] cnt_v ; //场计数
reg [7:0] pi_data_dly ; //pi_data数据寄存
reg wr_en1 ; //fifo1写使能
reg wr_en2 ; //fifo2写使能
reg [7:0] data_in1 ; //fifo1写数据
reg [7:0] data_in2 ; //fifo2写数据
reg rd_en ; //fifo1,fifo2共用读使能
reg [7:0] data_out1_dly ; //fifo1数据输出寄存
reg [7:0] data_out2_dly ; //fifo2数据输出寄存
reg dout_flag ; //使能信号
reg rd_en_dly1 ; //输出数据标志信号,延后rd_en一拍
reg rd_en_dly2 ; //a,b,c赋值标志信号
reg gx_gy_flag ; //gx,gy计算标志信号
reg gxy_flag ; //gxy计算标志信号
reg compare_flag; //阈值比较标志信号
reg [7:0] cnt_rd ; //读出数据计数器
reg [7:0] a1 ;
reg [7:0] a2 ;
reg [7:0] a3 ;
reg [7:0] b1 ;
reg [7:0] b2 ;
reg [7:0] b3 ;
reg [7:0] c1 ;
reg [7:0] c2 ;
reg [7:0] c3 ; //图像数据
reg [8:0] gx ;
reg [8:0] gy ; //gx,gy
reg [7:0] gxy ; //gxy
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_h:行数据个数计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_h <= 8'd0;
else if((cnt_h == (LENGTH_P - 1'b1)) && (pi_flag == 1'b1))
cnt_h <= 8'd0;
else if(pi_flag == 1'b1)
cnt_h <= cnt_h + 1'b1;
//cnt_v:场计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_v <= 8'd0;
else if((cnt_v == (WIDE_P - 1'b1)) && (pi_flag == 1'b1)
&& (cnt_h == (LENGTH_P - 1'b1)))
cnt_v <= 8'd0;
else if((cnt_h == (LENGTH_P - 1'b1)) && (pi_flag == 1'b1))
cnt_v <= cnt_v + 1'b1;
//cnt_rd:fifo数据读出个数计数,用来判断何时对gx,gy进行运算
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_rd <= 8'd0;
else if((cnt_rd == (LENGTH_P - 1'b1)) && (rd_en == 1'b1))
cnt_rd <= 8'd0;
else if(rd_en == 1'b1)
cnt_rd <= cnt_rd + 1'b1;
//wr_en1:fifo1写使能,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en1 <= 1'b0;
else if((cnt_v == 8'd0) && (pi_flag == 1'b1))
wr_en1 <= 1'b1; //第0行写入fifo1
else
wr_en1 <= dout_flag; //2-198行写入fifo1
//wr_en2,fifo2的写使能,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en2 <= 1'b0;
else if((cnt_v >= 8'd1)&&(cnt_v <= ((WIDE_P - 1'b1) - 1'b1))
&& (pi_flag == 1'b1))
wr_en2 <= 1'b1; //2-199行写入fifo2
else
wr_en2 <= 1'b0;
//data_in1:fifo1的数据写入
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_in1 <= 8'b0;
else if((pi_flag == 1'b1) && (cnt_v == 8'b0))
data_in1 <= pi_data;
else if(dout_flag == 1'b1)
data_in1 <= data_out2;
else
data_in1 <= data_in1;
//data_in2:fifo2的数据写入
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_in2 <= 8'b0;
else if((pi_flag == 1'b1) && (cnt_v >= 8'd1)
&& (cnt_v <= ((WIDE_P - 1'b1) - 1'b1)))
data_in2 <= pi_data;
else
data_in2 <= data_in2;
//rd_en:fifo1和fifo2的共用读使能,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if((pi_flag == 1'b1) && (cnt_v >= 8'd2)
&& (cnt_v <= (WIDE_P - 1'b1)))
rd_en <= 1'b1;
else
rd_en <= 1'b0;
//dout_flag:控制fifo1写使能wr_en1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
dout_flag <= 1'b0;
else if((wr_en2 == 1'b1) && (rd_en == 1'b1))
dout_flag <= 1'b1;
else
dout_flag <= 1'b0;
//rd_en_dly1:输出数据标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en_dly1 <= 1'b0;
else if(rd_en == 1'b1)
rd_en_dly1 <= 1'b1;
else
rd_en_dly1 <= 1'b0;
//data_out1_dly:data_out1数据寄存
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_out1_dly <= 8'b0;
else if(rd_en_dly1 == 1'b1)
data_out1_dly <= data_out1;
//data_out2_dly:data_out2数据寄存
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_out2_dly <= 8'b0;
else if(rd_en_dly1 == 1'b1)
data_out2_dly <= data_out2;
//pi_data_dly:输入数据pi_data寄存
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data_dly <= 8'b0;
else if(rd_en_dly1 == 1'b1)
pi_data_dly <= pi_data;
//rd_en_dly2:a,b,c赋值标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en_dly2 <= 1'b0;
else if(rd_en_dly1 == 1'b1)
rd_en_dly2 <= 1'b1;
else
rd_en_dly2 <= 1'b0;
//gx_gy_flag:gx,gy计算标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gx_gy_flag <= 1'b0;
else if((rd_en_dly2 == 1'b1) && ((cnt_rd >= 8'd3) || (cnt_rd == 8'd0)))
gx_gy_flag <= 1'b1;
else
gx_gy_flag <= 1'b0;
//gxy_flag:gxy计算标准信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gxy_flag <= 1'b0;
else if(gx_gy_flag == 1'b1)
gxy_flag <= 1'b1;
else
gxy_flag <= 1'b0;
//compare_flag,阈值比较标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
compare_flag <= 1'b0;
else if(gxy_flag == 1'b1)
compare_flag <= 1'b1;
else
compare_flag <= 1'b0;
//a,b,c赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
a1 <= 8'd0;
a2 <= 8'd0;
a3 <= 8'd0;
b1 <= 8'd0;
b2 <= 8'd0;
b3 <= 8'd0;
c1 <= 8'd0;
c2 <= 8'd0;
c3 <= 8'd0;
end
else if(rd_en_dly2==1)
begin
a1 <= data_out1_dly;
b1 <= data_out2_dly;
c1 <= pi_data_dly;
a2 <= a1;
b2 <= b1;
c2 <= c1;
a3 <= a2;
b3 <= b2;
c3 <= c2;
end
//gx:计算gx
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gx <= 9'd0;
else if(gx_gy_flag == 1'b1)
gx <= a3 - a1 + ((b3 - b1) << 1) + c3 - c1;
else
gx <= gx;
//gy:计算gy
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gy <= 9'd0;
else if(gx_gy_flag == 1'b1)
gy <= a1 - c1 + ((a2 - c2) << 1) + a3 - c3;
else
gy <= gy;
//gxy:gxy计算
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gxy <= 0;
else if((gx[8] == 1'b1) && (gy[8] == 1'b1) && (gxy_flag == 1'b1))
gxy <= (~gx[7:0] + 1'b1) + (~gy[7:0] + 1'b1);
else if((gx[8] == 1'b1) && (gy[8] == 1'b0) && (gxy_flag == 1'b1))
gxy <= (~gx[7:0] + 1'b1) + (gy[7:0]);
else if((gx[8] == 1'b0) && (gy[8] == 1'b1) && (gxy_flag == 1'b1))
gxy <= (gx[7:0]) + (~gy[7:0] + 1'b1);
else if((gx[8] == 1'b0) && (gy[8] == 1'b0) && (gxy_flag == 1'b1))
gxy <= (gx[7:0]) + (gy[7:0]);
//po_data:通过gxy与阈值比较,赋值po_data
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_data <= 8'b0;
else if((gxy >= THRESHOLD) && (compare_flag == 1'b1))
po_data <= BLACK;
else if(compare_flag == 1'b1)
po_data <= WHITE;
//po_flag:输出标志位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else if(compare_flag == 1'b1)
po_flag <= 1'b1;
else
po_flag <= 1'b0;
//inst
fifo fifo_inst_1 (
.clock ( sys_clk ),
.data ( data_in1 ),
.rdreq ( rd_en ),
.wrreq ( wr_en1 ),
.q ( data_out1 )
);
fifo fifo_inst_2 (
.clock ( sys_clk ),
.data ( data_in2 ),
.rdreq ( rd_en ),
.wrreq ( wr_en2 ),
.q ( data_out2 )
);
endmodule
2.tb文件(读写txt文件)
代码如下(示例):文章来源:https://www.toymoban.com/news/detail-479133.html
`timescale 1ns/1ps
module sobel_edge_tb;
reg sys_clk;
reg sys_rst_n;
reg [7:0] pi_data;
reg pi_flag;
wire [7:0] po_data;
wire po_flag;
reg [7:0] sobel_mem[127747:0];
reg [18:0] addr;
integer fid;
initial fid=$fopen("sobel_out.txt");
always @ (posedge sys_clk,negedge sys_rst_n)
begin
if(po_flag==1'b1)
$fdisplay(fid,"%d",po_data);
end
initial sys_clk=0;
always #10 sys_clk=~sys_clk;
initial $readmemh("sobel.txt",sobel_mem);
initial begin
sys_rst_n=0;
pi_data=0;
pi_flag=0;
addr=0;
#105;
sys_rst_n=1;
repeat (127748) begin
@(posedge sys_clk);
#2;
pi_data=sobel_mem[addr];
pi_flag=1;
#5;
addr=addr+1;
end
@(posedge sys_clk);
#2;
pi_data=0;
pi_flag=0;
#200;
$fclose(fid);
$stop;
end
//inst
sobel_edge sobel_edge_inst(
. sys_clk (sys_clk),
. sys_rst_n (sys_rst_n),
. pi_data (pi_data),
. pi_flag (pi_flag),
. po_data (po_data),
. po_flag (po_flag)
);
endmodule
注意的问题
实际上txt文件在转成图像的时候,会有很多的夹杂的噪声,有能力的最好还是外接一个显示器。文章来源地址https://www.toymoban.com/news/detail-479133.html
到了这里,关于基于fpga的sobel边缘检测的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!