PyQt5+Caffe+Opencv搭建人脸识别登录界面


Posted in Python onAugust 28, 2019

最近开始学习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,显示欢迎。

PyQt5+Caffe+Opencv搭建人脸识别登录界面

注册界面

PyQt5+Caffe+Opencv搭建人脸识别登录界面

识别界面

PyQt5+Caffe+Opencv搭建人脸识别登录界面

登录成功

点击capPicture按钮后,开始计算相似度,大于0.9提示登录成功,并显示用户名。

PyQt5+Caffe+Opencv搭建人脸识别登录界面

###缺点和不足

程序用pyinstaller打包后,亲测可以在别的linux电脑下运行。代码和文件可以参考我的Github(没有VGG face的权重),第一次写博客,非常感谢大家的意见。总结一下不足:

1.初始话caffe模型很费时间,所以程序打开时要等一两秒;
2.用户信息用文件的形式保存并不安全,可以用mysql保存到数据库,需要时调出;
3.人脸位置检测可以用faster rcnn代替,再加上对齐;
4.识别很耗费时间,因此不能实时检测,应该可以用多线程解决。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用Python的Treq on Twisted来进行HTTP压力测试
Apr 16 Python
在Python中操作时间之strptime()方法的使用
Dec 30 Python
python抓取网页中图片并保存到本地
Dec 01 Python
Python双向循环链表实现方法分析
Jul 30 Python
3个用于数据科学的顶级Python库
Sep 29 Python
我喜欢你 抖音表白程序python版
Apr 07 Python
详解用Python实现自动化监控远程服务器
May 18 Python
Python利用requests模块下载图片实例代码
Aug 12 Python
在Python中使用MySQL--PyMySQL的基本使用方法
Nov 19 Python
python爬虫开发之PyQuery模块详细使用方法与实例全解
Mar 09 Python
多个版本的python共存时使用pip的正确做法
Oct 26 Python
python+appium+yaml移动端自动化测试框架实现详解
Nov 24 Python
关于Python核心框架tornado的异步协程的2种方法详解
Aug 28 #Python
python使用celery实现异步任务执行的例子
Aug 28 #Python
Python Gitlab Api 使用方法
Aug 28 #Python
face++与python实现人脸识别签到(考勤)功能
Aug 28 #Python
OpenCV+face++实现实时人脸识别解锁功能
Aug 28 #Python
Python的垃圾回收机制详解
Aug 28 #Python
Python通过cv2读取多个USB摄像头
Aug 28 #Python
You might like
世界收音机发展史
2021/03/01 无线电
解决GD中文乱码问题
2007/02/14 PHP
Netbeans 8.2将支持PHP7 更精彩
2016/06/13 PHP
php获取当前url地址的方法小结
2017/01/10 PHP
通过PHP的Wrapper无缝迁移原有项目到新服务的实现方法
2020/04/02 PHP
javascript改变position值实现菜单滚动至顶部后固定
2013/01/18 Javascript
js之onload事件的一点使用心得
2013/08/14 Javascript
给html超链接设置事件不使用href来完成跳
2014/04/20 Javascript
JavaScript数据结构与算法之链表
2016/01/29 Javascript
js实现简单的省市县三级联动效果实例
2016/02/18 Javascript
jQuery查找节点方法完整实例
2016/09/13 Javascript
Nodejs进阶:如何将图片转成datauri嵌入到网页中去实例
2016/11/21 NodeJs
Angular使用Md5加密的解决方法
2017/09/16 Javascript
Three.js如何用轨迹球插件(trackball)增加对模型的交互功能详解
2017/09/25 Javascript
vue使用ElementUI时导航栏默认展开功能的实现
2018/07/04 Javascript
JavaScript动画实例之粒子文本的实现方法详解
2020/07/28 Javascript
vant 解决tab切换插件标题样式自定义的问题
2020/11/13 Javascript
python列表去重的二种方法
2014/02/14 Python
Windows系统下使用flup搭建Nginx和Python环境的方法
2015/12/25 Python
opencv-python 读取图像并转换颜色空间实例
2019/12/09 Python
python小项目之五子棋游戏
2019/12/26 Python
python获取依赖包和安装依赖包教程
2020/02/13 Python
浅谈python量化 双均线策略(金叉死叉)
2020/06/03 Python
解决Python paramiko 模块远程执行ssh 命令 nohup 不生效的问题
2020/07/14 Python
html特殊符号示例 html特殊字符编码对照表
2014/01/14 HTML / CSS
HTML5实现晶莹剔透的雨滴特效
2014/05/14 HTML / CSS
阿玛尼美妆加拿大官方商城:Giorgio Armani Beauty加拿大
2017/10/24 全球购物
SKECHERS斯凯奇中国官网:来自美国的运动休闲品牌
2018/11/14 全球购物
优质飞蝇钓和渔具:RiverBum
2020/05/10 全球购物
导购员的岗位职责
2014/02/08 职场文书
根叔历年演讲稿
2014/05/20 职场文书
《地震中的父与子》教学反思
2016/02/16 职场文书
2019森林防火宣传标语大全!
2019/07/03 职场文书
如何书写读后感?(附范文)
2019/07/26 职场文书
创业计划书之奶茶店开店方案范本!
2019/08/06 职场文书
高一作文之乐趣
2019/11/21 职场文书