深刻理解MTCNN原理, 超級(jí)詳細(xì),從零開(kāi)始做人臉檢測(cè)。Tensorflow2實(shí)現(xiàn)

前言

水了幾篇不痛不癢的博客,于是決定把mtcnn的原理記錄下,分享給想要學(xué)習(xí)人臉識(shí)別,但是很糾結(jié)如何開(kāi)始的人。網(wǎng)上關(guān)于mtcnn的教程大同小異,不同不癢,認(rèn)真看完此文,如果還不會(huì)mtcnn,那么請(qǐng)你來(lái)掐死我


來(lái)看看效果


關(guān)于檢測(cè)

檢測(cè),顧名思義就是找到我們需要的物體,并且標(biāo)注他在圖像中的位置。

  • 從mnist數(shù)據(jù)集開(kāi)始
    • 大多數(shù)朋友上有的第一個(gè)數(shù)據(jù)集,有著深度學(xué)習(xí)的hello world之稱!我們先看看數(shù)據(jù)集的樣子:

      經(jīng)過(guò)我處理后可視化的mnist數(shù)據(jù)集的一部分
      經(jīng)過(guò)處理后(可能以后我會(huì)出文如何處理得到這樣的圖片),我把里面的零拿了出來(lái),每張圖片是28x28大小,都有一個(gè)0~9之間的數(shù)字

      今天不講如何處理mnist,稍微提一下,我們把每張有數(shù)字的圖片放到神經(jīng)網(wǎng)絡(luò)中,然后會(huì)輸出一個(gè)結(jié)果告訴我們這張圖片是數(shù)字幾,這樣就做到了物體的識(shí)別

      同樣地,我們可以把0~9的數(shù)字圖片換成其他,例如貓狗,那我們就可以訓(xùn)練得到區(qū)分貓還是狗的神經(jīng)網(wǎng)絡(luò)。

import tensorflow as tf
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
print(train_images.shape) # 6000,28,28,1

  • 出現(xiàn)問(wèn)題
    貌似我們可以用這個(gè)思路區(qū)分其他的物體,可是,看mnist數(shù)據(jù)集的圖片我們發(fā)現(xiàn),每張圖片只有一個(gè)數(shù)字,也就是,我們訓(xùn)練好的網(wǎng)絡(luò),將來(lái)也只能識(shí)別單個(gè)物體,如果要識(shí)別的圖片有兩個(gè)數(shù)字,我們需要向辦法將兩個(gè)數(shù)字分開(kāi),分別輸入到網(wǎng)絡(luò)進(jìn)行預(yù)測(cè)。那我們有沒(méi)有辦法讓我們訓(xùn)練的網(wǎng)絡(luò)一次可以識(shí)別多個(gè)物體呢?
  • 解決

滑動(dòng)窗口

先看張圖片:

有人有動(dòng)物,很唯美很和諧
如果直接把圖片輸入神經(jīng)網(wǎng)絡(luò)預(yù)測(cè)可行嗎?顯然,大多數(shù)情況下是不可以直接傳入神經(jīng)網(wǎng)絡(luò)獲得預(yù)測(cè)結(jié)果的。
把一張圖片分成許多張小圖片
像這樣,我們把圖片分成許多小圖片,然后每張小圖片截取下來(lái),上圖我們可以得到8張小圖片,我們將這8張圖片一次傳入網(wǎng)絡(luò)中判斷那個(gè)小格子里面是什么物體。問(wèn)題解決了嗎?我們可以看到,小動(dòng)物的頭我們可以找到,可是小女孩的頭部被分成了兩張圖片了,顯然這樣子我們找不到小女孩。此時(shí),我們可以利用滑動(dòng)窗口的方法
圖片有點(diǎn)亂,慢慢看~~
最左上角的紅色框框就是我們選定的框框,我們先把紅色框框的圖片截取下來(lái)傳入神經(jīng)網(wǎng)絡(luò)中預(yù)測(cè),然后我們把紅色框框向右平移,我們得到粉紅色的框框,然后我們?cè)侔逊奂t色框框的圖片截取出來(lái)送到網(wǎng)絡(luò)中預(yù)測(cè),以此類推我們每次將框框向右移動(dòng)一定的長(zhǎng)度,然后獲得下一個(gè)框框,當(dāng)框框移動(dòng)到第一行的最后時(shí)候,也就是和黑色框框重合的時(shí)候,第一行我們?nèi)⊥炅耍缓髮⒖蚩?strong>向下平移,此時(shí)當(dāng)作第二行,簡(jiǎn)單總結(jié)就是將框框按照一定的長(zhǎng)度平移,遍歷整張圖片
此刻,我們可也找到小女孩和小動(dòng)物的位置。
我們暫且叫這個(gè)方法框框滑動(dòng)理論

我們來(lái)看看這個(gè)方法有什么有缺點(diǎn):

優(yōu)點(diǎn)是可以找到一幅圖像的多個(gè)物體,還能找到大致位置,缺點(diǎn)也很明顯,越是想要找到多的物體,那么框框移動(dòng)的距離就要小,當(dāng)移動(dòng)的距離越小,那么圖片越多,識(shí)別的效率就降低。

MTCNN

我們看了一下單個(gè)物體的檢測(cè)和多個(gè)物體的檢測(cè),接下來(lái)是有關(guān)人臉的檢測(cè)建議看看原論文

  • MTCNN由三個(gè)級(jí)聯(lián)的網(wǎng)絡(luò)組成,分別是PNET,RNET,ONET

圖片經(jīng)過(guò)預(yù)處理,先經(jīng)過(guò)pnet網(wǎng)絡(luò),將結(jié)果給rnet網(wǎng)絡(luò),rnet網(wǎng)絡(luò)的輸出再傳入onet網(wǎng)絡(luò),onet最后得到輸出結(jié)果

  • Pnet
    pnet網(wǎng)絡(luò)結(jié)構(gòu)
    不需要看懂,我們看最左邊的正方形,下面標(biāo)注12x12x3,沒(méi)錯(cuò)這個(gè)就是框框的大小,就是我們我們上面找到小女孩的框框,長(zhǎng)寬都是12個(gè)像素,最后面的3代表圖像是三通道的,也就是RGB彩色圖像。我們將用12x12大小的框框來(lái)找人臉(重點(diǎn)),這里框框每次移動(dòng)的距離是兩個(gè)像素(不展開(kāi)論述)
  • 小朋友你現(xiàn)在是否有很多問(wèn)號(hào)
    • 12x12不會(huì)太小了嗎?我的臉肯定比12x12大呀QvQ

別急,聽(tīng)我娓娓道來(lái)。
論文對(duì)要進(jìn)行檢測(cè)的圖像做了圖像金字塔

圖像金字塔

大家在腦海里想一想金字塔長(zhǎng)什么樣子,尖尖的,對(duì)吧!其實(shí)就是將圖像進(jìn)行一定比例,一系列的縮放。。。(瞬間low了不少)

縮放的極限是到12x12,再小下去的話,框框都比圖像大了,哪怕你再大的頭,總會(huì)有個(gè)縮放比例,把你的頭變小的,此刻應(yīng)該有一波掌聲,原論文就是妙.不妨給我點(diǎn)個(gè)小贊,加個(gè)關(guān)注
下面我將驗(yàn)證我的框框滑動(dòng)理論,將有一大波代碼來(lái)襲,請(qǐng)注意—_—

import cv2  # opencv庫(kù)
import tensorflow as tf
import numpy as np

下面是網(wǎng)絡(luò)結(jié)構(gòu)

def Pnet():
    input = tf.keras.Input(shape=[None, None, 3])
    x = tf.keras.layers.Conv2D(10, (3, 3), strides=1, padding='valid', name='conv1')(input)
    x = tf.keras.layers.PReLU(shared_axes=[1, 2], name='PReLU1')(x)
    x = tf.keras.layers.MaxPooling2D()(x)
    x = tf.keras.layers.Conv2D(16, (3, 3), strides=1, padding='valid', name='conv2')(x)
    x = tf.keras.layers.PReLU(shared_axes=[1, 2], name='PReLU2')(x)
    x = tf.keras.layers.Conv2D(32, (3, 3), strides=1, padding='valid', name='conv3')(x)
    x = tf.keras.layers.PReLU(shared_axes=[1, 2], name='PReLU3')(x)
    classifier = tf.keras.layers.Conv2D(2, (1, 1), activation='softmax',name='conv4-1')(x)
    bbox_regress = tf.keras.layers.Conv2D(4, (1, 1), name='conv4-2')(x)
    model = tf.keras.models.Model([input], [classifier, bbox_regress])
    model.summary()
    return model
model = Pnet() # 讀取網(wǎng)絡(luò)結(jié)構(gòu)
model.load_weights("./pnet.h5", by_name=True) # 讀取預(yù)訓(xùn)練權(quán)重,此步驟可以省略

講講我的思路,我將準(zhǔn)備一張12x12x3大小的人臉圖片傳入網(wǎng)絡(luò),看看輸出結(jié)果,之后,我在這張圖片的最后邊添加兩列大小全為255的像素點(diǎn),底下也添加兩行,此時(shí)圖片變成14x14

12x12x3
img = cv2.imread("./face1.jpg") # 用opencv的方法把圖像讀取進(jìn)來(lái)
img = cv2.cvtColor(img, cv2.COLOR_BRG2RGB)
img = (img-127.5)/127.5 # 歸一化,不展開(kāi)
img = img.reshape(1, *img.shape) # shape=1x12x12x3 只有四維才符合輸入格式
  • 圖像處理完畢,接下來(lái)傳入網(wǎng)絡(luò)進(jìn)行預(yù)測(cè),我們看看會(huì)得到什么結(jié)果
out = model.predict(img)

結(jié)果
我們可以看到兩個(gè)array,為什么會(huì)這樣?我們回到pnet的網(wǎng)絡(luò)結(jié)構(gòu)圖:
pnet結(jié)構(gòu)
這次我們看到最右邊,從上到下一次是face classification boundingbox regressino Facial landmark localization也就是人臉概率邊框回歸標(biāo)記點(diǎn),簡(jiǎn)單來(lái)說(shuō)就是會(huì)輸出三個(gè)結(jié)果,第一個(gè)結(jié)果的大小是1x1x2的這個(gè)代表是否是人臉的概率也就對(duì)應(yīng)我們得到結(jié)果的第一個(gè)array:
第一個(gè)array
這個(gè)數(shù)組有兩個(gè)值,第一個(gè)代表非人臉的概率,第二個(gè)值代表人臉的概率,我們很清晰看到第二值約等于0.9926633,換成百分比就是百分之九九,我們的網(wǎng)絡(luò)說(shuō)這個(gè)框框有99的概率是人臉!!!先別激動(dòng),我們看看第二個(gè)數(shù)組:
第二個(gè)array
這個(gè)array有四個(gè)值,也就是對(duì)應(yīng)網(wǎng)絡(luò)結(jié)構(gòu)中的boundingbox regression,它的shape是1x1x4,也就對(duì)應(yīng)我們第二個(gè)數(shù)組的四個(gè)輸出值,這四個(gè)值可以姑且當(dāng)作offset(偏移量), 分別對(duì)應(yīng)左上角坐標(biāo)x和y值的偏移量,右下角坐標(biāo)x和y的偏移量,用原來(lái)的坐標(biāo)加上坐標(biāo)對(duì)應(yīng)的偏移量的數(shù)值,就可以得到我們網(wǎng)絡(luò)預(yù)測(cè)的人臉框的大小。

對(duì)于最后的Facial landmark, pnet代碼中的代碼中沒(méi)有體現(xiàn),所以就沒(méi)有輸出結(jié)果。

如果沒(méi)有看明白,可以聯(lián)系我,討論學(xué)習(xí)。

  • 接下來(lái)讓我把圖片處理一下,變成14x14大小
    14x14x3
    為了方便看,我在右邊和下邊加了兩條綠色的像素,接著我們把這張圖片傳入網(wǎng)絡(luò)看看是什么效果:
    結(jié)果
    看著有點(diǎn)小復(fù)雜,不荒,我們研究一下:
    同樣只有兩個(gè)array,第一個(gè)array,里面是4x2的矩陣第二個(gè)array是4x4的矩陣
    為什么是4x2和4x4?
  • 當(dāng)我們輸入的大小是12x12的時(shí)候,輸出是1x2和1x4
  • 當(dāng)我們輸入的大學(xué)是14x14的時(shí)候,輸出是4x2和4x4

由此猜測(cè),當(dāng)輸入16x16的時(shí)候,輸出結(jié)果是9x2和9x4,感興趣可以試試,一定是這個(gè)結(jié)果或者說(shuō)應(yīng)該是3x3x2和3x3x4

  • 所以,14x14,輸出大小準(zhǔn)確點(diǎn)應(yīng)該是2x2x2和2x2x4。(有點(diǎn)小復(fù)雜)

畫(huà)重點(diǎn)
也就是說(shuō),14x14的圖片被分成了4張12x12的小圖片被傳入了網(wǎng)絡(luò),得到的結(jié)果在組合起來(lái),因?yàn)榭蚩虻拇笮∈?2x12,所以可以去到左上角一張圖片右上角一張圖片,左下角和右下角的圖片,有4張,并且位置對(duì)應(yīng)2x2,所以才有這個(gè)結(jié)果

有點(diǎn)饒,多看幾次

換個(gè)角度

我上面已經(jīng)得到了14x14的圖片,我安下面方式截取四張圖片

也就是對(duì)應(yīng)于原圖的左上右上左下右下各取12x12大小出來(lái),依次傳入網(wǎng)絡(luò)中


左上
右上
左下

右下

左上
右上
左下

右下

下面我放一下直接傳入14x14的原圖的輸出結(jié)果:
結(jié)果

自行比對(duì)一下,我不用多說(shuō)


小結(jié)

通過(guò)這種敘事方式,我還沒(méi)有看到敘事得比我詳細(xì)的mtcnn講解,限于篇幅原因,我不會(huì)放大量代碼,也只是講一下我當(dāng)時(shí)最難以理解的地方,后面會(huì)考慮出源碼的解析(當(dāng)然是我自己重構(gòu)后的代碼),剩余部分我會(huì)后面更新完,不過(guò)我覺(jué)得到這里已經(jīng)茶并不多了,后面大同小異,最難的部分已經(jīng)過(guò)去了。
如果覺(jué)得對(duì)你有幫助,可以給我點(diǎn)個(gè)贊,能幫到你我很開(kāi)心,如果有任何疑問(wèn),可以留言,也可以和我聯(lián)系。
加油,有緣人!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評(píng)論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,067評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,467評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,468評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,184評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,582評(píng)論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,794評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,343評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,096評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,291評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,513評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,941評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,190評(píng)論 1 291
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,026評(píng)論 3 396
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,253評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容