【摸鱼神器】基于python的BOSS识别系统

【摸鱼神器】基于python的BOSS识别系统

  • 前言
  • 一、整体设计
  • 二、调用摄像头
  • 三、人脸识别
  • 1. 构建白名单库
  • 2. 人脸匹配
  • 四、切换屏幕
  • 五、完整代码
  • 写在最后
  • 前言

    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君

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【摸鱼神器】基于python的BOSS识别系统

    发表评论