解讀卷積神經(jīng)網(wǎng)絡的人臉識別
作為一個小白,用人臉識別上手了一下,收獲還是挺大的。
使用環(huán)境
- python3.7
- tensorflow 2.2.0
- opencv-python 4.4.0.40
- Keras 2.4.3
- numpy 1.18.5,具體安裝過程以及環(huán)境搭建省略,可借鑒網(wǎng)上。
具體目標
通過卷積神經(jīng)網(wǎng)絡訓練自己數(shù)據(jù)并能成功識別自己
實現(xiàn)步驟如下圖所示
1. 人臉數(shù)據(jù)采集與讀取
1.1 數(shù)據(jù)采集
本數(shù)據(jù)集使用opencv打開攝像頭,從攝像頭當中采集圖片信息以及外來的某些人圖片,共采集5個人的信息(這里沒有直接從攝像頭裁剪臉部信息是為了方便外部采集的圖片進行處理,每個人圖片為800張,本數(shù)據(jù)集把采取到每個人的圖片以名字縮寫開始放在同一個文件夾里面。)
相關代碼如下:
import os import cv2 import time from PIL import Image #只實現(xiàn)截屏的功能 global path path='./images/' #人臉采樣,封裝函數(shù) def cy(path): #path為保存圖片的路徑 #調用筆記本內置攝像頭,參數(shù)為0,如果有其他的攝像頭可以調整參數(shù)為1,2 cap = cv2.VideoCapture(0) #為即將錄入的臉標記一個id face_id = input('\n 用戶臉部信息錄入,輸入用戶名字(最好用英文):\n') #sampleNum用來計數(shù)樣本數(shù)目 count = 0 while True: #從攝像頭讀取圖片 success,img = cap.read() count += 1 #保存圖像,把灰度圖片看成二維數(shù)組來檢測人臉區(qū)域 #保存到相應的文件夾里 cv2.imwrite(path+str(face_id)+'.'+str(count)+'.jpg',img) #顯示圖片 cv2.imshow('image',img) #保持畫面的連續(xù)。waitkey方法可以綁定按鍵保證畫面的收放,通過q鍵退出攝像 k = cv2.waitKey(1) if k == '27': break #或者得到800個樣本后退出攝像,這里可以根據(jù)實際情況修改數(shù)據(jù)量,實際測試后800張的效果是比較理想的 elif count >= 500: time.sleep(2) success,img = cap.read() break #關閉攝像頭,釋放資源 cap.release() cv2.destroyAllWindows() #調用函數(shù)進行人臉采樣 cy(path)
獲取每個人的人臉部分區(qū)域,這里用到人臉檢測級聯(lián)分類器,并將圖片保存到特定文件夾中。
import cv2 import os #對圖片進行處理,輸入的不是灰度圖片,方便外部采集的圖片進行處理 CASE_PATH = "haarcascade_frontalface_default.xml" RAW_IMAGE_DIR = 'images/' DATASET_DIR = 'hh/' path='D:\\pythonlx\\test\\images\\' #人臉分類器 face_cascade = cv2.CascadeClassifier(CASE_PATH) #定義人臉大小 def save_feces(img, name,x, y, width, height): image = img[y:y+height, x:x+width] cv2.imwrite(name, image) image_list = os.listdir(RAW_IMAGE_DIR) #列出文件夾下所有的目錄與文件 for image_path in range(len(image_list)): gh=path+image_list[image_path] # print(gh) image = cv2.imread(gh) #gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(image, scaleFactor=1.2, minNeighbors=5, minSize=(5, 5), ) for (x, y, width, height) in faces: save_feces(image, '%ss%d.jpg' % (DATASET_DIR, image_path+1), x, y - 30, width, height+30)
1.2 數(shù)據(jù)讀取
將圖片數(shù)據(jù)集轉為四維數(shù)組,進行歸一化處理,并使用one-hot編碼將標簽向量化,按照訓練集80%,測試集20%隨機劃分。
并進行歸一化處理。
#讀取圖片 def read_image(): data_x, data_y = [], [] image_list = os.listdir('mine/') for i in range(len(image_list)): try: im = cv2.imread('mine/{}'.format(image_list[i])) im = resize_without_deformation(im) data_x.append(np.asarray(im, dtype = np.int8)) #定義標簽 a=image_list[i].split('.')[0] if a=='s2': data_y.append(0) elif a=='s4': data_y.append(1) elif a=='s5': data_y.append(2) elif a=='s6': data_y.append(3) elif a=='s7': data_y.append(4) except IOError as e: print(e) except: print('Unknown Error!') return data_x,data_y #讀取所有圖片以及標簽 raw_images, raw_labels = read_image() ##查看數(shù)據(jù)每個標簽的數(shù)據(jù)量 #a=raw_labels.count(0)#583 #b=raw_labels.count(1)#621 #c=raw_labels.count(2)#717 #d=raw_labels.count(3)#765 #e=raw_labels.count(4)#698 #轉為浮點型 raw_images, raw_labels = np.asarray(raw_images, dtype = np.float32),np.asarray(raw_labels, dtype = np.int32) #將標簽轉化為one_hot類型 ont_hot_labels = np_utils.to_categorical(raw_labels) #劃分數(shù)據(jù)集,訓練集80%,測試集20% train_input, valid_input, train_output, valid_output =train_test_split(raw_images, ont_hot_labels, test_size = 0.2) #數(shù)據(jù)歸一化處理 train_input /= 255.0 valid_input /= 255.0
2. 圖片預處理
采集的圖片樣本形狀可能存在不規(guī)則大小,須對圖片做尺寸變換,轉化為100*100大小,為防止圖片變形,將圖片較短的一側涂黑進行填充。
使它變成和目標圖像相同的比例,然后再resize,這樣既可以保留原圖的人臉信息,又可以防止圖像形變;最后對灰度圖片直方圖均衡化,增強圖片的細節(jié)與對比度,提高識別率。
def resize_without_deformation(image, size = (100, 100)): height, width, _ = image.shape #對于長度不等的邊,找到最長邊 longest_edge = max(height, width) #使用0填充邊框 top, bottom, left, right = 0, 0, 0, 0 #計算短邊需要增加多上像素寬度使其與長邊等長 if height < longest_edge: height_diff = longest_edge - height top = int(height_diff // 2) bottom = height_diff - top elif width < longest_edge: width_diff = longest_edge - width left = int(width_diff // 2) right = width_diff - left #給圖像增加邊界,是圖片長、寬等長,cv2.BORDER_CONSTANT指定邊界顏色由value指定 image_with_border = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = [0, 0, 0]) resized_image = cv2.resize(image_with_border, size) #將裁剪的圖片轉化為灰度圖 resize_image= cv2.cvtColor(resized_image,cv2.COLOR_BGR2GRAY) #直方圖均衡化 hist = cv2.equalizeHist(resize_image) img2 = hist.reshape((100, 100, 1)) return img2
3. 模型搭建與訓練
根據(jù)卷積神經(jīng)網(wǎng)絡中各組成結構的不同作用搭建卷積網(wǎng)絡模型,調整各個參數(shù),實現(xiàn)對模型的優(yōu)化,提高模型訓練效果。
模型框架:
模型參數(shù):
搭建卷積網(wǎng)絡及訓練:
由于數(shù)據(jù)集的圖片可能過于單一,以及變化小,因此后面加了一個數(shù)據(jù)提升,利用生成器進行訓練模型。
#搭建卷積神經(jīng)網(wǎng)絡,順序模型 model = models.Sequential() #卷積層,卷積核大小32,每個大小383,步長為1,輸入的類型為(100,100,1),1為通道,激活函數(shù)relu model.add(Conv2D(filters=32,kernel_size=(3,3),padding='valid',strides= (1, 1),#1 input_shape = (100, 100,1), activation='relu')) model.add(Conv2D(filters=32,kernel_size=(3,3),padding='valid',strides= (1, 1),#2 activation='relu')) #池化層 model.add(MaxPooling2D(pool_size=(2, 2)))#3 #Dropout層 model.add(Dropout(0.25))#4 #卷積層 model.add(Conv2D(64, (3, 3), padding='valid', strides = (1, 1), activation = 'relu'))#5 model.add(Conv2D(64, (3, 3), padding='valid', strides = (1, 1), activation = 'relu'))#6 #池化層 model.add(MaxPooling2D(pool_size=(2, 2)))#7 model.add(Dropout(0.25))#8 #全連接 model.add(Flatten())#9 model.add(Dense(512, activation = 'relu'))#10 model.add(Dropout(0.25))#11 #輸出層,神經(jīng)元數(shù)是標簽種類數(shù),使用sigmoid激活函數(shù),輸出最終結果 model.add(Dense(len(ont_hot_labels[0]), activation = 'sigmoid'))#12 #優(yōu)化模型, #SGD----梯度下降算子 #learning_rate = 1#學習率 #decay = 1e-6#學習率衰減因子 #momentum = 0.8#沖量 #nesterov = True #sgd_optimizer = SGD(lr = learning_rate, decay = decay, # momentum = momentum, nesterov = nesterov) #categorical_crossentropy #優(yōu)化器用Adam算法,損失函數(shù)用交叉熵的方法 model.compile(optimizer='adam',loss='categorical_crossentropy', metrics=['accuracy']) #輸出模型參數(shù) model.summary() #定義數(shù)據(jù)生成器用于數(shù)據(jù)提升,其返回一個生成器對象datagen,datagen每被調用一 #次其生成一組數(shù)據(jù)(順序生成),節(jié)省內存,其實就是python的數(shù)據(jù)生成器 datagen = ImageDataGenerator( featurewise_center = False, #是否使輸入數(shù)據(jù)去中心化(均值為0), samplewise_center = False, #是否使輸入數(shù)據(jù)的每個樣本均值為0 featurewise_std_normalization = False, #是否數(shù)據(jù)標準化(輸入數(shù)據(jù)除以數(shù)據(jù)集的標準差) samplewise_std_normalization = False, #是否將每個樣本數(shù)據(jù)除以自身的標準差 zca_whitening = False, #是否對輸入數(shù)據(jù)施以ZCA白化 rotation_range = 20, #數(shù)據(jù)提升時圖片隨機轉動的角度(范圍為0~180) width_shift_range = 0.2, #數(shù)據(jù)提升時圖片水平偏移的幅度(單位為圖片寬度的占比,0~1之間的浮點數(shù)) height_shift_range = 0.2, #同上,只不過這里是垂直 horizontal_flip = True, #是否進行隨機水平翻轉 vertical_flip = False) #是否進行隨機垂直翻轉 #計算整個訓練樣本集的數(shù)量以用于特征值歸一化、ZCA白化等處理 datagen.fit(train_input) #利用生成器開始訓練模型 history=model.fit_generator(datagen.flow(train_input, train_output,batch_size = 50), epochs = 10, validation_data = (valid_input, valid_output)) #模型驗證 print(model.evaluate(valid_input, valid_output, verbose=2)) ##畫出LOSS變化曲線圖 plt.plot(history.history['accuracy']) plt.plot(history.history['val_accuracy'],label='val_accuracy') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.ylim([0.5,1]) plt.legend(loc='lower right') #保存模型 MODEL_PATH = 'face_model.h5' model.save(MODEL_PATH)
4. 識別與驗證
將保存的模型重新進行加載,打開攝像頭進行識別,可以很準確的識別出自己以及別人(由于不能輕易公開別人的身份,將預測改為識別自己與非己),在進行模型預測時,需要將從攝像頭中獲取的每一幀圖片進行處理轉化為相應的格式,否則會出現(xiàn)shape的問題。
并且預測出來的是對應每個標簽的置信度,返回到其最大值的列索引即可知道對應的標簽,即可識別出是誰。
效果圖如下所示
- 識別自己與自己:
- 識別自己與非己:
5 總結
在初次嘗試進行模型訓練時,沒有對圖片進行細節(jié)處理以及數(shù)據(jù)提升,雖然模型準確率很高,但當使用攝像頭進行識別時,會存在識別不準確的情況,因此后面增加了對圖像的細節(jié)處理以及數(shù)據(jù)提升處理,預測效果達到預期值,模型最高準確率達到99.4%,損失率0.015。
數(shù)據(jù)集的問題,由于是直接采用攝像頭進行拍攝,會存在有些角度沒有采集完全或者會受到環(huán)境因素的影響。到人臉識別對比時,就會只能識別那些角度的特征,換了一個角度就會識別不出來.
本例當中對于人臉的定位是直接使用opencv自帶的級聯(lián)分類器,可以在安裝的opencv文件夾下找到相關的分類器。沒有自己寫算法。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Python 實現(xiàn)使用dict 創(chuàng)建二維數(shù)據(jù)、DataFrame
下面小編就為大家分享一篇Python 實現(xiàn)使用dict 創(chuàng)建二維數(shù)據(jù)、DataFrame,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04Python連接打印機實現(xiàn)自動化打印的實用技巧和示例代碼
在計算機科學領域,打印機是一種重要的外部設備,用于將電子文檔轉換成實際的紙質文件,下面這篇文章主要給大家介紹了關于Python連接打印機實現(xiàn)自動化打印的實用技巧和示例代碼,需要的朋友可以參考下2024-05-05python人工智能tensorflow常見損失函數(shù)LOSS匯總
這篇文章主要為大家介紹了python人工智能tensorflowf常見損失函數(shù)LOSS匯總,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05