【摸鱼神器】基于python的BOSS识别系统
前言
Tip:本文仅供技术学习和参考,切勿滥用。珍爱工作,从我做起,滴滴~
试想这样一个场景:一天,风和日丽,波澜不惊。你正在愉快地摸着鱼,是如此的惬意,如此的巴适。
就在这时,你的BOSS突然出现,打断了这美好的瞬间,迎接你的将是无尽的…
经过这件事之后,你可能会想:”有没有一个工具,可以帮我迅速发现BOSS的到来,并迅速切换屏幕呢?“
于是,它来了。Boss Recognition System 迈着轻快的步伐来了。各位小伙伴,请系好安全带,咋们发车了🚗…(滴,学生卡)
一、整体设计
思路还是比较清晰,简单的。调用摄像头实时采集画面,通过人脸识别算法对人像进行检测,如果是BOSS则将屏幕切换到指定界面。示意图如下:
下面将对涉及到的每一部分进行介绍。
二、调用摄像头
这里可以直接用python的cv2库即可,利用pip安装:
pip install opencv-python
具体调用方法如下:
import cv2
if __name__ == '__main__':
# 开启摄像头
video_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
# 实时捕获图像
while True:
_, frame = video_capture.read() # 读取每一帧图像
cv2.imshow('capture', frame) # 展示画面
if cv2.waitKey(1) & 0xFF == ord('q'):# 按Q退出
break
# 关闭摄像头
video_capture.release()
这样便能获取到视频的每一祯画面frame,接下来我们只需要对frame进行处理和识别就可以了。
三、人脸识别
目前人脸识别的开源算法和在线API接口有很多,都能帮助我们快速地实现人脸识别效果。但是考虑到BOSS识别是分秒必争的事儿🙈,网络延迟可能会让我们错过最佳“战机”。于是,我决定用本地的人脸识别算法。
本次采用的是python中的开源人脸识别项目:face_recognition,只需要简单的几行代码便可实现人脸识别的效果。其安装方法如下:
pip install face_recognition
注意:使用face_recognition前需要安好dlib库。
BOSS识别有两个思路:一个基于白名单,另一个则基于黑名单。白名单指的是除了白名单里的人像之外,都会触发切屏,其好处是不需要BOSS的照片用于学习,缺点是容易造成误判;而黑名单的好处是可以“精确打击”,但是需要搜集黑名单中对象的照片。
毕竟偷拍BOSS👴的照片成本太高了,老实巴交的我还是决定采用白名单的方式。
1. 构建白名单库
读取本地图像库,计算每个人的人脸特征编码,构建出人脸白名单库:
# 读取本地肖像库,建立识别白名单
def load_portrait(path=path):
'''
path:肖像库路径
'''
# 消息提示
print('>>>本地图像库读取中,请稍后',end='')
for i in range(5):
print('.', end='')
time.sleep(0.3)
# 建立白名单
white_map = {}
for name in os.listdir(path):
filepath = '%s/%s'%(path, name)
avg_coding = np.zeros(128)
n = 0
for file in os.listdir(filepath):
if '.jpg' in file:
image = face_recognition.load_image_file('%s/%s'%(filepath, file))
encoding = face_recognition.face_encodings(image)[0]
avg_coding += encoding
n += 1
avg_coding /= n
white_map[name] = avg_coding
print('>>"%s"人脸数据加载完成!'%name)
return white_map
这里的目录结构如下:
data为人像数据存储的根目录,每个人的照片单独存于一个文件夹,并以001、002的顺序进行编号。我们可以手工搜集照片并添加进去,当然也能通过代码自动采集,方法如下:
import os
import cv2
import time
import threading
# 全局变量
path = './data' # 采集的图片存储位置
choice = '-1' # 选择,用于判断操作,-1表示暂无选择
# 定义捕捉函数
def capture(name):
'''
捕捉人像函数
'''
# 定于图片路径及文件名
global path, choice
jpgpath = '%s/%s'%(path, name)
if not os.path.exists(jpgpath):
os.mkdir(jpgpath)
i = 1 # 图片标号
else:
try:
i = int(os.listdir(jpgpath)[0][-7:-4])+1
except:
i = 1
# 开启摄像头
video_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
# 循环捕捉头像
while True:
ret, frame = video_capture.read()
cv2.imshow('monitor', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if choice == '0':
choice = '-1'
filename = '%s-%s.jpg'%(name, str(i).zfill(3))
cv2.imwrite('%s/%s'%(jpgpath, filename), frame)
i += 1
print('"%s"保存成功!'%filename)
elif choice == '1':
choice = '-2' # 用于标记退回上一层
break
elif choice == '-1':
continue
else:
choice = '-1'
print('输入错误...')
# 关闭摄像头
video_capture.release()
if __name__ == '__main__':
# 创建目录
if not os.path.exists(path):
os.mkdir(path)
# 显示欢迎界面和说明
os.system('cls')
welcome = '''
_____ _ _ _ _____ _ _ _
| __ \ | | (_) | / ____| | | | | |
| |__) |__ _ __| |_ _ __ __ _ _| |_ | | ___ | | | ___ ___| |_
| ___/ _ \| '__| __| '__/ _` | | __| | | / _ \| | |/ _ \/ __| __|
| | | (_) | | | |_| | | (_| | | |_ | |___| (_) | | | __/ (__| |_
|_| \___/|_| \__|_| \__,_|_|\__| \_____\___/|_|_|\___|\___|\__| v0.1
'''
print(welcome)
# 循环捕捉头像
while True:
choice_1 = input('>>选择(0->输入姓名,1->退出):')
if choice_1 == '0':
name = input('>>姓名:')
print('摄像头启动中...')
time.sleep(1)
threading.Thread(target=capture, args=(name,)).start()
while True:
choice = input('>>选择(0->保存,1->退出):')
time.sleep(0.1) # 等待子线程执行,防止逻辑混乱
if choice == '-2':
choice = '-1'
break
elif choice_1 == '1':
break
else:
print('输入错误')
2. 人脸匹配
在得到人像白名单之后,需要做的便是将视频中出现的人脸与白名单进行匹配,进而判断其身份。运用到的关键函数是compare_faces,其用法如下:
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
其中known_face_encodings为白名单中的人脸特征编码,face_encoding为视频中出现的人脸特征编码,与之一一进行比较,如果相吻合返回值则为True。
据此,可以定义一个人脸匹配的函数:
def recognize(frame, white_map):
'''
frame: 捕获的摄像头帧
white_map: 人像库白名单
'''
# 根据白名单,提取肖像编码
known_face_encodings = list(white_map.values())
known_face_names = list(white_map.keys())
# 图像预处理(包括大小调整、格式转换)
frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25) # 调整图像大小,以减小计算需求
frame = frame[:, :, ::-1] # BGR->RGB
# 计算人脸的编码值
face_locations = face_recognition.face_locations(frame)
face_encodings = face_recognition.face_encodings(frame, face_locations)
# 计算的编码与白名单比较,获得其匹配的名字
face_names = []
for face_encoding in face_encodings:
# 默认为"未知"
name = '未知'
# 匹配
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
if True in matches:
index = matches.index(True)
name = known_face_names[index]
face_names.append(name)
return face_names, face_locations
注:①这里对图像进行了压缩,目的是提高计算效率,对其准确率的影响很小。②这里用了for循环,是因为一张图像中出现的人脸可能不止一个。
四、切换屏幕
利用PyQt5库打开一个全屏的窗口,把背景图替换为正在努力工作的画面,便能轻松地实现瞒天过海,开心摸鱼了~
def lock_screen(image_path='lock.jpg'):
app = QApplication(sys.argv)
pixmap = QPixmap(image_path)
screen = QLabel()
screen.setPixmap(pixmap)
screen.showFullScreen()
sys.exit(app.exec_())
切换屏幕仅仅是一个参考,其实这一步你可以尝试更多的玩法,比如文字提示或者语音提示,又或者直接调用快捷键切换到指定窗口。
五、完整代码
结合上面几部分,完整代码如下:
import os
import cv2
import sys
import time
import numpy as np
import face_recognition
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication,QLabel
# 全局变量
path = './data' # 人像存储路径
showflag = True # 是否实时显示图像
# 利用PyQt5打开全屏窗口,实现窗口替换效果
def lock_screen(image_path='lock.jpg'):
app = QApplication(sys.argv)
pixmap = QPixmap(image_path)
screen = QLabel()
screen.setPixmap(pixmap)
screen.showFullScreen()
sys.exit(app.exec_())
# 读取本地肖像库,建立识别白名单
def load_portrait(path=path):
'''
path:肖像库路径
'''
# 消息提示
print('>>>本地图像库读取中,请稍后',end='')
for i in range(5):
print('.', end='')
time.sleep(0.3)
# 建立白名单
white_map = {}
for name in os.listdir(path):
filepath = '%s/%s'%(path, name)
avg_coding = np.zeros(128)
n = 0
for file in os.listdir(filepath):
if '.jpg' in file:
image = face_recognition.load_image_file('%s/%s'%(filepath, file))
encoding = face_recognition.face_encodings(image)[0]
avg_coding += encoding
n += 1
avg_coding /= n
white_map[name] = avg_coding
print('>>"%s"人脸数据加载完成!'%name)
return white_map
# 人脸识别,判断当前画面的人像是否与白名单中的匹配
def recognize(frame, white_map):
'''
frame: 捕获的摄像头帧
white_map: 人像库白名单
'''
# 根据白名单,提取肖像编码
known_face_encodings = list(white_map.values())
known_face_names = list(white_map.keys())
# 图像预处理(包括大小调整、格式转换)
frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25) # 调整图像大小,以减小计算需求
frame = frame[:, :, ::-1] # BGR->RGB
# 计算人脸的编码值
face_locations = face_recognition.face_locations(frame)
face_encodings = face_recognition.face_encodings(frame, face_locations)
# 计算的编码与白名单比较,获得其匹配的名字
face_names = []
for face_encoding in face_encodings:
# 默认为"未知"
name = '未知'
# 匹配
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
if True in matches:
index = matches.index(True)
name = known_face_names[index]
face_names.append(name)
return face_names, face_locations
if __name__ == '__main__':
# 加载白名单
white_map = load_portrait(path)
#开启摄像头
video_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
# 采集图像
flag = 0
while True:
flag %= 3
_, frame = video_capture.read()
if flag == 0: # 每3帧处理因此(提高处理速度,防止视频卡顿)
face_names, face_locations = recognize(frame, white_map)
if '未知' in face_names: # 如果有白名单之外的人
lock_screen()
break
flag += 1
if showflag:
# 将人脸框出
for (top, right, bottom, left), name in zip(face_locations, face_names):
# 改变坐标位置(因为处理时原图被缩小了4*4)
top *= 4
right *= 4
bottom *= 4
left *= 4
# 矩形框
cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
#加上姓名
cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
# 显示图像
cv2.imshow('monitor', frame)
# 按Q退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()
运行代码之后,先前的场景变得有所不同:还是那日,微风荡漾,你开心的摸着鱼。BOSS加快脚步想要抓个现行,当他刚一过来,电脑屏幕就已经切到了工作画面。BOSS微笑地频频点头:“小伙子,不错,工作很刻苦。”
试想一下,就这操作,咱距离升职加薪,走上人生巅峰还远吗?(手动滑稽脸🙈)
写在最后
总体来说,本次的BOSS识别系统只是从想法的萌生到一次简单的尝试,还有许多可以优化的地方,比如识别的准确率和效率问题,感兴趣的小伙伴可以尝试着进一步优化。
最后,想说的是:摸鱼不是重点,学习技术才是关键,希望大家可以在摸索和尝试中不断提升自己。
我是kimol君,咋们下次再会~
创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 (๑◕ܫ←๑)
版权归原作者 不正经的kimol君 所有, 如有侵权,请联系我们删除。