├── README.md ├── baseConnect.py ├── connect.py └── demo.py /README.md: -------------------------------------------------------------------------------- 1 | # Check-in-and-Check-out-System-for-Face-Recognition 2 | 基于python的人脸识别签到/签退系统 3 | 4 | # 简介: 5 | 人脸识别签到签退系统,使用摄像头采集数据,后根据本地数据训练,在调用摄像头识别人脸并且辨别身份。 6 | 将签到/签退信息保存到数据库中,可以导出签到信息等excel表。 7 | 8 | # 环境: 9 | 开发平台Windows python3.7.3 10 | project interpret: 11 | numpy 12 | opencv-contrib-python 13 | pymssql 14 | pandas 15 | pyttsx3 16 | 17 | # 使用说明: 18 | 直接运行demo.py 19 | demo.py会调用connect.py baseConnect.py 20 | 21 | 数据库使用的是sql server 此处用到(需要建立)两个表: 22 | 学生信息表(ID (int), name (char), StudentID(int), Sex(char)), 23 | 签到表(ID(int), Name(char) StudentID(int), Sex(char), starttime(text), stoptime(text), count(text), flag(int)) 24 | 以及数据库里面连接需要使用自己的库名和密码 25 | 26 | 在首次使用时: 27 | 会创建环境:三个文件夹:face_trainer, Facedata,excel 分别存储训练后的结果 人脸照片库 导出信息表 28 | 29 | 其他步骤按照语音播报进行 30 | 只有使用的人脸分类器.xml可以直接写上地址也可以放在运行后提示存放的路径中。 31 | 32 | 33 | # 其他: 34 | ·还想继续扩充语音播报开别的线程来执行写可以中止 35 | ·加上c/s架构 36 | ·关于数据库的安全性也还有待改进 37 | -------------------------------------------------------------------------------- /baseConnect.py: -------------------------------------------------------------------------------- 1 | import pymssql as py 2 | import time 3 | import pandas as pd 4 | 5 | server = "DESKTOP-G8THN71"# 连接服务器地址 6 | user = "sa" # 连接帐号 7 | password = "" # 连接密码 8 | conn = py.connect(server, user, password, "student_message") #获取连接 9 | cursor = conn.cursor() # 获取光标 10 | 11 | def insertd(idnum,Name,StudentID,Sex): # 签到 12 | conn = py.connect(server, user, password, "student_message") # 获取连接 13 | timenow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 14 | cursor = conn.cursor() 15 | cursor.execute("INSERT INTO qiandao VALUES (%d, %s,%d,%s, %s, %s, %s,%d )", (idnum,Name,StudentID,Sex,timenow, '0', '0', 0)) 16 | conn.commit() 17 | # 必须调用 commit() 来保持数据的提交 18 | def insertt(idnum,Name,StudentID,Sex): # 签退 19 | conn = py.connect(server, user, password, "student_message") # 获取连接 20 | cursor = conn.cursor() 21 | cursor.execute("SELECT starttime FROM qiandao WHERE ID=%s and flag=%d",(idnum,0)) 22 | starttimeget = cursor.fetchone() 23 | sat = str(tuple(starttimeget)) 24 | timenow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 25 | timeArray = time.strptime(sat, "('%Y-%m-%d %H:%M:%S',)") 26 | timeStamp = int(time.mktime(timeArray)) 27 | timecout = time.time() - timeStamp 28 | m, s = divmod(timecout, 60) 29 | h, m = divmod(m, 60) 30 | timepass = str(h) + '小时 ' + str(m) + '分钟 ' + str(s) + '秒' 31 | print(timepass) 32 | cursor.executemany("INSERT INTO qiandao VALUES (%d, %s,%d,%s, %s, %s, %s,%d )", 33 | [(idnum,Name,StudentID,Sex,'0', timenow, timepass, 1)]) 34 | conn.commit() 35 | 36 | def peoson_sign(StudentID):# 导出学生信息表_按照学号 37 | conn = py.connect(server, user, password, "student_message") # 获取连接 38 | cursor = conn.cursor() 39 | sql = "select * from qiandao where StudentID=" + str(StudentID) 40 | df = pd.read_sql(sql, conn) 41 | df.to_excel(r'E:\01STUDY\20190701\work\openVersion\excel\studentID_sign.xlsx',index=False) 42 | print('ok') 43 | conn.commit() 44 | 45 | #peoson_sign(2016002105) 46 | 47 | def sign():# 导出签到表 48 | conn = py.connect(server, user, password, "student_message") # 获取连接 49 | cursor = conn.cursor() 50 | sql = "select * from qiandao" 51 | df = pd.read_sql(sql, conn) 52 | df.to_excel(r'E:\01STUDY\20190701\work\openVersion\excel\sign_all.xlsx', index=False) 53 | print('ok') 54 | conn.commit() 55 | #sign() 56 | 57 | def total_time():# 导出时长表#sign() 58 | conn = py.connect(server, user, password, "student_message") # 获取连接 59 | cursor = conn.cursor() 60 | sql = "select * from qiandao where convert(nvarchar(max),count) != convert(nvarchar(max),0)" 61 | df = pd.read_sql(sql, conn) 62 | df.to_excel(r'E:\01STUDY\20190701\work\openVersion\excel\total_time.xlsx', index=False) 63 | print('ok') 64 | conn.commit() 65 | 66 | # 67 | # if __name__=='__main': 68 | # sign() 69 | # #peoson_sign(2016002105) 70 | 71 | conn.close() 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /connect.py: -------------------------------------------------------------------------------- 1 | import pymssql as py 2 | import pandas as pd 3 | # 连接数据库,创建学生表,进行表查询,表录入 4 | server = "DESKTOP-G8THN71"# 连接服务器地址 5 | user = "sa"# 连接帐号 6 | password = ""# 连接密码 7 | conn = py.connect(server, user, password, "student_message") #获取连接 8 | cursor = conn.cursor() # 获取光标 9 | 10 | # 创建表 11 | # cursor.execute(""" 12 | # IF OBJECT_ID('students', 'U') IS NOT NULL 13 | # DROP TABLE students 14 | # CREATE TABLE students ( 15 | # ID INT NOT NULL, 16 | # name VARCHAR(100), 17 | # StudentID INT, 18 | # Sex VARCHAR(100) 19 | # ) 20 | # """) 21 | # commit() 提交动作 22 | # conn.commit() 23 | 24 | def insert(Name, studentID, Sex): 25 | count_students = 0 26 | conn = py.connect(server, user, password, "student_message") # 获取连接 27 | cursor =conn.cursor() 28 | cursor.execute(' select count(ID) from students') 29 | for row in cursor: 30 | count_students = row[0] 31 | print(row[0]) 32 | cursor.executemany( 33 | "INSERT INTO students VALUES (%d, %s, %d,%s)", 34 | [(count_students+1, Name, studentID, Sex)]) 35 | # 你必须调用 commit() 来保持你数据的提交如果你没有将自动提交设置为true 36 | conn.commit() 37 | # 导出学生信息表 38 | def find_student_all(): 39 | conn = py.connect(server, user, password, "student_message") # 获取连接 40 | cursor =conn.cursor() 41 | sql = "select * from students" 42 | df = pd.read_sql(sql, conn) 43 | df.to_excel(r'E:\01STUDY\20190701\work\openVersion\excel\all.xlsx',index=False) 44 | print('ok') 45 | conn.commit() 46 | def readName(idnum): 47 | Name = -1 48 | conn = py.connect(server, user, password, "student_message") # 获取连接 49 | cursor =conn.cursor() 50 | cursor.execute(' select Name from students where ID='+str(idnum)) 51 | for row in cursor: 52 | if row[0]!=[]: 53 | Name = row[0] 54 | conn.commit() 55 | return Name 56 | def readIDbaseStudentID(StudentID): 57 | ID = -1 58 | conn = py.connect(server, user, password, "student_message") # 获取连接 59 | cursor =conn.cursor() 60 | cursor.execute(' select ID from students where StudentID='+str(StudentID)) 61 | for row in cursor: 62 | if row[0]!=[]: 63 | ID = row[0] 64 | conn.commit() 65 | conn.close() 66 | return ID 67 | def readSex(idnum): 68 | Sex = -1 69 | conn = py.connect(server, user, password, "student_message") # 获取连接 70 | cursor =conn.cursor() 71 | cursor.execute(' select Sex from students where ID='+str(idnum)) 72 | 73 | for row in cursor: 74 | if row[0]!=[]: 75 | Sex = row[0] 76 | conn.commit() 77 | return Sex 78 | def readID(name): 79 | ID = -1 80 | conn = py.connect(server, user, password, "student_message") # 获取连接 81 | cursor =conn.cursor() 82 | cursor.execute(' select ID from students where name='+'\''+str(name)+'\'') 83 | for row in cursor: 84 | if row[0]!=[]: 85 | ID = row[0] 86 | conn.commit() 87 | return ID 88 | def readStudentID(idnum): 89 | StudentID = -1 90 | conn = py.connect(server, user, password, "student_message") # 获取连接 91 | cursor =conn.cursor() 92 | cursor.execute(' select StudentID from students where ID='+str(idnum)) 93 | for row in cursor: 94 | if row[0]!=[]: 95 | StudentID = row[0] 96 | conn.commit() 97 | return StudentID 98 | conn.close() 99 | # 100 | # # 注:在任何时候,在一个连接下,一次正在执行的数据库操作只会出现一个cursor对象 -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import os 3 | import numpy as np 4 | from PIL import Image #pillow 5 | import pyttsx3 6 | import sys 7 | import deathymzFace.connect as connect 8 | import deathymzFace.baseConnect as baseConnect 9 | import time 10 | import json 11 | 12 | def makeDir(engine): 13 | flag= 0 14 | if not os.path.exists("face_trainer"): 15 | print("创建预训练环境") 16 | engine.say('检测到第一次启动,未检测到环境,正在创建环境') 17 | engine.say('正在创建预训练环境') 18 | os.mkdir("face_trainer") 19 | engine.say('创建成功') 20 | engine.runAndWait() 21 | flag=1 22 | if not os.path.exists("Facedata"): 23 | print("创建训练环境") 24 | engine.say('正在创建训练环境') 25 | os.mkdir("Facedata") 26 | engine.say('创建成功') 27 | engine.runAndWait() 28 | flag=1 29 | if not os.path.exists("excel"): 30 | print("创建导出表环境") 31 | engine.say('正在创建导出表环境') 32 | os.mkdir("excel") 33 | engine.say('创建成功') 34 | engine.runAndWait() 35 | flag = 1 36 | return flag 37 | 38 | def getFace(cap,path_id): 39 | # 调用笔记本内置摄像头,所以参数为0,如果有其他的摄像头可以调整参数为1,2 40 | #cap = cv2.VideoCapture(0) 41 | face_detector = cv2.CascadeClassifier(r'C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect\haarcascades\haarcascade_frontalface_default.xml') 42 | #face_id = input('\n enter user id:') 43 | print('\n Initializing face capture. Look at the camera and wait ...') 44 | count = 0 45 | while True: 46 | # 从摄像头读取图片 47 | sucess, img = cap.read() 48 | # 转为灰度图片 49 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 50 | # 检测人脸 51 | faces = face_detector.detectMultiScale(gray, 1.3, 5) 52 | for (x, y, w, h) in faces: 53 | cv2.rectangle(img, (x, y), (x+w, y+w), (255, 0, 0)) 54 | count += 1 55 | # 保存图像 56 | cv2.imwrite("Facedata/User." + str(path_id) + '.' + str(count) + '.jpg', gray[y: y + h, x: x + w]) 57 | cv2.imshow('image', img) 58 | # 保持画面的持续。 59 | k = cv2.waitKey(1) 60 | if k == 27: # 通过esc键退出摄像 61 | break 62 | elif count >= 100: # 得到1000个样本后退出摄像 63 | break 64 | cv2.destroyAllWindows() 65 | 66 | def getImagesAndLabels(path, detector): 67 | imagePaths = [os.path.join(path, f) for f in os.listdir(path)] # join函数的作用 68 | faceSamples = [] 69 | ids = [] 70 | for imagePath in imagePaths: 71 | PIL_img = Image.open(imagePath).convert('L') # convert it to grayscale 72 | img_numpy = np.array(PIL_img, 'uint8') 73 | id = int(os.path.split(imagePath)[-1].split(".")[1]) 74 | faces = detector.detectMultiScale(img_numpy) 75 | for (x, y, w, h) in faces: 76 | faceSamples.append(img_numpy[y:y + h, x: x + w]) 77 | ids.append(id) 78 | return faceSamples, ids 79 | 80 | 81 | def trainFace(): 82 | # 人脸数据路径 83 | path = 'Facedata' 84 | recognizer = cv2.face.LBPHFaceRecognizer_create() 85 | detector = cv2.CascadeClassifier(r'C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect\haarcascades\haarcascade_frontalface_default.xml') 86 | print('Training faces. It will take a few seconds. Wait ...') 87 | faces, ids = getImagesAndLabels(path, detector) 88 | recognizer.train(faces, np.array(ids)) 89 | recognizer.write(r'face_trainer\trainer.yml') 90 | print("{0} faces trained. Exiting Program".format(len(np.unique(ids)))) 91 | 92 | def checkFace(cam,names,engine,sign_flag): 93 | sex = {"female":"女士","male":"先生"} 94 | recognizer = cv2.face.LBPHFaceRecognizer_create() 95 | recognizer.read('face_trainer/trainer.yml') 96 | cascadePath = r"C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect\haarcascades\haarcascade_frontalface_default.xml" 97 | faceCascade = cv2.CascadeClassifier(cascadePath) 98 | font = cv2.FONT_HERSHEY_SIMPLEX 99 | idnum = 0 100 | minW = 0.1 * cam.get(3) 101 | minH = 0.1 * cam.get(4) 102 | while True: 103 | ret, img = cam.read() 104 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 105 | faces = faceCascade.detectMultiScale( 106 | gray, 107 | scaleFactor=1.2, 108 | minNeighbors=5, 109 | minSize=(int(minW), int(minH)) 110 | ) 111 | for (x, y, w, h) in faces: 112 | cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) 113 | idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w]) 114 | if confidence < 100: 115 | Name =connect.readName(idnum) #connect 传入ID 学生信息表找到 返回 name 116 | Sex =connect.readSex(idnum) #connect ID 学生信息表找到 返回 Sex 117 | StudentID =connect.readStudentID(idnum) #connect ID 学生信息表找到 返回 studentID 118 | #idnum = names[idnum] #利用数据库 读取学生信息表 该id 对应的name 119 | confidence = "{0}%".format(round(100 - confidence)) 120 | if sign_flag=='0': #签到 121 | say(engine, "欢迎 "+Name+ sex[Sex]+" 签到成功 ") 122 | baseConnect.insertd(idnum,Name,StudentID,Sex) #签到表中 插入签到信息 123 | print("欢迎 "+Name+ sex[Sex] + "签到成功 ") 124 | else : 125 | say(engine, "欢迎 "+Name+ sex[Sex]+" 签退成功 ") 126 | baseConnect.insertt(idnum,Name,StudentID,Sex) #签到表中 插入签退信息 127 | print("欢迎 "+Name+ sex[Sex] + "签退成功 ") 128 | 129 | # cv2.imshow("img",img) 130 | # os.system("pause") 131 | return 132 | else: 133 | idnum = "unknown" 134 | confidence = "{0}%".format(round(100 - confidence)) 135 | cv2.putText(img, str(idnum), (x + 5, y - 5), font, 1, (0, 0, 255), 1) 136 | cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1) 137 | cv2.imshow('camera', img) 138 | k = cv2.waitKey(10) 139 | if k == 27: 140 | break 141 | cam.release() 142 | cv2.destroyAllWindows() 143 | 144 | 145 | def say(engine,str): 146 | engine.say(str) 147 | engine.runAndWait() 148 | 149 | def admission(): #录入信息模块 150 | #names = {"yumengzhen":0,"dujuanjuan":1,"litingting":2} 151 | say(engine, "请输入您的学号 ") 152 | StudentID = input("请输入学号:") 153 | # 读取数据库信息表 取出Name 对应ID 154 | ID=connect.readIDbaseStudentID(StudentID) #connect 传入name 学生信息表找到 返回 ID 155 | if ID==-1:#没有找到该学生插入学生信息 156 | while True: 157 | say(engine,"没有找到该学生信息 输人 0 注册 1重新输入") 158 | op=input("\n 没有找到该学生信息 输人数字 0 注册学生信息 1重新输入") 159 | if op=='0': 160 | Name,studentID,Sex=input("输入学生信息: Name studentID Sex").split() 161 | connect.insert(Name,studentID,Sex) #插入学生信息信息 162 | else: 163 | StudentID = input("请输入学号:") 164 | ID=connect.readIDbaseStudentID(StudentID) #connect 传入name 学生信息表找到 返回 ID 165 | if ID!=-1 : 166 | break 167 | say(engine, "正在打开摄像头") 168 | cam = cv2.VideoCapture(0) 169 | say(engine, "注视摄像头,开始采集人脸数据") 170 | getFace(cam, ID) # 实际传入的是id 171 | cam.release() 172 | 173 | 174 | if __name__ == '__main__': 175 | names = {"yumengzhen":0,"dujuanjuan":1,"litingting": 2} 176 | password="123456" #密码 177 | engine = pyttsx3.init() 178 | rate = engine.getProperty('rate') 179 | engine.setProperty('rate', rate - 20) 180 | flag=makeDir(engine) 181 | #trainFace() 182 | while True: 183 | if flag==1 : 184 | flag = 0 185 | say(engine, "首次使用 没有人脸信息 ") 186 | say(engine, "是否要录入新的人脸信息 ") 187 | say(engine, "输入0 代表是 输入其他表示退出") 188 | value = input("0:是 or other:否") 189 | if value=='0': 190 | while True: 191 | admission() 192 | say(engine, "是否要继续录入新的人脸信息 ") 193 | say(engine, "输入0 代表是 输入其他表示退出") 194 | firstflag = input("0:是 其他:退出") 195 | if firstflag != '0': 196 | break 197 | say(engine, "采集完毕,开始训练") 198 | trainFace() 199 | say(engine, "训练完毕 ") 200 | 201 | #say(engine, "请选择登录方式 ") 202 | say(engine, "输入 0管理人员模式 1 进入签到/签退模式 2 退出学生签到系统 ") 203 | user=input("\n0:管理人员模式 1:进入签到/签退模式 2:退出学生签到系统\n") 204 | 205 | if user=='0': 206 | say(engine, "输入管理员密码 ") 207 | pd=input("\n输入管理员密码 :\n") 208 | count=1 209 | while True: 210 | if count==3: 211 | say(engine," 输入密码错误超过3次 强制退出输入 ") 212 | break 213 | 214 | if password == pd: 215 | say(engine, "管理员模式 ") 216 | #say(engine, "输入数字 0 导出签到表 1 导出个人签到表 2 导出时长表 3 导出信息表 4 录入人脸信息 5 退出") 217 | op = input("\n0:导出所有同学签到表 1:导出个人签到表 2:导出所有人员时长表 3:导出学生信息表 4 录入人脸信息 5 退出\n") 218 | if op == '0': 219 | baseConnect.sign()#导出签到表 220 | say(engine, "导出签到表成功 ") 221 | print( " 导出签到表成功 " ) 222 | pass 223 | elif op == '1': 224 | say(engine,"输入导出学生的学号") 225 | StudentID=input("输入导出学生的学号") 226 | ID=connect.readIDbaseStudentID(StudentID) 227 | if ID==-1: 228 | say(engine, "没有该学生信息 ") 229 | print( "没有该学生信息 ") 230 | else: 231 | baseConnect.peoson_sign(StudentID)#导出个人签到表 232 | Name =connect.readName(ID) #connect 传入ID 学生信息表找到 返回 name 233 | say(engine, "导出 "+Name+" 信息成功") 234 | print( "导出 "+Name+" 信息成功") 235 | 236 | elif op == '2': 237 | baseConnect.total_time()#导出时长表 238 | say(engine,"导出时长表成功 ") 239 | print( "导出时长表成功 ") 240 | elif op == '3': 241 | #导出学生信息表 242 | connect.find_student_all() 243 | print("导出学生信息成功 ") 244 | elif op == '4': 245 | while True: 246 | admission() 247 | say(engine, "是否要继续录入新的人脸信息 ") 248 | say(engine, "输入0 代表是 输入其他表示退出") 249 | secondflag = input("0:是 其他:退出") 250 | if secondflag != '0': 251 | break 252 | say(engine, "采集完毕,开始训练") 253 | trainFace() 254 | say(engine, "训练完毕 ") 255 | elif op == '5': 256 | say(engine, "已退出 管理员模式 ") 257 | break 258 | else: 259 | say(engine, "输入形式错误 请重新输入 ") 260 | else: 261 | say(engine, "输入密码错误 请重新输入 ") 262 | pd = input("\n输入管理员密码 :\n") 263 | count += 1; 264 | 265 | elif user=='1': 266 | say(engine, "欢迎进入学生系统签到/签退模式 ") 267 | sign_flag=0; 268 | while True: 269 | say(engine, "输入数字 0 签到 1 签退") 270 | sign_flag = input("\n0: 签到 1 签退\n") 271 | if sign_flag=='1' or sign_flag=='0' : 272 | break 273 | else : 274 | say(engine," 请输入正确的输入形式") 275 | say(engine, "开始人脸识别") 276 | say(engine, "正在打开摄像头") 277 | cam = cv2.VideoCapture(0) 278 | checkFace(cam, names, engine,sign_flag) 279 | 280 | elif user=='2': 281 | say(engine, "信息已保存") 282 | say(engine, "再见") 283 | sys.exit(0) 284 | else: 285 | say(engine, "输入错误请重新输入 ") 286 | 287 | 288 | 289 | --------------------------------------------------------------------------------