基於MSCNN的人羣密度估計之生成人羣密度圖

終於到了在家躺着都是給國家做貢獻的時候(手動捂臉),對人員密集場所的監控和自動報警也算是對疫情的一份貢獻吧,哈哈!項目地址:https://github.com/zzubqh/CrowdCount,如果你感覺有點用就幫忙點個start吧 😃

概述和生成人羣密度圖

基於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. 最終的預測結果:
    在這裏插入圖片描述

密度圖生成方法

  • 原理
    • 如果一個標註點的位置爲xix_{i} ,則改點可以表示爲δ(xxi)\delta(x-x_i) ,其中δ(xxi)\delta(x-x_i)爲衝擊函數。因此具有N個人頭的標籤可以表示爲:
      H(x)=i=0Nδ(xxi)H(x) = \sum_{i=0}^{N} \delta(x-x_i)
      上式說人話就是:一副圖具有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濾波後得:
      array([[0.05469418, 0.12447951, 0.05469418],
      [0.12447951, 0.28330525, 0.12447951],
      [0.05469418, 0.12447951, 0.05469418]])
      可知,兩個數組求和後值都爲1,但消除了稀疏矩陣,更利於網絡的loss計算!
    • 高斯核卷積核選擇。在真實場景下特別是人羣密度很高的時候,每個xix_{i} 的位置並不是獨立的,由於圖片存在着透視失真,導致像素與周邊樣本在不同場景區域尺度不一致。因此爲了精確估計羣體密度函數需要考慮透視變換,假設每個頭部周圍的人羣是均勻分佈的,那麼頭部與最近k個鄰居之間的圖像中的平均距離可以給出幾何失真的合理估計,即根據圖像中k個頭部間的距離來確定參數 σ\sigma。對於每個人頭xix_{i} ,給出 k 個近鄰頭部距離{x1i,x2i,...,xmix_{1}^{i},x_{2}^{i},...,x_{m}^{i}},計算平均距離di=1mj=1mdji\overline{d_{i}}=\frac{1}{m}\sum_{j=1}^{m}d_{j}^{i}xix_{i}在圖片上所在的位置對應一個區域,該區域內的人羣密度與di\overline{d_{i}}成比例,所以使用自適應高斯核進行卷積,高斯核的σi\sigma_{i}可變且與di\overline{d_{i}}成比例
      F(x)=i=0Nδ(xxi)Gσi(x)F(x) = \sum_{i=0}^{N} \delta(x-x_i)\cdot G_{\sigma_{i}}(x) 其中 σi(x)=βdi\sigma_{i}(x)=\beta\overline{d_{i}},這裏的 β\beta 取0.3。體現在代碼中,就是先計算N個頭部間的距離,然後計算出每個頭部對應的平均距離,我在代碼中K取1,因爲訓練集中存在大量只包含一個人或者兩個人的圖片。然後使用對應的di\overline{d_{i}}計算高斯核的σ\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

生成的密度圖如下所示:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章