Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 12996f7

Browse files
author
winterssy
committed
核心框架:整合人脸跟踪+人脸识别;开源完整代码
1 parent a6c5b1f commit 12996f7

File tree

11 files changed

+1616
-87
lines changed

11 files changed

+1616
-87
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,8 @@ ENV/
103103
.idea/
104104
datasets/
105105
recognizer/
106+
unknown/
107+
tmp/
108+
config/telegramBot.cfg
106109
*.db
110+
*.log.*

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# face_recognition_py
2-
本项目基于OpenCV使用Haar级联与dlib库进行人脸检测及实时跟踪,应用LBPH算法开发了一个简单的人脸识别系统。系统采用sqlite3进行序列化数据存储,能够对陌生人脸闯入进行报警,并拥有基于PyQt5设计的GUI实现。
2+
本项目基于OpenCV使用Haar级联与dlib库进行人脸检测及实时跟踪,应用LBPH算法开发了一个功能相对完整的人脸识别系统。系统采用sqlite3进行序列化数据存储,能够对陌生人脸闯入进行报警,并拥有基于PyQt5设计的GUI实现。
33

44
## 系统预览
55
### 核心框架
@@ -37,15 +37,15 @@ $ pip install -r requirements.txt
3737
```
3838
### 运行核心框架
3939
```
40-
$ python core.pyc
40+
$ python core.py
4141
```
4242
### 运行人脸采集系统
4343
```
44-
$ python dataRecord.pyc
44+
$ python dataRecord.py
4545
```
4646
### 运行数据管理系统
4747
```
48-
$ python dataManage.pyc
48+
$ python dataManage.py
4949
```
5050
### 更新
5151
```

config/telegramBot.cfg.bak

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[telegramBot]
2+
read_only = true
3+
token = your_telegramBot_api_token
4+
chat_id = your_telegram_id
5+
proxy_url = socks5://127.0.0.1:1080
6+
message = 【OpenCV人脸识别自动报警系统】发现陌生目标进入监控区域,请及时处理。
7+

core.py

Lines changed: 714 additions & 0 deletions
Large diffs are not rendered by default.

core.pyc

-19.1 KB
Binary file not shown.

dataManage.py

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
#!/usr/bin/env python3
2+
# Author: winterssy <[email protected]>
3+
4+
import cv2
5+
import numpy as np
6+
7+
from PyQt5.QtCore import pyqtSignal
8+
from PyQt5.QtGui import QIcon, QTextCursor
9+
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QTableWidgetItem, QAbstractItemView
10+
from PyQt5.uic import loadUi
11+
12+
import logging
13+
import logging.config
14+
import os
15+
import shutil
16+
import sqlite3
17+
import sys
18+
import threading
19+
import multiprocessing
20+
21+
from datetime import datetime
22+
23+
24+
# 自定义数据库记录不存在异常
25+
class RecordNotFound(Exception):
26+
pass
27+
28+
29+
class DataManageUI(QWidget):
30+
logQueue = multiprocessing.Queue() # 日志队列
31+
receiveLogSignal = pyqtSignal(str) # 日志信号
32+
33+
def __init__(self):
34+
super(DataManageUI, self).__init__()
35+
loadUi('./ui/DataManage.ui', self)
36+
self.setWindowIcon(QIcon('./icons/icon.png'))
37+
self.setFixedSize(931, 577)
38+
39+
# 设置tableWidget只读,不允许修改
40+
self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
41+
42+
# 数据库
43+
self.database = './FaceBase.db'
44+
self.datasets = './datasets'
45+
self.isDbReady = False
46+
self.initDbButton.clicked.connect(self.initDb)
47+
48+
# 用户管理
49+
self.queryUserButton.clicked.connect(self.queryUser)
50+
self.deleteUserButton.clicked.connect(self.deleteUser)
51+
52+
# 训练人脸数据
53+
self.trainButton.clicked.connect(self.train)
54+
55+
# 系统日志
56+
self.receiveLogSignal.connect(lambda log: self.logOutput(log))
57+
self.logOutputThread = threading.Thread(target=self.receiveLog, daemon=True)
58+
self.logOutputThread.start()
59+
60+
# 初始化/刷新数据库
61+
def initDb(self):
62+
# 刷新前重置tableWidget
63+
while self.tableWidget.rowCount() > 0:
64+
self.tableWidget.removeRow(0)
65+
try:
66+
if not os.path.isfile(self.database):
67+
raise FileNotFoundError
68+
69+
conn = sqlite3.connect(self.database)
70+
cursor = conn.cursor()
71+
72+
res = cursor.execute('SELECT * FROM users')
73+
for row_index, row_data in enumerate(res):
74+
self.tableWidget.insertRow(row_index)
75+
for col_index, col_data in enumerate(row_data):
76+
self.tableWidget.setItem(row_index, col_index, QTableWidgetItem(str(col_data)))
77+
cursor.execute('SELECT Count(*) FROM users')
78+
result = cursor.fetchone()
79+
dbUserCount = result[0]
80+
except FileNotFoundError:
81+
logging.error('系统找不到数据库文件{}'.format(self.database))
82+
self.isDbReady = False
83+
self.initDbButton.setIcon(QIcon('./icons/error.png'))
84+
self.logQueue.put('Error:未发现数据库文件,你可能未进行人脸采集')
85+
except Exception:
86+
logging.error('读取数据库异常,无法完成数据库初始化')
87+
self.isDbReady = False
88+
self.initDbButton.setIcon(QIcon('./icons/error.png'))
89+
self.logQueue.put('Error:读取数据库异常,初始化/刷新数据库失败')
90+
else:
91+
cursor.close()
92+
conn.close()
93+
94+
self.dbUserCountLcdNum.display(dbUserCount)
95+
if not self.isDbReady:
96+
self.isDbReady = True
97+
self.logQueue.put('Success:数据库初始化完成,发现用户数:{}'.format(dbUserCount))
98+
self.initDbButton.setText('刷新数据库')
99+
self.initDbButton.setIcon(QIcon('./icons/success.png'))
100+
self.trainButton.setToolTip('')
101+
self.trainButton.setEnabled(True)
102+
self.queryUserButton.setToolTip('')
103+
self.queryUserButton.setEnabled(True)
104+
else:
105+
self.logQueue.put('Success:刷新数据库成功,发现用户数:{}'.format(dbUserCount))
106+
107+
# 查询用户
108+
def queryUser(self):
109+
stu_id = self.queryUserLineEdit.text().strip()
110+
conn = sqlite3.connect(self.database)
111+
cursor = conn.cursor()
112+
113+
try:
114+
cursor.execute('SELECT * FROM users WHERE stu_id=?', (stu_id,))
115+
ret = cursor.fetchall()
116+
if not ret:
117+
raise RecordNotFound
118+
face_id = ret[0][1]
119+
cn_name = ret[0][2]
120+
except RecordNotFound:
121+
self.queryUserButton.setIcon(QIcon('./icons/error.png'))
122+
self.queryResultLabel.setText('<font color=red>Error:此用户不存在</font>')
123+
except Exception as e:
124+
logging.error('读取数据库异常,无法查询到{}的用户信息'.format(stu_id))
125+
self.queryResultLabel.clear()
126+
self.queryUserButton.setIcon(QIcon('./icons/error.png'))
127+
self.logQueue.put('Error:读取数据库异常,查询失败')
128+
else:
129+
self.queryResultLabel.clear()
130+
self.queryUserButton.setIcon(QIcon('./icons/success.png'))
131+
self.stuIDLineEdit.setText(stu_id)
132+
self.cnNameLineEdit.setText(cn_name)
133+
self.faceIDLineEdit.setText(str(face_id))
134+
self.deleteUserButton.setEnabled(True)
135+
finally:
136+
cursor.close()
137+
conn.close()
138+
139+
# 删除用户
140+
def deleteUser(self):
141+
text = '从数据库中删除该用户,同时删除相应人脸数据,<font color=red>该操作不可逆!</font>'
142+
informativeText = '<b>是否继续?</b>'
143+
ret = DataManageUI.callDialog(QMessageBox.Warning, text, informativeText, QMessageBox.Yes | QMessageBox.No,
144+
QMessageBox.No)
145+
146+
if ret == QMessageBox.Yes:
147+
stu_id = self.stuIDLineEdit.text()
148+
conn = sqlite3.connect(self.database)
149+
cursor = conn.cursor()
150+
151+
try:
152+
cursor.execute('DELETE FROM users WHERE stu_id=?', (stu_id,))
153+
except Exception as e:
154+
cursor.close()
155+
logging.error('无法从数据库中删除{}'.format(stu_id))
156+
self.deleteUserButton.setIcon(QIcon('./icons/error.png'))
157+
self.logQueue.put('Error:读写数据库异常,删除失败')
158+
else:
159+
cursor.close()
160+
conn.commit()
161+
if os.path.exists('{}/stu_{}'.format(self.datasets, stu_id)):
162+
try:
163+
shutil.rmtree('{}/stu_{}'.format(self.datasets, stu_id))
164+
except Exception as e:
165+
logging.error('系统无法删除删除{}/stu_{}'.format(self.datasets, stu_id))
166+
self.logQueue.put('Error:删除人脸数据失败,请手动删除{}/stu_{}目录'.format(self.datasets, stu_id))
167+
168+
text = '你已成功删除学号为 <font color=blue>{}</font> 的用户记录。'.format(stu_id)
169+
informativeText = '<b>请在右侧菜单重新训练人脸数据。</b>'
170+
DataManageUI.callDialog(QMessageBox.Information, text, informativeText, QMessageBox.Ok)
171+
172+
self.stuIDLineEdit.clear()
173+
self.cnNameLineEdit.clear()
174+
self.faceIDLineEdit.clear()
175+
self.initDb()
176+
self.deleteUserButton.setIcon(QIcon('./icons/success.png'))
177+
self.deleteUserButton.setEnabled(False)
178+
self.queryUserButton.setIcon(QIcon())
179+
finally:
180+
conn.close()
181+
182+
# 检测人脸
183+
def detectFace(self, img):
184+
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
185+
face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
186+
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)
187+
188+
if (len(faces) == 0):
189+
return None, None
190+
(x, y, w, h) = faces[0]
191+
return gray[y:y + w, x:x + h], faces[0]
192+
193+
# 准备图片数据
194+
def prepareTrainingData(self, data_folder_path):
195+
dirs = os.listdir(data_folder_path)
196+
faces = []
197+
labels = []
198+
199+
face_id = 1
200+
conn = sqlite3.connect(self.database)
201+
cursor = conn.cursor()
202+
203+
# 遍历人脸库
204+
for dir_name in dirs:
205+
if not dir_name.startswith('stu_'):
206+
continue
207+
stu_id = dir_name.replace('stu_', '')
208+
try:
209+
cursor.execute('SELECT * FROM users WHERE stu_id=?', (stu_id,))
210+
ret = cursor.fetchall()
211+
if not ret:
212+
raise RecordNotFound
213+
cursor.execute('UPDATE users SET face_id=? WHERE stu_id=?', (face_id, stu_id,))
214+
except RecordNotFound:
215+
logging.warning('数据库中找不到学号为{}的用户记录'.format(stu_id))
216+
self.logQueue.put('发现学号为{}的人脸数据,但数据库中找不到相应记录,已忽略'.format(stu_id))
217+
continue
218+
subject_dir_path = data_folder_path + '/' + dir_name
219+
subject_images_names = os.listdir(subject_dir_path)
220+
for image_name in subject_images_names:
221+
if image_name.startswith('.'):
222+
continue
223+
image_path = subject_dir_path + '/' + image_name
224+
image = cv2.imread(image_path)
225+
face, rect = self.detectFace(image)
226+
if face is not None:
227+
faces.append(face)
228+
labels.append(face_id)
229+
face_id = face_id + 1
230+
231+
cursor.close()
232+
conn.commit()
233+
conn.close()
234+
235+
return faces, labels
236+
237+
# 训练人脸数据
238+
# Reference:https://github.com/informramiz/opencv-face-recognition-python
239+
def train(self):
240+
try:
241+
if not os.path.isdir(self.datasets):
242+
raise FileNotFoundError
243+
244+
text = '系统将开始训练人脸数据,界面会暂停响应一段时间,完成后会弹出提示。'
245+
informativeText = '<b>训练过程请勿进行其它操作,是否继续?</b>'
246+
ret = DataManageUI.callDialog(QMessageBox.Question, text, informativeText,
247+
QMessageBox.Yes | QMessageBox.No,
248+
QMessageBox.No)
249+
if ret == QMessageBox.Yes:
250+
face_recognizer = cv2.face.LBPHFaceRecognizer_create()
251+
if not os.path.exists('./recognizer'):
252+
os.makedirs('./recognizer')
253+
faces, labels = self.prepareTrainingData(self.datasets)
254+
face_recognizer.train(faces, np.array(labels))
255+
face_recognizer.save('./recognizer/trainingData.yml')
256+
except FileNotFoundError:
257+
logging.error('系统找不到人脸数据目录{}'.format(self.datasets))
258+
self.trainButton.setIcon(QIcon('./icons/error.png'))
259+
self.logQueue.put('未发现人脸数据目录{},你可能未进行人脸采集'.format(self.datasets))
260+
except Exception as e:
261+
logging.error('遍历人脸库出现异常,训练人脸数据失败')
262+
self.trainButton.setIcon(QIcon('./icons/error.png'))
263+
self.logQueue.put('Error:遍历人脸库出现异常,训练失败')
264+
else:
265+
text = '<font color=green><b>Success!</b></font> 系统已生成./recognizer/trainingData.yml'
266+
informativeText = '<b>人脸数据训练完成!</b>'
267+
DataManageUI.callDialog(QMessageBox.Information, text, informativeText, QMessageBox.Ok)
268+
self.trainButton.setIcon(QIcon('./icons/success.png'))
269+
self.logQueue.put('Success:人脸数据训练完成')
270+
self.initDb()
271+
272+
# 系统日志服务常驻,接收并处理系统日志
273+
def receiveLog(self):
274+
while True:
275+
data = self.logQueue.get()
276+
if data:
277+
self.receiveLogSignal.emit(data)
278+
else:
279+
continue
280+
281+
# LOG输出
282+
def logOutput(self, log):
283+
time = datetime.now().strftime('[%Y/%m/%d %H:%M:%S]')
284+
log = time + ' ' + log + '\n'
285+
286+
self.logTextEdit.moveCursor(QTextCursor.End)
287+
self.logTextEdit.insertPlainText(log)
288+
self.logTextEdit.ensureCursorVisible() # 自动滚屏
289+
290+
# 系统对话框
291+
@staticmethod
292+
def callDialog(icon, text, informativeText, standardButtons, defaultButton=None):
293+
msg = QMessageBox()
294+
msg.setWindowIcon(QIcon('./icons/icon.png'))
295+
msg.setWindowTitle('OpenCV Face Recognition System - DataManage')
296+
msg.setIcon(icon)
297+
msg.setText(text)
298+
msg.setInformativeText(informativeText)
299+
msg.setStandardButtons(standardButtons)
300+
if defaultButton:
301+
msg.setDefaultButton(defaultButton)
302+
return msg.exec()
303+
304+
305+
if __name__ == '__main__':
306+
logging.config.fileConfig('./config/logging.cfg')
307+
app = QApplication(sys.argv)
308+
window = DataManageUI()
309+
window.show()
310+
sys.exit(app.exec())

dataManage.pyc

-9.52 KB
Binary file not shown.

0 commit comments

Comments
 (0)