实现思想:图像的灰度值0-255,那么就把灰度值当成RAM的地址,这样就把统计转成了对RAM的读写。 根据网上的资料再结合自己的思想,我用了两种方法来实现了对灰度数据的统计,第一种是倍频的方法(不适用于实际),只使用了1个RAM,另一种是使用多个RAM错位实现。 方法一:读数据2CLK,写数据CLK,即读取数据的时钟频率是写入的两倍,板子上跑的时候跑不到时钟的两倍,这个方法仅仅适用在仿真,没有太大实用价值,所以稍微提下。 为了方便对每个流程的控制,使用了状态机,如图: 状态转换:空闲时接收到图像开始信号后跳转到清楚状态,该状态是将上张图片的数据清除,清除完毕后跳转到统计状态,在统计状态下对图像的灰度值进行统计,当图像结束信号来到后,就可以读取灰度统计数据,读取完成后跳转到空闲状态等待下一张图片。 图像中相邻的像素点有可能是相同的,如果采用来个像素就对该地址上的数值+1,当两个相邻像素点相同时就会导致数据丢失,因为写入数据到稳定(能够被读出来)需要1个CLK,多个灰度值相同时就会出现数据还没写进去就开始读数据,读出来的数据就是0,所以我用2倍频的时钟用于读,就能够避免冲突。 先对相邻的数据进行对比,只有当相邻数据不同时才将统计数据写入到RAM,同时计数值回到1。如图: 读取的数据需要和写入的地址、写入的统计数据一一对齐。使用的RAM是没有输出寄存器的,也就是从开始读取到输出数据需要1个CLK,中间计算需要1个CLK,那么写入地址(相对于读取数据)就需要延迟2个CLK。 方法二:使用4个 RAM,用类似乒乓操作的来实现。 状态机是一样的,但与方法一相比的状态机跳转条件有些变动:
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
STATE <= 4'b0001;
end
else begin
case (STATE)
IDEL : begin
if(clear_ram_flag) begin
STATE <= CLEAR;
end
else begin
STATE <= IDEL;
end
end
CLEAR :begin
if(clear_addr_add==255)begin
STATE <= STATISTICS;
end
else begin
STATE <= CLEAR;
end
end
STATISTICS: begin
if(avalon_streaming_source_endofpacket_wait2clk) begin
STATE <= GET_RESULT;
end
else begin
STATE <= STATISTICS;
end
end
GET_RESULT: begin
if(get_statistics_addr>257)begin
STATE <= IDEL;
end
else begin
STATE <= GET_RESULT;
end
end
default : begin
STATE <= IDEL;
end
endcase
end
end
状态跳转:空闲情况下一上电或者图片传输完成标志就跳转到清除状态,这跟方法一有些不同,主要考虑到了清除RAM中的数据只需要256个CLK,板子实际晶振是125MHz,也就是说清除RAM需要的时间只需2us左右,而使用的工业相机每张图片传输的时间间隔远远大于2us ,这样同时也解决了方法一需要使用一个RAM先缓存开始清除时错过的256个数据。如图: 清除完毕后跳转到统计状态,在统计状态下接收到图像传输开始标志位后对图像的灰度值进行统计,当图像结束信号来到后,就可以读取灰度统计数据。 统计的方法也不同,由于我用了4个RAM做类似乒乓操作,所以不用担心存在读写冲突,来一个灰度值就在对应地址的数据上+1。如图:
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b0000;
wr_addr <= 8'd0;
wr_data <= 32'd0;
end
else if(STATE==CLEAR)begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b1111;
wr_addr <= clear_addr_add;
wr_data <= 32'd0;
end
else if(STATE==STATISTICS && avalon_streaming_source_valid_wait4clk)begin
wr_addr <= avalon_streaming_source_data_wait3clk;
wr_data <= histo_data + 1'b1;
case (ram_switch_wait4clk)
3'd0 :begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b1000;
end
3'd1 :begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b0100;
end
3'd2 :begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b0010;
end
3'd3 :begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b0001;
end
default : {wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b0000;
endcase
end
else begin
{wr_en0,wr_en1,wr_en2,wr_en3} <= 4'b0000;
wr_addr <= 8'd0;
wr_data <= 32'd0;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rd_en <= 1'b0;
rd_addr <= 8'd0;
end
else if(STATE==STATISTICS && avalon_streaming_source_valid)begin
rd_en <= 1'b1;
rd_addr <= avalon_streaming_source_data;
case (ram_switch)
3'd0 : begin
histo_data <= q1;
end
3'd1 : begin
histo_data <= q2;
end
3'd2 : begin
histo_data <= q3;
end
3'd3 : begin
histo_data <= q0;
end
default : histo_data <= 32'd0;
endcase
end
else if(STATE==GET_RESULT&&get_statistics_addr<=255)begin
rd_en <= 1'b1;
rd_addr <= get_statistics_addr;
end
else begin
rd_en <= 1'b0;
rd_addr <= 8'd0;
histo_data <= 32'd0;
end
end
用一张1920*1024的图片来验证,在Matlab得到一张相机原图的灰度直方图,然后与仿真得到的结果对比,从大体上看是没什么问题的,,具体统计出来的数据有点小偏差,但是找了前面几帧没发现统计出错的问题,后面继续深究这个问题,或许又想到另一种更简单又准确的方法。
|