行人(客流)计数的一些底层技术

概述

自己之前写过一篇文章介绍自己做的行人(客流)计数的工作:基于检测和多目标跟踪的客流统计功能小结

这篇文章利用行人检测及多目标跟踪技术,对过边线的人数进行计数。检测和多目标跟踪我就不多说了,这里说一下如何进行过线检测。

基本思想

多目标跟踪会对每一帧的每个检测框赋予一个持续不变的ID,作为同一个目标的标识。同时,为了进行位置判断,我会把每个检测框抽象为一个点,比如将检测框上边缘的中心作为人的位置。

用于计数的边线我设计了两条,而且可以在任意位置。这两条边线我称为A线和B线,做业务的时候可以约定A线在外B线在内,这样就好统计进出的方向。之所以不叫外线内线,是因为存在在一些开放区域,比如马路,无所谓内外。这两条边线在界面上由人工来画的,然后配置到算法中来。下图界面上我的边线支持三段折线,后面讲的时候就用一根线段来讲。实际项目应用中,可以在界面画多组边线,每组边线对应一个路口或门口。边线的长度及角度都是可以根据场景任意画的。

有了上面的工作,每个人的在我的算法里就是一个有着编号的点,每一帧都能知道这个点的位置。同时算法里还有A\B两根边线的端点坐标。流量计数就是统计这些点是否跨过了两根边线,以及先从哪根线段开始跨的。

image

如何判断跨线

在有两条边线的情况下,我总结了下五种情况,如下图,只有前两种是真正算跨线的,这两种情况才是算法要检查的人流量。

在这里插入图片描述

先后跨越的情况

上面说了,每个行人都被抽象为一个带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了。

客流计数要考虑的其他问题

  1. 单人反复跨线。如果本意是为了检测门店的进出人数,但是店长或营销人员来回踱步会增加无意义的客流增加,为此需要将一直在画面中来回跨线的情况去掉。
  2. 算法速度。如果检测和多目标跟踪算法不能以接近实时的速度运算,一切都没有意义。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值