FPGA-图像处理-仿真
文章目录
任务
读入一张bmp图片,对图片进行灰度处理,二值化以及边缘检测(sobel算子),将处理后的数据写入bmp显示,全部过程以仿真形式。我这用的vivado。
说明
在图像处理领域,要实现Sobel或者均值滤波等算法,则需要按照3*3矩阵的格式提取数据,
需要用到移位寄存器SHIFT RAM IP核。
Altera厂家的EDA环境下移位寄存器IP核可以实现多行的缓存输出,但是Xilinx下的Vivado环境,仅仅支持缓存输出一行数据,不过其实也无关紧要,我们这里可以使用两个SHIFT RAM IP核和正在输入的一行数据三者共同组成3行数据。
图片读取和写入
可参考FPGA-仿真读写bmp图片
使用图片(128*128):
本次实验的图片读取写入tb
`timescale 1ns / 1nsmodule top_tb;reg clk;reg rst;wire[23:0] dout;reg din_vld;wire dout_vld;//图像属性:图像宽度 图像高度 图像尺寸 图像像素点起始位integer bmp_width;integer bmp_high;integer bmp_size;integer start_index;//bmp file idinteger bmp_file_id;integer bmp_dout_id;integer dout_txt_id;//文件句柄integer h;//文件bmp文件数据reg[7:0]rd_data [0:49300];reg [7:0] rd_data2 [0:49300];//写操作reg[23:0]wr_data;integer i = 0;integer index;integer j = 0;parameter CYCLE=20;always #(CYCLE/2) clk=~clk;initialbegin clk=1'b1; rst=1'b0; #(CYCLE); rst=1'b1; #CYCLE; rst=1'b0; din_vld=1'b0; //打开原始图像bmp_file_id = $fopen("D:\\python\\pic\\er.bmp","rb");//打开输出数据dout_txt_id = $fopen("D:\\python\\pic\\output_file.txt","w+");//读取bmp文件h = $fread(rd_data,bmp_file_id); // 图像宽度bmp_width = {rd_data[21], rd_data[20], rd_data[19], rd_data[18]};// 图像宽度bmp_high = {rd_data[25], rd_data[24], rd_data[23], rd_data[22]};// 像素起始位置start_index = {rd_data[13], rd_data[12], rd_data[11], rd_data[10]};// 图像尺寸bmp_size = {rd_data[5], rd_data[4], rd_data[3], rd_data[2]};$fclose(bmp_file_id); //输出txt for(index = start_index; index < bmp_size-2; index = index + 3)begin //将像素点数据写入txt文件 din_vld=1'b1; wr_data = {rd_data[index + 2], rd_data[index + 1], rd_data[index]}; $fwrite(dout_txt_id, "%d,", wr_data[7:0]); $fwrite(dout_txt_id, "%d,", wr_data[15:8]); $fwrite(dout_txt_id, "%d\n", wr_data[23:16]); #(CYCLE); end din_vld=1'b0; $fclose(dout_txt_id);endinitialbegin #(3*CYCLE);//打开输出图像bmp_dout_id = $fopen("D:\\python\\pic\\output_file.bmp","wb");//将数据写入bmpfor(i = 0; i < start_index; i = i + 1)begin //写入文件头部信息 $fwrite(bmp_dout_id, "%c", rd_data[i]); endj=start_index;while(j<bmp_size) //写入像素点信息beginif(dout_vld==1'b1)begin$fwrite(bmp_dout_id, "%c", dout[7:0]);$fwrite(bmp_dout_id, "%c", dout[15:8]);$fwrite(bmp_dout_id, "%c", dout[23:16]);j=j+3;endelsebeginj=j;end#CYCLE;end$fclose(bmp_dout_id);endtop u_top( //clock and reset .rst (rst), //reset .clk (clk), //clock .rgb_din (wr_data), //输入数据 .din_vld (din_vld), //数据有效 .rgb_out (dout), //输出数据 .dout_vld (dout_vld) //数据有效);endmodule
灰度转换
对于彩色图像转化为灰度图像,有一个著名的色彩心理学公式,即:
但是在实际应用时,由于FPGA希望避免复杂的浮点运算,所以这里我们采用整数运算。将R、G、B三个分量对应的系数放大256倍得到整数结果:
`timescale 1ns/100ps//---------------------------------------------------------------------- // MODULE DEFINTION //---------------------------------------------------------------------- module rgb2gray( //clock and reset rst, //reset clk, //clock din, din_vld, dout, dout_vld ); //interface parameter //---------------------------------------------------------------------- // PORT SIGNAL DEFINATION //---------------------------------------------------------------------- //clock and reset input rst; //reset input clk; //clock input[23:0]din;input din_vld;output [7:0] dout;output dout_vld; //---------------------------------------------------------------------- // PORT SIGNAL TYPE //---------------------------------------------------------------------- //clock and reset wire rst; //reset wire clk; //clock wire[23:0] din;wire din_vld;reg [7:0] dout;reg dout_vld; //---------------------------------------------------------------------- // INTERNAL SIGNAL DEFINATION //---------------------------------------------------------------------- //RGB reg[7:0]data_r;reg [7:0] data_g;reg [7:0] data_b;//加权值后RGBreg [17:0] pixel_r ;reg [17:0] pixel_g ;reg [17:0] pixel_b ;//vldreg [1:0] vld;//always@(posedge clk,posedge rst)begin if(rst==1'b1) begin data_r<=8'b0; data_g<=8'b0; data_b<=8'b0; end else begin data_r<=din[23:16]; data_g<=din[15:8]; data_b<=din[7:0]; endend//vld 寄存always@(posedge clk,posedge rst)begin if(rst==1'b1) begin vld<=2'b0; end else begin vld<={vld[0],din_vld}; endend //RGB权值always@(posedge clk,posedge rst)begin if(rst==1'b1) begin pixel_r<=17'b0; pixel_g<=17'b0; pixel_b<=17'b0; end else if(vld[0]==1'b1) begin pixel_r<=data_r*77; pixel_g<=data_g*150; pixel_b<=data_b*29; endend//always@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout<=19'b0; end else if(vld[1]==1'b1) begin dout<=(pixel_r+pixel_g+pixel_b)>>8; endend //vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout_vld<=1'b0; end else begin dout_vld<=vld[1]; endend endmodule
效果展示
高斯滤波
在灰度转换过程中,可能会因为取整操作引入噪声,所以接下来使用高斯滤波算法来去除灰度转化过程中引入的噪声。在本次使用实际处理过程中发现使用了高斯滤波最后效果并不理想,最后是去掉了这个模块,有兴趣的童鞋可以研究。
高斯滤波的具体操作是使用一个NN卷积模板对整幅图像扫描,用模板确定的邻域内的像素加权平均值代替模板中心像素点的值。本文使用的33卷积模板如下:
其中,I(x,y)表示原图像中坐标为(x,y)的像素值;G(x,y)表示高斯滤波之后的值。这里为了计算方便,选取的模板H的权重系数都是2的系数。模板中心的权值最大,这样有利于克服边界效应,避免经过高斯滤波之后图像模糊。
`timescale 1ns/100ps//---------------------------------------------------------------------- // MODULE DEFINTION //---------------------------------------------------------------------- module gauss( //clock and reset rst, //reset clk, //clock din, din_vld, dout, dout_vld ); //interface parameter //---------------------------------------------------------------------- // PORT SIGNAL DEFINATION //---------------------------------------------------------------------- //clock and reset input rst; //reset input clk; //clock input [7:0] din;input din_vld;output [7:0] dout;output dout_vld; //---------------------------------------------------------------------- // PORT SIGNAL TYPE //---------------------------------------------------------------------- //clock and reset wire rst; //reset wire clk; //clock wire [7:0] din;wire din_vld;reg [7:0] dout;reg dout_vld; //---------------------------------------------------------------------- // INTERNAL SIGNAL DEFINATION //---------------------------------------------------------------------- wire[7:0]row1_data;wire [7:0] row2_data;wire [7:0] row3_data; wire [7:0] row1_data_r;wire [7:0] row2_data_r;//有效值reg [2:0] vld;//3*3reg [7:0] row1_1;reg [7:0] row1_2;reg [7:0] row1_3;reg [7:0] row2_1;reg [7:0] row2_2;reg [7:0] row2_3;reg [7:0] row3_1;reg [7:0] row3_2;reg [7:0] row3_3;//sumreg [9:0] sum_1;reg [9:0] sum_2;reg [9:0] sum_3;reg [11:0] sum; //缓存三行 第一级流水always@(posedge clk,posedge rst)begin if(rst==1'b1) begin row1_1<=7'b0;row1_2<=7'b0;row1_3<=7'b0; row2_1<=7'b0;row2_2<=7'b0;row2_3<=7'b0; row3_1<=7'b0;row3_2<=7'b0;row3_3<=7'b0; end else if(vld[0]==1'b1) begin row1_1<=row1_data;row1_2<=row1_1;row1_3<=row1_2; row2_1<=row2_data;row2_2<=row2_1;row2_3<=row2_2; row3_1<=row3_data;row3_2<=row3_1;row3_3<=row3_2; endend//第二级流水always@(posedge clk,posedge rst)begin if(rst==1'b1)begin sum_1 <= 10'b0; sum_2 <= 10'b0; sum_3 <= 10'b0; end else if(vld[1]==1'b1)begin sum_1 <= {2'd0,row1_1} + {1'd0,row1_2,1'd0} + {2'd0,row1_3}; sum_2 <= {1'b0,row2_1,1'b0} + {row2_2,2'd0} + {1'd0,row2_3,1'b0}; sum_3 <= {2'd0,row3_1} + {1'd0,row3_2,1'd0} + {2'd0,row3_3}; end end//第三级流水sumalways@(posedge clk,posedge rst)begin if(rst==1'b1)begin sum<=12'b0; end else if(vld[2]==1'b1)begin sum<=sum_1+sum_2+sum_3; end end//vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin vld<=3'b0; end else begin vld<={vld[1:0],din_vld}; endend//dout_vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout_vld<=1'b0; end else begin dout_vld<=vld[2]; endend//doutalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout<=8'b0; end else begin dout<=sum[4+:8];//从第4个开始,位宽为8 endend assign row1_data=row1_data_r;assign row2_data=row1_data_r;assign row3_data=din;c_shift_ram_1 u_shift_ram_0 ( .D(din), // input wire [0 : 0] D .CLK(clk), // input wire CLK .SCLR(rst), // input wire SCLR .Q(row2_data_r) // output wire [0 : 0] Q); c_shift_ram_1 u_shift_ram_1 ( .D(row2_data_r), // input wire [0 : 0] D .CLK(clk), // input wire CLK .SCLR(rst), // input wire SCLR .Q(row1_data_r) // output wire [0 : 0] Q); endmodule
效果展示:(不理想,所以最后没有用这个模块)
二值化
了解更多二值化操作可参考图像二值化算法
首先使用的是全局二值化,给定一个全局阈值
`timescale 1ns/100ps//---------------------------------------------------------------------- // MODULE DEFINTION //---------------------------------------------------------------------- module gray2bin( //clock and reset rst, //reset clk, //clock din, din_vld, dout, dout_vld ); //interface parameter //---------------------------------------------------------------------- // PORT SIGNAL DEFINATION //---------------------------------------------------------------------- //clock and reset input rst; //reset input clk; //clock input[7:0] din;input din_vld;output dout;output dout_vld; //---------------------------------------------------------------------- // PORT SIGNAL TYPE //---------------------------------------------------------------------- //clock and reset wire rst; //reset wire clk; //clock wire [7:0] din;wire din_vld;reg dout;reg dout_vld; //---------------------------------------------------------------------- // INTERNAL SIGNAL DEFINATION //---------------------------------------------------------------------- //阈值比较always@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout_vld<=1'b0; dout<=1'b0; end else begin dout_vld<=din_vld; dout<=(din>100); endend endmodule
`timescale 1ns/100ps//---------------------------------------------------------------------- // MODULE DEFINTION //---------------------------------------------------------------------- module gray2local_bin( //clock and reset rst, //reset clk, //clock din, din_vld, dout, dout_vld ); //interface parameter //---------------------------------------------------------------------- // PORT SIGNAL DEFINATION //---------------------------------------------------------------------- //clock and reset input rst; //reset input clk; //clock input[7:0] din;input din_vld;output dout;output dout_vld; //---------------------------------------------------------------------- // PORT SIGNAL TYPE //---------------------------------------------------------------------- //clock and reset wire rst; //reset wire clk; //clock wire [7:0] din;wire din_vld;reg dout;reg dout_vld; //---------------------------------------------------------------------- // INTERNAL SIGNAL DEFINATION //---------------------------------------------------------------------- wire [7:0] row1_data;wire [7:0] row2_data;wire [7:0] row3_data; wire [7:0] row1_data_r;wire [7:0] row2_data_r;//有效值reg [3:0] vld;//3*3reg [7:0] row1_1;reg [7:0] row1_2;reg [7:0] row1_3;reg [7:0] row2_1;reg [7:0] row2_2;reg [7:0] row2_3;reg [7:0] row3_1;reg [7:0] row3_2;reg [7:0] row3_3;//sumreg [12:0] sum;//阈值reg [7:0] thresh; //中心值延迟reg [7:0] value_delay1;reg [7:0] value_delay2;//3*3滑动窗口parameter NUM = 5'd9;//缓存三行 第一级流水always@(posedge clk,posedge rst)begin if(rst==1'b1) begin row1_1<=1'b0;row1_2<=1'b0;row1_3<=1'b0; row2_1<=1'b0;row2_2<=1'b0;row2_3<=1'b0; row3_1<=1'b0;row3_2<=1'b0;row3_3<=1'b0; end else if(vld[0]==1'b1) begin row1_1<=row1_data;row1_2<=row1_1;row1_3<=row1_2; row2_1<=row2_data;row2_2<=row2_1;row2_3<=row2_2; row3_1<=row3_data;row3_2<=row3_1;row3_3<=row3_2; endend//第二级流水sum always@(posedge clk,posedge rst)begin if(rst==1'b1)begin sum<= 13'b0; end else if(vld[1]==1'b1)begin sum<=row1_1+row1_2+row1_3+row2_1+row2_2+row2_3+row3_1+row3_2+row3_3; end end//第三级流水均值+kalways@(posedge clk,posedge rst)begin if(rst==1'b1)begin thresh <= 8'b0; end else if(vld[2]==1'b1)begin thresh <= (sum+{sum,3'b0})>>7;//取近似 sum*0.9/NUM~sum*9/96 >>6 >>5 NUM==9 //thresh <= sum>>3; endend//vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin vld<=4'b0; end else begin vld<={vld[2:0],din_vld}; endend//dout_vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout_vld<=1'b0; end else begin dout_vld<=vld[3]; endend//输出阈值大于thresh输出1always@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout<=1'b0; end else if(thresh<value_delay2) begin dout<=1'b1; end else begin dout<=1'b0; endend//当前中心值always@(posedge clk,posedge rst)begin if(rst==1'b1) begin value_delay1<=8'b0; value_delay2<=8'b0; end else begin value_delay1<=row2_2; value_delay2<=value_delay1; endend assign row1_data=row1_data_r;assign row2_data=row2_data_r;assign row3_data=din;c_shift_ram_1 u_shift_ram_0 ( .D(din), // input wire [0 : 0] D .CLK(clk), // input wire CLK .SCLR(rst), // input wire SCLR .Q(row2_data_r) // output wire [0 : 0] Q); c_shift_ram_1 u_shift_ram_1 ( .D(row2_data_r), // input wire [0 : 0] D .CLK(clk), // input wire CLK .SCLR(rst), // input wire SCLR .Q(row1_data_r) // output wire [0 : 0] Q); endmodule
效果展示:
如果换张图片带有阴影:
会是怎样呢?
边缘检测(sobel)
Sobel算子主要用于检测图像边缘,在物体的边缘通常都有像素的变化,反映了物体与背景之间的差异,或者两个物体之间的差异。它是一个离散差分算子,用来计算像素点上下、左右领域内像素点的加权差,根据在边缘处达到极值来检测边缘。另外,Sobel算子对噪声也有一定的平滑作用,检测出精确的边缘信息,但是边缘定位精度不高。
Sobel算子在水平方向和垂直方向各采用一个模板,检测各方向上的边缘,其优点是计算简单,速度快;但是对于纹理较为复杂的图像,检测效果不理想。水平方向模板Sx和垂直方向模板Sy如下:
图像中每个像素点对应的梯度值按照以下公式计算:
图像中每个像素点对应的梯度值按照以下公式计算:
`timescale 1ns/100ps//---------------------------------------------------------------------- // MODULE DEFINTION //---------------------------------------------------------------------- module sobel( //clock and reset rst, //reset clk, //clock din,//输入二值化图像 din_vld, dout, dout_vld ); //interface parameter //---------------------------------------------------------------------- // PORT SIGNAL DEFINATION //---------------------------------------------------------------------- //clock and reset input rst; //reset input clk; //clock input din;input din_vld;output dout;output dout_vld; //---------------------------------------------------------------------- // PORT SIGNAL TYPE //---------------------------------------------------------------------- //clock and reset wire rst; //reset wire clk; //clock wire din;wire din_vld;reg dout;reg dout_vld; //---------------------------------------------------------------------- // INTERNAL SIGNAL DEFINATION //---------------------------------------------------------------------- wire row1_data;wire row2_data;wire row3_data; wire row1_data_r;wire row2_data_r;//有效值reg [3:0] vld;//3*3reg row1_1;reg row1_2;reg row1_3;reg row2_1;reg row2_2;reg row2_3;reg row3_1;reg row3_2;reg row3_3;//x, y sumreg [2:0] x0_sum;reg [2:0] x2_sum;reg [2:0] y0_sum;reg [2:0] y2_sum;//x,y absreg [2:0] x_abs;reg [2:0] y_abs;//阈值reg [2:0] g; //缓存三行 第一级流水always@(posedge clk,posedge rst)begin if(rst==1'b1) begin row1_1<=1'b0;row1_2<=1'b0;row1_3<=1'b0; row2_1<=1'b0;row2_2<=1'b0;row2_3<=1'b0; row3_1<=1'b0;row3_2<=1'b0;row3_3<=1'b0; end else if(vld[0]==1'b1) begin row1_1<=row1_data;row1_2<=row1_1;row1_3<=row1_2; row2_1<=row2_data;row2_2<=row2_1;row2_3<=row2_2; row3_1<=row3_data;row3_2<=row3_1;row3_3<=row3_2; endend//第二级流水always@(posedge clk,posedge rst)begin if(rst==1'b1)begin x0_sum <= 3'b0; x2_sum <= 3'b0; y0_sum <= 3'b0; y2_sum <= 3'b0; end else if(vld[1]==1'b1)begin x0_sum <= {2'd0,row1_1} + {1'd0,row1_2,1'd0} + {2'd0,row1_3}; x2_sum <= {2'd0,row3_1} + {1'd0,row3_2,1'd0} + {2'd0,row3_3}; y0_sum <= {2'd0,row1_1} + {1'd0,row2_1,1'd0} + {2'd0,row3_1}; y2_sum <= {2'd0,row1_3} + {1'd0,row2_3,1'd0} + {2'd0,row3_3}; end end//第三级流水absalways@(posedge clk,posedge rst)begin if(rst==1'b1)begin x_abs <= 3'b0; y_abs <= 3'b0; end else if(vld[2]==1'b1)begin x_abs <= (x0_sum >= x2_sum)?(x0_sum-x2_sum):(x2_sum-x0_sum); y_abs <= (y0_sum >= y2_sum)?(y0_sum-y2_sum):(y2_sum-y0_sum); end end//第四级流水计算梯度always@(posedge clk,posedge rst)begin if(rst==1'b1)begin g <= 3'b0; end else if(vld[3]==1'b1)begin g <= x_abs + y_abs;//绝对值之和 近似 平方和开根号 endend//vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin vld<=4'b0; end else begin vld<={vld[2:0],din_vld}; endend//dout_vldalways@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout_vld<=1'b0; end else begin dout_vld<=vld[3]; endend//输出阈值大于3输出1always@(posedge clk,posedge rst)begin if(rst==1'b1) begin dout<=1'b0; end else if(g>2) begin dout<=1'b1; end else begin dout<=1'b0; endend assign row1_data=row1_data_r;assign row2_data=row2_data_r;assign row3_data=din;c_shift_ram_0 u_shift_ram_0 ( .D(din), // input wire [0 : 0] D .CLK(clk), // input wire CLK .SCLR(rst), // input wire SCLR .Q(row2_data_r) // output wire [0 : 0] Q); c_shift_ram_0 u_shift_ram_1 ( .D(row2_data_r), // input wire [0 : 0] D .CLK(clk), // input wire CLK .SCLR(rst), // input wire SCLR .Q(row1_data_r) // output wire [0 : 0] Q); endmodule
效果展示
顶层文件
`timescale 1ns/100ps //---------------------------------------------------------------------- // MODULE DEFINTION //---------------------------------------------------------------------- module top( //clock and reset rst, //reset clk, //clock rgb_din, // din_vld, rgb_out, // dout_vld ); //interface parameter //---------------------------------------------------------------------- // PORT SIGNAL DEFINATION //---------------------------------------------------------------------- //clock and reset input rst; //reset input clk; //clock input [23:0] rgb_din;input din_vld;output [23:0] rgb_out; output dout_vld; //---------------------------------------------------------------------- // PORT SIGNAL TYPE //---------------------------------------------------------------------- //clock and reset wire rst; //reset wire clk; //clock wire [23:0] rgb_din;wire din_vld;reg [23:0] rgb_out;reg dout_vld; //---------------------------------------------------------------------- // INTERNAL SIGNAL DEFINATION //---------------------------------------------------------------------- //灰度wire[7:0]dout_gray;wire gray_vld;//高斯滤波wire [7:0] dout_gauss;wire gauss_vld;//二值化wire dout_bin;wire bin_vld;//sobelwire dout_sobel;wire sobel_vld;//灰度转化rgb2gray u_rgb2gray( //clock and reset .rst (rst ), //reset .clk(clk), //clock .din(rgb_din ), .din_vld (din_vld ), .dout(dout_gray ), .dout_vld (gray_vld ) ); //对灰度图进行高斯滤波//gauss u_gauss//(// //clock and reset// .rst (rst ), //reset// .clk (clk ), //clock//// .din (dout_gray ),// .din_vld (gray_vld ),//// .dout (dout_gauss),// .dout_vld (gauss_vld )//); //灰度转全局二值化gray2bin u_gray2bin( //clock and reset .rst(rst), //reset .clk(clk), //clock .din(dout_gray ), .din_vld(gray_vld), .dout(dout_bin), .dout_vld (bin_vld ) ); //局部二值化//gray2local_bin u_gray2local_bin//( // //clock and reset // .rst(rst), //reset // .clk(clk), //clock // // .din(dout_gray ),// .din_vld(gray_vld),//// .dout(dout_bin),// .dout_vld (bin_vld ) // //); //sobelsobel u_sobel( //clock and reset .rst(rst), //reset .clk(clk), //clock .din(dout_bin ),//输入二值化图像 .din_vld(bin_vld), .dout(dout_sobel ), .dout_vld (sobel_vld ) ); always@(posedge clk,posedge rst)begin if(rst==1'b1) begin rgb_out<=24'b0; dout_vld<=1'b0; end else begin rgb_out<={24{dout_sobel}}; dout_vld<=sobel_vld;// rgb_out<={3{dout_gauss}};// dout_vld<=gauss_vld; endend endmodule
改进中- - - -
来源地址:https://blog.csdn.net/Mouer__/article/details/127295239
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341