本帖最后由 UT发布 于 2025-4-2 15:45 编辑
软件版本:Anlogic -TD5.6.1-64bit 操作系统:WIN10 64bit 硬件平台:适用安路(Anlogic)FPGA
1概述本文简述了图像直方图均衡变换的算法,讲解如何进行Verilog的算法实现,并进行上板实验。 2算法原理简介在统计学中,直方图(Histogram)是一种对数据分布情况的图形表示,是一种二维统计图表,它的两个坐标分别是统计样本和该样本对应的某个属性的度量。直方图是品质管理七大工具之一。把直方图上每个属性的计数除以所有属性的计数之和,就得到了归一化直方图。之所以叫“归一”,是因为归一化直方图的所有属性的计数之和为1,也就是说,每个属性对应计数都是0到1之间的一个数(百分比)。 这种方法通常用来增加许多图像的全局对比度,尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。 这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景噪声的对比度并且降低有用信号的对比度。 按照上面的公式进行图像的均衡化后,得到如下图所示: 3算法仿真
3.1Matlab算法仿真
3.1.1Matlab算法代码分析
- clear;clear all;clc;
-
- image_in = imread('lena_1280x720.jpg');
- % [row,col,n] = size(image_in);
-
- image_gray = rgb2gray(image_in);
- image_equal = histeq(image_gray,255);
-
- histogram = imhist(image_gray);
- [row,col] = size(image_gray);
- sum = 0;
- for i = 1:256
- sum = sum + histogram(i);
- LUT(i) = round(255 * 1.0 * sum / (row * col));
- end
- for i = 1:row
- for j = 1:col
- image(i,j) = LUT(image_gray(i,j)+1);
- end
- end
- image_equal_1 = uint8(image);
-
- figure
- subplot(321);
- imshow(image_gray), title('the original image');
- subplot(322);
- imhist(image_gray), title('the hist image 0 ');
- subplot(323);
- imshow(image_equal), title('the image equalization ');
- subplot(324);
- imhist(image_equal), title('the hist image 1');
- subplot(325);
- imshow(image_equal_1);title('the equalization image');
- subplot(326);
- imhist(image_equal_1);title('the hist image 2');
复制代码
3.1.2Matlab实验结果
3.2Verilog算法仿真
3.2.1Modelsim仿真
3.2.1.1仿真执行在件夹Algorithm_simulation下进行算法的仿真,分为sim,src和tb三个子文件夹。在sim文件夹下有win系统的快捷执行文件sim.bat,可以一键进行仿真,src文件下放的是Verilog的核心图像算法及其顶层与输入图像激励,tb文件下放的是测试激励文件及输出图像的保存。 双击执行sim文件夹下sim.bat,自动打开Modelsim仿真,自动添加仿真波形,执行完成后自动保存图像,仿真波形如图所示: 3.2.1.2仿真关键部分代码解析- #
- # Create work library
- #
- vlib work
- #
- # Compile sources
- #
- vlog "../src/*.v"
- vlog "../tb/*.v"
- #
- # Call vsim to invoke simulator
- #
- vsim -voptargs=+acc work.top_tb
- #
- # Add waves
- #
- do wave.do
- #
- # Run simulation
- #
- run -all
- #
- # End
复制代码
- module image_rom
- (
- input clka,
- input [18:0] addra,
- input ena,
- output [23:0] douta
- );
- reg [23:0] DATA;
- assign douta = DATA;
- always@(*)
- begin
- case(addra)
- 0 : DATA<=24'hE49061;
- 1 : DATA<=24'hDF8B5C;
- 2 : DATA<=24'hD98556;
- 3 : DATA<=24'hD58153;
- 4 : DATA<=24'hCD794D;
- 5 : DATA<=24'hC46F48;
- 6 : DATA<=24'hBF6A45;
- 7 : DATA<=24'hBF6946;
- 8 : DATA<=24'hB65F41;
- 9 : DATA<=24'hB76042;
- 10 : DATA<=24'hB86143;
- 11 : DATA<=24'hB96244;
- 12 : DATA<=24'hBB6446;
- 13 : DATA<=24'hBD6648;
- 14 : DATA<=24'hBE6749;
- 15 : DATA<=24'hBE6749;
- 16 : DATA<=24'hBF6445;
- 17 : DATA<=24'hC06546;
- 18 : DATA<=24'hC16647;
- 19 : DATA<=24'hC16647;
- 20 : DATA<=24'hC16647;
- 21 : DATA<=24'hC26748;
- 。。。。。。中间省略
- 307194 : DATA<=24'h513534;
- 307195 : DATA<=24'h4F3332;
- 307196 : DATA<=24'h492F30;
- 307197 : DATA<=24'h442C2C;
- 307198 : DATA<=24'h432D2F;
- 307199 : DATA<=24'h483234;
- default: DATA<= 0;
- endcase
- end
- endmodule
复制代码
- reg clk;
- reg rst_n;
- wire hsync;
- wire vsync;
- wire[23:0] data;
- wire de;
- integer image_txt;
- reg [31:0] pixel_cnt;
- reg vsync_d;
- reg frame_de;
-
- top u_top
- (
- .i_clk (clk ),
- .i_rst_n (rst_n ),
- .o_gray_hsyn (hsync ),
- .o_gray_vsyn (vsync ),
- .o_gray_data (data ),
- .o_gray_de (de )
- );
-
- always #(5) clk = ~clk;
-
- initial
- begin
- clk = 1;
- rst_n = 0;
- #100
- rst_n = 1;
-
- end
- always@(posedge clk )
- begin
- vsync_d <= vsync;
- end
-
- always @(posedge clk)
- begin
- if(!rst_n)
- frame_de <= 1'b0;
- else if(~vsync && vsync_d)
- frame_de <= 1'b1;
- end
-
- initial
- begin
- image_txt = $fopen("../matlab_src/image_720_1280_3_out.txt");
- end
-
- always@(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- begin
- pixel_cnt <= 0;
- end
- else if(de && frame_de)
- begin
- pixel_cnt = pixel_cnt + 1;
- $fwrite(image_txt,"%h\n",data);
- end
- end
-
- always@(posedge clk )
- begin
- if(pixel_cnt == 720*1280)
- begin
- $display("*******************************************************************************");
- $display("*** Success:image_720_1280_3_out.txt is output complete! %t", $realtime, "ps***");
- $display("*******************************************************************************");
- $fclose(image_txt);
- $stop;
- end
- end
复制代码
3.2.2Modelsim实验结果- clear;clear all;clc;
- % row = 480;
- % col = 640;
- row = 720;
- col = 1280;
- n = 3;
-
- image_sim_pass = uint8(zeros(row,col,n));
- % fid = fopen('image_480_640_3_out.txt','r');
- fid = fopen('image_720_1280_3_out.txt','r');
- for x = 1:row
- for y = 1:col
- RGB = fscanf(fid,'%s',1);
- image_sim_pass(x,y,1) = uint8(hex2dec(RGB(1:2)));
- image_sim_pass(x,y,2) = uint8(hex2dec(RGB(3:4)));
- image_sim_pass(x,y,3) = uint8(hex2dec(RGB(5:6)));
- end
- end
- fclose(fid);
-
- image_1 = imread('lena_640x480.jpg');
-
- subplot(121);
- imshow(image_1), title('The original image');
-
- subplot(122);
- imshow(image_sim_pass),title('After processing images');
-
- imwrite(image_sim_pass,'lena_720x128_sim_pass.jpg');
复制代码
4工程实现4.1Verilog代码分析参数定义 - parameter H_ACTIVE = 160; //显示区域宽度
- parameter V_ACTIVE = 120; //显示区域高度
- parameter BEGIN_X = 432; //显示起始坐标,实际区域1024
- parameter BEGIN_Y = 240; //显示起始坐标,实际区域600
复制代码
变量声明 - reg [7:0] rd_addr_cnt;
- reg [31:0] sum_hist;
- reg sum_hist_flag;
- reg sum_hist_flag_d1;
- reg sum_hist_flag_d2;
- reg sum_hist_flag_d3;
- reg sum_hist_flag_d4;
- reg [49:0] image_equal_0;
- reg [7:0] image_equal;
- reg [7:0] wr_addr_cnt;
- wire[31:0] wr_data_hist;
- wire[7:0] rd_addr_hist;
- wire[31:0] rd_data_hist;
-
- reg [10:0] h_cnt;
- reg [10:0] v_cnt;
- reg [10:0] h_cnt_d;
- reg [10:0] v_cnt_d;
- reg [18:0] rd_addr;
- reg [4:0] out_en;
-
- reg hsyn_d0;
- reg vsyn_d0;
- reg en_pos_d0;
-
- reg hsyn_d1;
- reg vsyn_d1;
- reg en_pos_d1;
-
- reg hsyn_d2;
- reg vsyn_d2;
- reg en_pos_d2;
-
- reg hsyn_d3;
- reg vsyn_d3;
- reg en_pos_d3;
-
- reg hsyn_d4;
- reg vsyn_d4;
- reg en_pos_d4;
-
- wire [7:0] r_d0;
- wire [7:0] g_d0;
- wire [7:0] b_d0;
- reg [15:0] r_d1;
- reg [15:0] g_d1;
- reg [15:0] b_d1;
- reg [15:0] gray_d0;
- reg [31:0] hist_ram[255:0];
-
- wire de_d0;//显示区域使能信号
-
- wire [7:0] hist_out;
- reg hist_rst;
复制代码
输出赋值 - assign o_r = out_en[4] ? hist_out : 8'hff;
- assign o_g = out_en[4] ? hist_out : 8'hff;
- assign o_b = out_en[4] ? hist_out : 8'hff;
-
- assign o_hs = hsyn_d4;
- assign o_vs = vsyn_d4;
- assign o_en = en_pos_d4;
复制代码
显示区域使能 - assign de_d0 = i_en_pos && (i_x_pos >= BEGIN_X) && (i_x_pos < BEGIN_X + H_ACTIVE) &&
- (i_y_pos >= BEGIN_Y) && (i_y_pos < BEGIN_Y + V_ACTIVE) ? 1'b1:1'b0;
-
- //同步输出使能信号
- always@(posedge i_clk )
- begin
- hsyn_d0 <= i_hsyn;
- vsyn_d0 <= i_vsyn;
- en_pos_d0 <= i_en_pos;
-
- hsyn_d1 <= hsyn_d0;
- vsyn_d1 <= vsyn_d0;
- en_pos_d1 <= en_pos_d0;
-
- hsyn_d2 <= hsyn_d1;
- vsyn_d2 <= vsyn_d1;
- en_pos_d2 <= en_pos_d1;
-
- hsyn_d3 <= hsyn_d2;
- vsyn_d3 <= vsyn_d2;
- en_pos_d3 <= en_pos_d2;
-
- hsyn_d4 <= hsyn_d3;
- vsyn_d4 <= vsyn_d3;
- en_pos_d4 <= en_pos_d3;
- end
-
- //同步输出使能信号
- always@(posedge i_clk )
- begin
- out_en <= {out_en[3:0],de_d0};
- end
-
- //显示区域行计数
- always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- h_cnt <= 11'd0;
- end
- else if(de_d0)
- begin
- if(h_cnt == H_ACTIVE - 1'b1)
- h_cnt <= 11'd0;
- else
- h_cnt <= h_cnt + 11'd1;
- end
- end
-
- //显示区域场计数
- always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- v_cnt <= 11'd0;
- end
- else if(h_cnt == H_ACTIVE - 1'b1)
- begin
- if(v_cnt == V_ACTIVE - 1'b1)
- v_cnt <= 11'd0;
- else
- v_cnt <= v_cnt + 11'd1;
- end
- end
-
- //显示区域存储ROM
- lena_160x120 u_image_buffer(
- .doa ({r_d0,g_d0,b_d0}),
- .addra (rd_addr ),
- .clka (i_clk ),
- .rsta (1'b0 )
- );
复制代码
ROM的地址刷新 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- rd_addr <= 'd0;
- end
- else if(de_d0)
- begin
- rd_addr <= v_cnt * H_ACTIVE + h_cnt;
- end
- end
复制代码
执行乘法运算 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- r_d1 <= 16'd0;
- g_d1 <= 16'd0;
- b_d1 <= 16'd0;
- end
- else
- begin
- r_d1 <= 77 * r_d0;
- g_d1 <= 150 * g_d0;
- b_d1 <= 29 * b_d0;
- end
- end
复制代码
执行加法运算 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- gray_d0 <= 16'd0;
- end
- else
- begin
- gray_d0 <= r_d1 + g_d1 + b_d1;
- end
- end
复制代码
直方图求和计算 复制代码
求和标志信号产生 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- sum_hist_flag <= 'd0;
- end
- else if(vsyn_d0 && (~i_vsyn))
- begin
- sum_hist_flag <= 1;
- end
- else if(rd_addr_cnt == 255)
- begin
- sum_hist_flag <= 0;
- end
- else
- begin
- sum_hist_flag <= sum_hist_flag;
- end
- end
复制代码
求和运算 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- rd_addr_cnt <= 'd0;
- end
- else if(sum_hist_flag)
- begin
- rd_addr_cnt <= rd_addr_cnt + 1;
- end
- else
- begin
- rd_addr_cnt <= 'd0;
- end
- end
复制代码
标志信号同步 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- sum_hist_flag_d1 <= 0;
- sum_hist_flag_d2 <= 0;
- sum_hist_flag_d3 <= 0;
- sum_hist_flag_d4 <= 0;
- end
- else
- begin
- sum_hist_flag_d1 <= sum_hist_flag;
- sum_hist_flag_d2 <= sum_hist_flag_d1;
- sum_hist_flag_d3 <= sum_hist_flag_d2;
- sum_hist_flag_d4 <= sum_hist_flag_d3;
- end
- end
复制代码
求累和运算 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- sum_hist <= 'd0;
- end
- else if(sum_hist_flag_d1)
- begin
- sum_hist <= sum_hist + hist_ram[rd_addr_cnt];
- end
- else
- begin
- sum_hist <= 'd0;
- end
- end
复制代码
简便运算过程 - // 640*480
- //sum_hist*255/640*480 ~= sum_hist*272/640*512
- //sum_hist*255/640*480 ~= sum_hist*27/64*512
- //sum_hist*27/64*512 = sum_hist*(16+8+2+1)/(2^6*2^9)
- //sum_hist*(16+8+2+1)/(2^6*2^9) =sum_hist*(2^4+2^3+2^1+2^0)/(2^15)
- // 160*120
- //sum_hist*255/160*120 ~= sum_hist*218/4*4096
- //sum_hist*255/160*120 ~= sum_hist*54/4096
- //sum_hist*27/64*512 = sum_hist*(32+16+4+2)/(2^12)
- //=sum_hist*(2^5+2^4+2^2+2^1)/(2^12)
-
- always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- image_equal_0 <= 'd0;
- end
- else if(sum_hist_flag_d2)
- begin
- // 160*120
- image_equal_0 <= {13'd0,sum_hist,5'd0} +
- {14'd0,sum_hist,4'd0} +
- {16'd0,sum_hist,2'd0} +
- {17'd0,sum_hist,1'd0} ;
- end
- else
- begin
- image_equal_0 <= 'd0;
- end
- end
-
- always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- image_equal <= 'd0;
- end
- else if(sum_hist_flag_d3)
- begin
- image_equal <= image_equal_0[19:12];
- end
- else
- begin
- image_equal <= 'd0;
- end
- end
-
- always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n || hist_rst)
- begin
- wr_addr_cnt <= 'd0;
- end
- else if(sum_hist_flag_d3)
- begin
- wr_addr_cnt <= wr_addr_cnt + 1;
- end
- else
- begin
- wr_addr_cnt <= 'd0;
- end
- end
复制代码
复位信号产生 - always@(posedge i_clk or negedge i_rst_n)
- begin
- if(!i_rst_n)
- begin
- hist_rst <= 'd0;
- end
- else if(!sum_hist_flag_d3 && sum_hist_flag_d4)
- begin
- hist_rst <= 1;
- end
- else
- begin
- hist_rst <= hist_rst;
- end
- end
复制代码
双口ram缓存数据 - ram_dp#(
- .DATA_WIDTH (8 ),
- .ADDRESS_WIDTH (8 )
- ) u_image_equal_ram_dp(
- .i_clk (i_clk ),
- .i_data_a (image_equal ),
- .i_addr_a (wr_addr_cnt ),
- .i_wea (sum_hist_flag_d3 ),
- .o_qout_a ( ),
- .i_data_b ( ),
- .i_addr_b (gray_d0[15:8] ),
- .i_web ( ),
- .o_qout_b (hist_out )
- );
复制代码
5上板实验
|