【旧文更新】基于OpenCV的Python人脸识别、检测、框选 (遍历目录下所有照片依次识别 视频随时标注)

【旧文更新】基于OpenCV的Python人脸识别、检测、框选
(遍历目录下所有照片依次识别 视频随时标注)

文章目录

  • 关于旧文新发
  • 一、功能概览
  • 二、使用说明
  • 三、分部测试功能程序
    • 1.遍历目录功能
    • 2.界面获取目录功能
    • 3.人脸框选功能
    • 4.摄像头及框选功能
    • 5.延时功能
  • 四、主程序部分
    • 1.API算法部分
    • 2.界面程序部分
  • 五、最终测试
    • 1.识别成功测试
    • 2.识别失败功能测试
  • 六、总结
  • 附录:列表的赋值类型和py打包
    • 列表赋值
      • BUG复现
      • 代码改进
      • 优化
      • 总结
    • py打包
  • 附录:关于旧文新发

关于旧文新发

为何要进行旧文新发?
因为我在2023年博客之星评选中发现 有的人转载、抄袭他人文章 稍微改动几下也能作为高质量文章入选
所以我将把我的旧文重新发一次 然后也这样做

2023年博客之星规则:
在这里插入图片描述
移步:

blog.csdn.net/weixin_53403301/article/details/125121329

【优秀毕设】基于OpenCV的人脸识别打卡/签到/考勤管理系统(最简基本库开发、可基于树莓派)

一、功能概览

可以实现在摄像头下实时的人脸识别、检测、框选功能

原理是将摄像头下的图像人脸和存放照片的目录下的人脸依次进行对比 调用百度的API人脸识别接口 返回相似度的值进行识别

识别成功和失败均有提示 成功时能将对应的信息写入到识别记录中 并终止程序 当所有照片对比后均失败则提示失败 终止程序

窗口利用tkinter函数所写 利用PIL和opencv对图像进行处理 最后将识别结果和人脸框选图像在窗口中显示出来

人脸框选功能用到了opencv官方的人脸识别器 然后利用opencv画出相应外框即可

另外在测试时 用matplotlib.pyplot的相关函数进行输出图像的测试

文件资源包:(文章末尾有百度网盘版)https://download.csdn.net/download/weixin_53403301/66918342

主要代码为工程包内的api_face.py和gui_face_new.py文件

对应识别速度更快的版本为api_face_faster.py和gui_face_new_faster.py文件

另外 gui_face.py文件所对应的程序功能为选择任一目录下的指定图像进行识别 在我之前的文章中有写到 gui_face_new.py文件是在其上的改进

之前的文章和资源:
https://blog.csdn.net/weixin_53403301/article/details/117464715
https://download.csdn.net/download/weixin_53403301/19651352?spm=1001.2014.3001.5503

另外还有应用在树莓派和89C52单片机上的硬件控制版本:
https://blog.csdn.net/weixin_53403301/article/details/118575731
https://download.csdn.net/download/weixin_53403301/20086349

如下图为源文件根目录下的所有文件图示
在这里插入图片描述

二、使用说明

主要代码为工程包内的api_face.py和gui_face_new.py文件 运行gui_face_new.py文件即可工作

对应识别速度更快的版本为api_face_faster.py和gui_face_new_faster.py文件

OpenCV版本号为4.40.46 4.5.1版本以上调用摄像头会出现很明显的色差问题

需要在img目录先放入照片 最好为正脸证件照

tmp目录为摄像头截图的临时文件目录

运行程序 点击“使用相机识别”则识别开始 依次对img目录下的照片和摄像头内容进行对比识别

返回的相似度值大于80则成功 小于80则失败

若识别成功 则不在遍历目录下的剩余文件 直接提示成功窗口 中断识别

若识别失败 则继续遍历目录下的剩余文件进行识别 直到识别成功

若所有文件都以识别完 但仍然未识别成功 则提示失败窗口 中断识别

已有的识别记录为Windows系统下创建

Linux系统下的识别记录若为乱码 则需要更改code编码 或删除后重新运行程序 自动建立

在Linux系统建立的识别记录文件 在Windows环境下可以正常读取 不受影响

三、分部测试功能程序

1.遍历目录功能

采用os库遍历img目录下的所有文件 并将其文件名存入到列表中
再利用opencv读取 mat进行显示测试
代码如下:

# -*- coding: utf-8 -*-
"""
Created on Sun Aug  1 11:16:18 2021

@author: 16016
"""

import cv2
import os

import matplotlib.pyplot as plt # 调用matplotlib绘图库
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体

def pshow(words,picture):
    plt.imshow(picture[:,:,::-1])   # 将读取的图片以RGB转换为BGR
    plt.title(words), plt.xticks([]), plt.yticks([])
    plt.show() # 显示图片
    
dir_name='./img'
fileimg_list = []
fileimg_list=os.listdir(dir_name)
file_num=len(fileimg_list)
i=0
while True:
    if i<file_num:
        print(i)
        img=cv2.imread(dir_name + '/' + fileimg_list[i])    
        pshow(fileimg_list[i],img)
        i=i+1
    else:
        break

运行结果如图所示:
在这里插入图片描述

2.界面获取目录功能

此功能是应用在gui_face.py文件夹 目的是通过按钮选择图像并输出图像路径
代码如下:

#coding=utf-8


#建立容器
import tkinter as tk
from tkinter import ttk
from tkinter.filedialog import *
import tkinter.messagebox
tk=Tk()
tk.title("个人GUI界面学习")
mainfarm=Frame(tk,width=800, height=100,bg="black")
mainfarm.grid_propagate(0)
mainfarm.grid()
fram=Frame(mainfarm,width=400, height=100,bg="black")
fram.grid_propagate(0)
fram.grid()


e = Entry(fram)
e.grid(row=0,column=2)

e.delete(0, END)  # 将输入框里面的内容清空
e.insert(0, '选择人像图片')
filepath=StringVar()
def filefound():
    filepath= askopenfilename()
    print (filepath)
    e.delete(0, END)  # 将输入框里面的内容清空
    e.insert(0, filepath)

#button1=Button(fram,text="button1").grid(row=0,column=1)
button2=Button(fram,text="选择文件",command=filefound).grid(row=0,column=3)
#print (fram.size())

mainloop()

运行结果如图所示:
在这里插入图片描述

3.人脸框选功能

加载目录下的opencv官方人脸识别器(haarcascade_frontalface_default.xml) 进行识别后画出外框
代码如下:

import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体

#print('请输入需录入的人脸图片路径/文件名:')
#pic_name=input()

# 利用MATLAB显示图片  
def pshow(words,picture):
    plt.imshow(picture[:,:,::-1])
    plt.title(words), plt.xticks([]), plt.yticks([])
    plt.show()

# 图像路径 我用的相对路径
filepath = 'img/123.jpg'
# 读取文件
faceImg = cv2.imread(filepath)
# 转换灰色
gray = cv2.cvtColor(faceImg,cv2.COLOR_BGR2GRAY)

# 加载人脸识别分类器
# 官方已有的分类器  https://github.com/opencv/opencv/tree/master/data/haarcascades
# github的不好下载, 可以从码云上找
# Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
color = (0,255,0)

# 识别器进行识别
faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
# 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
# print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的

# 判断识别结果集合长度
if len(faceRects):
    for faceRect in faceRects:
        x,y,w,h = faceRect
        # 框选出人脸   最后一个参数2是框线宽度
        cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)
        

pshow('1',faceImg)
# 显示图像


运行结果如下:
在这里插入图片描述

4.摄像头及框选功能

同上一个功能 不过是对摄像头图像进行实时的框选 并实时显示 同时还加上了人眼部分框选(haarcascade_eye.xml)
通过opencv进行显示 同时按esc键退出
代码如下:

import cv2

cap = cv2.VideoCapture(0)  # 开启摄像头

# 循环读取图像
while True:
    ok, img = cap.read()  # 读取摄像头图像
    if ok is False:
        print('无法读取到摄像头!')
        break
    
   
    faceImg = img
    
    gray = cv2.cvtColor(faceImg,cv2.COLOR_BGR2GRAY)

    # 加载人脸识别分类器
    # 官方已有的分类器  https://github.com/opencv/opencv/tree/master/data/haarcascades
    # github的不好下载, 可以从码云上找
    # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
    classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
    color = (0,255,0)

    # 识别器进行识别
    faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))

    if len(faceRects):
        for faceRect in faceRects:
            x,y,w,h = faceRect
            # 框选出人脸   最后一个参数2是框线宽度
            cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)

    
    
    
    # 转换灰色
    gray = cv2.cvtColor(faceImg,cv2.COLOR_BGR2GRAY)

    # 加载人脸识别分类器
    # 官方已有的分类器  https://github.com/opencv/opencv/tree/master/data/haarcascades
    # github的不好下载, 可以从码云上找
    # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
    classifier = cv2.CascadeClassifier('haarcascade_eye.xml')
    color = (255,0,0)

    # 识别器进行识别
    faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))

    if len(faceRects):
        for faceRect in faceRects:
            x,y,w,h = faceRect
            # 框选出人脸   最后一个参数2是框线宽度
            cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)
            
    
    #faceImg= cv2.cvtColor(faceImg,cv2.COLOR_RGB2BGR)
    cv2.imshow("faceImg",faceImg)
    # 展示图像
    
    

    k = cv2.waitKey(10)  # 键盘值
    if k == 27:   # 通过esc键退出摄像
        break

# 关闭摄像头
cap.release()
cv2.destroyAllWindows()


运行结果如下:
在这里插入图片描述

5.延时功能

此功能主要用于测试time库 获取系统时间的功能
测试这个功能的目的主要是为了在主程序中 通过控制时间减少资源的使用 使其每隔一段时间读取、识别一次(0.1s)
主程序中 识别函数为一个循环 读取一次摄像头并进行人脸识别 若识别成功 则中断循环 若识别失败 也跳出循环
代码如下:

# -*- coding: utf-8 -*-
"""
Created on Tue Jun  1 00:14:45 2021

@author: ZHOU
"""

import time
import threading



def delay(a):
    print( time.time(),'10s',a)
    print( time.time())
    s = threading.Timer(10,delay,("delay",))
    s.start()
    print( time.time())
    
def main():
    print('1')
    delay(10)
    print('2')

main()

运行结果如下:
延时时间为10s
在这里插入图片描述

四、主程序部分

1.API算法部分

调用 requests的HTTP协议库
调用os多操作系统接口库
调用base64编码库
调用JavaScript Object Notation数据交换格式
登入百度的人脸识别API 然后对给出的两个图像进行对比识别 输出相相似度
所有相关功能和函数的语句已在代码中注释好了
代码如下:(第一段为常规版,第二段为快速版)

# -*- coding: utf-8 -*-
"""
Created on Mon May 31 23:40:16 2021

@author: ZHOU
"""

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests # 调用 requests的HTTP协议库
import os # 调用os多操作系统接口库
import base64 # 调用base64编码库
import json # 调用JavaScript Object Notation数据交换格式
 
ACCESS_TOKEN = ''
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #去掉文件名,返回目录 
 
# ID,KEY的配置信息
INFO_CONFIG = { 
    'ID': '15788358',
    'API_KEY': 'ohtGa5yYoQEZ8Try8lnL99UK',
    'SECRET_KEY': 'qaDjyuXkf5MZ28g5C8pwFngDZenhswC3'
}
 
# URL配置
URL_LIST_URL = {
    # ACCESS_TOKEN_URL用于获取ACCESS_TOKEN, POST请求,
    #  grant_type必须参数,固定为client_credentials,client_id必须参数,应用的API Key,client_secre 必须参数,应用的Secret Key.
    'ACCESS_TOKEN_URL': 'https://aip.baidubce.com/oauth/2.0/token?' + 'grant_type=client_credentials&client_id={API_KEYS}&client_secret={SECRET_KEYS}&'.format(
        API_KEYS=INFO_CONFIG['API_KEY'], SECRET_KEYS=INFO_CONFIG['SECRET_KEY']),
    # 登入人脸识别机器学习库
    'FACE_PLATE': 'https://aip.baidubce.com/rest/2.0/face/v3/match',
 
}
 
 
class AccessTokenSuper(object):
    pass
 
 
class AccessToken(AccessTokenSuper): # 定义登陆API大类
    def getToken(self):
        accessToken = requests.post(url=URL_LIST_URL['ACCESS_TOKEN_URL']) #登入网址
        accessTokenJson = accessToken.json()
        if dict(accessTokenJson).get('error') == 'invalid_client':
            return '获取accesstoken错误,请检查API_KEY,SECRET_KEY是否正确!'
        return accessTokenJson
 
 
ACCESS_TOKEN = AccessToken().getToken()['access_token']
 
LICENSE_PLATE_URL = URL_LIST_URL['FACE_PLATE'] + '?access_token={}'.format(ACCESS_TOKEN)
 
 
class faceSuper(object):
    pass
 
 
class face(faceSuper): # 定义图像输入大类
 
    def __init__(self, image=None, image2=None): # 定义初始化函数
        self.HEADER = {
            'Content-Type': 'application/json; charset=UTF-8',
        }
        if image is not None: 	# 没有图像1
            imagepath = os.path.exists(image)
            if imagepath == True:
                images = image
                with open(images, 'rb') as images:
                    img1 = base64.b64encode(images.read())
            else:
                print("图像1不存在")
                return
        if image2 is not None: 	# 没有图像2
            imagepath2 = os.path.exists(image2)
            if imagepath2 == True:
                images2 = image2
                with open(images2, 'rb') as images2:
                    img2 = base64.b64encode(images2.read())
            else:
                print("图像2不存在")
                return
        self.img = img1
        self.imgs = img2
        self.IMAGE_CONFIG1 = {"image": str(img1, 'utf-8'), "image_type": "BASE64"}
        self.IMAGE_CONFIG2 = {"image": str(img2, 'utf-8'), "image_type": "BASE64"}
        self.IMAGE_CONFIG = json.dumps([self.IMAGE_CONFIG1, self.IMAGE_CONFIG2])
 
    def postface(self):  # 定义从服务器进行数据获取函数
        if (self.img==None and self.imgs==None):
            return '图像不存在'
        face = requests.post(url=LICENSE_PLATE_URL, headers=self.HEADER, data=self.IMAGE_CONFIG)
        # 登陆服务器获取数据
        return face.json() 	# 输出结果
 
 
def facef(FA1, FA2): # 人脸识别逻辑函数
    testAccessToken = AccessToken() # 获取API配置
    testface = face(image=FA1, image2=FA2) # 赋值给图像输入大类
    result_json = testface.postface()  # 从服务器获取数据
    result = result_json['result']['score'] #输出结果
    print('人脸相似度:', result)
    if result > 80: # 识别结果大于80则成功
        print("人脸匹配成功!")        
#    if result < 20:
#        print("未检测到人脸!")
    else:
        print("人脸匹配失败!")
    return '人脸相似度:' + str(result), result # 输出字符串结果
# -*- coding: utf-8 -*-
"""
Created on Mon May 31 23:40:16 2021

@author: ZHOU
"""

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests # 调用 requests的HTTP协议库
import os # 调用os多操作系统接口库
import base64 # 调用base64编码库
import json # 调用JavaScript Object Notation数据交换格式
 
ACCESS_TOKEN = ''
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #去掉文件名,返回目录 
 
# ID,KEY的配置信息
INFO_CONFIG = { 
    'ID': '15788358',
    'API_KEY': 'ohtGa5yYoQEZ8Try8lnL99UK',
    'SECRET_KEY': 'qaDjyuXkf5MZ28g5C8pwFngDZenhswC3'
}
 
# URL配置
URL_LIST_URL = {
    # ACCESS_TOKEN_URL用于获取ACCESS_TOKEN, POST请求,
    #  grant_type必须参数,固定为client_credentials,client_id必须参数,应用的API Key,client_secre 必须参数,应用的Secret Key.
    'ACCESS_TOKEN_URL': 'https://aip.baidubce.com/oauth/2.0/token?' + 'grant_type=client_credentials&client_id={API_KEYS}&client_secret={SECRET_KEYS}&'.format(
        API_KEYS=INFO_CONFIG['API_KEY'], SECRET_KEYS=INFO_CONFIG['SECRET_KEY']),
    # 登入人脸识别机器学习库
    'FACE_PLATE': 'https://aip.baidubce.com/rest/2.0/face/v3/match',
 
}
 
 
class AccessTokenSuper(object):
    pass
 
 
class AccessToken(AccessTokenSuper): # 定义登陆API大类
    def getToken(self):
        accessToken = requests.post(url=URL_LIST_URL['ACCESS_TOKEN_URL']) #登入网址
        accessTokenJson = accessToken.json()
        if dict(accessTokenJson).get('error') == 'invalid_client':
            return '获取accesstoken错误,请检查API_KEY,SECRET_KEY是否正确!'
        return accessTokenJson
 
 
ACCESS_TOKEN = AccessToken().getToken()['access_token']
 
LICENSE_PLATE_URL = URL_LIST_URL['FACE_PLATE'] + '?access_token={}'.format(ACCESS_TOKEN)
 
 
class faceSuper(object):
    pass
 
 
class face(faceSuper): # 定义图像输入大类
 
    def __init__(self, image=None, image2=None): # 定义初始化函数
        self.HEADER = {
            'Content-Type': 'application/json; charset=UTF-8',
        }
        if image is not None: 	# 没有图像1
            imagepath = os.path.exists(image)
            if imagepath == True:
                images = image
                with open(images, 'rb') as images:
                    img1 = base64.b64encode(images.read())
            else:
                print("图像1不存在")
                return
        if image2 is not None: 	# 没有图像2
            imagepath2 = os.path.exists(image2)
            if imagepath2 == True:
                images2 = image2
                with open(images2, 'rb') as images2:
                    img2 = base64.b64encode(images2.read())
            else:
                print("图像2不存在")
                return
        self.img = img1
        self.imgs = img2
        self.IMAGE_CONFIG1 = {"image": str(img1, 'utf-8'), "image_type": "BASE64"}
        self.IMAGE_CONFIG2 = {"image": str(img2, 'utf-8'), "image_type": "BASE64"}
        self.IMAGE_CONFIG = json.dumps([self.IMAGE_CONFIG1, self.IMAGE_CONFIG2])
 
    def postface(self):  # 定义从服务器进行数据获取函数
        if (self.img==None and self.imgs==None):
            return '图像不存在'
        face = requests.post(url=LICENSE_PLATE_URL, headers=self.HEADER, data=self.IMAGE_CONFIG)
        # 登陆服务器获取数据
        return face.json() 	# 输出结果
 
 
def facef(FA1, FA2): # 人脸识别逻辑函数
    testAccessToken = AccessToken() # 获取API配置
    testface = face(image=FA1, image2=FA2) # 赋值给图像输入大类
    result_json = testface.postface()  # 从服务器获取数据
    result = result_json['result']['score'] #输出结果
    print('人脸相似度:', result)
#    if result > 80: # 识别结果大于80则成功
#        print("人脸匹配成功!")        
#    if result < 20:
#        print("未检测到人脸!")
#    else:
#        print("人脸匹配失败!")
    return '人脸相似度:' + str(result), result # 输出字符串结果

此函数需要配合网络进行使用
单独运行时不报错则可以连接上API

利用facef(FA1, FA2)函数 传入入两个图像值 则能进行识别

若API出错 则改为

    'ID': '15050553',
    'API_KEY': 'rlRrtRL5oRdXGh71jgg1OmyN',
    'SECRET_KEY': 'dK5TpuTAZn2nw5eVpspZLmF5Qs1Uu8A1'

2.界面程序部分

测试时 利用mat显示图像 pshow函数展示 在实际运行中 为了加快运行速度 将测试部分的代码注释掉了
所有相关功能和函数的语句已在代码中注释好了
(测试图片中为原始版本,文字提示中没有姓名,现代码已更新为包含姓名提示的版本)
代码如下:(第一段为常规版,第二段为快速版)

# -*- coding: utf-8 -*-
"""
Created on Mon May 31 23:39:19 2021

@author: ZHOU
"""

# -*- coding: utf-8 -*-
 
import tkinter as tk # 调用窗口tk
from tkinter import ttk
from tkinter.filedialog import *
import tkinter.messagebox
from PIL import Image, ImageTk # 调用图像处理库pillow
import api_face # 调用本地函数库 用于登入外部机器学习库并调用人脸识别函数
import cv2 # 调用OpenCV图像处理库
import threading # 调用threading多线程运行库
import time # 调用系统时间戳库
import os # 调用os多操作系统接口库
import matplotlib.pyplot as plt # 调用matplotlib绘图库
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体

#print('请输入需录入的人脸图片路径/文件名:')
#pic_name=input()

# 利用matplotlib显示图片函数  
def pshow(words,picture):
    plt.imshow(picture[:,:,::-1])   # 将读取的图片以RGB转换为BGR
    plt.title(words), plt.xticks([]), plt.yticks([])
    plt.show() # 显示图片
    

class Login(ttk.Frame): # 定义窗口大类
    def __init__(self, win):
        ttk.Frame.__init__(self, win)
        frame0 = ttk.Frame(self)
        frame1 = ttk.Frame(self)
        win.title("人脸识别")
        win.minsize(1240, 620)
        self.center_window()    # 执行中置窗口函数
        self.thread_run = None 	# 赋值 线程1默认关闭
        self.thread_run2 = None 	# 线程2默认关闭
        self.camera = None 	# 摄像头默认关闭
 
        #定义tk窗口属性
        
        #self.pilImage = Image.open("img/start.png")
        #self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
        #self.image_ctl = tk.Label(frame0, image=self.tkImage)
        #self.image_ctl.pack()
 
        frame0.pack(side=TOP, fill=tk.Y, expand=1)
        frame1.pack(side=TOP, fill=tk.Y, expand=1)
 
        self.facer = ttk.Label(frame1, text='', font=('Times', '20')) # 字体
        self.facer.pack()
        

#        def filefound(): # 定义获取图片路径的函数
#            filepath= askopenfilename() # 获取文件路径
#            pic_name=filepath
#            self.pic_path2 = pic_name # 赋值给图像2
#            
#            pshow('所选人像图片',pic_img) # 显示所选图片
        self.pic_path3='./star.png'
#        pic_img=cv2.imread(self.pic_path3)
#        pic_xz = pic_img.shape # 计算图像大小
#        pic_h=pic_xz[0] # 得出图片高度
#        pic_w=pic_xz[1] # 得出图片宽度
#        turn_w=pic_w*500/pic_h # 限制最大高度为580 以防窗口过小不完全显示 等比例转换宽度
#        turn_w=int(turn_w)
#            print('人像图像大小(高 宽):',pic_h,pic_w)
#            print ('路径:',filepath)
#            # 在tk窗口中显示所选图片
        
        self.pilImage = Image.open(self.pic_path3)            
        self.photo = self.pilImage.resize((500,500)) # 限制最大高度为580 等比缩放显示
        self.tkImage = ImageTk.PhotoImage(image=self.photo)
        self.image_ctl = tk.Label(frame0, image=self.tkImage)
        self.image_ctl.pack()
#            #e.delete(0, END)  # 将输入框里面的内容清空
#            #e.insert(0, filepath)
#        
#        #button2=Button(frame1,text="button2",command=filefound).grid(row=0,column=3)
#        # 按钮1 调用filefound函数 获取选择图片的路径 并赋值给self.pic_path2 输出图像
#        self.face_button1 = ttk.Button(frame1, text="1. 选择人像图片", width=15, command=filefound)
#        self.face_button1.pack(side=TOP)        
        # 按钮2 调用摄像头函数
        self.url_face_button = ttk.Button(frame1, text="使用相机识别", width=15, command=self.cv_face)
        self.url_face_button.pack(side=TOP)
        #self.file_pic_button = ttk.Button(frame1, text="本地文件识别", width=15, command=self.file_pic)
        #self.file_pic_button.pack(side=TOP)
 
        self.pack(fill=tk.BOTH, expand=tk.YES, padx="10", pady="10")
 
    #使弹出的窗体处于屏幕的中间位置
    def center_window(self):
        screenwidth = log.winfo_screenwidth()	# 获取屏幕分辨率宽
        screenheight = log.winfo_screenheight()	# 获取屏幕分辨率高
        log.update()	# 更新窗口
        width = log.winfo_width()	# 重新赋值
        height = log.winfo_height()
        size = '+%d+%d' % ((screenwidth - width)/2, (screenheight - height)/2)
        # 重新赋值大小 大小为屏幕大小/2
        log.geometry(size) 	# 以新大小定义窗口
 
#    def file1(self):
#        self.pic_path = askopenfilename(title="选择识别图片", filetypes=[("jpg图片", "*.jpg"), ("png图片", "*.png")])
 
    def cv_face(self):   # 调用摄像头函数
        if self.thread_run:
            if self.camera.isOpened(): # 如果已经打开则关闭
                self.camera.release()
                print("关闭摄像头")
                self.camera = None
                self.thread_run = False
            return
        if self.camera is None: # 如果没有摄像头则尝试打开
            self.camera = cv2.VideoCapture(1) # 利用OpenCV调用外摄像头
            if not self.camera.isOpened(): # 如果没有打开 则调用内摄像头
                self.camera = None
                print("没有外置摄像头")
                self.camera = cv2.VideoCapture(0) # 用OpenCV调用内摄像头
                if not self.camera.isOpened(): # 如果没有打开 则打开失败
                    print("没有内置摄像头")
                    tkinter.messagebox.showinfo('警告', '摄像头打开失败!')
                    self.camera = None
                    return
                else:
                    print("打开内置摄像头")
            else:
                print("打开外置摄像头")
        self.thread = threading.Thread(target=self.video_thread) # 多线程函数执行摄像头运行函数
        self.thread.setDaemon(True)
        self.thread.start()
        self.thread_run = True
 
    def video_thread(self): # 开始摄像头运行
        self.thread_run = True # 多线程1开启
        self.thread2 = threading.Thread(target=self.video_pic)
        self.thread2.setDaemon(True)
        self.thread2.start()
        self.thread_run2 = True
        while self.thread_run: # 循环一直调用摄像头
            _, img_bgr = self.camera.read() # 以bgr格式读取摄像头内的截图 
            gray = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2GRAY) # 灰度转换
            # 在CV官方机器学习库内加载人脸识别分类器                
            # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
            classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
            color = (0,255,0) # 绿色线
            # 识别器进行识别
            faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
            # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
            # print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的

            # 判断识别结果集合长度
            if len(faceRects):
                for faceRect in faceRects:
                    x,y,w,h = faceRect
                    # 用矩形框选出人脸   最后一个参数2是框线宽度
                    cv2.rectangle(img_bgr,(x, y), (x + h, y + w), color, 2)                 
            img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 颜色转换
            im = Image.fromarray(img)
            w, h = im.size
            pil_image_resized = self.resize(w, h, im) # 调整大小函数
            self.imgtk = ImageTk.PhotoImage(image=pil_image_resized)
            self.image_ctl.configure(image=self.imgtk)
        print("结束运行")
 
    def video_pic(self): # 视频截图保存及框选函数
        dir_name='./img' # 给出图片目录路径
        fileimg_list = [] # 建立列表型图片列表
        fileimg_list=os.listdir(dir_name) # 获取目录下文件名
        file_num=len(fileimg_list) # 获取列表长度
        fileimg_num=0   # 定义文件序号初始值为0
        self.thread_run2 = True     # 开启多线程
        predict_time = time.time() # 获得系统时间
        while self.thread_run2: # 循环读取
            if time.time() - predict_time > 0.1: #每0.1s读取一次摄像头截图
                print("正在识别中")
                _, img_bgr = self.camera.read() # 重新读取摄像头图像
                cv2.imwrite("tmp/test.jpg", img_bgr) #利用cv写入到tmp/test.jpg路径下
#                test_pic=cv2.imread('tmp/test.jpg') # 重新读取截图
#                pshow('识别截图',test_pic) # 显示截图
#                
#                # 图像路径 我用的相对路径
#                face_mark = 'tmp/test.jpg'
#                # 读取截图
#                faceImg = cv2.imread(face_mark)
#                # 转换灰色
#                gray = cv2.cvtColor(faceImg,cv2.COLOR_RGB2GRAY) # 由于tmp/test.jpg路径下的已经转换成RGB保存 所以不用再进行BGR转换
#
#                # 在CV官方机器学习库内加载人脸识别分类器                
#                # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
#                classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
#                color = (0,255,0) # 绿色线
#
#                # 识别器进行识别
#                faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
#                # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
#                # print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的
#
#                # 判断识别结果集合长度
#                if len(faceRects):
#                    for faceRect in faceRects:
#                        x,y,w,h = faceRect
#                        # 用矩形框选出人脸   最后一个参数2是框线宽度
#                        cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)
#        
#                
#                pshow('识别结果',faceImg)    # 输出框选结果图像并显示
#                cv2.imwrite("tmp/test2.jpg", faceImg)
                self.pic_path = "tmp/test.jpg" # 重新读取摄像头截图
#                self.pilImage = Image.open(self.pic_path)
#                self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
#                self.image_ctl = tk.Label(frame0, image=self.tkImage)
#                self.image_ctl.pack()
                try:

                
                    if fileimg_num<file_num: #当文件序号小于总列表长度时
                        #print(fileimg_num)
                        #img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])    
                        #pshow(fileimg_list[fileimg_num],img)
                        self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num] # 给出pic_path2的值为目录名+/+文件名
                        #print(self.pic_path2)
#                        self.pic_path2='./img/111.jpg'
                        facestr, result = api_face.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
                        self.facer.configure(text=str(facestr))
                                #self.pic() # 对摄像头图像进行尺度变换
                        if result > 80: #识别结果大于80
                            humantitle='  姓名:  '+fileimg_list[fileimg_num].partition('.')[0]+"  人脸匹配成功!  \n"
                            print(humantitle) # 输出人像文件文件名
                            tkinter.messagebox.showinfo('提示', humantitle) # tk窗口提示
                            
                            try:
                                f=open("识别记录.txt","r")
                                fi=open("识别记录.txt","a")
                                txt=time.ctime()
                                fi.write(txt+humantitle)
                                f.close()
                                fi.close() # 将识别成功的记录保存在txt文件下
                            except:
                                f=open("识别记录.txt","w")
                                txt=time.ctime()
                                f.write(txt+humantitle)
                                f.close()
                                        
                                    
                                    
                                    # close_window()
                                    # os.system("python3 ./main.py")
                                #if result < 20:
                                #    tkinter.messagebox.showinfo('提示', '未检测到人脸!')
                            break
                        else: # 小于80失败
                            fileimg_num=fileimg_num+1 # 文件序号+1
                                
                            
                    else: # 超出文件列表长度
                        tkinter.messagebox.showinfo('提示', '查无此人!人脸匹配失败!')
                        break
                except:
                    pass
                predict_time = time.time()      # 读取时间          
                print("识别结束")
                
                # 看门狗程序(调试用)
                # 防止程序关闭时进入死循环跑飞
                #print('请输入任意值以继续,否则请关闭窗口以终止程序:')
                #a=input()
                #print(a)
        pass
 
#    def file_pic(self): #识别函数
#        dir_name='./img'
#        fileimg_list = []
#        fileimg_list=os.listdir(dir_name)
#        file_num=len(fileimg_list)
#        fileimg_num=0   
#        while True:
#            if fileimg_num<file_num:
#                print(fileimg_num)
#                img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])    
#                pshow(fileimg_list[fileimg_num],img)
#                self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num]
#                print(self.pic_path2)
##                self.pic_path2='./img/111.jpg'
#                facestr, result = api_face.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
#                self.facer.configure(text=str(facestr))
#                        #self.pic() # 对摄像头图像进行尺度变换
#                if result > 80: #识别结果大于80
#                    tkinter.messagebox.showinfo('提示', '人脸匹配成功!') # tk窗口提示
#                    print('人像图片路径:'+self.pic_path2) # 输出人像文件路径
#                    try:
#                        f=open("识别记录.txt","r")
#                        fi=open("识别记录.txt","a")
#                        txt=time.ctime()
#                        fi.write(txt+'   人像图片路径:   '+self.pic_path2+"     人脸匹配成功! \n")
#                        f.close()
#                        fi.close() # 将识别成功的记录保存在txt文件下
#                    except:
#                        f=open("识别记录.txt","w")
#                        txt=time.ctime()
#                        f.write(txt+'   人像图片路径:   '+self.pic_path2+"     人脸匹配成功! \n")
#                        f.close()
#                                
#                            
#                            
#                            # close_window()
#                            # os.system("python3 ./main.py")
#                        #if result < 20:
#                        #    tkinter.messagebox.showinfo('提示', '未检测到人脸!')
#                        break
#                    else: # 小于80失败
#                        fileimg_num=fileimg_num+1
#                        
#                    
#            else:
#                tkinter.messagebox.showinfo('提示', '人脸匹配失败!')
#                break
        
        
 
#    def pic(self): # 对摄像头图像进行尺度变换
#        self.pilImage3 = Image.open(self.pic_path) # 用pillow读取摄像头图像
#        w, h = self.pilImage3.size # 计算大小赋值给宽 高
#        pil_image_resized = self.resize(w, h, self.pilImage3) # 调整大小函数
#        self.tkImage3 = ImageTk.PhotoImage(image=pil_image_resized) 
#        self.image_ctl.configure(image=self.tkImage3) # 输出结果
 
    def resize(self, w, h, pil_image): # 调整大小函数
        w_box = 1000 # 定义最大宽度
        h_box = 500 # 最大高度
        f1 = 1.0*w_box/w # 最大值/真实值
        f2 = 1.0*h_box/h
        factor = min([f1, f2]) # 取最小值
        width = int(w*factor) # 用最小值*对应值 调整到最大定义值 等比调整另一个值
        height = int(h*factor)
        return pil_image.resize((width, height), Image.ANTIALIAS) # 输出调整
 
 
def close_window():
    print("已关闭人脸识别")
    if Login.thread_run:
        Login.thread_run = False
        Login.thread.join(2.0)
    log.destroy()
    

    
if __name__ == '__main__':
    log = tk.Tk()
 
    login = Login(log)
    # close,退出输出destroy
    log.protocol('清除窗口', close_window)
    # 进入消息循环
    log.mainloop()
    
    


# -*- coding: utf-8 -*-
"""
Created on Mon May 31 23:39:19 2021

@author: ZHOU
"""

# -*- coding: utf-8 -*-
 
import tkinter as tk # 调用窗口tk
from tkinter import ttk
from tkinter.filedialog import *
import tkinter.messagebox
from PIL import Image, ImageTk # 调用图像处理库pillow
import api_face_faster # 调用本地函数库 用于登入外部机器学习库并调用人脸识别函数
import cv2 # 调用OpenCV图像处理库
import threading # 调用threading多线程运行库
import time # 调用系统时间戳库
import os # 调用os多操作系统接口库
import matplotlib.pyplot as plt # 调用matplotlib绘图库
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体

#print('请输入需录入的人脸图片路径/文件名:')
#pic_name=input()

# 利用matplotlib显示图片函数  
def pshow(words,picture):
    plt.imshow(picture[:,:,::-1])   # 将读取的图片以RGB转换为BGR
    plt.title(words), plt.xticks([]), plt.yticks([])
    plt.show() # 显示图片
    

class Login(ttk.Frame): # 定义窗口大类
    def __init__(self, win):
        ttk.Frame.__init__(self, win)
        frame0 = ttk.Frame(self)
        frame1 = ttk.Frame(self)
        win.title("人脸识别")
        win.minsize(1240, 620)
        self.center_window()    # 执行中置窗口函数
        self.thread_run = None 	# 赋值 线程1默认关闭
        self.thread_run2 = None 	# 线程2默认关闭
        self.camera = None 	# 摄像头默认关闭
 
        #定义tk窗口属性
        
        #self.pilImage = Image.open("img/start.png")
        #self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
        #self.image_ctl = tk.Label(frame0, image=self.tkImage)
        #self.image_ctl.pack()
 
        frame0.pack(side=TOP, fill=tk.Y, expand=1)
        frame1.pack(side=TOP, fill=tk.Y, expand=1)
 
        self.facer = ttk.Label(frame1, text='', font=('Times', '20')) # 字体
        self.facer.pack()
        

#        def filefound(): # 定义获取图片路径的函数
#            filepath= askopenfilename() # 获取文件路径
#            pic_name=filepath
#            self.pic_path2 = pic_name # 赋值给图像2
#            
#            pshow('所选人像图片',pic_img) # 显示所选图片
        self.pic_path3='./star.png'
#        pic_img=cv2.imread(self.pic_path3)
#        pic_xz = pic_img.shape # 计算图像大小
#        pic_h=pic_xz[0] # 得出图片高度
#        pic_w=pic_xz[1] # 得出图片宽度
#        turn_w=pic_w*500/pic_h # 限制最大高度为580 以防窗口过小不完全显示 等比例转换宽度
#        turn_w=int(turn_w)
#            print('人像图像大小(高 宽):',pic_h,pic_w)
#            print ('路径:',filepath)
#            # 在tk窗口中显示所选图片
        
        self.pilImage = Image.open(self.pic_path3)            
        self.photo = self.pilImage.resize((500,500)) # 限制最大高度为580 等比缩放显示
        self.tkImage = ImageTk.PhotoImage(image=self.photo)
        self.image_ctl = tk.Label(frame0, image=self.tkImage)
        self.image_ctl.pack()
#            #e.delete(0, END)  # 将输入框里面的内容清空
#            #e.insert(0, filepath)
#        
#        #button2=Button(frame1,text="button2",command=filefound).grid(row=0,column=3)
#        # 按钮1 调用filefound函数 获取选择图片的路径 并赋值给self.pic_path2 输出图像
#        self.face_button1 = ttk.Button(frame1, text="1. 选择人像图片", width=15, command=filefound)
#        self.face_button1.pack(side=TOP)        
        # 按钮2 调用摄像头函数
        self.url_face_button = ttk.Button(frame1, text="使用相机识别", width=15, command=self.cv_face)
        self.url_face_button.pack(side=TOP)
        #self.file_pic_button = ttk.Button(frame1, text="本地文件识别", width=15, command=self.file_pic)
        #self.file_pic_button.pack(side=TOP)
 
        self.pack(fill=tk.BOTH, expand=tk.YES, padx="10", pady="10")
 
    #使弹出的窗体处于屏幕的中间位置
    def center_window(self):
        screenwidth = log.winfo_screenwidth()	# 获取屏幕分辨率宽
        screenheight = log.winfo_screenheight()	# 获取屏幕分辨率高
        log.update()	# 更新窗口
        width = log.winfo_width()	# 重新赋值
        height = log.winfo_height()
        size = '+%d+%d' % ((screenwidth - width)/2, (screenheight - height)/2)
        # 重新赋值大小 大小为屏幕大小/2
        log.geometry(size) 	# 以新大小定义窗口
 
#    def file1(self):
#        self.pic_path = askopenfilename(title="选择识别图片", filetypes=[("jpg图片", "*.jpg"), ("png图片", "*.png")])
 
    def cv_face(self):   # 调用摄像头函数
        if self.thread_run:
            if self.camera.isOpened(): # 如果已经打开则关闭
                self.camera.release()
                print("关闭摄像头")
                self.camera = None
                self.thread_run = False
            return
        if self.camera is None: # 如果没有摄像头则尝试打开
            self.camera = cv2.VideoCapture(1) # 利用OpenCV调用外摄像头
            if not self.camera.isOpened(): # 如果没有打开 则调用内摄像头
                self.camera = None
                print("没有外置摄像头")
                self.camera = cv2.VideoCapture(0) # 用OpenCV调用内摄像头
                if not self.camera.isOpened(): # 如果没有打开 则打开失败
                    print("没有内置摄像头")
                    tkinter.messagebox.showinfo('警告', '摄像头打开失败!')
                    self.camera = None
                    return
                else:
                    print("打开内置摄像头")
            else:
                print("打开外置摄像头")
        self.thread = threading.Thread(target=self.video_thread) # 多线程函数执行摄像头运行函数
        self.thread.setDaemon(True)
        self.thread.start()
        self.thread_run = True
 
    def video_thread(self): # 开始摄像头运行
        self.thread_run = True # 多线程1开启
        self.thread2 = threading.Thread(target=self.video_pic)
        self.thread2.setDaemon(True)
        self.thread2.start()
        self.thread_run2 = True
        while self.thread_run: # 循环一直调用摄像头
            _, img_bgr = self.camera.read() # 以bgr格式读取摄像头内的截图 
            gray = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2GRAY) # 灰度转换
            # 在CV官方机器学习库内加载人脸识别分类器                
            # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
            classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
            color = (0,255,0) # 绿色线
            # 识别器进行识别
            faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
            # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
            # print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的

            # 判断识别结果集合长度
            if len(faceRects):
                for faceRect in faceRects:
                    x,y,w,h = faceRect
                    # 用矩形框选出人脸   最后一个参数2是框线宽度
                    cv2.rectangle(img_bgr,(x, y), (x + h, y + w), color, 2)                 
            img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 颜色转换
            im = Image.fromarray(img)
            w, h = im.size
            pil_image_resized = self.resize(w, h, im) # 调整大小函数
            self.imgtk = ImageTk.PhotoImage(image=pil_image_resized)
            self.image_ctl.configure(image=self.imgtk)
        print("结束运行")
 
    def video_pic(self): # 视频截图保存及框选函数
        dir_name='./img' # 给出图片目录路径
        fileimg_list = [] # 建立列表型图片列表
        fileimg_list=os.listdir(dir_name) # 获取目录下文件名
        file_num=len(fileimg_list) # 获取列表长度
        fileimg_num=0   # 定义文件序号初始值为0
        self.thread_run2 = True     # 开启多线程
#        predict_time = time.time() # 获得系统时间
        while self.thread_run2: # 循环读取
#            if time.time() - predict_time > 0.1: #每0.1s读取一次摄像头截图
#            print("正在识别中")
            _, img_bgr = self.camera.read() # 重新读取摄像头图像
            cv2.imwrite("tmp/test.jpg", img_bgr) #利用cv写入到tmp/test.jpg路径下
#                test_pic=cv2.imread('tmp/test.jpg') # 重新读取截图
#                pshow('识别截图',test_pic) # 显示截图
#                
#                # 图像路径 我用的相对路径
#                face_mark = 'tmp/test.jpg'
#                # 读取截图
#                faceImg = cv2.imread(face_mark)
#                # 转换灰色
#                gray = cv2.cvtColor(faceImg,cv2.COLOR_RGB2GRAY) # 由于tmp/test.jpg路径下的已经转换成RGB保存 所以不用再进行BGR转换
#
#                # 在CV官方机器学习库内加载人脸识别分类器                
#                # Python\Python38-32\Lib\site-packages\cv2\data  这个目录下也有
#                classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
#                color = (0,255,0) # 绿色线
#
#                # 识别器进行识别
#                faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
#                # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
#                # print(faceRects)  #[[113  42  60  60]]    前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的
#
#                # 判断识别结果集合长度
#                if len(faceRects):
#                    for faceRect in faceRects:
#                        x,y,w,h = faceRect
#                        # 用矩形框选出人脸   最后一个参数2是框线宽度
#                        cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)
#        
#                
#                pshow('识别结果',faceImg)    # 输出框选结果图像并显示
#                cv2.imwrite("tmp/test2.jpg", faceImg)
            self.pic_path = "tmp/test.jpg" # 重新读取摄像头截图
#                self.pilImage = Image.open(self.pic_path)
#                self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
#                self.image_ctl = tk.Label(frame0, image=self.tkImage)
#                self.image_ctl.pack()
            try:

                
                if fileimg_num<file_num: #当文件序号小于总列表长度时
                        #print(fileimg_num)
                        #img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])    
                        #pshow(fileimg_list[fileimg_num],img)
                    self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num] # 给出pic_path2的值为目录名+/+文件名
                        #print(self.pic_path2)
#                        self.pic_path2='./img/111.jpg'
                    facestr, result = api_face_faster.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
                    self.facer.configure(text=str(facestr))
                                #self.pic() # 对摄像头图像进行尺度变换
                    if result > 80: #识别结果大于80
                        humantitle='  姓名:  '+fileimg_list[fileimg_num].partition('.')[0]+"  人脸匹配成功!  \n"
                        print(humantitle) # 输出人像文件文件名
                        tkinter.messagebox.showinfo('提示', humantitle) # tk窗口提示                          
                        try:
                            f=open("识别记录.txt","r")
                            fi=open("识别记录.txt","a")
                            txt=time.ctime()
                            fi.write(txt+humantitle)
                            f.close()
                            fi.close() # 将识别成功的记录保存在txt文件下
                        except:
                            f=open("识别记录.txt","w")
                            txt=time.ctime()
                            f.write(txt+humantitle)
                            f.close()
                                        
                                    
                                    
                                    # close_window()
                                    # os.system("python3 ./main.py")
                                #if result < 20:
                                #    tkinter.messagebox.showinfo('提示', '未检测到人脸!')
                        break
                    else: # 小于80失败
                        fileimg_num=fileimg_num+1 # 文件序号+1
                                
                            
                else: # 超出文件列表长度
                    tkinter.messagebox.showinfo('提示', '查无此人!人脸匹配失败!')
                    break
            except:
                pass
#                predict_time = time.time()      # 读取时间          
#            print("识别结束")
                
                # 看门狗程序(调试用)
                # 防止程序关闭时进入死循环跑飞
                #print('请输入任意值以继续,否则请关闭窗口以终止程序:')
                #a=input()
                #print(a)
        pass
 
#    def file_pic(self): #识别函数
#        dir_name='./img'
#        fileimg_list = []
#        fileimg_list=os.listdir(dir_name)
#        file_num=len(fileimg_list)
#        fileimg_num=0   
#        while True:
#            if fileimg_num<file_num:
#                print(fileimg_num)
#                img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])    
#                pshow(fileimg_list[fileimg_num],img)
#                self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num]
#                print(self.pic_path2)
##                self.pic_path2='./img/111.jpg'
#                facestr, result = api_face.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
#                self.facer.configure(text=str(facestr))
#                        #self.pic() # 对摄像头图像进行尺度变换
#                if result > 80: #识别结果大于80
#                    tkinter.messagebox.showinfo('提示', '人脸匹配成功!') # tk窗口提示
#                    print('人像图片路径:'+self.pic_path2) # 输出人像文件路径
#                    try:
#                        f=open("识别记录.txt","r")
#                        fi=open("识别记录.txt","a")
#                        txt=time.ctime()
#                        fi.write(txt+'   人像图片路径:   '+self.pic_path2+"     人脸匹配成功! \n")
#                        f.close()
#                        fi.close() # 将识别成功的记录保存在txt文件下
#                    except:
#                        f=open("识别记录.txt","w")
#                        txt=time.ctime()
#                        f.write(txt+'   人像图片路径:   '+self.pic_path2+"     人脸匹配成功! \n")
#                        f.close()
#                                
#                            
#                            
#                            # close_window()
#                            # os.system("python3 ./main.py")
#                        #if result < 20:
#                        #    tkinter.messagebox.showinfo('提示', '未检测到人脸!')
#                        break
#                    else: # 小于80失败
#                        fileimg_num=fileimg_num+1
#                        
#                    
#            else:
#                tkinter.messagebox.showinfo('提示', '人脸匹配失败!')
#                break
        
        
 
#    def pic(self): # 对摄像头图像进行尺度变换
#        self.pilImage3 = Image.open(self.pic_path) # 用pillow读取摄像头图像
#        w, h = self.pilImage3.size # 计算大小赋值给宽 高
#        pil_image_resized = self.resize(w, h, self.pilImage3) # 调整大小函数
#        self.tkImage3 = ImageTk.PhotoImage(image=pil_image_resized) 
#        self.image_ctl.configure(image=self.tkImage3) # 输出结果
 
    def resize(self, w, h, pil_image): # 调整大小函数
        w_box = 1000 # 定义最大宽度
        h_box = 500 # 最大高度
        f1 = 1.0*w_box/w # 最大值/真实值
        f2 = 1.0*h_box/h
        factor = min([f1, f2]) # 取最小值
        width = int(w*factor) # 用最小值*对应值 调整到最大定义值 等比调整另一个值
        height = int(h*factor)
        return pil_image.resize((width, height), Image.ANTIALIAS) # 输出调整
 
 
def close_window():
    print("已关闭人脸识别")
    if Login.thread_run:
        Login.thread_run = False
        Login.thread.join(2.0)
    log.destroy()
    

    
if __name__ == '__main__':
    log = tk.Tk()
 
    login = Login(log)
    # close,退出输出destroy
    log.protocol('清除窗口', close_window)
    # 进入消息循环
    log.mainloop()
    
    

五、最终测试

1.识别成功测试

读取img目录下所有图像 对摄像头图像依次进行对比识别 相似度不断变化 当大于80时 输出成功提示 并获取图像名称、写入记录文档中
如图所示,我的图像位于img目录下的第4个 列表序号为3 程序应在识别到此的时候停止
(测试图片中为原始版本,文字提示中没有姓名,现代码已更新为包含姓名提示的版本)
在这里插入图片描述
初始界面:
在这里插入图片描述
点击“使用相机识别”后开始开启摄像头识别
如图所示为识别成功结果:
在这里插入图片描述
点击确认后终止程序
在识别记录.txt文档中可以看到刚刚的照片名称 识别时间
在这里插入图片描述
在实际运行时 对前三个图像的相似度较低 最大只有36 成功后程序停止 不再对后面的图像进行识别
如图所示:
在这里插入图片描述
在这里插入图片描述

2.识别失败功能测试

戴上口罩进行识别(或删掉目录下对应人物的图像) 相似度达不到80 遍历的图片都无法成功识别 最后提示失败

删除对应图像:
在这里插入图片描述
在这里插入图片描述
戴上口罩 相似度最高只有60多 识别失败:
在这里插入图片描述
在这里插入图片描述

六、总结

参考文章链接:

blog.csdn.net/weixin_53403301/article/details/118575731
blog.csdn.net/weixin_53403301/article/details/118005313
blog.csdn.net/weixin_53403301/article/details/117464715

主程序部分代码注释完整 基本上每一行都有说明和注释 直接看主程序代码就可以弄懂其原理
关于树莓派的人脸识别 目前还是之前的版本(指定图像进行识别) 由于还没开学 没有硬件可以测试 所以尚未加入遍历目录下所有文件的功能进行 等加上以后 我会单独再写一篇
树莓派效果展示图:
在这里插入图片描述
资源:

download.csdn.net/download/weixin_53403301/66918342

最后 此次开源系统的百度网盘资源包:

pan.baidu.com/s/1oOZKc_I3G5OdcRDpkZM7yQ 

提取码:hml1

这里是光电Mike Zhou 一位光电专业在读的网易独家签约音乐人
歌手页面:

music.163.com/#/artist?id=12115205

附录:列表的赋值类型和py打包

列表赋值

BUG复现

闲来无事写了个小程序 代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021

@author: 16016
"""

a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):
    for j in range(16):
        a_list[j]=str(a_list[j])+'_'+str(j)
        print("序号:",j)
        print('a_list:\n',a_list)
        
        
        c_list[j]=a_list
        print('c_list[0]:\n',c_list[0])
        print('\n')
#        b_list[j]=a_list[7],a_list[8]
#        print(b_list[j])
        # 写入到Excel:
#print(c_list,'\n')    

我在程序中 做了一个16次的for循环 把列表a的每个值后面依次加上"_"和循环序号
比如循环第x次 就是把第x位加上_x 这一位变成x_x 我在输出测试中 列表a的每一次输出也是对的
循环16次后列表a应该变成[‘0_0’, ‘1_1’, ‘2_2’, ‘3_3’, ‘4_4’, ‘5_5’, ‘6_6’, ‘7_7’, ‘8_8’, ‘9_9’, ‘10_10’, ‘11_11’, ‘12_12’, ‘13_13’, ‘14_14’, ‘15_15’] 这也是对的

同时 我将每一次循环时列表a的值 写入到空列表c中 比如第x次循环 就是把更改以后的列表a的值 写入到列表c的第x位
第0次循环后 c[0]的值应该是[‘0_0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘10’, ‘11’, ‘12’, ‘13’, ‘14’, ‘15’] 这也是对的
但是在第1次循环以后 c[0]的值就一直在变 变成了c[x]的值
相当于把c_list[0]变成了c_list[1]…以此类推 最后得出的列表c的值也是每一项完全一样
我不明白这是怎么回事
我的c[0]只在第0次循环时被赋值了 但是后面它的值跟着在改变

如图:
在这里插入图片描述
第一次老出bug 赋值以后 每次循环都改变c[0]的值 搞了半天都没搞出来
无论是用appen函数添加 还是用二维数组定义 或者增加第三个空数组来过渡 都无法解决

代码改进

后来在我华科同学的指导下 突然想到赋值可以赋的是个地址 地址里面的值一直变化 导致赋值也一直变化 于是用第二张图的循环套循环深度复制实现了

代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021

@author: 16016
"""

a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):
    for j in range(16):
        a_list[j]=str(a_list[j])+'_'+str(j)
        print("序号:",j)
        print('a_list:\n',a_list)
        
        
        for i in range(16):
            c_list[j].append(a_list[i])
        print('c_list[0]:\n',c_list[0])
        print('\n')
#        b_list[j]=a_list[7],a_list[8]
#        print(b_list[j])
        # 写入到Excel:
print(c_list,'\n')    

解决了问题

在这里插入图片描述

优化

第三次是请教了老师 用copy函数来赋真值

代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021

@author: 16016
"""

a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):
    for j in range(16):
        a_list[j]=str(a_list[j])+'_'+str(j)
        print("序号:",j)
        print('a_list:\n',a_list)
        
        
        c_list[j]=a_list.copy()
        print('c_list[0]:\n',c_list[0])
        print('\n')
#        b_list[j]=a_list[7],a_list[8]
#        print(b_list[j])
        # 写入到Excel:
#print(c_list,'\n')    

同样能解决问题
在这里插入图片描述
最后得出问题 就是指针惹的祸!

a_list指向的是个地址 而不是值 a_list[i]指向的才是单个的值 copy()函数也是复制值而不是地址

如果这个用C语言来写 就直观一些了 难怪C语言是基础 光学Python不学C 遇到这样的问题就解决不了

C语言yyds Python是什么垃圾弱智语言

总结

由于Python无法单独定义一个值为指针或者独立的值 所以只能用列表来传送
只要赋值是指向一个列表整体的 那么就是指向的一个指针内存地址 解决方法只有一个 那就是将每个值深度复制赋值(子列表内的元素提取出来重新依次连接) 或者用copy函数单独赋值

如图测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
部分代码:

# -*- coding: utf-8 -*-
"""
Created on Sat Nov 20 16:45:48 2021

@author: 16016
"""

def text1():
    A=[1,2,3]
    B=[[],[],[]]
    for i in range(len(A)):
        A[i]=A[i]+i
        B[i]=A
        print(B)

def text2():
    A=[1,2,3]
    B=[[],[],[]]
    
    A[0]=A[0]+0
    B[0]=A
    print(B)
    A[1]=A[1]+1
    B[1]=A
    print(B)
    A[2]=A[2]+2
    B[2]=A
    print(B)
    
if __name__ == '__main__':
    text1()
    print('\n')
    text2()

py打包

Pyinstaller打包exe(包括打包资源文件 绝不出错版)

依赖包及其对应的版本号

PyQt5 5.10.1
PyQt5-Qt5 5.15.2
PyQt5-sip 12.9.0

pyinstaller 4.5.1
pyinstaller-hooks-contrib 2021.3

Pyinstaller -F setup.py 打包exe

Pyinstaller -F -w setup.py 不带控制台的打包

Pyinstaller -F -i xx.ico setup.py 打包指定exe图标打包

打包exe参数说明:

-F:打包后只生成单个exe格式文件;

-D:默认选项,创建一个目录,包含exe文件以及大量依赖文件;

-c:默认选项,使用控制台(就是类似cmd的黑框);

-w:不使用控制台;

-p:添加搜索路径,让其找到对应的库;

-i:改变生成程序的icon图标。

如果要打包资源文件
则需要对代码中的路径进行转换处理
另外要注意的是 如果要打包资源文件 则py程序里面的路径要从./xxx/yy换成xxx/yy 并且进行路径转换
但如果不打包资源文件的话 最好路径还是用作./xxx/yy 并且不进行路径转换

def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

而后再spec文件中的datas部分加入目录
如:

a = Analysis(['cxk.py'],
             pathex=['D:\\Python Test\\cxk'],
             binaries=[],
             datas=[('root','root')],
             hiddenimports=[],
             hookspath=[],
             hooksconfig={},
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

而后直接Pyinstaller -F setup.spec即可

如果打包的文件过大则更改spec文件中的excludes 把不需要的库写进去(但是已经在环境中安装了的)就行

这些不要了的库在上一次编译时的shell里面输出
比如:
在这里插入图片描述

在这里插入图片描述
然后用pyinstaller --clean -F 某某.spec

附录:关于旧文新发

为何要进行旧文新发?
因为我在2023年博客之星评选中发现 有的人转载、抄袭他人文章 稍微改动几下也能作为高质量文章入选
所以我将把我的旧文重新发一次 然后也这样做

2023年博客之星规则:

  1. 自2023年1月1日起算起,平均每周创作过至少一篇高质量且非付费专栏的原创文章即可入围。由于博客之星是年度评选,所以统计时间一直截止到2023年12月17日。
  2. 高质量博文为80分以上原创博文,质量分查询地址:https://www.csdn.net/qc
  3. 入围条件补充说明:当前的入围状态为动态,一旦未达到每周平均创作过至少一篇高质量且非付费专栏的原创文章入围资格将会跳出入围资格,若当前还未入围者通过后期创作也可入围,当下并非最终结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/659684.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

若依框架官网

RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-Cloud|RuoYi框架|RuoYi开源|RuoYi视频|若依视频|RuoYi开发文档|若依开发文档|Java开源框架|Java|SpringBoot|SrpingBoot2.0…

Linux--进程间通信(2)(有名管道)

目录 1.原理 2.创建命名管道 3.使用命名通道实现简单的通信 4.使用创建的命名管道 1.原理 匿名管道没有名称&#xff0c;它们是通过句柄在父进程和子进程之间传递的。这意味着匿名管道只能用于具有父子关系的进程之间。 但如果程序之间没关系&#xff0c;那么这时候就要用…

香橙派AIpro初体验

1.开发板资料 开发板资源 产品介绍主页&#xff1a;http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-AIpro.html开发板案例源码&#xff1a;https://gitee.com/ascend/EdgeAndRobotics工具&原理图&案例源码&开发手册&#x…

Centos安装,window、ubuntus双系统基础上安装Centos安装

文章目录 前言一、准备工作二、开始安装1、2、首先选择DATE&TIME2、选择最小安装3、 选择安装位置 总结 前言 因工作需要&#xff0c;我需要在工控机上额外装Centos7系统&#xff0c;不过我是装在机械硬盘上了不知道对性能是否有影响&#xff0c;若有影响&#xff0c;后面…

整理了六个正规靠谱的兼职赚钱软件,适合普通人做的兼职副业~

​随着互联网时代的到来&#xff0c;越来越多的人选择通过互联网赚钱。在这篇文章中&#xff0c;我们将探讨一些可以在网上长期赚钱的方法。 在网络上面其实有很多的赚钱方法&#xff0c;尽管方法很多&#xff0c;但是对于一些网络新手&#xff0c;刚进入互联网圈子不久的伙伴…

gradio image 类型

3种类型&#xff0c;默认是 numpy.array numpy.array PIL.Image str file path. 互相转换 # 从路径到 numpy.ndarray import cv2 image_mask cv2.imread(imagePath) print(type(image_mask))# 从路径到 PIL.IMAGE from PIL import Image image_maskImage.open(imagePath) pri…

记录第一次使用U盘重装系统(win10专业版)的流程与总结

写在前面 刚进大学那会儿不懂电脑&#xff0c;什么东西都往 C 盘装&#xff0c;以至于很多环境、文件这些都很乱 尽管我已经把能移的都移动到 D盘了&#xff0c;能清理的东西也清理了&#xff0c;C 盘还是时不时会爆红 刚好最近 CTF 比赛打得差不多了&#xff0c;因此勇师傅决定…

2024 GIAC 全球互联网架构大会:拓数派向量数据库 PieCloudVector 架构设计与案例实践

5月24-25日&#xff0c;msup 和高可用架构联合举办了第11届 GIAC 全球互联网架构大会。会议聚焦“共话AI技术的最新进展、架构实践和未来趋势”主题&#xff0c;邀请了 100 余位行业内的领军人物和革新者&#xff0c;分享”Agent/RAG 技术、云原生、基座大模型“等多个热门技术…

以果决其行的古圣先贤

大家好&#xff0c;昨天给大家讲倪海夏老师以果决其行&#xff0c;这个思维逻辑,能解天下的万事万物&#xff0c;讲之前想的时候&#xff0c;想到了要讲历史上的人物&#xff0c;但是讲的当时忘了&#xff0c;今天补出来。 我们现在往前捋&#xff0c;在建国以后&#xff0c;我…

FineReport帆软设计器,远程连接服务器

FineReport报表工具一款纯Java编写的企业级web报表软件工具。它能够全面支持主流的B/S架构以及传统的C/S架构&#xff0c;部署方式简单而灵活. 需要使用FineReport帆软设计器&#xff0c;配置远程服务器的方式如下&#xff1a; 1、打开帆软设计器&#xff0c;点击文件&#x…

ModuleNotFoundError: No module named ‘qcloud_cos‘

这个错误表示Python无法找到名为qcloud_cos的模块。qcloud_cos是腾讯云提供的一个Python SDK&#xff0c;用于与腾讯云对象存储&#xff08;COS&#xff09;服务进行交互。 使用pip安装qcloud_cos报以下错误 解决办法 pip3 install cos-python-sdk-v5

优思学院:什么是DMADV模式?和DMAIC有何区别?

在现代企业管理中&#xff0c;质量管理是一项至关重要的工作。六西格玛管理法作为一种高效的质量管理方法&#xff0c;已在全球范围内得到了广泛应用。它不仅在制造业中发挥了巨大的作用&#xff0c;在服务业和其他行业中也同样表现出了强大的生命力。六西格玛管理法主要有两种…

GDPU Java 天码行空13

&#xff08;一&#xff09;实验目的 1、掌握JAVA中与网络程序开发相关的知识点&#xff1b; 2、理解并掌握网络编程开发思想及方法&#xff1b; 3、熟悉项目开发的分包方法和依据&#xff1b; 4、实现聊天室中客服端和服务器端的实现方法&#xff1b; 5、熟悉多线程程序开发方…

可视化大屏:随意堆数据,错!要主次分明、重点突出,动静结合。

可视化大屏是一种展示数据的方式&#xff0c;它的设计应该遵循一些原则&#xff0c;以确保信息的传递和理解效果最佳。以下是一些关键点&#xff0c;可以帮助设计出主次分明、重点突出、动静结合的可视化大屏&#xff1a; 定义目标和重点&#xff1a; 在开始设计可视化大屏之前…

亚马逊云服务器会不会限制服务器使用?

亚马逊云服务器&#xff08;Amazon Web Services&#xff0c;AWS&#xff09;是全球领先的云计算服务提供商之一&#xff0c;其强大的基础设施和灵活的服务模式吸引了无数企业和个人用户。然而&#xff0c;许多人对于亚马逊云服务器是否会对服务器使用进行限制存在疑虑。我们九…

NI PXIe-7857R与PXIe-8842的区别

一、NI PXIe-7857R 类型&#xff1a; FPGA模块&#xff1a;基于FPGA的可编程I/O模块。 主要功能&#xff1a; FPGA处理&#xff1a;包含Xilinx Kintex-7 FPGA&#xff0c;支持自定义逻辑和处理。 I/O接口&#xff1a;提供丰富的模拟和数字I/O通道。 高速数据处理&#xff1a…

留守儿童|基于SprinBoot+vue的留守儿童爱心网站(源码+数据库+文档)

留守儿童爱心网站 目录 基于SprinBootvue的留守儿童爱心网站 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&…

云计算-无服务器计算与AWS Lambda (Serverless Computing with AWS Lambda)

AWS Lambda 无服务器计算与AWS Lambda AWS Lambda支持无服务器计算&#xff0c;不需要任何预配置和管理&#xff0c;同时还能最大限度地降低成本。我们将看到如何创建一个简单的Lambda函数&#xff0c;以及如何将其与AWS事件映射。在现实生活中&#xff0c;任何托管在线的应用…

GDPU 操作系统 天码行空13

文章目录 ❌ TODO&#xff1a;本文仅供参考&#xff0c;极有可能有误1.生产者消费者问题&#xff08;信号量&#xff09;&#x1f496; ProducerConsumerExample.java&#x1f3c6; 运行结果 &#x1f496; ProducerConsumerSelectiveExample.java&#x1f3c6; 运行结果 2.实现…