您好,登錄后才能下訂單哦!
最近開始學習Qt,結合之前學習過的caffe一起搭建了一個人臉識別登錄系統的程序,新手可能有理解不到位的情況,還請大家多多指教。
我的想法是用opencv自帶的人臉檢測算法檢測出面部,利用caffe訓練好的卷積神經網絡來提取特征,通過計算當前檢測到的人臉與已近注冊的所有用戶的面部特征之間的相似度,如果最大的相似度大于一個閾值,就可以確定當前檢測到的人臉對應為這個相似度最大的用戶了。
###Caffe人臉識別
因為不斷有新的用戶加入,然而添加新用戶后重新調整CNN的網絡結構太費時間,所以不能用CNN去判別一個用戶屬于哪一類。一個訓練好的人臉識別網絡擁有很強大的特征提取能力(例如這里用到的VGG face),我們finetune預訓練的網絡時會調整最后一層的分類數目,所以最后一層的目的是為了分類,倒數第二個全連接層(或者前面的)提取到的特征通過簡單的計算距離也可以達到很高的準確率,因此可以用計算相似度的方式判斷類別。
載入finetune后的VGG模型
代碼就不詳細解釋了,我用的是拿1000個人臉微調后的VGGface,效果比用直接下載來的weight文件好一點,這里可以用原始的權重文件代替。
import caffe model_def = 'VGG_FACE_deploy.prototxt' model_weights = 'VGG_Face_finetune_1000_iter_900.caffemodel' # create transformer for the input called 'data' net = caffe.Net(model_def, # defines the structure of the model model_weights, # contains the trained weights caffe.TEST) transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) transformer.set_transpose('data', (2,0,1)) # move image channels to outermost dimension transformer.set_mean('data', np.array([104, 117, 123])) # subtract the dataset-mean value in each channel transformer.set_raw_scale('data', 255) # rescale from [0, 1] to [0, 255] transformer.set_channel_swap('data', (2,1,0)) # swap channels from RGB to BGRxpor
計算余弦相似度
import numpy as np # 計算余弦距離 def cal_cos(A,B): num = A.dot(B.T) #若為行向量則 A * B.T print(B.shape) if B.ndim == 1: denom = np.linalg.norm(A) * np.linalg.norm(B) else: denom = np.linalg.norm(A) * np.linalg.norm(B, axis=1) #print(num) cos = num / denom #余弦值 sim = 0.5 + 0.5 * cos #歸一化 return sim def cal_feature(image): #for i,img_name in enumerate(os.listdir(path)): #image = caffe.io.load_image(os.path.join(path,img_name)) transformed_image = transformer.preprocess('data', image) net.blobs['data'].data[0,:,:,:] = transformed_image output = net.forward() return net.blobs['fc7'].data[0]
cal_feature函數返回fc7層的輸出,也就是image通過網絡提取到的特征;A的維度為[1, 4096],為需要檢測的目標,B的維度為[n,4096],表示所有已注冊的用戶的特征,cal_cos返回n個相似度,值越大,越可能是同一個人。
###Opencv人臉檢測
檢測人臉位置的算法用了opencv自帶的人臉檢測器。
import cv2 face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
PyQt界面
定義全局變量存儲用戶的信息,提取到的特征,我用文件的形式將這些信息保存到本地,下一次運行時提前載入。
import sys import os import pickle global ALLFEATURE, NEWFEATURE, tempUsrName, ALLUSER, USRNAME with open('USRNAME.pickle', 'rb') as f: USRNAME = pickle.load(f) with open('ALLUSER.pickle', 'rb') as f: ALLUSER = pickle.load(f) ALLFEATURE = np.load('usrfeature.npy') NEWFEATURE = np.array([]) tempUsrName = {}
設計一個登錄界面
用PyQt設計一個界面,實現用戶注冊,注冊時錄入照片,用戶密碼登錄,人臉識別登錄等功能。
創建一個TabWidget界面
tab1用來實現密碼登錄,注冊,tab2用來實現人臉識別登錄。
from PyQt5.QtWidgets import (QWidget, QMessageBox, QLabel, QDialog, QApplication, QPushButton, QDesktopWidget, QLineEdit, QTabWidget) from PyQt5.QtGui import QIcon, QPixmap, QImage, QPalette, QBrush from PyQt5.QtCore import Qt, QTimer class TabWidget(QTabWidget): def __init__(self, parent=None): super(TabWidget, self).__init__(parent) self.setWindowTitle('Face Recognition') self.setWindowIcon(QIcon('camera.png')) self.resize(400, 260) self.center() self.mContent = passWordSign() self.mIndex = faceSign() self.addTab(self.mContent, QIcon('camera.png'), u"密碼登錄") self.addTab(self.mIndex, u"人臉識別") palette=QPalette() icon=QPixmap('background.jpg').scaled(400, 260) palette.setBrush(self.backgroundRole(), QBrush(icon)) #添加背景圖片 self.setPalette(palette) def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def closeEvent(self, event): reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() if __name__ == '__main__': app = QApplication(sys.argv) t = TabWidget() t.show() #ex = Example() sys.exit(app.exec_())
用戶注冊和密碼登錄
分別添加兩個按鈕和兩個文本框,文本框用于用戶名和密碼輸入,按鈕分別對應事件注冊和登錄。addPicture用于注冊時錄入用戶照片。
class passWordSign(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): #self.setGeometry(0, 0, 450, 300) self.signUpButton = QPushButton(QIcon('camera.png'), 'Sign up', self) self.signUpButton.move(300, 200) self.signInButton = QPushButton(QIcon('camera.png'), 'Sign in', self) self.signInButton.move(200, 200) self.usrNameLine = QLineEdit( self ) self.usrNameLine.setPlaceholderText('User Name') self.usrNameLine.setFixedSize(200, 30) self.usrNameLine.move(100, 50) self.passWordLine = QLineEdit(self) self.passWordLine.setEchoMode(QLineEdit.Password) self.passWordLine.setPlaceholderText('Pass Word') self.passWordLine.setFixedSize(200, 30) self.passWordLine.move(100, 120) self.signInButton.clicked.connect(self.signIn) self.signUpButton.clicked.connect(self.signUp) self.show() def signIn(self): global ALLFEATURE, NEWFEATURE, tempUsrName, ALLUSER, USRNAME if self.usrNameLine.text() not in ALLUSER: QMessageBox.information(self,"Information","用戶不存在,請注冊") elif ALLUSER[self.usrNameLine.text()] == self.passWordLine.text(): QMessageBox.information(self,"Information","Welcome!") else: QMessageBox.information(self,"Information","密碼錯誤!") def signUp(self): global ALLFEATURE, NEWFEATURE, tempUsrName, ALLUSER, USRNAME if self.usrNameLine.text() in ALLUSER: QMessageBox.information(self,"Information","用戶已存在!") elif len(self.passWordLine.text()) < 3: QMessageBox.information(self,"Information","密碼太短!") else: tempUsrName.clear() tempUsrName[self.usrNameLine.text()] = self.passWordLine.text() self.addPicture() def addPicture(self): dialog = Dialog(parent=self) dialog.show()
錄入用戶人臉
點擊sign up按鈕后彈出一個對話框,用一個label控件顯示攝像頭獲取的照片。首先用opencv打開攝像頭,用自帶的人臉檢測器檢測到人臉self.face后,繪制一個藍色的框,然后resize到固定的大小(對應網絡的輸入)。將opencv的圖片格式轉換為Qlabel可以顯示的格式,用Qtimer定時器每隔一段時間刷新圖片。檢測鼠標點擊事件mousePressEvent,點擊鼠標后保存當前錄入的用戶注冊信息和對應的特征。關閉攝像頭,提示注冊成功。
class Dialog(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.resize(240, 200) self.label = QLabel(self) self.label.setFixedWidth(150) self.label.setFixedHeight(150) self.label.move(40, 20) pixMap = QPixmap("face.jpg").scaled(self.label.width(),self.label.height()) self.label.setPixmap(pixMap) self.label.show() self.timer = QTimer() self.timer.start() self.timer.setInterval(100) self.cap = cv2.VideoCapture(0) self.timer.timeout.connect(self.capPicture) def mousePressEvent(self, event): global ALLFEATURE, NEWFEATURE, tempUsrName, ALLUSER, USRNAME self.cap.release() NEWFEATURE = cal_feature(self.face).reshape([1,-1]) if NEWFEATURE.size > 0: for key, value in tempUsrName.items(): ALLUSER[key] = value USRNAME.append(key) with open('ALLUSER.pickle', 'wb') as f: pickle.dump(ALLUSER, f) with open('USRNAME.pickle', 'wb') as f: pickle.dump(USRNAME, f) print(ALLFEATURE,NEWFEATURE) ALLFEATURE = np.concatenate((ALLFEATURE, NEWFEATURE), axis=0) np.save('usrfeature.npy', ALLFEATURE) QMessageBox.information(self,"Information","Success!") def capPicture(self): if (self.cap.isOpened()): # get a frame ret, img = self.cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] self.face = cv2.resize(img[y:y+h, x:x+w],(224, 224), interpolation=cv2.INTER_CUBIC) height, width, bytesPerComponent = img.shape bytesPerLine = bytesPerComponent * width # 變換彩色空間順序 cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # 轉為QImage對象 self.image = QImage(img.data, width, height, bytesPerLine, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(self.image).scaled(self.label.width(),self.label.height()))
人臉識別登錄
登錄部分與之前類似,添加一個label控件用來顯示圖片,兩個按鈕用來開始檢測和選定圖片。當最大的相似度大于0.9時,顯示登錄成功。
class faceSign(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.label = QLabel(self) self.label.setFixedWidth(260) self.label.setFixedHeight(200) self.label.move(20, 15) self.pixMap = QPixmap("face.jpg").scaled(self.label.width(),self.label.height()) self.label.setPixmap(self.pixMap) self.label.show() self.startButton = QPushButton('start', self) self.startButton.move(300, 50) self.capPictureButton = QPushButton('capPicture', self) self.capPictureButton.move(300, 150) self.startButton.clicked.connect(self.start) self.capPictureButton.clicked.connect(self.cap) #self.cap = cv2.VideoCapture(0) #self.ret, self.img = self.cap.read() self.timer = QTimer() self.timer.start() self.timer.setInterval(100) def start(self,event): self.cap = cv2.VideoCapture(0) self.timer.timeout.connect(self.capPicture) def cap(self,event): global ALLFEATURE, NEWFEATURE, tempUsrName, ALLUSER, USRNAME self.cap.release() feature = cal_feature(self.face) #np.save('usrfeature.npy', ALLFEATURE) sim = cal_cos(feature,np.array(ALLFEATURE)) m = np.argmax(sim) if max(sim)>0.9: print(sim, USRNAME) QMessageBox.information(self,"Information","Welcome," + USRNAME[m]) else: QMessageBox.information(self,"Information","識別失敗!") self.label.setPixmap(self.pixMap) def capPicture(self): if (self.cap.isOpened()): # get a frame ret, img = self.cap.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] self.face = cv2.resize(img[y:y+h, x:x+w],(224, 224), interpolation=cv2.INTER_CUBIC) height, width, bytesPerComponent = img.shape bytesPerLine = bytesPerComponent * width # 變換彩色空間順序 cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # 轉為QImage對象 self.image = QImage(img.data, width, height, bytesPerLine, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(self.image).scaled(self.label.width(),self.label.height()))
###效果
密碼登錄,輸入合法的密碼后點擊sign in,顯示歡迎。
注冊界面
識別界面
登錄成功
點擊capPicture按鈕后,開始計算相似度,大于0.9提示登錄成功,并顯示用戶名。
###缺點和不足
程序用pyinstaller打包后,親測可以在別的linux電腦下運行。代碼和文件可以參考我的Github(沒有VGG face的權重),第一次寫博客,非常感謝大家的意見。總結一下不足:
1.初始話caffe模型很費時間,所以程序打開時要等一兩秒;
2.用戶信息用文件的形式保存并不安全,可以用mysql保存到數據庫,需要時調出;
3.人臉位置檢測可以用faster rcnn代替,再加上對齊;
4.識別很耗費時間,因此不能實時檢測,應該可以用多線程解決。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。