基于MSCNN的人群密度估计之生成人群密度图

终于到了在家躺着都是给国家做贡献的时候(手动捂脸),对人员密集场所的监控和自动报警也算是对疫情的一份贡献吧,哈哈!项目地址:https://github.com/zzubqh/CrowdCount,如果你感觉有点用就帮忙点个start吧

概述和生成人群密度图

基于MSCNN的人群密度估计:

  • 概述和生成人群密度图
  • 数据集制作和数据生成器
  • 密度等级分类网络
  • MSCNN的训练和预测

人群密度估计算法

  • 传统的人群计数方法
    • 基于检测和回归的方法
    • 基于密度图的方法
  • 深度学习的方法
    深度学习的方法在目标检测、识别、分割相比传统的方法都取得了很大的进步,同样在人群密度估计上也比传统的方法效果要好的多。

基于深度学习的人群密度估计方法概述

  1. 生成数据集中图片的密度图,生成方法见密度图生成算法,此密度图作为ground truth。
  2. 利用cnn做特征提取,这里就是每种算法最大差异的地方。原始图片通过精心构造的网络提取特征后,生成一副同密度图具有相同大小的图片,用第一步生成的密度图作为ground truth 进行loss计算。在计算loss的时候,简单期间可以直接使用mse,也可以使用一些论文中提出的专门针对密度图的loss。
  3. 对训练好的网络输出,直接求和(原因见密度图生成算法)即为图片中包含的人数。

项目概述

  1. 由于本项目对于人数大于100的统统记为100,不需要对人群密度过大的做出人数预测,所以选了一个网络结构相对简单的mscnn。
  2. 检测的图片中包括:不包含任何人的图片、只有有限数量人的图片和密度很高的图片,所以先利用一个密度分类网络将图片的密度等级分成了三个等级,分别为0:不包含任何人的图片;1:包含的人员数小于100;2:包含的人员数大于100。
  3. 在项目中,实现了一个使用vgg16作为特征提取的密度等级分类网络;一个mscnn网络用于人数估计;一个使用c#编写的对密度等级打标签的小工具。
  4. 最终的预测结果:
    基于MSCNN的人群密度估计之生成人群密度图_第1张图片

密度图生成方法

  • 原理
    • 如果一个标注点的位置为 x i x_{i} xi ,则改点可以表示为 δ ( x − x i ) \delta(x-x_i) δ(xxi) ,其中 δ ( x − x i ) \delta(x-x_i) δ(xxi)为冲击函数。因此具有N个人头的标签可以表示为:
      H ( x ) = ∑ i = 0 N δ ( x − x i ) H(x) = \sum_{i=0}^{N} \delta(x-x_i) H(x)=i=0Nδ(xxi)
      上式说人话就是:一副图具有N个人头的图片的密度图,人头位于图片的(i,j)处,则在该处像素值为1,即 p(i,j)=1 其余所有的地方像素值为0。
    • 上式就是密度图了,但是这样生成的密度图存在一个很严重的问题,会造成生成的密度图很稀疏导致在网络计算loss的时候整体输出趋近于0,而且不利于统计人群密度大的时候的人数。故,可以使用高斯函数对上式进行卷积,将标记为人头的位置变成该区域的密度函数,这样即在一定程度上解决了图片的稀疏问题,又不改变图片中人数的计数方式,依然只对密度图求和即可!
      假设密度图上在[1,1]处标记出一个人头,则有如下数组:
      array([[0., 0., 0.],
      [0., 1., 0.],
      [0., 0., 0.]])
      取高斯核的 σ = 0.75 \sigma=0.75 σ=0.75滤波后得:
      array([[0.05469418, 0.12447951, 0.05469418],
      [0.12447951, 0.28330525, 0.12447951],
      [0.05469418, 0.12447951, 0.05469418]])
      可知,两个数组求和后值都为1,但消除了稀疏矩阵,更利于网络的loss计算!
    • 高斯核卷积核选择。在真实场景下特别是人群密度很高的时候,每个 x i x_{i} xi 的位置并不是独立的,由于图片存在着透视失真,导致像素与周边样本在不同场景区域尺度不一致。因此为了精确估计群体密度函数需要考虑透视变换,假设每个头部周围的人群是均匀分布的,那么头部与最近k个邻居之间的图像中的平均距离可以给出几何失真的合理估计,即根据图像中k个头部间的距离来确定参数 σ \sigma σ。对于每个人头 x i x_{i} xi ,给出 k 个近邻头部距离{ x 1 i , x 2 i , . . . , x m i x_{1}^{i},x_{2}^{i},...,x_{m}^{i} x1i,x2i,...,xmi},计算平均距离 d i ‾ = 1 m ∑ j = 1 m d j i \overline{d_{i}}=\frac{1}{m}\sum_{j=1}^{m}d_{j}^{i} di=m1j=1mdji x i x_{i} xi在图片上所在的位置对应一个区域,该区域内的人群密度与 d i ‾ \overline{d_{i}} di成比例,所以使用自适应高斯核进行卷积,高斯核的 σ i \sigma_{i} σi可变且与 d i ‾ \overline{d_{i}} di成比例
      F ( x ) = ∑ i = 0 N δ ( x − x i ) ⋅ G σ i ( x ) F(x) = \sum_{i=0}^{N} \delta(x-x_i)\cdot G_{\sigma_{i}}(x) F(x)=i=0Nδ(xxi)Gσi(x) 其中 σ i ( x ) = β d i ‾ \sigma_{i}(x)=\beta\overline{d_{i}} σi(x)=βdi,这里的 β \beta β 取0.3。体现在代码中,就是先计算N个头部间的距离,然后计算出每个头部对应的平均距离,我在代码中K取1,因为训练集中存在大量只包含一个人或者两个人的图片。然后使用对应的 d i ‾ \overline{d_{i}} di计算高斯核的 σ \sigma σ
  • 密度图生成代码
   def get_densemap(self, img, img_index, size):
        """
        生成密度图,准备输入神经网络
        :param img
        :param img_index
        :param positions
        :param size 神经网络输入层图片大小
        """
        h, w = img.shape[:-1]
        proportion_h, proportion_w = size / h, size / w  # 输入层需求与当前图片大小对比
        gts = self.positions[0][img_index].copy()
        for i, p in enumerate(self.positions[0][img_index]):
            # 取出每个人的坐标
            now_x, now_y = int(p[0] * proportion_w), int(p[1] * proportion_h)  # 按照输入层要求调整坐标位置
            gts[i][0] = now_x
            gts[i][1] = now_y

        res = np.zeros(shape=[size, size])
        bool_res = (gts[:, 0] < size) & (gts[:, 1] < size)
        for k in range(len(gts)):
            gt = gts[k]
            if bool_res[k] == True:
                res[int(gt[1])][int(gt[0])] = 1

        pts = np.array(list(zip(np.nonzero(res)[1], np.nonzero(res)[0])))
        map_shape = [size, size]
        density = np.zeros(shape=map_shape, dtype=np.float32)
        if len(pts) == 1:
            sigmas = [0]
        else:
            neighbors = NearestNeighbors(n_neighbors=1, algorithm='kd_tree', leaf_size=1200)
            neighbors.fit(pts.copy())
            distances, _ = neighbors.kneighbors()
            sigmas = distances.sum(axis=1) * 0.3

        for i in range(len(pts)):
            pt = pts[i]
            pt2d = np.zeros(shape=map_shape, dtype=np.float32)
            pt2d[pt[1]][pt[0]] = 1
            density += cv2.GaussianBlur(pt2d, (3, 3), sigmas[i])
            # density += filters.gaussian_filter(pt2d, sigmas[i], mode='constant')
        return density

生成的密度图如下所示:
基于MSCNN的人群密度估计之生成人群密度图_第2张图片

你可能感兴趣的:(深度学习,mscnn,人群密度图,人群密度估计)