人臉識別學習筆記

在公司的上個項目,用到了車輛和人臉識別,本人主要負責車輛模塊所有的應用服務,只知道調用第三方的接口傳入參數rtsp流地址或是一張圖片,接口返回一組或一條識別結果,作爲一位共產主義的接班人,優秀的少先隊員,我帶着一臉的懵逼,開始了爬坑。

本文參考了一位日本的大牛Hironsan的一個有趣的項目 BossSensor,有興趣的同學的試着玩一下

首先梳理下我們需要做的流程:

  1. rtsp、rtmp視頻流中顯示出視頻並找出頭像(二選一)
    • 直接用opencv的imshow方法在本地顯示
    • 用服務推送協議,在web頁面顯示(因爲服務器不在本地,所以本文使用此方法)
  2. 從視頻流中獲取我們需要訓練的數據(如果以前有數據這一步可以跳過)
  3. 加載已經保存的圖片用於訓練
  4. 訓練數據建立數據模型並保存
  5. 使用之前訓練好的模型識別圖片

用到主要的模塊的版本:

  • opencv-python 3.3.0.10
  • python 3.6.1
  • numpy 1.12.1
  • keras 2.0.8

一、從視頻流或者攝像頭中顯示視頻並找出頭像

該模塊主要應用的是opencv模塊
import cv2

# rtsp流地址
CAMER_URL = 'rtsp://192.168.1.101:8554/1234' 
cap = cv2.VideoCapture(CAMER_URL)

# 加載分類器
classfier = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml') 

 # 識別框顏色
color = (0, 255, 0)

while cap.isOpened(): 
    # 獲取每一幀圖像
    ok, frame = cap.read() 
    if not ok:
        break
    # 灰度化,降低圖片維度
    grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
    # 分類對象調用
    faceRects = classfier.detectMultiScale(
        grey, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
    # 判斷若未識別到則不進行處理
    if len(faceRects) > 0: 
        for faceRect in faceRects:
            # 獲取識別信息的座標信息
            x, y, w, h = faceRect 
            # 繪製方框(這裏是給原圖上繪製,不是灰度圖片)
            cv2.rectangle(frame, (x - 10, y - 10),
                          (x + 10, y + 10), color, 2) 
    # 展示圖片(本地)
    cv2.imshow('windowTitle', frame)

    # 每隔10ms監聽鍵盤輸入,若爲q 退出循環
    c = cv2.waitkey(10)
    if c & 0xFF == orq('q')
        break

# 釋放攝像頭並關閉窗口
cap.release()
cv2.destroyAllWindows()

CAMER_URL 是視頻來源地址,因爲我是臺式機而且沒有攝像頭,所以這次採用的是外部的rtsp實時數據流,如果你有攝像頭或者是筆記本的話,只需要將改參數設置爲0即可
這裏主要解釋下classfier.detectMultiScale()參數的具體含義:

  • grey :也要檢測圖片(這裏使用的是灰度圖片,加快檢測速度)
  • scaleFactor : 表示前後兩次相繼的掃描中,搜索窗口的比例係數,默認爲1.1即每次搜索窗口擴大10%
  • minNeighbors: 表示構成檢測目標的相鄰矩形的最小個數(默認是3個) ,若果組成檢測的目標小於min_neighbors -1都會被排除,如果min_neighbors 爲0,則函數不做任何操作就返回所有的被檢測候選矩形框,這種設定值一般用在用戶自定義對檢測結果組合程序上
  • minSize限制得到區域的最小範圍,當然也有最大範圍maxSize,我這裏只設置了最小範圍

上面是本地窗口顯示視頻,下面我用web頁面顯示視頻代碼,用flask框架搭建了一個簡單的web服務,flask框架不是本文的重點,我只貼出用到關鍵用到的服務推送(multipart/x-mixed-replace)部分代碼

from importlib import import_module
import os
from flask import Flask, render_template, Response
from camera_opencv import Camera

app = Flask(__name__)

@app.route('/')
def index():
    # 渲染視頻主頁面
    return render_template('index.html')

def gen(camera):
    # 視頻流生成方法
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route('/video_feed')
def video_feed():
    # 視頻流路由,推送到主頁面的img標籤
    return Response(gen(Camera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host='0.0.0.0', threaded=True)

get_frame()方法是從上面代碼識別的獲取frame圖片,封裝在Camera裏,在index.html 裏面img標籤的路徑是vieo_feed路由,建立長連接直接將數據推送web頁面

效果如下圖:
這裏寫圖片描述


二、從視頻流中獲取我們需要訓練的數據

如果已經用了訓練數據可以跳過直接看後面的

這裏的代碼是在上面方法基礎上只是將識別出來的頭像添加保存而已,代碼如下:

import cv2,os

# rtsp流地址
CAMER_URL = 'rtsp://192.168.1.101:8554/1234' 
cap = cv2.VideoCapture(CAMER_URL)

# 加載分類器
classfier = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml') 

 # 識別框顏色
color = (0, 255, 0)

while cap.isOpened(): 
    # 獲取每一幀圖像
    ok, frame = cap.read() 
    if not ok:
        break
    # 灰度化,降低圖片維度
    grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
    # 分類對象調用
    faceRects = classfier.detectMultiScale(
        grey, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))

    #定義數量
    num = 0 
    # 判斷若未識別到則不進行處理
    if len(faceRects) > 0: 
        for faceRect in faceRects:
            # 獲取識別信息的座標信息
            x, y, w, h = faceRect 

            ''' ----------圖片存儲開始----------'''
            # 生成圖片路徑
            img_name = '%s/%d.jpg' % (os.getcwd() + './img/me', num)
            # 獲取識別圖片區域
            image = frame[y - 10:y + h + 10, x - 10:x + w + 10]
            # 保存寫入圖片
            cv2.imwrite(img_name, image)
            num += 1
            # 如果圖片達到1000張時退出
            if num > 1000:
                break
            ''' ----------圖片存儲結束----------'''

            # 繪製方框(這裏是給原圖上繪製,不是灰度圖片)
            cv2.rectangle(frame, (x - 10, y - 10),
                          (x + 10, y + 10), color, 2) 
    # 展示圖片(本地)
    cv2.imshow('windowTitle', frame)

    # 每隔10ms監聽鍵盤輸入,若爲q 退出循環
    c = cv2.waitkey(10)
    if c & 0xFF == orq('q')
        break

# 釋放攝像頭並關閉窗口
cap.release()
cv2.destroyAllWindows()

這裏需要保存兩組圖片各一千張一個是我自己的照片(保存在me文件夾中),另一個保存別人的照片(other文件夾中,當然名字可以自己命名,但儘量不要用中文)用於訓練分類,目錄需要提前建好,否則圖片保存不進去


三、加載已經保存的圖片用於訓練

創建一個load_data.py 文件,這個文件主要是加載圖指定目錄下所有的圖圖片到緩存中用於後面的數據訓練使用

import os
import sys
import numpy as np
import cv2

# 定義加載識別圖片尺寸
IMAGE_SIZE = 32 
# 重置圖片尺寸,將圖片修改爲以最大邊爲基準的正方形(不足的地方補黑)方便訓練
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(w, h)
        # 判斷各邊的長短 並補差值
        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

        # 定義補充顏色(黑色)
        BLACK = [0, 0, 0]
        # 補充邊界
        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):
        # 遍歷改路徑下所有的目錄(我們這裏只有me和other目錄)
        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:
                # 如是文件判斷後綴名是否是jpg
                        if dir_item.endswith('.jpg'):
                                # 獲取該路徑圖片
                                image = cv2.imread(full_path)
                                # 初始化圖片
                                image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
                                # 圖片添加到圖片集
                                images.append(image)
                                # 名稱添加的到分類集
                                labels.append(path_name)
        return images, labels

# 調用加載圖片
def load_dataset(path_name):
        # 獲取加載的各個結果集
        images, labels = read_path(path_name)
        # 將圖片集轉爲四維數組(2000,30,30,3) 
        # 第一個是數量,第二個高度,第三個寬度,第四個每張圖片的顏色數量(rgb三種)
        images = np.array(images)
        # 將分類集進行分類二維數組(2000,2) 第一個是數量,第二個是每張圖片分類值(me是0和other是1) 
        labels = np.array([0 if label.endswith('me') else 1 for label in labels])
        return images, labels

四、訓練數據建立數據模型並保存

keras 默認的後端引擎是TensorFlow,本文采用的也是默認的後端引擎,當然也可以切換爲後端引擎,目前keras支持的有兩個一個tensorFlow另一個是Theano,具體的切換方式請查看api, 點擊這裏

我們訓練數據用到的是卷積神經網絡(以下簡稱CNN)
關於CNN的具體的網上教程有很多,基本上都是引用了大量的數據公式,如果想進一步的瞭解卷積,請戳下面連接:
最形象的卷積神經網絡詳解:從算法思想到編程實現
如何通俗易懂地解釋卷積?
離散卷積
而對於新手或者是非數學科班出身的人來說非常晦澀難懂,本文屬於入門文章,沒有太多公式基本全是大白話

CNN的工作流程:

CNN的工作流程

從上圖可以看出CNN的工作流程和大腦工作的流程大致相同,是一個不斷迭代、不斷抽象的過程
比如,我們去看一個場景,首先看到場景中有一個人,然後看到這個人的臉,最後才發覺這個人誰。

神經網絡一般包含三種層:輸入層、隱含層(多個)、輸出層,如下圖:
這裏寫圖片描述

卷積神經網絡是一種非全連接的神經網絡,包含2中特殊的結構層:卷積層和次抽樣層。

每個卷積層都會緊跟1個次抽樣層,輸入數據經過卷積後,進入高維空間,換句話說,卷積層進行了升維映射。卷積層的每個特徵平面都對應了次抽樣層的1個平面,次抽樣層的神經元對其接受區域中的數據進行抽樣(例如,去大、取小,去平均值,等等),因此次抽樣層的特徵平面上的神經元的個數往往會減半。卷積層的每一個平面都抽去了前一某一個方面的特徵,每個卷積層的每個節點作爲特徵探測器,共同抽泣輸入圖像的某個特徵,如45度角、反色、拉伸、翻轉、平移等,圖像經過一層卷積,就由原始空間被映射到特徵空間,在特徵空間中進行重構,卷積層輸出,爲圖像在特徵空間中重構座標,作爲下一層也就就是次抽樣層的輸入。
卷積層由多個特徵平面構成,完成抽取特徵的任務,每個特徵平面有神經元構成, 在同一個特徵平面上的所有神經元具有相同的連接權重,對於每個神經元,定義了相應的接受域,值接受從起接受域傳輸的信號,同一個特徵平面的神經元的接受具有相同的大小,故而每個神經元的連接矩陣相同。

卷積層的map個數是在網絡初始化指定的,而卷積層的map的大小是由卷積核的上一層輸入的map的大小決定的,假設上一次層的大小是n*n、卷積核的大小是k*k,則該層的map大小是(n-k+1)*(n-k+1),比如下圖:

這裏寫圖片描述

傳入map大小是5*5,卷積核的大小是3*3 那麼該層的map大小就是(5-3+1)*(5-3+1) = 3*3

採樣層是對上一層map的一個採樣處理,這裏的採樣方式是對上一層map的相鄰小區域進行聚合統計,區域大小爲scale*scale,有些實現是取小區域的最大值,而toolbox裏面的實現是採用2*2小區域的平均值。
注意,卷積的計算窗口是有重疊的,而採樣的計算窗口沒有重疊,toolbox裏面計算採用也是用卷積(conv2(A,K,’valid’))來實現的,卷積核是2*2每個元素都是1/4,去掉計算得到的卷積結果中有重疊的部分

cnn的基本結構包括兩種特殊的神經元層,其一爲卷基層,每個神經元的輸入與前一層的局部相連,並提取該局部的特徵;
其二是池化層,用來求局部敏感性與二次特徵提取的計算層,這兩種特徵提取結構減少了特徵分辨率,減少了需要優化的參數數目

一般地,CNN的基本結構包括兩層:
其一,爲特徵提取層,每個神經元輸入與前一層的局部接受域相連,並提取該局部特徵。一旦該局部特徵被提取後,他與其他特徵間的位置關係也隨之確定下來;
其二,爲特徵映射層,網絡的每個計算層由多個特徵映射組成,每個特徵映射是一個平面,平面上每個神經元的權值相等。
特徵映射結構採用影響函數核小的sigmoid函數作爲卷積網絡的激活函數,使得特徵映射具有位移不變性。
此外,由於一個映射面上的神經元共享權值(如上圖),因而減少了網絡自由參數的個數,卷積神經網絡中的每一個卷積層都緊跟着一個用來求局部平均與二次提取的計算層,這種特有的兩層特徵提取結構減少了特徵分辨率

LeNet-5手寫數字識別結構(下圖)分析

CNN是一種帶有卷積結構的深度神經網絡,通常至少有兩個非線性可訓練的卷積層,兩個非線性的固定卷積層(又叫Pooling Layer或降採樣層)和一個全連接層,一共至少5個隱含層。

在上圖中 訓練的樣本大小是 32*32

卷積層(Convolutions)C1:
輸入大小:32*32
卷積核大小:5*5
卷積核數量:6
輸出的特徵圖大小:(32-5+1)*(32-5+1) = 28*28
輸出的特徵圖數量:6

次抽樣層(subsampling)S2:
卷積核大小:2*2
輸入大小:28*28*6
卷積核數量:6
輸出採樣大小:(28/2 )*(28/2) = 14*14

卷積層c3:
輸入大小14*14*16
卷積核大小:5*5
輸出圖片大小:(14-10+1)*(14-10+1) = 10*10

次抽樣層S4:
輸入圖片大小:10*10*16
卷積核大小:2*2
輸出圖片大小:5*5*16

卷積層C5:
輸入圖片大小:10*10*16
卷積核大小:5*5
卷積核數:120
輸出大小:(5-5+1)*(5-5+1)*120 = 1*1*120

全連接層(full Connection)F6:
輸入圖片大小:1*1*120
卷積和大小:1*1
卷積核數:84
輸出大小:1*1*84

輸出層(Out Put):
輸入大小:1*1
輸出數量:10

知識點可能比較零散,我只是把我平時的筆記拼接在一起了,哈哈哈

接下來看我們的項目,新建一個文件face_train_user_keras.py,先看代碼:

import random

from sklearn.model_selection 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 Conv2D, MaxPooling2D
from keras.optimizers import SGD
from keras.utils import np_utils
from keras.models import load_model
from keras import backend as K
import sys
import os

from load_face_dataset import load_dataset, resize_image, IMAGE_SIZE


class Dataset(object):
    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

        # 加載數據集並按照交叉驗證的原則劃分數據並進行相關預處理
        '''
    1.按照交叉驗證的原則將數據集劃分三部分:訓練集、驗證集、測試集
    2.按照keras庫運行的系統要求改變數據集維度順序
    3.將數據標籤one-hot編碼,使其向量化
    4.歸一化圖像數據
    '''

    def load(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE, img_channels=3, nb_classes=2):

        # 加載數據集到內存
        images, labels = load_dataset(self.path_name)
        ''' 這裏訓練集、驗證集、測試集分配數據,我個人建議:
        訓練集:65%
        測試集:25%
        驗證集:10%

        我裏我是爲了各個集合之間數據不重複,先給驗證集分配35%(訓練集65%),
        再衝驗證集分配70%給測試集,剩下的就是驗證集了,基本上按照上面的分配法則
        '''

        train_images, valid_images, train_labels, valid_labels = train_test_split(
            images, labels, test_size=0.35, random_state=random.randint(0, 100))
        valid_images, test_images, valid_labels, test_labels = train_test_split(
            valid_images, valid_labels, test_size=0.7, 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_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 sample')
        print(valid_images.shape[0], 'valid sample')
        print(test_images.shape[0], 'test sample')

        # 我們的模型使用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')

        # 將像素歸一化
        train_images /= 255
        valid_images /= 255
        test_images /= 255

        self.train_images = train_images
        self.train_labels = train_labels
        self.valid_images = valid_images
        self.valid_labels = valid_labels
        self.test_images = test_images
        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()
        self.model.add(Conv2D(
            32, (3, 3), padding='same', input_shape=dataset.input_shape))
        self.model.add(Activation('relu'))

        self.model.add(Conv2D(32, (3, 3)))
        self.model.add(Activation('relu'))

        self.model.add(MaxPooling2D(pool_size=(2, 2)))
        self.model.add(Dropout(0.25))

        self.model.add(Conv2D(64, (3, 3), padding='same'))
        self.model.add(Activation('relu'))

        self.model.add(Conv2D(64, (3, 3)))
        self.model.add(Activation('relu'))

        self.model.add(MaxPooling2D(pool_size=(2, 2)))
        self.model.add(Dropout(0.25))

        self.model.add(Flatten())
        self.model.add(Dense(512))
        self.model.add(Activation('relu'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(nb_classes))
        self.model.add(Activation('softmax'))

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

    def train(self, dataset, batch_size=20, nb_epoch=10, data_augmentation=True):
        # 採用sgd+momentum的優化器進行訓練,首先生成一個優化器
        sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
        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,
                           epochs=nb_epoch,
                           validation_data=(
                               dataset.valid_images, dataset.valid_labels),
                           shuffle=True)
        # 使用數據提升
        else:
            # 定義數據生成器用於數據提升,其返回一個生成器對象datagen,
            # datagen每調用一次其生成一組數據(順序生成),節省內存,其實就是python的數據生成器
            datagen = ImageDataGenerator(
                featurewise_center=False,  # 布爾值,使輸入數據集去中心化(均值爲0), 按feature執行
                samplewise_center=False,  # 布爾值,使輸入數據的每個樣本均值爲0
                featurewise_std_normalization=False,  # 布爾值,將輸入除以數據集的標準差以完成標準化, 按feature執行
                samplewise_std_normalization=False,  # 布爾值,將輸入的每個樣本除以其自身的標準差
                zca_whitening=False,  # 布爾值,對輸入數據施加ZCA白化
                rotation_range=20,  # 整數,數據提升時圖片隨機轉動的角度。隨機選擇圖片的角度,是一個0~180的度數,取值爲0~180
                width_shift_range=0.2,  # 浮點數,圖片寬度的某個比例,數據提升時圖片隨機水平偏移的幅度
                height_shift_range=0.2,  # 浮點數,圖片高度的某個比例,數據提升時圖片隨機豎直偏移的幅度
                # 布爾值,進行隨機水平翻轉。隨機的對圖片進行水平翻轉,這個參數適用於水平翻轉不影響圖片語義的時候
                horizontal_flip=True,  
                vertical_flip=True  # 布爾值,進行隨機豎直翻轉
            )

            # 計算整個訓練樣本集的數量以用於特徵值歸一化,ZCA白化等處理
            datagen.fit(dataset.train_images)
            # 利用生成器開始訓練
            '''
            在新版本keras中
            去掉了samples_per_epoch 改爲 steps_per_epoch(整數,
            當生成器返回steps_per_epoch次數據時計一個epoch結束,執行下一個epoch)
            nb_epoch 參數命名該爲epochs(整數,數據迭代的輪數)
            '''
            self.model.fit_generator(
                datagen.flow(dataset.train_images,
                             dataset.train_labels, batch_size=batch_size),
                steps_per_epoch=dataset.train_images.shape[0] // batch_size,
                epochs=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 = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE))
        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的概率各爲多少
        # resultRat = self.model.predict_proba(image)

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


if __name__ == '__main__':
    dataset = Dataset(os.getcwd() + '/img/')
    dataset.load()

    model = Model()
    model.build_model(dataset)

執行該文件在控制檯中可以看見訓練模型的概況:

這裏寫圖片描述

從上圖中可以看出我的訓練樣本數量是817個、驗證樣本132個、測試樣本309個
注意:在keras2.0之後model.summary()方法中去掉了 connected to 列

我們只是展示出了,訓練模型並沒有開始訓練,在程序的後面再添加調用執行訓練、驗證和保存方法

在執行方法最後面添加下面方法

    # 訓練模型
    model.train(dataset)
    # 保存模型文件
    # 注意如果存儲的文件夾路徑不存在,則保存失敗
    model.save_model(file_path=os.getcwd() +'/model/me.face.model.h5')

    # 評估模型
    model = Model()
    # 加載保存好的模型
    model.load_model(file_path=os.getcwd() +'/model/me.face.model.h5')
    # 執行驗證方法
    model.evaluate(dataset)

再次執行該文件,打印出訓練信息如下圖:

這裏寫圖片描述

從上圖可以看出收斂的速度還是挺快的,訓練10次,最後的精確度在 99.87%還是挺不錯的,最後驗證的結果是100%

五. 使用之前訓練好的模型識別圖片

執行的方法是在我們第一個顯示視頻流的地方調用model類中定義的face_predict() 方法,如下:

    # 判斷若未識別到則不進行處理
    if len(faceRects) > 0: 
        for faceRect in faceRects:
            # 獲取識別信息的座標信息
            x, y, w, h = faceRect 
            # 繪製方框(這裏是給原圖上繪製,不是灰度圖片)
            cv2.rectangle(frame, (x - 10, y - 10),
                          (x + 10, y + 10), color, 2) 

            ''' --------------識別代碼開始-----------------'''
            faceID = model.face_predict(img)[0]
              if faceID== 0:
                  self.printText(cv2,img,x,y,w,h,'me',(0,255,0))
              else:
                  self.printText(cv2,img,x,y,w,h,'not me')
            ''' --------------識別代碼結束-----------------'''

在數據加載時me 分類的代號是0 所以如果識別的代號是0時就說明檢測到了自己,如果是1 的話就是檢測到另一個分類集(other文件夾下圖片的人)

在圖片中,若檢測到了自己就會在人臉識別區域圖片顯示‘me’字樣,若不是則會顯示‘not me ’字樣

結果如下圖:

這裏寫圖片描述

以上是我運用卷積神經網絡做人臉識別的學習筆記,識別率並不是很高,後面有時間的話,我會再分享另一個識別率較高的機器學習,這個對於設備的要求比較高,沒有GPU的話建議別試,沒有檢測到人臉還行,檢測到會卡死的,別問我怎麼知道的,不說了 我重啓機器去了……

最後,論顯卡的重要性:

這裏寫圖片描述

這裏寫圖片描述

如若有錯誤的地方,還請大神留言指出或加QQ:754395771

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章