概述
自己之前寫過一篇文章介紹自己做的行人(客流)計數的工作:基於檢測和多目標跟蹤的客流統計功能小結
這篇文章利用行人檢測及多目標跟蹤技術,對過邊線的人數進行計數。檢測和多目標跟蹤我就不多說了,這裏說一下如何進行過線檢測。
基本思想
多目標跟蹤會對每一幀的每個檢測框賦予一個持續不變的ID,作爲同一個目標的標識。同時,爲了進行位置判斷,我會把每個檢測框抽象爲一個點,比如將檢測框上邊緣的中心作爲人的位置。
用於計數的邊線我設計了兩條,而且可以在任意位置。這兩條邊線我稱爲A線和B線,做業務的時候可以約定A線在外B線在內,這樣就好統計進出的方向。之所以不叫外線內線,是因爲存在在一些開放區域,比如馬路,無所謂內外。這兩條邊線在界面上由人工來畫的,然後配置到算法中來。下圖界面上我的邊線支持三段折線,後面講的時候就用一根線段來講。實際項目應用中,可以在界面畫多組邊線,每組邊線對應一個路口或門口。邊線的長度及角度都是可以根據場景任意畫的。
有了上面的工作,每個人的在我的算法裏就是一個有着編號的點,每一幀都能知道這個點的位置。同時算法裏還有A\B兩根邊線的端點座標。流量計數就是統計這些點是否跨過了兩根邊線,以及先從哪根線段開始跨的。
如何判斷跨線
在有兩條邊線的情況下,我總結了下五種情況,如下圖,只有前兩種是真正算跨線的,這兩種情況纔是算法要檢查的人流量。
先後跨越的情況
上面說了,每個行人都被抽象爲一個帶ID和座標的點,由於多目標跟蹤的緣故,同一個人的ID在多幀畫面間是保持不變的,而座標則隨着人走動而改變。
每兩幀畫面的座標都可以點連成一個線段,用這個線段判斷是否和兩個邊線交叉就可以了。如果一個人先跨越了A邊線,後續又跨越了B邊線,那麼妥妥的A到B的走動方向無疑了。
- 注意這裏有一個技術:判斷兩個線段是否相交
爲了判斷是否和邊線相交,每個ID的歷史座標要在程序裏存下來,直到這個ID在多目標算法裏消失。
一次跨越兩個邊線
有些情況下兩條邊線距離很窄,或者視頻畫面出現了丟幀,那麼算法檢測到行人一次性跨越了兩條邊線也是正常的。
有了上面提到的判斷線段相交的算法,那麼判跨過兩根邊線就容易了。但是如何判斷方向呢?
- 這裏用到了第二個技術:計算點和線段的距離
如果起點離A邊線近,離B邊線遠,那麼就是從A到B的跨越,否則就是反過來的。
圖形學判斷
基本思想講完了,下面就剩下如何實現線段交叉和距離的計算。
說明一下,我這裏無論是行人的座標還是邊線端點的座標,用的都是對圖像尺寸做的歸一化。所以我的座標大小都是0~1的小數。
判斷兩個線段是否相交
注意這裏說的是線段相交,不是直線。
下面是一個python實現的判斷線段是否相交的函數,其中(point_aa, point_bb)表示一根線段的兩個端點,( point_cc, point_dd)表示另一個線段的兩個端點。相交返回Ture,否則返回False。
def __intersect( point_aa, point_bb,
point_cc, point_dd):
# this fuction will judge whether two line-segment is intersect
# from https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
s10_x = point_bb.x - point_aa.x
s10_y = point_bb.y - point_aa.y
s32_x = point_dd.x - point_cc.x
s32_y = point_dd.y - point_cc.y
denom = s10_x * s32_y - s32_x * s10_y
if (denom == 0):
return False
denomPositive = denom > 0
s02_x = point_aa.x - point_cc.x
s02_y = point_aa.y - point_cc.y
s_numer = s10_x * s02_y - s10_y * s02_x
if ((s_numer < 0) == denomPositive):
return False
t_numer = s32_x * s02_y - s32_y * s02_x
if ((t_numer < 0) == denomPositive):
return False
if (((s_numer > denom) == denomPositive) or ((t_numer > denom) == denomPositive)):
return False
return True
這個算法參考自網上:https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
計算點和線段的距離
下面就是我的一個實現。(x, y)是點的座標, ( x1, y1, x2, y2)是線段兩個端點的座標。
def __distance_to_line( x, y, x1, y1, x2, y2 ):
# from:http://blog.sina.com.cn/s/blog_5d5c80840101bnhw.html
cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1)
if (cross <= 0) :
return math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1))
d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
if (cross >= d2) :
return math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2))
r = cross / d2
px = x1 + (x2 - x1) * r
py = y1 + (y2 - y1) * r
return math.sqrt( (x - px) * (x - px) + (py - y1) * (py - y1) )
這段算法參考自:http://blog.sina.com.cn/s/blog_5d5c80840101bnhw.html
原始代碼是C#,我給改成python了。
客流計數要考慮的其他問題
- 單人反覆跨線。如果本意是爲了檢測門店的進出人數,但是店長或營銷人員來回踱步會增加無意義的客流增加,爲此需要將一直在畫面中來回跨線的情況去掉。
- 算法速度。如果檢測和多目標跟蹤算法不能以接近實時的速度運算,一切都沒有意義。