1.前言
2.預處理
3.後處理
4.項目總結
正文:
1.前言
總結一下,目前為止做了什麼:在前面幾篇博文中,對資料進行了處理、搭建了神經網絡、訓練了神經網絡并進行了測試。在實際使用中,因為檢測的圖檔可能是整個人的圖像,是以需要從這些圖像中框出人臉的位置,這也是這個項目的目的。由于神經網絡隻能對一張含有人臉或者非人臉的圖檔進行判斷,是以在檢測一張圖檔時,需要先進行預處理。一張圖檔進行預處理以及神經網絡的判斷輸出後,可能會有很多個結果,是以還要進行後處理,找出最優解。
2.預處理
-
滑動視窗
由于一張圖檔可能有很多個部分組成(人臉、非人臉)。是以需要先對輸入的圖檔進行每部分的截取。滑動金字塔是按照固定的視窗大小在圖檔中滑動,進而得到很多個視窗,每個視窗都作為神經網絡的輸入。
上圖展示了滑動視窗将一張圖檔分為很多張子圖檔,除此之位,視窗還記錄了子圖檔在圖檔中的位置,最後可用于人臉标記。每張圖檔都輸入到訓練好的模型,如果神經網絡的輸出機率(神經網絡softmax輸出)大于我們設定的門檻值,就可以認為這張子圖檔是人臉部分,進行位置标記。
下面是程式實作,函數傳回子視窗圖檔和對應的位置。
import math def slid_win(input_img, win_size_h, win_size_w, slid_step,other): """滑動視窗函數""" #輸入參數-輸入、視窗w、視窗h和步長 #輸出滑動圖檔、滑動坐标(坐标格式:[y1,y2,x1,x2] sli_images = [] locates = [] image_h, image_w, _ = input_img.shape lim_w = math.ceil((image_w-win_size_w)/ slid_step) lim_h = math.ceil((image_h-win_size_h)/slid_step) # print(lim_w, lim_h) for i in range(lim_h): strat_h = slid_step*i end_h = strat_h + win_size_h for j in range(lim_w): start_w = slid_step*j end_w = start_w + win_size_w # print(strat_h, end_h, start_w, end_w) if (end_h< image_h) or (end_w< image_w): part_img = input_img[strat_h:end_h, start_w:end_w] locate = [strat_h, end_h, start_w, end_w] sli_images.append(part_img) locates.append(locate) # print(locate) #添加沒有滑動到的區域 if not isinstance(((image_w-win_size_w)/ slid_step), int): #判斷橫方向是不是為整數 不是标志有餘留的邊緣 for i in range(lim_h): #周遊縱軸 strat_h = slid_step*i end_h = strat_h + win_size_h start_w = image_w-win_size_w end_w = image_w part_img = input_img[strat_h:end_h, start_w:end_w] locate = [int(strat_h), int(end_h), int(start_w), int(end_w)] sli_images.append(part_img) # print(locate) locates.append(locate) if not isinstance(((image_h-win_size_h)/ slid_step), int):#判斷縱方向是不是為整數 不是标志有餘留的邊緣 for j in range(lim_w): #周遊橫軸 start_w = slid_step*j end_w = start_w + win_size_w strat_h = image_h-win_size_h end_h = image_h part_img = input_img[strat_h:end_h, start_w:end_w] locate = [strat_h, end_h, start_w, end_w] sli_images.append(part_img) # print(locate) locates.append(locate) condition1 = not isinstance(((image_w-win_size_w)/ slid_step), int) condition2 = not isinstance(((image_h-win_size_h)/ slid_step), int) if condition1 and condition2: #添加最後一張 start_w = image_w-win_size_w end_w = image_w strat_h = image_h-win_size_h end_h = image_h part_img = input_img[strat_h:end_h, start_w:end_w] locate = [strat_h, end_h, start_w, end_w] sli_images.append(part_img) # print(locate) locates.append(locate) #格式y1 y2 x1 x2 return sli_images, locates
-
圖像金字塔
雖然滑動視窗可以把一張圖檔中的人臉和非人臉分離出來,但是還會存在一個問題,因為滑動視窗的尺寸是固定的,是以會存在視窗大小和人臉大小不比對的情況。是以需要先對輸入的圖檔進行尺寸變換的操作,使得滑動視窗的大小總能和人臉大小互相比對。
(圖檔來自于百度圖檔)
3.後處理
經過了預處理并輸入到神經網絡,且進行位置标記,得到的圖檔是這樣的。
一點也不美觀,也沒有達到效果。這主要是由于門檻值的設定(達到門檻值就框出人臉)、滑動視窗步長和比例因子所造成的。是以這裡需要進行非極大值抑制(NMS)處理,下面是NMS的算法思想。
NMS簡單的說就是對框進行合并,合并的基準是機率最大對應的視窗。如果一個視窗和機率最大對應的視窗重疊面積足夠大,就進行消除,隻保留機率值最大所對應的視窗。根據這個思想進行程式編寫如下。
def compuet_iou(rect1, rect2):
"""計算兩個矩形的IOU"""
#rect是矩形的四個坐标 左上角和右下角坐标 rect = [x1,y1,x2,y2]
area1 = (rect1[3] - rect1[1]) * (rect1[2] - rect1[0])
area2 = (rect2[3] - rect2[1]) * (rect2[2] - rect2[0])
all_area = area1 + area2
x1 = max(rect1[0], rect2[0])
y1 = max(rect1[1], rect2[1])
x2 = min(rect1[2], rect2[2])
y2 = min(rect1[3], rect2[3])
h = max(0, y2 - y1)
w = max(0, x2 - x1)
overlap = h * w
#判斷等于1情況
try :
iou = overlap/(all_area - overlap)
except:
iou = 1
else :
iou = overlap/(all_area - overlap)
return iou
def max_prop(locates, props):
"""取最大機率的nms"""
#輸入參數:所有超過門檻值的滑動窗位置、滑動視窗對應門檻值
#輸出 算法留下來的框位置
temp_locates = []
temp_prop = []
final_locates = []
return_locates = []
second_locates = []
second_prop = []
for i in range(len(locates)): #周遊目前所有框
temp_locate = [locates[i][2],locates[i][0],locates[i][3],locates[i][1]]
temp_locates.append(temp_locate)
temp_prop.append(props[i])
for j in range(len(locates)):
now_locate = [locates[j][2],locates[j][0],locates[j][3],locates[j][1]]
iou = compuet_iou(temp_locate,now_locate) #計算iou
if iou>0.3 and iou<1: #門檻值設定
temp_locates.append(now_locate)
temp_prop.append(props[j])
second_locates.append(temp_locates)
second_prop.append(temp_prop)
temp_locates = [] #清零 下一次儲存
temp_prop = []
for k in range(len(second_locates)):
area = []
for l in range(len(second_locates[k])):
area.append(second_prop[k][l]) #存儲機率值
max_index = area.index(max(area)) #取最大機率
final_locates.append(second_locates[k][max_index])#取最大機率對應的框
# #整理清單 删除相同元素
for final_locate in final_locates:
if not final_locate in return_locates:
return_locates.append(final_locate)
return return_locates #return_locates #格式[x1,y1,x2,y2]
4.項目總結
至此,整個人臉檢測項目就完成了。從資料處理到神經網絡搭建、訓練與測試,再到一張人臉圖檔的測試以及一張圖檔的測試。下面是這個項目的步驟總結。
- 确定資料集 --> 處理得到訓練、驗證以及測試的圖檔
- 搭建神經網絡 --> 用1中的圖檔進行訓練 --> 得到分類器模型
- 圖檔輸入 --> 圖像金字塔 --> 滑動視窗 --> 送入2中得到的分類器模型分類 --> 輸出機率值并标記視窗 --> 非極大值抑制 --> 框出人臉
步驟 | 備注 |
---|---|
确定資料集 | FDDB或WIDER FACE或其他 |
處理得到訓練、驗證以及測試的圖檔 | 該内容在FDDB資料集處理、WIDER FACE資料集處理、資料集的标簽生成中,資料也在其中。 |
搭建神經網絡 | 按照 神經網絡的搭建的架構進行搭建 |
用1中的圖檔進行訓練 | 設定學習率和訓練次數等參數進行訓練。訓練神經網絡中的訓練架構為了加快速度設定了比較小的batch、圖檔和訓練次數(實際使用時,并不是使用這麼小的batch、圖檔和訓練次數得到模型),可以自主進行選擇調節 |
得到分類器模型 | 訓練搭建好的神經網絡得到的模型。我的模型在這裡擷取人臉檢測模型密碼:f3vzk8 |
圖檔輸入 | 選擇一張圖檔或者視訊的一幀 |
圖像金字塔 | 本篇博文 |
滑動視窗 | 本篇博文 |
送入分類器模型分類 | 用驗證神經網絡中的圖檔驗證程式進行圖檔輸入,其中的輸出為經過softmax後的機率值。(用于本博文中滑動視窗輸入,輸出的機率值與門檻值比較) |
輸出機率值并标記視窗 | 輸出機率值指的是softmax輸出,當這個值超出自己設定的門檻值時就進行标記 |
非極大值抑制 | 本篇博文,經過非極大值抑制後的輸出就是最終人臉檢測結果 |