【旧文更新】【优秀课设】基于OpenCV的Python人脸检测、识别、框选(指定照片进行识别)
关于旧文新发
为何要进行旧文新发?
因为我在2023年博客之星评选中发现 有的人转载、抄袭他人文章 稍微改动几下也能作为高质量文章入选
所以我将把我的旧文重新发一次 然后也这样做
2023年博客之星规则:
前言
增加了视频随时标注功能
资源:
download.csdn.net/download/weixin_53403301/66920466
旧版:
download.csdn.net/download/weixin_53403301/66922685
最新功能:(遍历目录下所有图像进行识别)
blog.csdn.net/weixin_53403301/article/details/119422635
基于OpenCV的Python人脸识别、检测、框选(遍历目录下所有照片依次识别 视频随时标注)
资源
download.csdn.net/download/weixin_53403301/19330528
根据以下两个帖子改进
添加了框选功能和选择图片功能 优化了部分效果
blog.csdn.net/yql_617540298/article/details/108479057
blog.csdn.net/weixin_45304263/article/details/114697938
代码
# -*- 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):
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:
imagepath = os.path.exists(image)
if imagepath == True:
images = image
with open(images, 'rb') as images:
img1 = base64.b64encode(images.read())
else:
print("img1 not exits")
return
if image2 is not None:
imagepath2 = os.path.exists(image2)
if imagepath2 == True:
images2 = image2
with open(images2, 'rb') as images2:
img2 = base64.b64encode(images2.read())
else:
print("img2 not exits")
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 'image参数不能为空!'
face = requests.post(url=LICENSE_PLATE_URL, headers=self.HEADER, data=self.IMAGE_CONFIG)
return face.json()
def facef(FA1, FA2): # 人脸识别逻辑函数
testAccessToken = AccessToken()
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出错 则改为
'ID': '15050553',
'API_KEY': 'rlRrtRL5oRdXGh71jgg1OmyN',
'SECRET_KEY': 'dK5TpuTAZn2nw5eVpspZLmF5Qs1Uu8A1'
# -*- 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 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
pic_img=cv2.imread(self.pic_path2)
pshow('所选人像图片',pic_img) # 显示所选图片
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_path2)
self.photo = self.pilImage.resize((turn_w,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="2. 使用相机识别", 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): # 视频截图保存及框选函数
self.thread_run2 = True # 开启多线程
predict_time = time.time() # 获得系统时间
while self.thread_run2: # 循环读取
if time.time() - predict_time > 2: #每2s读取一次摄像头截图
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:
self.file_pic() #执行识别函数
except:
pass
predict_time = time.time() # 读取时间
print("识别结束")
# 看门狗程序(调试用)
# 防止程序关闭时进入死循环跑飞
#print('请输入任意值以继续,否则请关闭窗口以终止程序:')
#a=input()
#print(a)
pass
def file_pic(self): #识别函数
# self.pic_path2在按钮1中被赋值 self.pic_path为调用摄像头的图像
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('提示', '未检测到人脸!')
else: # 小于80失败
tkinter.messagebox.showinfo('提示', '人脸匹配失败!')
# 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()
附录:列表的赋值类型和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年博客之星规则:
- 自2023年1月1日起算起,平均每周创作过至少一篇高质量且非付费专栏的原创文章即可入围。由于博客之星是年度评选,所以统计时间一直截止到2023年12月17日。
- 高质量博文为80分以上原创博文,质量分查询地址:
https://www.csdn.net/qc
- 入围条件补充说明:当前的入围状态为动态,一旦未达到每周平均创作过至少一篇高质量且非付费专栏的原创文章入围资格将会跳出入围资格,若当前还未入围者通过后期创作也可入围,当下并非最终结果。