FPGA實現移動目標檢測


  上一篇整理了人臉檢測,這篇講一下移動目標檢測。

  目前逐漸形成三種運動目標的檢測算法:

   1)幀間差分法是采用視頻序列中的相鄰兩幀圖像做差的方法,來檢測視頻序列中 的移動目標。但是受運動目標和背景變化的影響,檢測過程中有可能出現偽目標或者目標中 出現“空洞”,在目標運動不是太快時可以有效的檢測到目標。

   2)背景減除法首先在沒有目標的場景中獲取背景圖像,然后利用實時視頻序列和背 景圖像做差,來實現地移動目標的檢測。如何獲得背景是背景減除法的關鍵。

   3)光流法是通過給圖像中每個像素點賦予一個速度矢量的方法建立光流場,利用光 流場中矢量運動的連續性來檢測移動目標。該方法的計算量通常很大,難以實現實時性的檢測。

  其中幀差法比較簡單,可操作性較強。

 

一、幀差法原理

  幀差法是通過兩幀相鄰圖像間做差,並選取合適的閾值對圖像進行二值化,從而選取出運動的物體。設 f(x,y)為灰度差分圖像,gk(x,y)、gk-1(x,y) 為相鄰的兩幀灰度圖像,D(x,y)為偵差圖像,T為差分閾值。

1、緩存兩幀灰度圖像。

2、兩幀灰度圖像做差,將結果和設置的閾值進行比較后轉二值化輸出。

3、對二值化結果進行框選,確定移動目標,類似人臉檢測。

 

  本設計的難點是如何能緩存兩幀圖像,以 SDRAM 為例,常用的方法有兩種:掩碼法和非掩碼法,下面分別介紹一下。 

 

二、移動目標檢測——掩碼法

1、結構框圖

  如圖所示:攝像頭采集數據后,再SDRAM通道0中緩存后輸出到 VGA_driver,正常的攝像頭顯示工程到這就結束了。而為了后續處理,我將 VGA_driver 的輸出數據先不輸出到VGA引腳,而是對其進行圖像處理:先進行 RGB轉YCbCr處理,得到 8bit 的灰度數據 Y 分量,然后將 Y 分量輸入到 SDRAM的通道 1 中,利用 SDRAM 的掩碼,通道 1 的讀出數據包含了 2 幀的灰度數據,將這兩幀數據進行幀差計算,然后進行一些圖像處理。最后和人臉檢測的方法類似,得到 4 個極值坐標,利用極值坐標繪制框框,將移動目標框住,並一起寫入到原圖中,最后一起在顯示器上顯示,實現移動目標的檢測。

  本框圖只是其中一種數據流走向,也可以改為:攝像頭數據直接進行圖像處理,得出極值坐標后加入到攝像頭數據中,一起寫入到另一個SDRAM通道中,最后直接輸出。

2、SDRAM 掩碼過程

  (1)攝像頭數據寫入通道 0,然后通過VGA_driver讀出通道0的數據,再將該數據轉換為 8bit 的灰度數據。

  (2)SDRAM 的數據位寬為 16bit,灰度數據為 8bit,設置通道 1 的寫使能為灰度數據的數據指示信號,通道 1 的寫數據為 ch1_wr_data = {Y_data[7:0],Y_data[7:0]},即高8位和低8位都存儲一樣的數據。有些人這一步就開始幀划分,用 8'bz 來填充丟棄的高8位或低8位,經過我實際測試發現直接寫就行,因為后續SDRAM會采用掩碼,在進SDRAM之前不需要高低位處理。同時設置通道 1 的讀使能等於通道1的寫使能。

  (3)通道 1 的SDRAM寫掩碼最開始設置為 2‘b10(遮掩高8位),當SDRAM寫完一幀圖像后則SDRAM寫掩碼翻轉變成 01(遮掩低位),如此循環反復。通道 1 的SDRAM讀過程中掩碼一直為2'b00(不遮掩)。寫不斷切換高低掩碼,讀不遮掩,因此讀出的 16 bit 數據里包含了兩幀的 8bit 灰度數據。示意圖如下:

 (4)SDRAM 通道 1 的讀使能可以設置和通道 1 的寫使能一樣,能把數據讀出來就行,讀出的數據進行高低位分離,二者相減實現偵差法。 

3、掩碼法重點

(1)首先要有雙通道的 4 口 SDRAM 控制器,自己寫的可以,移植別人的也行。

(2)寫掩碼 wr_dqm 和讀掩碼 rd_dqm 最終都賦值給SDRAM引腳的掩碼信號 sdram_dqm,我們先自行定義寫掩碼 wr_dqm 和讀掩碼 wr_dqm。

  • 寫掩碼 wr_dqm 初始為 2'b10(遮掩高位),當寫完一幀時(找對這個點),寫掩碼 wr_dqm 翻轉;
  • 讀掩碼 rd_dqm 始終為 2'b00(不遮掩);
  • 當SDRAM進行寫操作時,寫掩碼 wr_dqm 賦值給端口掩碼sdram_dqm;
  • 當SDRAM進行讀操作時,讀掩碼 rd_dqm 賦值給端口掩碼sdram_dqm。

(3)需要掩碼的是通道1,正常讀寫的通道0的掩碼必須始終為 2'b00。

(4)必須關閉乒乓操作,否則寫入的數據無法拼接。

 

三、移動目標檢測——非掩碼法

1、結構框圖

  非掩碼法的通道1直接寫入 8 bit的數據即可,非掩碼的關鍵在於通道1的讀使能:通道1的讀使能等於通道1的寫使能,但是比通道1的寫使能落后一幀時間,所以最后通道1的讀寫數據剛好就差了一幀,可以進行幀差計算,其他的和人臉檢測相似。上面框圖的數據流只是其中一種方式,實際上也可以在攝像頭之后,進入SDRAM之前完成圖像處理。

   通道1的讀使能延時一幀代碼如下:

//等待一幀
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n) begin
        Y_vsync_r <= 1'b0;
        wait_vld  <= 1'b0;
    end
    else if(~Y_vsync & Y_vsync_r) //下降沿
        wait_vld  <= 1'b1;
    else
        Y_vsync_r <= Y_vsync;
end

//通道1寫
//---------------------------------------------------
assign ch1_wr_de   = Y_de;
assign ch1_wr_data = {8'b0,Y_data[7:0]};

//通道1讀
//---------------------------------------------------
assign ch1_rd_de = wait_vld ? ch1_wr_de : 1'b0;

2、非掩碼法重點

  (1)非掩碼法的幀差計算實際是第一幀和第二幀,第三幀和第四幀......理論上的連貫性沒有掩碼法好;

  (2)可以用乒乓操作,因此成像效果比掩碼法好。

 

  關於非掩碼法,要感謝一下公眾號《FPGA自習室》的 Beyond 前輩,他提供了一個結構框圖給我,如下所示:

  因為想有些改變,所以我上面的框圖做的和這個有點不一樣,這兩種數據流都試過,效果都很好。

 

四、開發過程中的難點

  1、幀差法的計算不能直接二者相減,而是要判斷大小,取絕對值。我一開始忘了絕對值,二者直接相減,結果效果非常差,圖像全是閃動。於是開始懷疑攝像頭和SDRAM控制器是不是出了問題,弄了一天,最后發現是沒有取絕對值,改過來后效果立馬變好了,后續的加框一氣呵成。

//偵差計算
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n)
        diff_data <= 16'b0;
    else if(Y_data_1 >= Y_data_2 && Y_de_2)
        diff_data <= (Y_data_1 - Y_data_2 > 80) ? 16'hffff : 16'h0000;
    else if(Y_data_1 <  Y_data_2 && Y_de_2)
        diff_data <= (Y_data_2 - Y_data_1 > 80) ? 16'hffff : 16'h0000;
end

  2、閾值的設置要合理。移動目標檢測效果和當時的光照、物體運動速度、物體和背景相似度等都有關,可根據實際情況進行閾值調整。可以引入兩個外部按鍵,一個按鍵用於增加閾值,同時數碼管顯示當前閾值;另一個按鍵用於切換原圖和偵差圖,結果不合理時方便調試。

//按鍵設置閾值 0-120
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n)
        value <= 32'd0;
    else if(value > 32'd120) //120以上閾值的偵差法效果很差
        value <= 32'd0;
    else if(key_vld[0])
        value <= value + 32'd10;
end

SEG_driver u_SEG_driver
(
    .clk                    (clk                    ),
    .rst_n                  (rst_n                  ),
    .en                     (1                      ),
    .value                  (value                   ), //偵差閾值
    .SH_CP                  (SH_CP                  ),
    .ST_CP                  (ST_CP                  ),
    .DS                     (DS                     )
);

//偵差計算
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n)
        diff_data <= 16'b0;
    else if(Y_data_1 >= Y_data_2 && Y_de_2)
        diff_data <= (Y_data_1 - Y_data_2 > value) ? 16'hffff : 16'h0000;
    else if(Y_data_1 <  Y_data_2 && Y_de_2)
        diff_data <= (Y_data_2 - Y_data_1 > value) ? 16'hffff : 16'h0000;
end

  3、特別注意信號的同步。例如通道1的讀寫使能相同,我的 SDRAM 控制器的內部讀 FIFO 是 normal 模式,即讀數據會落后讀使能一拍。后續幀差計算要使用到這個讀數據,不能用通道1的寫數據和通道1的讀數據直接進行幀差計算,必須將寫數據打一拍后才是和讀數據對齊的,才能進行幀差計算。而且我這里幀差計算用的時序邏輯,因此數據過來是:讀使能和寫使能相同,讀數據落后一個時鍾周期,幀差計算又消耗一個時鍾周期,總的消耗了兩個周期。因此同步信號要特別注意對齊。

//偵差法消耗一拍,故打拍對齊
//---------------------------------------------------
always @(posedge clk_10m) begin
    diff_de    <= Y_de_2;
    diff_hsync <= Y_hsync_2;
    diff_vsync <= Y_vsync_2;
end

 

五、偵差法缺點和改進方法

  偵差法的核心思想是移動目標和背景做差,不同點即移動目標本身。如果移動目標和背景的顏色相似,檢測精度將大大降低。

  偵差法的缺點難以逾越,但是也可以通過一些技術手段提高檢測精度:

  1、對結果進行平滑濾波、邊緣檢測、形態學濾波等,提高檢測精度。

  2、兩幀圖像做差改為三幀圖像做差或四幀圖像做差,提高檢測精度。具體實現為增加緩存器件,或者上述的兩種方法結合,實現緩存三幀、四幀圖像的要求。

 

六、效果展示

  之前做圖像處理時,板子上TFT屏接口的引腳擊穿了,導致圖像偏紅色。后來去了修,修好后用了沒多久,又壞了,這次變成偏藍色了,唉。

  圖像失真偏藍是我板子的問題,只要關注結果就好了。可以看到閾值為 0 時,無法檢測到移動目標;按鍵 1 可以調節閾值,閾值為 10 時,非常靈敏,甚至靈敏過頭了,隨着閾值的增加,檢測精度也在不斷變化,閾值為 80 的效果還不錯,閾值為110以上后,基本就沒法檢測了。另外按鍵 2 可以切換顯示幀差圖像,方便和原圖對比。

 

參考資料:

[1]OpenS Lee:FPGA開源工作室(公眾號)

[2]NingHechuan:硅農(公眾號)

[3]Beyond:FPGA自習室(公眾號)

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM