1. 程式人生 > >mtcnn python 開原始碼詳細圖解

mtcnn python 開原始碼詳細圖解

開原始碼地址:https://github.com/kpzhang93/MTCNN_face_detection_alignment
程式碼詳細圖解:持續更新中

感覺自己陷入了程式碼細節誤區,本著時間緊任務重的理念,加上本人能力有限,暫時無法完成細節程式碼圖解,但是可以簡單寫一下自己兩週以來的學習感悟,和整體架構!
首先總體架構參考如下部落格:https://zhuanlan.zhihu.com/p/38520597
程式碼部分總的執行流程如下:
程式碼結構截圖:
在這裡插入圖片描述
程式碼實現模型的初始化,首先是構建mtcnn的tensorflow計算圖,然後載入訓練好的模型引數,細節是分別呼叫pnet,rnet,onet組裝成mtcnn。裡面涉及python裝飾器的內容,首先寫了一個基本的層,然後用裝飾器分別實現神經網路各種層,感覺有點類似caffe的工廠模式。
在這裡插入圖片描述


程式碼實現最終的檢測道德人臉框的四個點座標和5個點的包括雙眼,鼻尖,兩個嘴角的座標。
在這裡插入圖片描述
這是官方訓練好的模型檔案。
在這裡插入圖片描述
這裡會對檢測到的人臉框和五點位置進一步處理,最終得到我們想要的結果。
在這裡插入圖片描述
這裡是程式碼的核心了,首先得到影象金字塔,參考博文。https://blog.csdn.net/poem_qianmo/article/details/26157633
然後輸入pnet,返回值輸入框的初始化,然後nms,然後rect實現正方形框輸出,然後因為預測的框會超過原圖邊界,所以會涉及畫素填充呼叫pad函式,然後輸入到rnet,影象縮放、畫素值歸一化,歸一化就是等比例縮放數值,如下圖所示:
在這裡插入圖片描述
後面的也會有nms,等函式的呼叫就不一一細說了,先理解整體框架,具體程式碼實現講解後面補充。
下面列出pro:net的網路結構圖。
在這裡插入圖片描述

pnet 人臉得分 conv4-1, 人臉框對應 conv4-2,
在這裡插入圖片描述
在這裡插入圖片描述
rnet 人臉得分 conv5-1, 人臉框對應 conv5-2,
在這裡插入圖片描述
onet 人臉得分 conv6-1, 人臉框對應 conv6-2, 和 五點conv6-3 對應
程式碼解釋:detect_face.py

1、def imresample(img, sz):函式主要功能對圖片進行縮小放大。
im_data = cv2.resize(img, (sz[1], sz[0]), interpolation=cv2.INTER_AREA) 
img 輸入圖片
sz[1], sz[0] resiz的尺寸 
利用python opencv中的 cv.Resize(源,目標,變換方法)就可以實現變換為想要的尺寸了
原始檔:就不用說了
目標:你可以對影象進行倍數的放大和縮小也可以直接的輸入尺寸大小
變換的方法:
CV_INTER_NN <-- 最近鄰插值,  
CV_INTER_LINEAR <-- 雙線性插值 (預設使用)  
CV_INTER_AREA <-- 使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現。當影象放大時,類似於 CV_INTER_NN 方法..  
CV_INTER_CUBIC <-- 立方插值.  
2、def rerec(bboxA):函式主要功能是將檢測到的矩形框以原的中心點為中心轉換為正方形。
# convert bboxA to square
h = bboxA[:,3]-bboxA[:,1]<--求檢測框的高度
w = bboxA[:,2]-bboxA[:,0]<--求檢測框的寬度
l = np.maximum(w, h)<--求高度寬度的最大值
bboxA[:,0] = bboxA[:,0]+w*0.5-l*0.5<--先求框的中心點座標bboxA[:,0]+w*0.5,再按最大值以中心點為中心向外擴大l*0.5
bboxA[:,1] = bboxA[:,1]+h*0.5-l*0.5<--同上這樣就可得到新的Xmin、Ymin座標。
bboxA[:,2:4] = bboxA[:,0:2] + np.transpose(np.tile(l,(2,1)))<--首先將l的值複製兩行然後轉置然後前兩列分別對應加上後兩列,幾何上就是Xmax = Xmin + l,Ymax = Ymin + 1。

下面是測試程式和效果圖片

import numpy as np
import cv2
bboxA = np.array([[30, 20, 60, 60]])
img = np.ones((80, 80), dtype=np.uint8)
img[:, :] = 100
cv2.rectangle(img, (30, 20), (60, 60), (0, 255, 0), 1)
cv2.imwrite("pure.jpg", img)
h = bboxA[:, 3] - bboxA[:, 1]
w = bboxA[:, 2] - bboxA[:, 0]
l = np.maximum(w, h)
bboxA[:, 0] = bboxA[:, 0] + w * 0.5 - l * 0.5
bboxA[:, 1] = bboxA[:, 1] + h * 0.5 - l * 0.5
bboxA[:, 2:4] = bboxA[:, 0:2] + np.transpose(np.tile(l, (2, 1)))
img = cv2.imread("pure.jpg")
cv2.rectangle(img, (bboxA[0, 0:1], bboxA[0, 1:2]), (bboxA[0, 2:3], bboxA[0, 3]), (0, 255, 0), 1)
cv2.imwrite("pure.jpg", img)

在這裡插入圖片描述

4、def pad(total_boxes, w, h):
tmpw = (total_boxes[:,2]-total_boxes[:,0]+1).astype(np.int32)<--計算寬度有多少個畫素,加1就是至少又有個畫素值
tmph = (total_boxes[:,3]-total_boxes[:,1]+1).astype(np.int32)<--同上
numbox = total_boxes.shape[0]<--計算框的數目也就是矩陣有多少行
dx = np.ones((numbox), dtype=np.int32)<-建立陣列
例程:
numbox = 5
dx = np.ones((numbox), dtype=np.int32)
print(dx)
[1 1 1 1 1]
dy = np.ones((numbox), dtype=np.int32)<--同上
edx = tmpw.copy().astype(np.int32)<--複製陣列
例程:
total_boxes = np.array([[30, 20, 60, 60]])
tmpw = (total_boxes[:, 2] - total_boxes[:, 0] + 1).astype(np.int32)
print(tmpw)
edx = tmpw.copy().astype(np.int32)
print(edx)
[31]
[31]

x = total_boxes[:,0].copy().astype(np.int32)<--複製框的xmin、ymin,xmax、ymax 到 x 、y、ex、ey

total_boxes = np.array([[30, 20, 60, 60], [3, 2, 6, 6]])
x = total_boxes[:, 0].copy().astype(np.int32)
print(x)
y = total_boxes[:, 1].copy().astype(np.int32)
print(y)
ex = total_boxes[:, 2].copy().astype(np.int32)
print(ex)
ey = total_boxes[:, 3].copy().astype(np.int32)
print(ey)

[30  3]
[20  2]
[60  6]
[60  6]

tmp = np.where(ex>w)<--返回xmax大於w的索引

w = 6
total_boxes = np.array([[30, 20, 60, 60], [3, 2, 6, 6]])
x = total_boxes[:, 0].copy().astype(np.int32)
y = total_boxes[:, 1].copy().astype(np.int32)
ex = total_boxes[:, 2].copy().astype(np.int32)
print(ex)
ey = total_boxes[:, 3].copy().astype(np.int32)
tmp = np.where(ex > w)
print(tmp)

[60  6]
(array([0]),)
edx.flat[tmp] = np.expand_dims(-ex[tmp] + w + tmpw[tmp], 1)
-ex[tmp]<--xmax的負值,
tmpw[tmp]<--tmp是上一步獲取的索引、tmpw 是 Xmax值的陣列 通過索引值獲取Xmax的值
np.expand_dims<--在某一維度上進行擴增
edx<--是寬的值
.flat<--降維的意思
np.array([[[[[1, 2, 3, 4, 5]]]]]).flat
>[1, 2, 3, 4, 5]

total_boxes = np.array([[30, 20, 60, 60], [3, 2, 6, 6]])
w = 6
tmpw = (total_boxes[:, 2] - total_boxes[:, 0] + 1).astype(np.int32)
tmph = (total_boxes[:, 3] - total_boxes[:, 1] + 1).astype(np.int32)
numbox = total_boxes.shape[0]

dx = np.ones((numbox), dtype=np.int32)
dy = np.ones((numbox), dtype=np.int32)
edx = tmpw.copy().astype(np.int32)
edy = tmph.copy().astype(np.int32)

x = total_boxes[:, 0].copy().astype(np.int32)
y = total_boxes[:, 1].copy().astype(np.int32)
ex = total_boxes[:, 2].copy().astype(np.int32)
ey = total_boxes[:, 3].copy().astype(np.int32)

tmp = np.where(ex > w)
print(tmp)

print(-ex[tmp])
print(tmpw[tmp])
print(-ex[tmp] + w + tmpw[tmp],)
print(np.expand_dims(-ex[tmp] + w + tmpw[tmp], 0))
print(np.expand_dims(-ex[tmp] + w + tmpw[tmp], 1))
edx.flat[tmp] = np.expand_dims(-ex[tmp] + w + tmpw[tmp], 1)
print(edx.flat[tmp])
ex[tmp] = w
print(ex[tmp])

(array([0]),)
[-60]
[31]
(array([-23], dtype=int32),)
[[-23]]
[[-23]]
[-23]
[6]
 I = np.argsort(s)
 升序排列 排在最後的是值最大的下標

例程

a = np.array([6, 0, 5,])
b = np.argsort(a)
print(b)
[1 2 0]
pick = np.zeros_like(s, dtype=np.int16)
其維度與矩陣W一致,併為其初始化為全0
xx1 = np.maximum(x1[i], x1[idx])
接收兩個引數 分別和另一個比較 輸出大的值

例程

x1 = np.array([6, 0, 5,])
b = np.argsort(x1)
# print(b)
i = b[-1]
idx = b[0:-1]
# print(idx)
xx1 = np.maximum(x1[i], x1[idx])
print(x1[i])
print(x1[idx])
print(xx1)

np.maximum([-2, -1, 0, 1, 2], 0)
print(np.maximum([-2, -1, 0, 1, 2], 0))
print(np.maximum([5, 9, 5, 1], 3))
xx1 = np.maximum(x1[i], x1[idx])最大的xmin
yy1 = np.maximum(y1[i], y1[idx])最大的ymin
 xx2 = np.minimum(x2[i], x2[idx])最小的xmax
  yy2 = np.minimum(y2[i], y2[idx])最小的ymax
w = np.maximum(0.0, xx2-xx1+1)最小的寬
h = np.maximum(0.0, yy2-yy1+1)最小的高
獲取最小的面積
o = inter / np.minimum(area[i], area[idx])
返回的o是面積比值的陣列
I = I[np.where(o<=threshold)]
返回滿足條件o<=threshold的陣列I 然後I進入下一個迴圈 一直到I裡面一個元素都沒有,返回pick陣列 pick數組裡面記錄了比較過程中滿足o > threshold 值的索引。