直方圖統計是圖像處理算法中最基本和常見的算法之一,主要原理就是將圖像中各個灰度級的像素個數進行計算並統計,這在一些對灰度特性進行統計的算法中比較常見。雖然直方圖統計在MATLAB或軟件中耗時也很少,但是現在隨着FPGA的普及,更加快速的實現一些圖像處理算法成為了主流。
FPGA實現圖像處理算法現在有幾種主流的方式:1、HDL純邏輯代碼編寫;2、基於System generator的模塊搭建;3、Xilinx公司vivado套件中的HLS軟件進行C/C++代碼的轉換。
而本文主要采用第一種方法,即采用Verilog代碼形式直接實現直方圖統計算法。
- 方法1:倍頻操作
- 方法2:相鄰數據判斷
倍頻操作
直方圖統計給人的第一反應就是按照軟件中方法,設置256個寄存器,然后對每個像素大小進行判斷后再對對應的寄存器進行+1,而在FPGA中可以充分利用內部RAM的完成這一操作。而在這邊我們選用的是偽雙端口RAM。
在偽雙端口RAM中,需要主要的是有兩個設置選項:
1、Primitives Output Register
這一設置主要是在RAM的輸出端口添加一個寄存器,對輸出的數據進行打一拍緩存操作。如果勾選上這個,在高頻時鍾情況下,可以有效的保證輸出信號滿足建立和保持時間。而RAM本身默認讀出數據延時為一拍,則RAM讀出數據的總延時為2個時鍾單位。
2、Operating Mode
主要包括了No Change , Write First和Read First,顧名思義,主要指的讀出數據的先后順序。在No Change模式下,寫操作不改變輸出端數據,在Write First模式下,如果對同一地址進行讀寫,則先寫后讀,在Read First模式下,如果對同一地址進行讀寫,則先讀后寫。
倍頻方式的代碼如下:(本例程中未勾選Primitives Output Register,且Operating Mode設置為Write First)
module his_count( input rst_n, input clk_150M, // 150M input LVAL, //data的伴隨使能信號 input [7:0]data // 輸入2K的圖像,連續數據流 ); reg LVAL_temp1,LVAL_temp2; reg [7:0]data_temp1,data_temp2; reg [20:0]addra; reg [7:0]dina; reg ena,wea; reg [20:0]addrb; reg enb; reg [8:0]count=9'd0; wire [7:0]doutb; wire clk_300M; clk_wiz_1 clk_pll_inst ( // Clock in ports .clk_in1(clk_150M), // 150M .clk_out1(clk_300M), // 300M .reset(), // input reset .locked()); // output locked //RAM的讀出延時設為1 blk_mem_gen_1 L1 ( .clka(clk_300M), .ena(ena), .wea(wea), .addra(addra), .dina(dina), .clkb(clk_300M), .rstb(), .enb(enb), .addrb(addrb), .doutb(doutb) ); always @(posedge clk_150M or negedge rst_n) begin if(!rst_n) begin {LVAL_temp2,LVAL_temp1}<=2'd0; {data_temp2,data_temp1}<=16'd0; end else begin {LVAL_temp2,LVAL_temp1}<={LVAL_temp1,LVAL}; {data_temp2,data_temp1}<={data_temp1,data}; end end // 讀模塊 always @(posedge clk_150M or negedge rst_n) begin if(!rst_n) begin enb<=0; addrb<=21'd0; end else begin enb<=LVAL; addrb<={14'd0,data}; end end //寫模塊 always @(posedge clk_300M or negedge rst_n) begin if(!rst_n) begin ena<=0; wea<=0; addra<=21'd0; dina<=8'd0; end else begin ena<=LVAL_temp2; wea<=LVAL_temp2; addra<={14'd0,data_temp2}; dina<=doutb+1; end end
相鄰數判斷
相鄰數判斷主要通過判斷前一個像素與之是否相同來改變RAM寫入的數據。因為RAM的讀出至少有一個延時,所以假如相鄰的數遇到相同的情況,前一個數寫入的數值無法被后一個數所讀出,因此會遇到漏計數的情況。如果采用相鄰數判斷的方式,則可以避免倍頻帶來的時序問題。
在本情況下,Operating Mode設置為Write First。且未勾選Primitives Output Register。
代碼如下:
module his_count( input rst_n, input clk_150M, // 150M input LVAL, //data的伴隨使能信號 input [7:0]data // 輸入2K的圖像,連續數據流 ); reg LVAL_temp2,LVAL_temp1; reg [7:0]data_temp2,data_temp1; reg [20:0]addra; reg [7:0]dina; reg ena,wea; reg [20:0]addrb; reg enb; wire [7:0]doutb; //RAM的讀出延時設為1 blk_mem_gen_1 L1 ( .clka(clk_150M), .ena(ena), .wea(wea), .addra(addra), .dina(dina), .clkb(clk_150M), .rstb(), .enb(enb), .addrb(addrb), .doutb(doutb) ); always @(posedge clk_150M or negedge rst_n) begin if(!rst_n) begin {LVAL_temp2,LVAL_temp1}<=2'd0; {data_temp2,data_temp1}<=16'd0; end else begin {LVAL_temp2,LVAL_temp1}<={LVAL_temp1,LVAL}; {data_temp2,data_temp1}<={data_temp1,data}; end end // 讀模塊 always @(posedge clk_150M or negedge rst_n) begin if(!rst_n) begin enb<=0; addrb<=21'd0; end else begin enb<=LVAL; addrb<={14'd0,data}; end end //寫模塊 always @(posedge clk_150M or negedge rst_n) begin if(!rst_n) begin ena<=0; wea<=0; addra<=21'd0; dina<=8'd0; end else if(data_temp2 == data_temp1)begin ena<=LVAL_temp2; wea<=LVAL_temp2; addra<={14'd0,data_temp2}; dina<=doutb+2; end else begin ena<=LVAL_temp2; wea<=LVAL_temp2; addra<={14'd0,data_temp2}; dina<=doutb+1; end end