一、前言

    本篇主要针对牟新刚编著《基于FPGA的数字图像处理及应用》第六章第五节中直方图统计相关类容进行总结,包括代码实现及

基于Modelsim的仿真。书读百遍,其意自现。 2020-03-09 22:16:07

二、基于FPGA的直方图算法统计原理

  设计难点:

    (1)统计工作至少要等到当前图像“流过”之后才能完成。此限制决定了我们不可能对统计工作进行流水统计和输出。

    (2)必须对前期的统计结果进行缓存。

    (3)在下一次统计前需要将缓存结果清零。

  在直方图统计中,我们一般选择片内双口RAM作为缓存存储器。对于8位的深度图来说,统计结果的数据量并不大,因此选择片内

RAM。此外,一方面统计模块需要与其他时序进行配合,因此需提供双边读写接口;另一方面,统计过程中需要地址信息,因此选择

RAM形式的存储器。接下来的工作就是确定双口RAM的参数,主要包括数据位宽及地址位宽。假定输入图像宽度为IW,高度为IH,数据

位宽度为DW。那么待统计的像素总数为PixelTotal = IW * IH,像素灰度值的理论最大值为PixelMax=2DW-1。

  双口RAM的统计地址输入端为输入像素值,很明显,这个数据范围为0~2DW-1,因此RAM的地址位宽最少为DW,即 AWDPRAM ≥ DW。

  双口RAM的数据输出端为输入统计值,很明显,这个数据范围为0~PixelTotal,因此RAM的地址位宽最少为log2(PixelTotal)。即DWDPRAM ≥ log2(PixelTotal)。

  例如要对图像分辨率为640 x 512位宽为8位的图像进行直方图统计,则有

                AWDPRAM ≥ 8

           DWDPRAM ≥ log2(PixelTotal) = log2(640x512) ≈19。

  通常情况下会将两个参数取为2的整次幂,即AWDPRAM = 8 DWDPRAM =32。

  直方图统计步骤如下:

  (1)将当前统计值读出,加1后重新写入RAM。

  (2)重复以上步骤,直至当前图像统计完毕。

  (3)在下一副图像到来之前将结果读出。

  (4)读出之后对RAM内容进行清零。

  因此如下图所示,要完成直方图统计,需要至少设计三个电路:统计电路,读出电路,读出电路和清零电路。

  

  1.统计电路

  在实际的图像中,连续的像素点灰度值为相同值的情况非常常见,如果每来一个像素都对双口RAM进行一次寻址和写操作,显然降低了统计效率而提高了功耗。书中给出了一种优化的统计方法:采用一个相同灰度值计数器进行优化,

其中:

  (1)DPRAM:存放统计结果。分为A口和B口,A口负责统计结果写入,不输出。B口负责统计结果读出和清零,无输入。

  (2)CNT: 相同像素计数器。负责对连续相同灰度值的像素进行计数,复位值为1。

  (3)ADD(+):统计值加法器。对当前统计值和新的统计值进行加法运算,重新写入RAM。

  (4)B_ADDR MUX: B口地址mux,很明显,B口在统计阶段需要完成读出前一个统计值和清零的分时操作。因此需要一个mux对读出地址和清零地址进行选通。

  (5)reg:将输入数据打两拍以确保读出的是当前的统计值。

  统计原理如下:

  当前灰度值的统计值由B口读出,与相同灰度值计数器进行相加后重新写入RAM。CNT会不断检测当前像素和前一个像素是否一致,若不一致,则重置为1,实现统计值加1目的;若一致,则计数器加1,直到不一致之后将一致的总数

写入RAM,并在每一行图像的最后一个像素统一执行一次写入操作(没明白啥意思),这样大大减少读写RAM操作。

  下面几个关键信号的设计电路来说明统计电路的工作原理。首先将输入信号din,输入有效dvalid打两拍,分别为din_r,din_r2及dvalid_r,dvalid_r2。

  (1)inc_en

   此信号负责递增计数器的递增使能。当前待统计数据din_r2有效,且与前一个已经统计完成的数据din_r相同时,将递增计数器加1。否则计数器会复位到1。

  (2)rst_cnt

   此信号为递增计数器的复位信号。除了当前待统计灰度值与上一个统计过的灰度值不同的情况下会复位计数器,第一个有效数据到来时也会复位递增计数器,为新的一轮统计工作做准备。

  (3)we_a

   此信号为DPRAM写入信号,也是分为两种情况:若当前待统计灰度值与之前待统计值不同。则直接写入RAM。否则,就一直累加到数据无效时统一写入RAM。

  (4)count_en

   此信号为统计使能,很明显,在统计阶段此信号需要一直保持有效,统计完成后(也即当前图像遍历完毕),在读出和清零阶段,需要将此信号失能,这是由于B口在此阶段需要读出和清零地址。

     此信号的产生可以通过对图像进行行计数来实现,当到达一副图像的高度时,失能信号。新的行同步到来时使能信号。

  2.读出电路设计

    首先书中统计读出方法是顺序读出,即灰度值为0的统计值首先读出,其次是灰度值为1的统计值,最后是灰度值为255的统计值输出。

    读出和清零操作并不一定是在统计完成之后立即进行的,可以根据需要由外部时序控制,输入读出请求。当然在统计阶段,模块是不允许读出的,此时读出电路处于复位状态。在读出阶段,需设计

读出像素计数器对读出时序进行控制。

    只有当计数完成,并且外部时序申请读出时,输出地址才会进行递增。否则会被钳位到0。当一次读出完成之后此地址发生器复位,也就是count_en会重新使能,直到下一次统计完成。

    3.清零电路设计

   书中给出反相清零的方法,即在读出时钟的下一个时钟进行清零。因此每个像素的统计数据输出和清零操作均需占用1个时钟,奇数时钟输出,偶数时钟清零

三、代码实现

  代码的部分参照书中的代码实现,并与前面灰度图像生成代码进行整合编译仿真。唯一需要特别注意的是在实例化DPRAM IP核时,输出端口切记不要缓存(添加寄存器输出)。相应整理后的代码如下:

`timescale 1ps/1ps

//==============================================================================//
//FileName: histogram_2d.v
//Date: 2020-02-29
//==============================================================================// module histogram_2d(
rst_n,
clk,
din_valid, //输入有效
din, //输入待统计的数据
dout, //统计输出
vsync, //输入场同步
dout_valid, //输出有效
int_flag, //中断输出
rdyOutput, //数据读出请求
dout_clk //数据输出时钟
); //模块入口参数
parameter DW = ; //数据位宽
parameter IH = ; //图像高度
parameter IW = ; //图像宽度
parameter TW = ; //直方图统计数据位宽 localparam TOTAL_CNT = IW * IH; //像素总数
localparam HALF_WIDTH = (TW >> ); //将32位的数据位宽拆分为高低16位 //输入输出声明
input rst_n;
input clk;
input din_valid;
input [DW-:] din;
input rdyOutput; //output wire [HALF_WIDTH:0] dout; output wire [TW-:] dout; input vsync;
output reg dout_valid;
output reg int_flag;
output dout_clk; //变量声明
reg vsync_r;
reg dvalid_r;
reg dvalid_r2;
reg [DW-:] din_r;
reg [DW-:] din_r2; wire hsync_fall;
wire hsync_rise; reg [:] hsync_count;
reg count_en;
wire [DW-:] mux_addr_b;
wire [DW-:] mux_addr_b2; wire [TW-:] q_a;
wire [TW-:] q_b;
reg [TW-:] counter; wire [TW-:] count_value;
wire rst_cnt; //统计计数器复位信号
wire inc_en; //递增使能信号 //DPRAM 信号
wire we_a;
wire we_b;
wire we_b_l;
reg we_b_h; wire [DW-:] addr_a;
//中断寄存器
reg int_r;
wire [DW-:] clr_addr; //清零地址
reg [DW-:] clr_addr_r;
reg [DW:] out_pixel; //输出计数 reg count_all; //统计完成信号
//reg count_en_r;
reg count_en_r; reg [TW-:] hist_cnt; //直方图统计累加寄存器
wire rstOutput; //读出电路复位信号 wire [TW-:] dataTmp2;
wire clr_flag; //全局清零 //将输入数据打两拍
always@(posedge clk or negedge rst_n)begin
if(((~(rst_n))) == 'b1)
begin
vsync_r <= # 'b0;
dvalid_r <= # 'b0;
dvalid_r2 <= # 'b0;
din_r <= # {DW{'b0}};
din_r2 <= # {DW{'b0}};
end
else
begin
vsync_r <= # vsync;
dvalid_r <= # din_valid;
dvalid_r2 <= # dvalid_r;
din_r <= # din;
din_r2 <= # din_r;
end
end //输入行同步计数,确定统计的开始和结束时刻
assign # hsync_fall = dvalid_r & (~(din_valid));
assign # hsync_rise = (~(dvalid_r)) & din_valid; always@(posedge clk or negedge rst_n)begin
if(((~(rst_n))) == 'b1)
hsync_count <= # {{'b0}};
else
begin
if(vsync_r == 'b1)
hsync_count <= # {{'b0}};
else if(hsync_fall == 'b1)
hsync_count <= hsync_count + 'b1;
end
end //一帧图像结束后停止统计 下一帧图像到来时开始计数
always@(posedge clk or negedge rst_n)begin
if(((~(rst_n))) == 'b1)
count_en <= # 'b0;
else
begin
if(hsync_count >= IH)
count_en <= # 'b0;
else if(hsync_rise == 'b1)
count_en <= # 'b1;
else
count_en <= # count_en;
end
end assign mux_addr_b = ((count_en == 'b1)) ? din_r : clr_addr;
assign mux_addr_b2 = ((count_en == 'b1)) ? din_r : clr_addr_r; //统计递增计数器
always@(posedge clk)begin
if(rst_cnt == 'b1)
counter <= # {{TW-{'b0}},1'b1}; //复位值为1
else if(inc_en == 'b1)
counter <= # counter + {{TW-{'b0}},1'b1};
else
counter <= # counter;
end assign # rst_cnt = ((din_r != din_r2) | ((dvalid_r2 == 'b1) & (dvalid_r == 1'b0))) ? 'b1 : 1'b0;
assign # inc_en = (((din_r == din_r2) & (dvalid_r2 == 'b1))) ? 1'b1 : 'b0;
assign # we_a = ((((din_r != din_r2) & (dvalid_r2 == 'b1)) |((dvalid_r2 == 1'b1) & (dvalid_r == 'b0)))) ? 1'b1 : 'b0;
assign # count_value = ((count_en == 'b1)) ? counter+q_b : {TW{1'b0}};
assign # addr_a = din_r2; //直方图存储器 分高16位和低16位分别存储
hist_buffer dpram_bin_l(
.address_a(addr_a), //输入地址为像素灰度值
.address_b(mux_addr_b), //读出和清零地址
.clock(clk), //同步时钟
.data_a(count_value[HALF_WIDTH-:]), //当前计数值
.data_b({HALF_WIDTH{'b0}}), //清零数据
.wren_a(we_a),
.wren_b(we_b_l),
.q_a(q_a[HALF_WIDTH-:]),
.q_b(q_b[HALF_WIDTH-:])
); hist_buffer dpram_bin_h(
.address_a(addr_a),
.address_b(mux_addr_b2),
.clock(clk),
.data_a(count_value[TW-:HALF_WIDTH]),
.data_b({HALF_WIDTH{'b0}}),
.wren_a(we_a),
.wren_b(we_b_h),
.q_a(q_a[TW-:HALF_WIDTH]),
.q_b(q_b[TW-:HALF_WIDTH])
); always@(posedge clk or negedge rst_n)begin
if(((~(rst_n))) == 'b1)
count_en_r <= # 'b0;
else
count_en_r <= # count_en;
end //读出电路逻辑,计数时不能输出,读出请求时才输出
assign rstOutput = count_en_r | (~(rdyOutput)); //输出像素计数
always@(posedge clk)begin
if(rstOutput == 'b1)
out_pixel <= {DW+{'b0}};
else begin
if((~count_all) == 'b1)
begin
if(out_pixel == ((( ** (DW + )) - )))
out_pixel <= # {DW+{'b0}}; //输出完毕
else
out_pixel <= # out_pixel + 'b1;
end
end
end //统计结束信号
always@(posedge clk)begin
//count_all_r <= (~rstOutput);
we_b_h <= we_b_l;
clr_addr_r <= clr_addr;
if(out_pixel == ((( ** (DW + )) - )))
count_all <= # 'b1;
else if(count_en == 'b1)
count_all <= # 'b0;
end //全局清零信号
assign clr_flag = vsync; //中断输出 信号读出操作完成
always@(posedge clk or negedge rst_n)begin
if((~(rst_n)) == 'b1)
begin
int_flag <= 'b1;
int_r <= 'b1;
end
else
begin
int_flag <= # int_r;
if(clr_flag == 'b1)
int_r <= # 'b1;
else if(out_pixel >= ((( ** (DW + )) - )))
int_r <= # 'b0;
end
end assign we_b_l = (((out_pixel[] == 'b1) & (count_all == 1'b0))) ? 'b1 : 1'b0; //清零地址,与读出地址反相
assign clr_addr = out_pixel[DW:]; wire dout_valid_temp; wire [HALF_WIDTH-:] dout_temp; always@(posedge clk or negedge rst_n)begin
if((~(rst_n)) == 'b1)
begin
//dout <= {HALF_WIDTH{1'b0}};
dout_valid <= 'b0;
end
else
begin
//dout <= #1 dout_temp;
dout_valid <= # dout_valid_temp;
end
end assign dout_temp = (we_b_l == 'b1) ? q_b[HALF_WIDTH-1:0] : q_b[TW-1:HALF_WIDTH]; assign dout_valid_temp = we_b_h | we_b_l; //输出使能 assign dout_clk = (dout_valid) ? we_b_h : 'b0; assign dout = q_b; endmodule

 顶层代码设计如下:

 `timescale 1ps/1ps

 //===========================================================================================//
//FileName: histogram.v
//Date: 2020-03-02
//===========================================================================================// module histogram(
RSTn, //全局复位
CLOCK, //系统时钟 IMG_CLK, //像素时钟
IMG_DVD, //像素值
IMG_DVSYN, //输入场信号
IMG_DHSYN, //输入数据有效信号
HIST_DAT, //输出直方图统计数据
HIST_VALID, //输出直方图统计有效
HIST_RDY, //数据读出请求
HIST_INT, //数据中断输出
HIST_CLK //数据输出时钟 ); /*image parameter*/
parameter iw = ; //image width
parameter ih = ; //image height
parameter trig_value = ; //
parameter tw = ; //直方图统计数据位宽 localparam half_width = (tw >> ); //将32位的数据位宽拆分为高低16位 /*data width*/
parameter dvd_dw = ; //image source data width
parameter dvd_chn = ; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width
parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width //Port Declared
input RSTn;
input CLOCK;
input IMG_CLK;
input [dvd_dw-:] IMG_DVD;
input IMG_DVSYN;
input IMG_DHSYN;
output [tw-:] HIST_DAT;
output HIST_VALID;
input HIST_RDY;
output HIST_INT;
output HIST_CLK; //Variable Declared
wire GRAY_CLK;
wire GRAY_VSYNC;
wire GRAY_DVALID;
wire [dvd_dw-:] Y_DAT;
wire [dvd_dw-:] Cb_DAT;
wire [dvd_dw-:] Cr_DAT; wire [local_dw-:] RGB_DAT;
wire RGB_DVALID;
wire RGB_VSYNC; video_cap video_cap_inst(
.reset_l(RSTn), //异步复位信号
.DVD(IMG_DVD), //输入视频流
.DVSYN(IMG_DVSYN), //输入场同步信号
.DHSYN(IMG_DHSYN), //输入行同步
.DVCLK(IMG_CLK), //输入DV时钟
.cap_dat(RGB_DAT), //输出RGB通道像素流,24位
.cap_dvalid(RGB_DVALID), //输出数据有效
.cap_vsync(RGB_VSYNC), //输出场同步
.cap_clk(CLOCK), //本地逻辑时钟
.img_en(),
.cmd_rdy(), //命令行准备好,代表可以读取
.cmd_rdat(), //命令行数据输出
.cmd_rdreq() //命令行读取请求
); defparam video_cap_inst.DW_DVD = dvd_dw;
defparam video_cap_inst.DW_LOCAL = local_dw;
defparam video_cap_inst.DW_CMD = cmd_dw;
defparam video_cap_inst.DVD_CHN = dvd_chn;
defparam video_cap_inst.TRIG_VALUE = trig_value;
defparam video_cap_inst.IW = iw;
defparam video_cap_inst.IH = ih; RGB2YCrCb RGB2YCrCb_Inst(
.RESET(RSTn), //异步复位信号 .RGB_CLK(CLOCK), //输入像素时钟
.RGB_VSYNC(RGB_VSYNC), //输入场同步信号
.RGB_DVALID(RGB_DVALID), //输入数据有信号
.RGB_DAT(RGB_DAT), //输入RGB通道像素流,24位 .YCbCr_CLK(GRAY_CLK), //输出像素时钟
.YCbCr_VSYNC(GRAY_VSYNC), //输出场同步信号
.YCbCr_DVALID(GRAY_DVALID), //输出数据有效信号
.Y_DAT(Y_DAT), //输出Y分量
.Cb_DAT(Cb_DAT), //输出Cb分量
.Cr_DAT(Cr_DAT) //输出Cr分量
); defparam RGB2YCrCb_Inst.RGB_DW = local_dw;
defparam RGB2YCrCb_Inst.YCbCr_DW = dvd_dw; histogram_2d histogram_2d_inst(
.rst_n(RSTn),
.clk(GRAY_CLK),
.din_valid(GRAY_DVALID), //输入有效
.din(Y_DAT), //输入待统计的数据
.dout(HIST_DAT), //统计输出
.vsync(GRAY_VSYNC), //输入场同步
.dout_valid(HIST_VALID), //输出有效
.int_flag(HIST_INT), //中断输出
.rdyOutput(HIST_RDY), //数据读出请求
.dout_clk(HIST_CLK)
); defparam histogram_2d_inst.DW = dvd_dw;
defparam histogram_2d_inst.IH = ih;
defparam histogram_2d_inst.IW = iw;
defparam histogram_2d_inst.TW = tw; endmodule

  用于测试仿真的文件如下:

 `timescale 1ps/1ps

 module histogram_tb;

     /*image para*/
parameter iw = ; //image width
parameter ih = ; //image height
parameter trig_value = ; // /*video parameter*/
parameter h_total = ;
parameter v_total = ;
parameter sync_b = ;
parameter sync_e = ;
parameter vld_b = ; parameter clk_freq = ; /*data width*/
parameter dvd_dw = ; //image source data width
parameter dvd_chn = ; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width
parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width parameter hist_dw = >> ; /*test module enable*/
parameter cap_en = ; /*signal group*/
reg pixel_clk = 'b0;
reg reset_l;
reg [:] src_sel; /*input dv group*/
wire dv_clk;
wire dvsyn;
wire dhsyn;
wire [dvd_dw-:] dvd; wire [:] HIST_DAT;
wire HIST_VALID;
wire HIST_INT;
wire HIST_CLK; /*dvd source data generated for simulation*/
image_src //#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)
u1(
.clk(pixel_clk),
.reset_l(reset_l),
.src_sel(src_sel),
.test_data(dvd),
.test_dvalid(dhsyn),
.test_vsync(dvsyn),
.clk_out(dv_clk)
); defparam u1.iw = iw*dvd_chn;
defparam u1.ih = ih + ;
defparam u1.dw = dvd_dw;
defparam u1.h_total = h_total;
defparam u1.v_total = v_total;
defparam u1.sync_b = sync_b;
defparam u1.sync_e = sync_e;
defparam u1.vld_b = vld_b; /*local clk: also clk of all local modules*/
reg cap_clk = 'b0; /*video capture: capture image src and transfer it into local timing*/
histogram histogram_inst(
.RSTn(reset_l), //全局复位
.CLOCK(cap_clk), //系统时钟 .IMG_CLK(pixel_clk), //像素时钟
.IMG_DVD(dvd), //像素值
.IMG_DVSYN(dvsyn), //输入场信号
.IMG_DHSYN(dhsyn), //输入数据有效信号
.HIST_DAT(HIST_DAT), //输出直方图统计数据
.HIST_VALID(HIST_VALID), //输出直方图统计有效
.HIST_RDY('b1), //数据读出请求
.HIST_INT(HIST_INT), //数据中断输出
.HIST_CLK(HIST_CLK) //数据输出时钟
); initial
begin: init
reset_l <= 'b1;
src_sel <= 'b0000;
#(); //reset the system
reset_l <= 'b0;
#();
reset_l <= 'b1;
end //dv_clk generate
always@(reset_l or pixel_clk)begin
if((~(reset_l)) == 'b1)
pixel_clk <= 'b0;
else
begin
if(clk_freq == ) //48MHz
pixel_clk <= # (~(pixel_clk)); else if(clk_freq == 51.84) //51.84MHz
pixel_clk <= # (~(pixel_clk)); else if(clk_freq == ) //72MHz
pixel_clk <= # (~(pixel_clk));
end
end //cap_clk generate: 25MHz
always@(reset_l or cap_clk)begin
if((~(reset_l)) == 'b1)
cap_clk <= 'b0;
else
cap_clk <= # (~(cap_clk));
end wire clk;
assign clk = ~cap_clk; generate
if(cap_en != ) begin :capture_operation
integer fid, cnt_cap=; always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
cnt_cap = ;
else
begin
if(HIST_VALID == 'b1 && HIST_CLK == 1'b0)
begin
fid = $fopen("E:/Modelsim/histogram_2d/sim/hist_result.txt","r+");
$fseek(fid,cnt_cap,);
$fdisplay(fid,"%8x",HIST_DAT);
$fclose(fid);
cnt_cap<=cnt_cap+;
end
end
end
end
endgenerate endmodule

 用于modelsim仿真的.do文件如下:

 

 #切换至工程目录
cd E:/Modelsim/histogram_2d/sim #打开工程
project open E:/Modelsim/histogram_2d/sim/histogram_2d #添加指定设计文件
project addfile E:/Modelsim/histogram_2d/sim/histogram_tb.v
project addfile E:/Modelsim/histogram_2d/src/cross_clock_fifo.v
project addfile E:/Modelsim/histogram_2d/src/hist_buffer.v
project addfile E:/Modelsim/histogram_2d/src/histogram.v
project addfile E:/Modelsim/histogram_2d/src/histogram_2d.v
project addfile E:/Modelsim/histogram_2d/src/image_src.v
project addfile E:/Modelsim/histogram_2d/src/line_buffer_new.v
project addfile E:/Modelsim/histogram_2d/src/rgb2gray.v
project addfile E:/Modelsim/histogram_2d/src/RGB2YCbCr.v
project addfile E:/Modelsim/histogram_2d/src/video_cap.v #编译工程内的所有文件
project compileall #仿真work库下面的histogram_tb实例,同时调用altera_lib库,不进行任何优化
vsim -t 1ps -novopt -L altera_lib work.histogram_tb #添加输入灰度图像信号
add wave -divider GRAYImg add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/clk add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/vsync add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din_valid add wave -radix hex -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din #输入信号缓存
add wave -divider Data_Cache add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/vsync_r add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dvalid_r add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dvalid_r2 add wave -radix hex -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din_r add wave -radix hex -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din_r2 #统计开始、结束;统计使能
add wave -divider Statistics_Signal add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_rise add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_fall add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_count add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_fall #添加双口RAM读写控制时序
add wave -divider HIST_BUFFER_WR add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/rst_cnt add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/inc_en add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/we_a add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/count_value add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/addr_a #Buff数据读出控制时序
add wave -divider HIST_BUFFER_RD add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/rstOutput add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/out_pixel add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/count_all add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/clr_flag add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/int_flag add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/int_r add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/we_b_l add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/we_b_h add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/mux_addr_b add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/mux_addr_b2 #添加统计输出信号
add wave -divider Statistics_Out add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dout_valid add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dout_clk add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dout #复位
restart #取消警告
set StdArithNoWarnings #开始
run .1ms

  用于数据处理的Maltab文件如下:

 clc;
clear; fid1 = fopen('hist_result.txt','r'); %FPGA转换灰度图像
data1 = fscanf(fid1,'%8x');
fclose(fid1); RGBImg = imread('lena_512x512.jpg'); %rgb原始图像
RGBImg = imresize(RGBImg,[ ]); GRAYImg = rgb2gray(RGBImg); %Matlab变换灰度图像 fid2 = fopen('gray_image.txt','r'); %FPGA转换灰度图像
data2 = fscanf(fid2,'%2x');
data2 = uint8(data2);
gray_data = reshape(data2,,);
gray_data = gray_data';
fclose(fid2); figure();
subplot(,,);
imshow(GRAYImg);
title('Matlab变换灰度图像');
subplot(,,);
imhist(GRAYImg);
title('Matlab统计直方图');
subplot(,,);
imshow(gray_data);
title('FPGA变换灰度图像');
subplot(,,);
bar(data1);
title('FPGA统计直方图');

四、仿真结果

(1)输入信号缓存时序:

 
 (2)统计数据写入控制时序:

(3)统计数据读出控制时序:

(4)FPGA直方图统计结果与Maltab直方图统计结果对比:

 

 

    

    

  

基于Modelsim的直方图统计算法仿真的更多相关文章

  1. 基于FPGA的HDTV视频图像灰度直方图统计算法设计

    随着HDTV的普及,以LCD-TV为主的高清数字电视逐渐进入蓬勃发展时期.与传统CRT电视不同的是,这些高清数字电视需要较复杂的视频处理电路来驱动,比如:模数转换(A/D Converter).去隔行 ...

  2. 基于Modelsim的视频捕获模拟仿真

    一.前言 针对牟新刚编著的<基于FPGA的数字图像处理原理及应用>中第五章系统仿真中关于视频捕获模拟的例子进行补充和仿真验证,简言之,吊书袋子. 2020-02-27 21:09:05 二 ...

  3. 基于Modelsim的视频流仿真

    一.前言 最近在看牟新刚写的<基于FPGA的数字图像处理原理及应用>,书中关于FPGA数字图像处理的原理的原理写的非常透彻,在网上寻找了很久都没有找到完整的源代码工程,因此尝试自己做了补充 ...

  4. 基于FPGA的腐蚀膨胀算法实现

    本篇文章我要写的是基于的腐蚀膨胀算法实现,腐蚀膨胀是形态学图像处理的基础,,腐蚀在二值图像的基础上做"收缩"或"细化"操作,膨胀在二值图像的基础上做" ...

  5. 基于MeanShift的目标跟踪算法及实现

    这次将介绍基于MeanShift的目标跟踪算法,首先谈谈简介,然后给出算法实现流程,最后实现了一个单目标跟踪的MeanShift算法[matlab/c两个版本] csdn贴公式比较烦,原谅我直接截图了 ...

  6. 基于Hdl Coder实现卡尔曼滤波算法

    总所周知,FPGA极其不擅长复杂算法的运算,但是如果项目中又涉及一些高级算法的实现,在没有可封装IP核调用的形式下,我们应该如何进行程序开发呢?今夕已经是2020年,我们一味依赖于用verilog写代 ...

  7. 基于FPGA的肤色识别算法实现

    大家好,给大家介绍一下,这是基于FPGA的肤色识别算法实现. 我们今天这篇文章有两个内容一是实现基于FPGA的彩色图片转灰度实现,然后在这个基础上实现基于FPGA的肤色检测算法实现. 将彩色图像转化为 ...

  8. Python实现图像直方图均衡化算法

    title: "Python实现图像直方图均衡化算法" date: 2018-06-12T17:10:48+08:00 tags: [""] categorie ...

  9. 【图像配准】基于互信息的图像配准算法:MI、EMI、ECC算法

    简单介绍: 基于互信息的图像配准算法以其较高的配准精度和广泛的适用性而成为图像配准领域研究的热点之中的一个.而基于互信息的医学图像配准方法被觉得是最好的配准方法之中的一个.基于此.本文将介绍简单的基于 ...

随机推荐

  1. 快手为什么要全资收购Acfun?

    近日据媒体报道,快手已完成对「Acfun」以下简称A站的全资收购.未来A站仍将保持独立品牌和独立运营以及原有团队的独立发展.近年来,A站可谓命途多舛.相比隔壁B站风风光光顺利上市且成为真正的二次元大本 ...

  2. SPI以及IIC的verilog实现以及两者之间的对比

    一.SPI是一种常用的串行通信接口,与UART不同的地方在于.SPI可以同时挂多个从机,但是UART只能点对点的传输数据,此外SPI有四条线实现数据的传输,而UART采用的是2条实现串行数据的传输 1 ...

  3. 口语、听力:新概念英语2,lesson 45

    新概念英语2,lesson 45 练习听力

  4. python掉包侠与深浅拷贝

    今日所得 包 logging模块 hashlib模块 openpyxl模块 深浅拷贝 包 在学习模块的时候我们了解过模块的四种表现形式,其中的一种就是包. 什么是包? 它是一系列模块文件的结合体,表示 ...

  5. generate的使用verilog

    根据项目设计的需要,要实例化多个类似的模块,这些类似的模块包括方波波形发生器,这几个模块基本相同,除了参数传递值不同,其他他部分都是相同的 具体实现代码如下: 此外有计数模块的例化,这个模块例化多个的 ...

  6. VSAN磁盘扩容与收缩(二)

  7. 在HADOOP中使用MRUNIT进行单元测试

    在HADOOP中使用MRUNIT进行单元测试 前提 1. 了解JUnit4.x的使用. 2. 了解Mock的概念在单元测试中的应用. 3. 了解Hadoop中MapReduce的编程模型. 如果您对J ...

  8. jenkins邮件内容模板

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. [SDOI2008] 洞穴勘测 (LCT模板)

    bzoj 2049 传送门 洛谷P2147 传送门 这个大佬的LCT详解超级棒的! Link-Cut Tree的基本思路是用splay的森林维护一条条树链. splay的森林,顾名思义,就是若干spl ...

  10. TesterHome创始人思寒:如何从手工测试进阶自动化测试?十余年经验分享

      做测试十多年,有不少人问过我下面问题: 现在的手工测试真的不行了吗? 测试工程师,三年多快四年的经验,入门自动化测试需要多久? 自学自动化测试到底需要学哪些东西? 不得不说,随着行业的竞争加剧,互 ...