FPGA-图像处理-仿真

文章目录

任务
读入一张bmp图片,对图片进行灰度处理,二值化以及边缘检测(sobel算子),将处理后的数据写入bmp显示,全部过程以仿真形式。我这用的vivado。

说明

在图像处理领域,要实现Sobel或者均值滤波等算法,则需要按照3*3矩阵的格式提取数据,

FPGA-图像处理-仿真
需要用到移位寄存器SHIFT RAM IP核。
Altera厂家的EDA环境下移位寄存器IP核可以实现多行的缓存输出,但是Xilinx下的Vivado环境,仅仅支持缓存输出一行数据,不过其实也无关紧要,我们这里可以使用两个SHIFT RAM IP核和正在输入的一行数据三者共同组成3行数据。

; 图片读取和写入

可参考FPGA-仿真读写bmp图片
使用图片(128*128):

FPGA-图像处理-仿真

本次实验的图片读取写入tb


timescale 1ns / 1ns

module 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;

integer bmp_file_id;
integer bmp_dout_id;
integer dout_txt_id;

integer h;

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;
initial
begin
    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+");

    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);

    for(index = start_index; index < bmp_size-2; index = index + 3)begin
        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);
end

initial
begin
     #(3*CYCLE);

    bmp_dout_id = $fopen("D:\\python\\pic\\output_file.bmp","wb");

    for(i = 0; i < start_index; i = i + 1)begin
        $fwrite(bmp_dout_id, "%c", rd_data[i]);
    end

    j=start_index;
    while(j<bmp_size)
    begin
        if(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;
        end
        else
        begin
            j=j;
        end
        #CYCLE;
    end
    $fclose(bmp_dout_id);
end

top  u_top
(

   .rst       (rst),
   .clk       (clk),

   .rgb_din   (wr_data),
   .din_vld   (din_vld),

   .rgb_out   (dout),
   .dout_vld  (dout_vld)
);

endmodule
</code></pre>
<p>灰度转换</p>
<p>对于彩色图像转化为灰度图像,有一个著名的色彩心理学公式,即:
<img alt="FPGA-图像处理-仿真" src="https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/9b96cee6d3f04927b617027b3f25f2ef.png" />
但是在实际应用时,由于FPGA希望避免复杂的浮点运算,所以这里我们采用整数运算。将R、G、B三个分量对应的系数放大256倍得到整数结果:</p>
<p><img alt="FPGA-图像处理-仿真" src="https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/610df17e93db49b08fa84592cebf4b71.png" /></p>
<pre><code class="language-c">
 1ns/100ps

module rgb2gray
(

    rst,
    clk,

    din,
    din_vld,

    dout,
    dout_vld

);

input                       rst;
input                       clk;

input   [23:0]              din;
input                       din_vld;

output  [7:0]               dout;
output                      dout_vld;

wire                        rst;
wire                        clk;

wire    [23:0]              din;
wire                        din_vld;

reg     [7:0]               dout;
reg                         dout_vld;

reg     [7:0]               data_r;
reg     [7:0]               data_g;
reg     [7:0]               data_b;

reg     [17:0]              pixel_r ;
reg     [17:0]              pixel_g ;
reg     [17:0]              pixel_b ;

reg     [1:0]               vld;

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        data_r8'b0;
        data_g8'b0;
        data_b8'b0;
    end
    else
    begin
        data_rdin[23:16];
        data_gdin[15:8];
        data_bdin[7:0];
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        vld2'b0;
    end
    else
    begin
        vld{vld[0],din_vld};
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        pixel_r17'b0;
        pixel_g17'b0;
        pixel_b17'b0;
    end
    else if(vld[0]==1'b1)
    begin
        pixel_rdata_r*77;
        pixel_gdata_g*150;
        pixel_bdata_b*29;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout19'b0;
    end
    else if(vld[1]==1'b1)
    begin
        dout(pixel_r+pixel_g+pixel_b)>>8;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout_vld1'b0;
    end
    else
    begin
        dout_vldvld[1];
    end

end

endmodule

效果展示

FPGA-图像处理-仿真

高斯滤波

在灰度转换过程中,可能会因为取整操作引入噪声,所以接下来使用高斯滤波算法来去除灰度转化过程中引入的噪声。在本次使用实际处理过程中发现使用了高斯滤波最后效果并不理想,最后是去掉了这个模块,有兴趣的童鞋可以研究。

高斯滤波的具体操作是使用一个N _N卷积模板对整幅图像扫描,用模板确定的邻域内的像素加权平均值代替模板中心像素点的值。本文使用的3_3卷积模板如下:

FPGA-图像处理-仿真
FPGA-图像处理-仿真
其中,I(x,y)表示原图像中坐标为(x,y)的像素值;G(x,y)表示高斯滤波之后的值。这里为了计算方便,选取的模板H的权重系数都是2的系数。模板中心的权值最大,这样有利于克服边界效应,避免经过高斯滤波之后图像模糊。

timescale 1ns/100ps

module gauss
(

    rst,
    clk,

    din,
    din_vld,

    dout,
    dout_vld
);

input                       rst;
input                       clk;

input       [7:0]           din;
input                       din_vld;

output      [7:0]           dout;
output                      dout_vld;

wire                        rst;
wire                        clk;

wire        [7:0]           din;
wire                        din_vld;

reg         [7:0]           dout;
reg                         dout_vld;

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;

reg         [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;

reg         [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_17'b0;row1_2b0;row1_37'b0;
        row2_17'b0;row2_2b0;row2_37'b0;
        row3_17'b0;row3_2b0;row3_37'b0;
    end
    else if(vld[0]==1'b1)
    begin
        row1_1row1_data;row1_2row1_1;row1_3row1_2;
        row2_1row2_data;row2_2row2_1;row2_3row2_2;
        row3_1row3_data;row3_2row3_1;row3_3row3_2;
    end
end

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

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)begin
        sum12'b0;
    end
    else if(vld[2]==1'b1)begin
        sumsum_1+sum_2+sum_3;
    end

end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        vld3'b0;
    end
    else
    begin
       vld{vld[1:0],din_vld};
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout_vld1'b0;
    end
    else
    begin
        dout_vldvld[2];
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout8'b0;
    end
    else
    begin
        doutsum[4+:8];
    end
end

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),
  .CLK(clk),
  .SCLR(rst),
  .Q(row2_data_r)
);

c_shift_ram_1 u_shift_ram_1 (
  .D(row2_data_r),
  .CLK(clk),
  .SCLR(rst),
  .Q(row1_data_r)
);

endmodule

</code></pre>
<p>效果展示:(不理想,所以最后没有用这个模块)
<img alt="" src="https://img-blog.csdnimg.cn/e2db272aef1342afbccb9555c0d2e882.bmp#pic_center" /></p>
<p>二值化</p>
<p>了解更多二值化操作可参考<a href="https://blog.csdn.net/Mouer__/article/details/127211920">图像二值化算法</a></p>
<p>首先使用的是全局二值化,给定一个全局阈值</p>
<pre><code class="language-c">
 1ns/100ps

module gray2bin
(

    rst,
    clk,

    din,
    din_vld,

    dout,
    dout_vld

);

input                       rst;
input                       clk;

input       [7:0]           din;
input                       din_vld;

output                      dout;
output                      dout_vld;

wire                        rst;
wire                        clk;

wire        [7:0]           din;
wire                        din_vld;

reg                         dout;
reg                         dout_vld;

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout_vld1'b0;
        dout1'b0;
    end
    else
    begin
        dout_vlddin_vld;
        dout(din>100);
    end
end

endmodule


timescale 1ns/100ps

module gray2local_bin
(

    rst,
    clk,

    din,
    din_vld,

    dout,
    dout_vld

);

input                       rst;
input                       clk;

input       [7:0]           din;
input                       din_vld;

output                      dout;
output                      dout_vld;

wire                        rst;
wire                        clk;

wire        [7:0]           din;
wire                        din_vld;

reg                         dout;
reg                         dout_vld;

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;

reg       [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;

reg       [12:0]             sum;

reg       [7:0]             thresh;

reg       [7:0]             value_delay1;
reg       [7:0]             value_delay2;

parameter     NUM = 5'd9;

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        row1_11'b0;row1_2b0;row1_31'b0;
        row2_11'b0;row2_2b0;row2_31'b0;
        row3_11'b0;row3_2b0;row3_31'b0;
    end
    else if(vld[0]==1'b1)
    begin
        row1_1row1_data;row1_2row1_1;row1_3row1_2;
        row2_1row2_data;row2_2row2_1;row2_3row2_2;
        row3_1row3_data;row3_2row3_1;row3_3row3_2;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)begin
        sum 13'b0;
    end
    else if(vld[1]==1'b1)begin
        sumrow1_1+row1_2+row1_3+row2_1+row2_2+row2_3+row3_1+row3_2+row3_3;
    end

end

always@(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;

    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        vld4'b0;
    end
    else
    begin
       vld{vld[2:0],din_vld};
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout_vld1'b0;
    end
    else
    begin
        dout_vldvld[3];
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout1'b0;
    end
    else if(thresh<value_delay2)
    begin
        dout1'b1;
    end
    else
    begin
        dout1'b0;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        value_delay18'b0;
        value_delay28'b0;
    end
    else
    begin
        value_delay1row2_2;
        value_delay2value_delay1;
    end
end
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),
  .CLK(clk),
  .SCLR(rst),
  .Q(row2_data_r)
);

c_shift_ram_1 u_shift_ram_1 (
  .D(row2_data_r),
  .CLK(clk),
  .SCLR(rst),
  .Q(row1_data_r)
);
endmodule

</code></pre>
<p>效果展示:
<img alt="" src="https://img-blog.csdnimg.cn/54a0af0849a94986a822c774e64443ec.bmp#pic_center" />
如果换张图片带有阴影:
<img alt="" src="https://img-blog.csdnimg.cn/eb4d60ec950843cdb09d8339a820b4ac.bmp#pic_center" />
会是怎样呢?</p>
<p>边缘检测(sobel)</p>
<p>Sobel算子主要用于检测图像边缘,在物体的边缘通常都有像素的变化,反映了物体与背景之间的差异,或者两个物体之间的差异。它是一个离散差分算子,用来计算像素点上下、左右领域内像素点的加权差,根据在边缘处达到极值来检测边缘。另外,Sobel算子对噪声也有一定的平滑作用,检测出精确的边缘信息,但是边缘定位精度不高。
Sobel算子在水平方向和垂直方向各采用一个模板,检测各方向上的边缘,其优点是计算简单,速度快;但是对于纹理较为复杂的图像,检测效果不理想。水平方向模板Sx和垂直方向模板Sy如下:
<img alt="FPGA-图像处理-仿真" src="https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/682634a475b84f35b6ceea653806ca4f.png" />
<img alt="FPGA-图像处理-仿真" src="https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/68e282eecaf34190a5426dd3abfc44cb.png" /></p>
<p>图像中每个像素点对应的梯度值按照以下公式计算:
<img alt="FPGA-图像处理-仿真" src="https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/d16849cd042d4f5e8c15d3debc5d74b2.png" />
图像中每个像素点对应的梯度值按照以下公式计算:
<img alt="FPGA-图像处理-仿真" src="https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/b5e14efe1c68495cb45b803acb871887.png" /></p>
<pre><code class="language-c">
 1ns/100ps

module sobel
(

    rst,
    clk,

    din,
    din_vld,

    dout,
    dout_vld

);

input                       rst;
input                       clk;

input                       din;
input                       din_vld;

output                      dout;
output                      dout_vld;

wire                        rst;
wire                        clk;

wire                        din;
wire                        din_vld;

reg                         dout;
reg                         dout_vld;

wire                        row1_data;
wire                        row2_data;
wire                        row3_data;

wire                        row1_data_r;
wire                        row2_data_r;

reg       [3:0]             vld;

reg                         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;

reg      [2:0]              x0_sum;
reg      [2:0]              x2_sum;
reg      [2:0]              y0_sum;
reg      [2:0]              y2_sum;

reg      [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_11'b0;row1_2b0;row1_31'b0;
        row2_11'b0;row2_2b0;row2_31'b0;
        row3_11'b0;row3_2b0;row3_31'b0;
    end
    else if(vld[0]==1'b1)
    begin
        row1_1row1_data;row1_2row1_1;row1_3row1_2;
        row2_1row2_data;row2_2row2_1;row2_3row2_2;
        row3_1row3_data;row3_2row3_1;row3_3row3_2;
    end
end

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

always@(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;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        vld4'b0;
    end
    else
    begin
       vld{vld[2:0],din_vld};
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout_vld1'b0;
    end
    else
    begin
        dout_vldvld[3];
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        dout1'b0;
    end
    else if(g>2)
    begin
        dout1'b1;
    end
    else
    begin
        dout1'b0;
    end
end

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),
  .CLK(clk),
  .SCLR(rst),
  .Q(row2_data_r)
);

c_shift_ram_0 u_shift_ram_1 (
  .D(row2_data_r),
  .CLK(clk),
  .SCLR(rst),
  .Q(row1_data_r)
);
endmodule

效果展示

FPGA-图像处理-仿真

顶层文件


`timescale 1ns/100ps

module top
(

    rst,
    clk,

    rgb_din,
    din_vld,

    rgb_out,
    dout_vld

);

input                       rst;
input                       clk;

input     [23:0]            rgb_din;
input                       din_vld;

output    [23:0]            rgb_out;
output                      dout_vld;

wire                        rst;
wire                        clk;

wire      [23:0]            rgb_din;
wire                        din_vld;

reg       [23:0]            rgb_out;
reg                         dout_vld;

wire    [7:0]   dout_gray;
wire            gray_vld;

wire    [7:0]   dout_gauss;
wire            gauss_vld;

wire            dout_bin;
wire            bin_vld;

wire            dout_sobel;
wire            sobel_vld;

rgb2gray u_rgb2gray
(

    .rst            (rst        ),
    .clk            (clk        ),

    .din            (rgb_din    ),
    .din_vld        (din_vld    ),

    .dout           (dout_gray  ),
    .dout_vld       (gray_vld   )

);

gray2bin u_gray2bin
(

    .rst            (rst        ),
    .clk            (clk        ),

    .din            (dout_gray  ),
    .din_vld        (gray_vld   ),

    .dout           (dout_bin   ),
    .dout_vld       (bin_vld    )

);

sobel u_sobel
(

    .rst            (rst        ),
    .clk            (clk        ),

    .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_out24'b0;
        dout_vld1'b0;
    end
    else
    begin
        rgb_out{24{dout_sobel}};
        dout_vldsobel_vld;

    end
end

endmodule

改进中- – – –

Original: https://blog.csdn.net/Mouer__/article/details/127295239
Author: 伊木子曦
Title: FPGA-图像处理-仿真

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/632078/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球