您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關使用Kears和Opencv怎么實現一個人臉識別功能,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
1.首先利用Opencv檢測出人臉的區域
2.在成功的檢測出人臉區域后,將識別出的人臉區域截取成圖片,儲存起來,用作后續的訓練數據。
3.在獲取到了足夠多的數據后,搭建CNN網絡進行訓練。
5.訓練完成后,將模型存儲起來。 6.在利用Opencv實時讀取視頻流,將檢測出人臉的區域變成圖片放入模型中進行預測。
以上就是這個項目的一個基本的思路了。
1.利用Opencv檢測人臉的代碼如下,這個代碼在Opencv的官方文檔中也有,最重要的是加載的xml文件,因為這個xml文件中是保存這已經訓練好的人臉檢測模型。
import cv2 def identify_face(window_name, camera_idx): cv2.namedWindow(window_name) # 視頻來源,可以來自一段已存好的視頻,也可以直接來自USB攝像頭 cap = cv2.VideoCapture(camera_idx) # 告訴OpenCV使用人臉識別分類器 classfier = cv2.CascadeClassifier("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和4分別為圖片縮放比例和需要檢測的有效點數 faceRects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=4, 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__': identify_face("identify face", 0)
2.在檢測出人臉區域后,將人臉區域截取成圖片保存下來
import cv2 from threading import Thread def identify_face_and_store_face_image(window_name, camera_idx): cv2.namedWindow(window_name) # 視頻來源,可以來自一段已存好的視頻,也可以直接來自USB攝像頭 cap = cv2.VideoCapture(camera_idx) # 告訴OpenCV使用人臉識別分類器 classfier = cv2.CascadeClassifier("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=4, 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) # store_face_image(frame, h, num, w, x, y) # 開啟一個線程去存儲人臉圖片 t = Thread(target=store_face_image, args=(frame, h, num, w, x, y, )) t.start() # 顯示捕捉了多少張人臉照片 font = cv2.FONT_HERSHEY_SIMPLEX # 字體 cv2.putText(frame, ('num %d' % num), (x + 30, y + 30), font, 1, (255, 0, 255), 2) num += 1 if num <= 1000: # 保存1000張圖片后就退出 break if num >= 1000: break # 顯示圖像 cv2.imshow(window_name, frame) c = cv2.waitKey(10) if c & 0xFF == ord('q'): break # 釋放攝像頭并銷毀所有窗口 cap.release() cv2.destroyAllWindows() def store_face_image(frame, h, num, w, x, y): # 將當前幀保存為圖片 img_name = '%s/%d.jpg' % (r'face_image', num) image = frame[y - 10: y + h + 10, x - 10: x + w + 10] cv2.imwrite(img_name, image) if __name__ == '__main__': identify_face_and_store_face_image("identify face", 0)
3.將保存的數據進行處理,比如打上標簽,進行歸一化等等,下列代碼是load_datasets.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) # 共587張圖片,IMAGE_SIZE為64,故對我來說尺寸為587 * 64 * 64 * 3 # 圖片為64 * 64像素,一個像素3個顏色值(RGB) images = np.array(images) print(images.shape) # 標注數據,'my_face_image'文件夾下都是我的臉部圖像,全部指定為0,你可以在獲取他人的臉部圖片比如說同學的,指定為1, labels = np.array([0 if label.endswith('my_face_image') else 1 for label in labels]) return images, labels if __name__ == '__main__': if len(sys.argv) != 1: print("Usage:%s path_name\r\n" % (sys.argv[0])) else: images, labels = load_dataset("face_image")
4.搭建模型,進行訓練
import random import numpy as np 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 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_datasets 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 = 'zhuhaipeng.face.model.h6' 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('face_image') dataset.load() model = Model() model.build_model(dataset) # 測試訓練函數的代碼 model.train(dataset) if __name__ == '__main__': # 訓練并保存模型 dataset = Dataset('face_image') dataset.load() model = Model() model.build_model(dataset) model.train(dataset) model.save_model(file_path='model/zhuhaipeng.face.model.h6') if __name__ == '__main__': # 讀取模型進行評估 dataset = Dataset('face_image') dataset.load() # 評估模型 model = Model() model.load_model(file_path='model/zhuhaipeng.face.model.h6') model.evaluate(dataset)
5.訓練好模型后,使用Opencv實時讀取視頻流檢測人臉位置,再將人臉放入模型中進行預測
# -*- coding: utf-8 -*- import cv2 import sys from face_train import Model if __name__ == '__main__': if len(sys.argv) != 1: print("Usage:%s camera_id\r\n" % (sys.argv[0])) sys.exit(0) # 加載模型 model = Model() model.load_model(file_path='model/zhuhaipeng.face.model.h6') # 框住人臉的矩形邊框顏色 color = (0, 255, 0) # 捕獲指定攝像頭的實時視頻流 cap = cv2.VideoCapture(0) # 人臉識別分類器本地存儲路徑 cascade_path = "haarcascade_frontalface_alt2.xml" # 循環檢測識別人臉 while True: ret, frame = cap.read() # 讀取一幀視頻 if ret is True: # 圖像灰化,降低計算復雜度 frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) else: continue # 使用人臉識別分類器,讀入分類器 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, 'zhuhaipeng', (x + 30, y + 30), # 坐標 cv2.FONT_HERSHEY_SIMPLEX, # 字體 1, # 字號 (255, 0, 255), # 顏色 2) # 字的線寬 else: # 如果不是我 # 文字提示未知 cv2.putText(frame, 'Unknown people ', (x + 30, y + 30), # 坐標 cv2.FONT_HERSHEY_SIMPLEX, # 字體 1, # 字號 (255, 0, 255), # 顏色 2) # 字的線寬 cv2.imshow("identify me", frame) # 等待10毫秒看是否有按鍵輸入 k = cv2.waitKey(10) # 如果輸入q則退出循環 if k & 0xFF == ord('q'): break # 釋放攝像頭并銷毀所有窗口 cap.release() cv2.destroyAllWindows()
看完上述內容,你們對使用Kears和Opencv怎么實現一個人臉識別功能有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。