人群计数之生成密度图
一、概述
在近幾年人群計數領域的研究中,大多數論文都沿用了MCNN( Multi-column Convolutional Neural Network )中提出的生成密度圖的方法,本文將通過代碼和CSRNet網絡中生成密度圖的實例來詳細講解該方法。
二、生成密度圖
在進行人群計數的研究時,數據集通常由原始圖片和標注文件共同構成,本文使用的數據集是shanghaiTech,該數據集包含了part_A_final,part_B_final兩部分,標注文件是mat格式,里面記錄了每個注釋人頭的二維坐標和總人頭數量[1]。
由標注文件生成密度圖的過程如下:首先構造一個和原始圖片大小相同的矩陣,并將其全部置為0,然后將每個被標記的人頭對應的位置置為1,這樣就得到了一個只有0和1的矩陣,最后通過高斯核函數進行卷積得到一個連續的密度圖。
2.1 加載數據集
以ShanghaiTech數據集為例,path_sets中保存的就是part_B部分的訓練集和測試集
# set the root to the Shanghai dataset you downloadroot = 'E:/數據集/ShanghaiTech_Crowd_Counting_Dataset/'# now generate the ShanghaiA's ground truthpart_A_train = os.path.join(root, 'part_A_final/train_data', 'images')part_A_test = os.path.join(root, 'part_A_final/test_data', 'images')part_B_train = os.path.join(root, 'part_B_final/train_data', 'images')part_B_test = os.path.join(root, 'part_B_final/test_data', 'images')path_sets = [part_B_train, part_B_test] # 將訓練集和測試集放在一起然后依次讀取數據集中的每一張圖片將其放到列表img_paths中
img_paths = []for path in path_sets:for img_path in glob.glob(os.path.join(path, '*.jpg')):img_paths.append(img_path)print('圖片數量:', len(img_paths))2.2 讀取圖片并生成密度圖
for img_path in img_paths:print(img_path)# 獲取每張圖片對應的mat標記文件mat = io.loadmat(img_path.replace('images', 'ground_truth').replace('IMG_', 'GT_IMG_').replace('.jpg', '.mat'))img = plt.imread(img_path)# 生成密度圖gt_density_map = np.zeros((img.shape[0], img.shape[1]))gt = mat["image_info"][0, 0][0, 0][0]for i in range(0, len(gt)):if int(gt[i][1]) < img.shape[0] and int(gt[i][0]) < img.shape[1]:gt_density_map[int(gt[i][1]), int(gt[i][0])] = 1gt_density_map = gaussian_filter_density(gt_density_map)# 保存生成的密度圖with h5py.File(img_path.replace('images', 'ground_truth').replace('.jpg', '.h5'), 'w') as hf:hf['density'] = gt_density_map#測試print('總數量=',len(gt))print('密度圖=',gt_density_map.sum())原始圖片和其對應的mat文件在兩個不同的文件夾下,文件名和后綴名有一定的區別,所以通過replace()方法將圖片的路徑img_path轉換成mat文件的路徑并讀取。
生成密度圖時首先生成一個和原始圖像大小相同的全0矩陣(np.zeros()),然后遍歷標注文件中每一個位置坐標,將矩陣中對應的點置為1,最后調用高斯核函數生成密度圖并保存成h5py格式的文件。
2.3 高斯核函數
首先給出公式:
其中Xi為每個人頭標注點的位置,具有N個人頭的標簽可以表示為H(x)。這個H(x)可能理解起來比較抽象,要注意的是這里的x表示的是一個二維坐標,所以H(x)也就是上文中提到的只有0和1的矩陣。
我們可以使用高斯核函數G(x)對這個函數卷積得到密度函數F(x),結合公式一和公式二可以得到公式三。
放上二維高斯核函數的公式:
由這個公式我們可以看到輸入的是二維坐標點(x,y),其中參數是σ,作用是控制函數的徑向作用范圍。公式三中使用的是x來表示這個二維坐標,很多人可能沒有理解過來。
綜上所述,生成密度圖的過程就是將只有0和1的矩陣經過高斯卷積計算即可。
2.4 為什么要使用高斯核函數?
高斯核的函數圖像如下所示,它是一個正態分布鐘形線,坐標越趨近中心點,值就越大,反之越小。也就是說離中心點越近權值就越大,離中心點越遠,權值就越小[2]。
在真實圖片中,每個人頭是具有一定大小的,對應圖片中的一小片區域,而在標注文件中我們將一個像素點的值置為1來表示這個人頭,這顯然是十分不合理的。所以我們使用高斯核函數將這個中心點的像素值用它周圍點的像素值的加權平均代替,周圍像素點的權值相加起來等于1;這樣既不影響生成的密度圖中總人頭數,又能夠比較真實的反應每個人頭在空間里面的位置特征。
另一方面,我們需要考慮到透視畸變對人頭大小的影響。簡單地說,就是在拍攝照片時,距離鏡頭越遠的物體在照片上顯得越小。如下圖所示,距離鏡頭近的人頭較大,占用的空間像素點較多,而距離鏡頭遠的人頭較小,占用的空間像素點較少。所以在使用高斯核函數時需要根據不同人頭大小設置不同的模糊半徑,即根據圖像中每個人頭部大小來確定參數σ。
但是在實際情況下,我們不可能準確的獲得每個頭部的尺寸大小,而且很難找到頭部尺寸和密度圖之間的關系。接下來我們繼續研究上面的圖片,通過觀察圖片我們可以發現每個頭部大小通常與相鄰人頭中心點的距離有關。舉個例子,距離鏡頭最近的那個女士與其相鄰兩個人頭的距離要明顯大于遠處那個男士距離其相鄰兩個人頭的距離。
經過分析,在擁擠場景中,我們可以使用相鄰k個人頭與該人頭的平均距離來作為高斯核函數的參數σ,這樣就能夠更好地表示人頭大小的特征信息。
2.5 高斯核函數的代碼實現
def gaussian_filter_density(gt):print(gt.shape)density = np.zeros(gt.shape, dtype=np.float32)gt_count = np.count_nonzero(gt)if gt_count == 0:return densitypts = np.array(list(zip(np.nonzero(gt)[1], np.nonzero(gt)[0])))#構造KDTree尋找相鄰的人頭位置tree = scipy.spatial.KDTree(pts.copy(), leafsize=2048)distances, locations = tree.query(pts, k=4)print('generate density...')for i, pt in enumerate(pts):pt2d = np.zeros(gt.shape, dtype=np.float32)pt2d[pt[1],pt[0]] = 1.if gt_count > 1:#相鄰三個人頭的平均距離,其中beta=0.3sigma = (distances[i][1]+distances[i][2]+distances[i][3])*0.1else:sigma = np.average(np.array(gt.shape))/2./2. #case: 1 pointdensity += scipy.ndimage.filters.gaussian_filter(pt2d, sigma, mode='constant')print('done.')return density這里有幾個地方需要解釋一下:
1、構造KDTree調用的是scipy包中封裝好的函數,其中leafsize表示的是最大葉子數,如果圖片中人頭數量過多,可以自行修改其數值。
2、tree.query()中參數k=4查詢的是與當前結點相鄰三個結點的位置信息,因為distances[i][0]表示的是當前結點。
3、在論文中beta=0.3,因為這里計算的是三個點的平均距離,所以除以3然后乘以beta相當于直接乘以0.1。
三、補充
3.1 高斯核函數的參數
高斯核函數的參數σ在不同的數據集和方法中有所不同,自適應高斯核由以上方法計算得到,當然也可以將σ設置成一個固定數值進行計算。
在生成密度圖時涉及到的參數還有k(相鄰k個人頭的平均距離),以及系數beta,在進行人群計數時可以根據不同的數據集進行改變和調參。
3.2 生成密度圖時存在誤差
由于相鄰人頭的距離太近,可能會導致進行高斯卷積時部分區域重疊,所以由此方法生成的真實密度圖經過sum計算之后得到的人頭數和標注文件mat中的人頭數存在一定誤差。而卷積神經網絡計算誤差MAE、MSE時往往采用真實密度圖和預測密度圖進行計算。
3.3 相關研究
ICCV2019 Adaptive Density Map Generation for Crowd Counting (ADMG)
在過去的方法中,密度圖一旦制作完成就不再去修改;該論文提出了一種在訓練過程中微調密度圖的方法,這是一個很好的思路,后續也可以進行深入研究。由于編者沒有仔細閱讀該論文,僅提供論文鏈接[3]。
3.4 Write in the end
如果讀者關于以上內容有任何疑問或者建議,歡迎在評論區留言,編者看到了一定會進行回復。
參考文獻:
[1] https://www.pianshen.com/article/95011021159/
[2] https://www.cnblogs.com/herenzhiming/articles/5276106.html
[3] http://visal.cs.cityu.edu.hk/static/pubs/conf/iccv19-dmapgen.pdf
總結
以上是生活随笔為你收集整理的人群计数之生成密度图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: typora 分割线_最全Typora语
- 下一篇: [Study]JavaWeb