1. 程式人生 > >tensorflow實現人臉檢測及識別(簡單版)

tensorflow實現人臉檢測及識別(簡單版)

本教程主要是對人臉檢測及識別python實現系列碉堡了!程式設計師用深度學習寫了個老闆探測器(付原始碼) 的實現。主要實現的功能是用網路攝像頭自動識別在工位通道走過的人臉,如果確認是老闆的話,就用一張圖片覆蓋到整個螢幕上。雖然原教程已經寫的很好,但是我們在實現的時候仍然踩了很多坑。本著讓後來者少走點彎路的原則,我們特將最詳細的步驟記錄如下,以期讀者朋友只花最少的時間便能體驗用tensorflow實現人臉檢測及識別。硬體要求:一臺電腦和一個普通的USB攝像頭(或電腦自帶攝像頭),U盤(用來製作Ubuntu安裝盤)。

環境搭建:

1.Ubuntu 16.04

2.python 3.5 +

3.tensorflow-gpu 1.2 或著cpu版本的也可以的

4.opencv :pip3 install opencv-python

5.安裝keras、sklearn等 :pip3 install --user scikit-learn

獲取並顯示攝像頭視訊

檢查攝像頭是否正常工作

  • 安裝cheese:<sudo apt-get install cheese >
  • shell輸入cheese,如果彈出攝像頭視窗,說明攝像頭正常。
  • 檢查攝像頭id:<ls /dev/video*>,一般id為0。

利用OpenCV獲取攝像頭的視訊流並展示

  • 建立名為catch_usb_video.py的python檔案,將下列程式碼copy進去,儲存,退出。
import cv2
import sys
from PIL import Image

def CatchUsbVideo(window_name, camera_idx):
    cv2.namedWindow(window_name)

    #視訊來源,可以來自一段已存好的視訊,也可以直接來自USB攝像頭
    cap = cv2.VideoCapture(camera_idx)        

    while cap.isOpened():
        ok, frame = cap.read() #讀取一幀資料
        if not ok:            
            break                    

        #顯示影象並等待10毫秒按鍵輸入,輸入‘q’退出程式
        cv2.imshow(window_name, frame)
        c = cv2.waitKey(10)
        if c & 0xFF == ord('q'):
            break        

    #釋放攝像頭並銷燬所有視窗
    cap.release()
    cv2.destroyAllWindows() 
    
    
if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage:%s camera_id\r\n" % (sys.argv[0]))
    else:
        CatchUsbVideo("擷取視訊流", int(sys.argv[1]))
  • 在command視窗輸入:<python3 catch_usb_video.py 0>執行上述程式。
  • 按q關閉Capturing Video視窗

識別出人臉

  • 建立名為recognise_face.py的資料夾,存入下述程式碼:
#-*- coding: utf-8 -*-

import cv2
import sys
from PIL import Image

def CatchUsbVideo(window_name, camera_idx):
    cv2.namedWindow(window_name)

    #視訊來源,可以來自一段已存好的視訊,也可以直接來自USB攝像頭
    cap = cv2.VideoCapture(camera_idx)                

    #告訴OpenCV使用人臉識別分類器
    classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml")

    #識別出人臉後要畫的邊框的顏色,RGB格式
    color = (0, 255, 0)

    while cap.isOpened():
        ok, frame = cap.read() #讀取一幀資料
        if not ok:            
            break  

        #將當前幀轉換成灰度影象
        grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)                 

        #人臉檢測,1.2和2分別為圖片縮放比例和需要檢測的有效點數
        faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
        if len(faceRects) > 0:            #大於0則檢測到人臉                                   
            for faceRect in faceRects:  #單獨框出每一張人臉
                x, y, w, h = faceRect        
                cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
        #顯示影象
        cv2.imshow(window_name, frame)        
        c = cv2.waitKey(10)
        if c & 0xFF == ord('q'):
            break        

    #釋放攝像頭並銷燬所有視窗
    cap.release()
    cv2.destroyAllWindows() 

if __name__ == '__main__':
    # if len(sys.argv) != 2:
    #    print("Usage:%s camera_id\r\n" % (sys.argv[0]))
    #else:
     #   CatchUsbVideo("Recognising Face", int(sys.argv[1]))
    CatchUsbVideo("Recognising Face", 0 )

執行上述程式碼,跳出的視訊視窗中,人臉區域會被方框圈起來。

關於程式碼有幾個地方需要重點交代,首先就是人臉分類器這行:

 #告訴OpenCV使用人臉識別分類器
 classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml")

這行程式碼指定OpenCV選擇使用哪種分類器(注意,一定習慣分類這個說法,ML的監督學習研究的就是各種分類問題),OpenCV提供了多種分類器:


上圖為我的電腦上安裝的OpenCV3.2提供的所有分類器,有識別眼睛的(甚至包括左右眼),有識別身體的,有識別笑臉的,甚至還有識別貓臉的,有興趣的可以逐個試試。關於人臉識別,OpenCV提供多個分類器選擇使用,其中haarcascade_frontalface_alt_tree.xml是最嚴格的分類器,光線、帶個帽子都有可能識別不出人臉。其它的稍微好點,default那個識別最寬鬆,某些情況下我家裡的燈籠都會被識別成人臉;)。另外安裝環境不同,分類器的安裝路徑也有可能不同,請在安裝完OpenCV後根據分類器的實際安裝路徑修改程式碼。另外再多說一句,如果我們想構建自己的分類器,比如檢測火焰(火災報警)、汽車(確定路口汽車數量),我們依然可以使用OpenCV訓練構建,詳細說明參見OpenCV的官方文件。

接下來解釋如下幾行程式碼:

 #人臉檢測,1.2和2分別為圖片縮放比例和需要檢測的有效點數
 faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
 if len(faceRects) > 0:            #大於0則檢測到人臉                                   
    for faceRect in faceRects:  #單獨框出每一張人臉
        x, y, w, h = faceRect        
        cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)

其中classfier.detectMultiScale()即是完成實際人臉識別工作的函式,該函式引數說明如下:

grey:要識別的影象資料(即使不轉換成灰度也能識別,但是灰度圖可以降低計算強度,因為檢測的依據是哈爾特徵,轉換後每個點的RGB資料變成了一維的灰度,這樣計算強度就減少很多)

scaleFactor:影象縮放比例,可以理解為同一個物體與相機距離不同,其大小亦不同,必須將其縮放到一定大小才方便識別,該引數指定每次縮放的比例

minNeighbors:對特徵檢測點周邊多少有效點同時檢測,這樣可避免因選取的特徵檢測點太小而導致遺漏

minSize:特徵檢測點的最小值

對同一個畫面有可能出現多張人臉,因此,我們需要用一個for迴圈將所有檢測到的人臉都讀取出來,然後逐個用矩形框框出來,這就是接下來的for語句的作用。Opencv會給出每張人臉在影象中的起始座標(左上角,x、y)以及長、寬(h、w),我們據此就可以截取出人臉。其中,cv2.rectangle()完成畫框的工作,在這裡我有意識的外擴了10個畫素以框出比人臉稍大一點的區域。cv2.rectangle()函式的最後兩個引數一個用於指定矩形邊框的顏色,一個用於指定矩形邊框線條的粗細程度。


為模型訓練準備人臉資料

這部分採集兩個人臉資料,分別存到data/me和data/other 資料夾裡。
* 在主目錄下,建立data資料夾,data資料夾裡建立me和other資料夾愛。
* 在主目錄下,建立face_data.py檔案,存入下述程式碼

#-*- coding: utf-8 -*-

import cv2
import sys

from PIL import Image

def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
    cv2.namedWindow(window_name)

    #視訊來源,可以來自一段已存好的視訊,也可以直接來自USB攝像頭
    cap = cv2.VideoCapture(camera_idx)                

    #告訴OpenCV使用人臉識別分類器
    classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml")

    #識別出人臉後要畫的邊框的顏色,RGB格式
    color = (0, 255, 0)

    num = 0    
    while cap.isOpened():
        ok, frame = cap.read() #讀取一幀資料
        if not ok:            
            break                

        grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  #將當前楨影象轉換成灰度影象       
        #人臉檢測,1.2和2分別為圖片縮放比例和需要檢測的有效點數
        faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
        if len(faceRects) > 0:          #大於0則檢測到人臉                                   
            for faceRect in faceRects:  #單獨框出每一張人臉
                x, y, w, h = faceRect                        

                #將當前幀儲存為圖片
                img_name = '%s/%d.jpg'%(path_name, num)                
                image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                cv2.imwrite(img_name, image)                                

                num += 1                
                if num > (catch_pic_num):   #如果超過指定最大儲存數量退出迴圈
                    break

                #畫出矩形框
                cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)

                #顯示當前捕捉到了多少人臉圖片了,這樣站在那裡被拍攝時心裡有個數,不用兩眼一抹黑傻等著
                font = cv2.FONT_HERSHEY_SIMPLEX
                cv2.putText(frame,'num:%d' % (num),(x + 30, y + 30), font, 1, (255,0,255),4)                

        #超過指定最大儲存數量結束程式
        if num > (catch_pic_num): 
            break                

        #顯示影象
        cv2.imshow(window_name, frame)        
        c = cv2.waitKey(10)
        if c & 0xFF == ord('q'):
            break 
    #釋放攝像頭並銷燬所有視窗
    cap.release()
    cv2.destroyAllWindows() 

if __name__ == '__main__':
    if len(sys.argv) != 4:
        print("Usage:%s camera_id face_num_max path_name\r\n" % (sys.argv[0]))
    else:
        CatchPICFromVideo("Collecting face data", int(sys.argv[1]), int(sys.argv[2]), sys.argv[3])
            
    '''
    在shell先後執行:<python face_data.py 0 1000 data/me> 和
    <python face_data.py 0 1000 data/other>分別採集兩個人各1000個人臉資料存入相應資料夾。
    '''    
     
  • 手動剔除data資料夾中不是圖片檔案的資料。
OpenCV對人臉的識別也不是100%準確,因此,我們擷取的人臉影象中會有些不合格的,比如誤把燈籠當人臉存下來了或者人臉影象很模糊。在我擷取的1000張人臉中大約有幾十張這樣的,要想確保模型可靠,必須要把這樣的圖片去掉。這個活只能手動了,沒辦法。幸運的是,資料量不大,不會耽誤太多時間的。最後,請確保程式所在路徑下已經有了data/me和data/other兩個資料夾及各1000張人臉圖片。

利用keras庫訓練人臉識別模型

利用keras庫來建立模型和評估模型
* 主目錄下建立model資料夾:<mkdir model>

首先我們建立一個空白的python檔案,檔名為:load_face_dataset.py,程式碼如下:

# -*- coding: utf-8 -*-
# load_face_dataset.py

import os
import sys
import numpy as np
import cv2

IMAGE_SIZE = 64

#按照指定影象大小調整尺寸
def resize_image(image, height = IMAGE_SIZE, width = IMAGE_SIZE):
    top, bottom, left, right = (0, 0, 0, 0)
    
    #獲取影象尺寸
    h, w, _ = image.shape
    
    #對於長寬不相等的圖片,找到最長的一邊
    longest_edge = max(h, w)    
    
    #計算短邊需要增加多上畫素寬度使其與長邊等長
    if h < longest_edge:
        dh = longest_edge - h
        top = dh // 2
        bottom = dh - top
    elif w < longest_edge:
        dw = longest_edge - w
        left = dw // 2
        right = dw - left
    else:
        pass 
    
    #RGB顏色
    BLACK = [0, 0, 0]
    
    #給影象增加邊界,是圖片長、寬等長,cv2.BORDER_CONSTANT指定邊界顏色由value指定
    constant = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = BLACK)
    
    #調整影象大小並返回
    return cv2.resize(constant, (height, width))

#讀取訓練資料
images = []
labels = []
def read_path(path_name):    
    for dir_item in os.listdir(path_name):
        #從初始路徑開始疊加,合併成可識別的操作路徑
        full_path = os.path.abspath(os.path.join(path_name, dir_item))
        
        if os.path.isdir(full_path):    #如果是資料夾,繼續遞迴呼叫
            read_path(full_path)
        else:   #檔案
            if dir_item.endswith('.jpg'):
                image = cv2.imread(full_path)                
                image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
                
                #放開這個程式碼,可以看到resize_image()函式的實際呼叫效果
                #cv2.imwrite('1.jpg', image)
                
                images.append(image)                
                labels.append(path_name)                                
                    
    return images,labels
    

#從指定路徑讀取訓練資料
def load_dataset(path_name):
    images,labels = read_path(path_name)    
    
    #將輸入的所有圖片轉成四維陣列,尺寸為(圖片數量*IMAGE_SIZE*IMAGE_SIZE*3)
    #我和閨女兩個人共1200張圖片,IMAGE_SIZE為64,故對我來說尺寸為1200 * 64 * 64 * 3
    #圖片為64 * 64畫素,一個畫素3個顏色值(RGB)
    images = np.array(images)
    print(images.shape)    
    
    #標註資料,'me'資料夾下都是我的臉部影象,全部指定為0,另外一個資料夾下是閨女的,全部指定為1
    labels = np.array([0 if label.endswith('me') else 1 for label in labels])    
    
    return images, labels

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage:%s path_name\r\n" % (sys.argv[0]))    
    else:
        images, labels = load_dataset(sys.argv[1])
        

resize_image()函式。這個函式其實就做了一件事情,判斷圖片是不是四邊等長,也就是圖片是不是正方形。如果不是,則短的那兩邊增加兩條黑色的邊框,使影象變成正方形,這樣再呼叫cv2.resize()函式就可以實現等比例縮放了。因為我們指定縮放的比例就是64 x 64,只有縮放之前影象為正方形才能確保影象不失真。resize_image()函式的執行結果如下所示:


上圖為200 x 300的圖片,寬度小於高度,因此,需要增加寬度,正常應該是兩邊各增加寬50畫素的黑邊:


如我們所願,成了一個300 x 300的正方形圖片,這時我們再縮放到64 x 64就可以了:

上圖就是我們將要輸入到CNN中的圖片,之所以縮放到這麼小,主要是為了減少計算量及記憶體佔用,提升訓練速度。執行程式之前,請把圖片組織一下,結構參見下圖:

* 主目錄下建立face_train_use_keras.py檔案,存入下述程式碼:

#-*- coding: utf-8 -*-
import random

import numpy as np
from sklearn.cross_validation import train_test_split
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.optimizers import SGD
from keras.utils import np_utils
from keras.models import load_model
from keras import backend as K

from load_face_dataset import load_dataset, resize_image, IMAGE_SIZE

class Dataset:
    def __init__(self, path_name):
        #訓練集
        self.train_images = None
        self.train_labels = None

        #驗證集
        self.valid_images = None
        self.valid_labels = None

        #測試集
        self.test_images  = None            
        self.test_labels  = None

        #資料集載入路徑
        self.path_name    = path_name

        #當前庫採用的維度順序
        self.input_shape = None

    #載入資料集並按照交叉驗證的原則劃分資料集並進行相關預處理工作
    def load(self, img_rows = IMAGE_SIZE, img_cols = IMAGE_SIZE, 
             img_channels = 3, nb_classes = 2):
        #載入資料集到記憶體
        images, labels = load_dataset(self.path_name)        

        train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size = 0.3, random_state = random.randint(0, 100))        
        _, test_images, _, test_labels = train_test_split(images, labels, test_size = 0.5, random_state = random.randint(0, 100))                

        #當前的維度順序如果為'th',則輸入圖片資料時的順序為:channels,rows,cols,否則:rows,cols,channels
        #這部分程式碼就是根據keras庫要求的維度順序重組訓練資料集
        if K.image_dim_ordering() == 'th':
            train_images = train_images.reshape(train_images.shape[0], img_channels, img_rows, img_cols)
            valid_images = valid_images.reshape(valid_images.shape[0], img_channels, img_rows, img_cols)
            test_images = test_images.reshape(test_images.shape[0], img_channels, img_rows, img_cols)
            self.input_shape = (img_channels, img_rows, img_cols)            
        else:
            train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)
            valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)
            test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
            self.input_shape = (img_rows, img_cols, img_channels)            

            #輸出訓練集、驗證集、測試集的數量
            print(train_images.shape[0], 'train samples')
            print(valid_images.shape[0], 'valid samples')
            print(test_images.shape[0], 'test samples')

            #我們的模型使用categorical_crossentropy作為損失函式,因此需要根據類別數量nb_classes將
            #類別標籤進行one-hot編碼使其向量化,在這裡我們的類別只有兩種,經過轉化後標籤資料變為二維
            train_labels = np_utils.to_categorical(train_labels, nb_classes)                        
            valid_labels = np_utils.to_categorical(valid_labels, nb_classes)            
            test_labels = np_utils.to_categorical(test_labels, nb_classes)                        

            #畫素資料浮點化以便歸一化
            train_images = train_images.astype('float32')            
            valid_images = valid_images.astype('float32')
            test_images = test_images.astype('float32')

            #將其歸一化,影象的各畫素值歸一化到0~1區間
            train_images /= 255
            valid_images /= 255
            test_images /= 255            

            self.train_images = train_images
            self.valid_images = valid_images
            self.test_images  = test_images
            self.train_labels = train_labels
            self.valid_labels = valid_labels
            self.test_labels  = test_labels
            #CNN網路模型類
class Model:
    def __init__(self):
        self.model = None

    #建立模型
    def build_model(self, dataset, nb_classes = 2):
        #構建一個空的網路模型,它是一個線性堆疊模型,各神經網路層會被順序新增,專業名稱為序貫模型或線性堆疊模型
        self.model = Sequential()

        #以下程式碼將順序新增CNN網路需要的各層,一個add就是一個網路層
        self.model.add(Convolution2D(32, 3, 3, border_mode='same',
                                     input_shape = dataset.input_shape))    #1 2維卷積層
        self.model.add(Activation('relu'))                                  #2 啟用函式層

        self.model.add(Convolution2D(32, 3, 3))                             #3 2維卷積層
        self.model.add(Activation('relu'))                                  #4 啟用函式層

        self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #5 池化層
        self.model.add(Dropout(0.25))                                       #6 Dropout層

        self.model.add(Convolution2D(64, 3, 3, border_mode='same'))         #7  2維卷積層
        self.model.add(Activation('relu'))                                  #8  啟用函式層

        self.model.add(Convolution2D(64, 3, 3))                             #9  2維卷積層
        self.model.add(Activation('relu'))                                  #10 啟用函式層

        self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #11 池化層
        self.model.add(Dropout(0.25))                                       #12 Dropout層

        self.model.add(Flatten())                                           #13 Flatten層
        self.model.add(Dense(512))                                          #14 Dense層,又被稱作全連線層
        self.model.add(Activation('relu'))                                  #15 啟用函式層
        self.model.add(Dropout(0.5))                                        #16 Dropout層
        self.model.add(Dense(nb_classes))                                   #17 Dense層
        self.model.add(Activation('softmax'))                               #18 分類層,輸出最終結果

        #輸出模型概況
        self.model.summary()

#訓練模型
    def train(self, dataset, batch_size = 20, nb_epoch = 10, data_augmentation = True):
        sgd = SGD(lr = 0.01, decay = 1e-6,
                  momentum = 0.9, nesterov = True) #採用SGD+momentum的優化器進行訓練,首先生成一個優化器物件
        self.model.compile(loss='categorical_crossentropy',
                           optimizer=sgd,
                           metrics=['accuracy'])   #完成實際的模型配置工作

        #不使用資料提升,所謂的提升就是從我們提供的訓練資料中利用旋轉、翻轉、加噪聲等方法創造新的
        #訓練資料,有意識的提升訓練資料規模,增加模型訓練量
        if not data_augmentation:
            self.model.fit(dataset.train_images,
                           dataset.train_labels,
                           batch_size = batch_size,
                           nb_epoch = nb_epoch,
                           validation_data = (dataset.valid_images, dataset.valid_labels),
                           shuffle = True)
        #使用實時資料提升
        else:
            #定義資料生成器用於資料提升,其返回一個生成器物件datagen,datagen每被呼叫一
            #次其生成一組資料(順序生成),節省記憶體,其實就是python的資料生成器
            datagen = ImageDataGenerator(
                featurewise_center = False,             #是否使輸入資料去中心化(均值為0),
                samplewise_center  = False,             #是否使輸入資料的每個樣本均值為0
                featurewise_std_normalization = False,  #是否資料標準化(輸入資料除以資料集的標準差)
                samplewise_std_normalization  = False,  #是否將每個樣本資料除以自身的標準差
                zca_whitening = False,                  #是否對輸入資料施以ZCA白化
                rotation_range = 20,                    #資料提升時圖片隨機轉動的角度(範圍為0~180)
                width_shift_range  = 0.2,               #資料提升時圖片水平偏移的幅度(單位為圖片寬度的佔比,0~1之間的浮點數)
                height_shift_range = 0.2,               #同上,只不過這裡是垂直
                horizontal_flip = True,                 #是否進行隨機水平翻轉
                vertical_flip = False)                  #是否進行隨機垂直翻轉

            #計算整個訓練樣本集的數量以用於特徵值歸一化、ZCA白化等處理
            datagen.fit(dataset.train_images)

            #利用生成器開始訓練模型
            self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,
                                                   batch_size = batch_size),
                                     samples_per_epoch = dataset.train_images.shape[0],
                                     nb_epoch = nb_epoch,
                                     validation_data = (dataset.valid_images, dataset.valid_labels))

    MODEL_PATH = './me.face.model.h5'
    def save_model(self, file_path = MODEL_PATH):
        self.model.save(file_path)

    def load_model(self, file_path = MODEL_PATH):
        self.model = load_model(file_path)

    def evaluate(self, dataset):
        score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose = 1)
        print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))

#識別人臉
    def face_predict(self, image):    
        #依然是根據後端系統確定維度順序
        if K.image_dim_ordering() == 'th' and image.shape != (1, 3, IMAGE_SIZE, IMAGE_SIZE):
            image = resize_image(image)                             #尺寸必須與訓練集一致都應該是IMAGE_SIZE x IMAGE_SIZE
            image = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE))   #與模型訓練不同,這次只是針對1張圖片進行預測    
        elif K.image_dim_ordering() == 'tf' and image.shape != (1, IMAGE_SIZE, IMAGE_SIZE, 3):
            image = resize_image(image)
            image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))                    

        #浮點並歸一化
        image = image.astype('float32')
        image /= 255

        #給出輸入屬於各個類別的概率,我們是二值類別,則該函式會給出輸入影象屬於0和1的概率各為多少
        result = self.model.predict_proba(image)
        print('result:', result)

        #給出類別預測:0或者1
        result = self.model.predict_classes(image)        

        #返回類別預測結果
        return result[0]

if __name__ == '__main__':
    dataset = Dataset('./data/')
    dataset.load()

    #訓練模型
    model = Model()
    model.build_model(dataset)
    model.train(dataset)
    model.save_model(file_path = './model/me.face.model.h5')


  • 在shell執行:<python face_train_use_keras.py>儲存模型,可以看到訓練誤差(loss)、訓練準確率(acc)、驗證誤差(val_loass)、驗證準確率(val_acc)。
  • 修改face_train_use_keras.py,將訓練模型註釋掉,新增評估模型,如下:

    '''
    #訓練模型,註釋掉
    model = Model()
    model.build_model(dataset)
    model.train(dataset)
    model.save_model(file_path = './model/me.face.model.h5')
    '''
    #評估模型
    model = Model()
    model.load_model(file_path = './model/me.face.model.h5')
    model.evaluate(dataset)



  • 重新執行<python face_train_use_keras.py> 可以看到準確率。

從實時視訊流識別出自己

  • 主目錄下建立face_predict_use_keras.py檔案,存入下述程式碼:
#-*- coding: utf-8 -*-

import cv2
import sys
import gc
from face_train_use_keras import Model

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage:%s camera_id\r\n" % (sys.argv[0]))
        sys.exit(0)

    #載入模型
    model = Model()
    model.load_model(file_path = './model/me.face.model.h5')    

    #框住人臉的矩形邊框顏色       
    color = (0, 255, 0)

    #捕獲指定攝像頭的實時視訊流
    cap = cv2.VideoCapture(int(sys.argv[1]))

    #人臉識別分類器本地儲存路徑
    cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml"    

    #迴圈檢測識別人臉
    while True:
        _, frame = cap.read()   #讀取一幀視訊

        #影象灰化,降低計算複雜度
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        #使用人臉識別分類器,讀入分類器
        cascade = cv2.CascadeClassifier(cascade_path)                

        #利用分類器識別出哪個區域為人臉
        faceRects = cascade.detectMultiScale(frame_gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))        
        if len(faceRects) > 0:                 
            for faceRect in faceRects: 
                x, y, w, h = faceRect

                #擷取臉部影象提交給模型識別這是誰
                image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                faceID = model.face_predict(image)   

                #如果是“我”
                if faceID == 0:                                                        
                    cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)

                    #文字提示是誰
                    cv2.putText(frame,'Me', 
                                (x + 30, y + 30),                      #座標
                                cv2.FONT_HERSHEY_SIMPLEX,              #字型
                                1,                                     #字號
                                (255,0,255),                           #顏色
                                2)                                     #字的線寬
                else:
                    pass

        cv2.imshow("Recognise myself", frame)

        #等待10毫秒看是否有按鍵輸入
        k = cv2.waitKey(10)
        #如果輸入q則退出迴圈
        if k & 0xFF == ord('q'):
            break

    #釋放攝像頭並銷燬所有視窗
    cap.release()
    cv2.destroyAllWindows()
在shell輸入:<python face_predict_use_keras.py 0>可以看到視訊窗口裡自己的頭像會被文字“Me”標出。


結語

本文實現了一個簡單的深度學習專案:用tensorflow實現人臉檢測及識別。本文主要專注於實際的操作過程及步驟,本文對於程式碼中的原理沒有過多解釋。希望更進一步瞭解的朋友可以從開頭提供的部落格及尋找相關資料進行學習。

參考文獻:

1.http://www.cnblogs.com/neo-T/p/6511273.html

2.https://zhuanlan.zhihu.com/p/36962109