opencv+tkinter录像程序
程序员文章站
2022-05-16 15:42:16
...
opencv+tkinter 实现录屏控制
环境:
- opencv
- python3
- PIL (pillow)
编辑器:
- vscode
模块导入 为了方便之后的打包,统一使用from导包
from tkinter import Label, Button, Tk, Frame, ttk, Entry, StringVar
from cv2 import VideoCapture, waitKey, VideoWriter, VideoWriter_fourcc, putText, cvtColor, destroyAllWindows
from cv2 import FONT_HERSHEY_COMPLEX_SMALL, LINE_AA, CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT, COLOR_BGR2RGBA
from PIL import Image,ImageTk
from time import time, strftime, localtime
from tkinter.filedialog import askdirectory
from os.path import basename
设置全局变量
Record = False # 默认不开启录制
StartTime = 0 # 开始录制时间,存储时间戳
filename = '' # 用来存储录制的视频的名字
filePathChoice = False # 文件路径是否选择
startSave = False # 判断是否开始保存文件,用来创建保存的文件名以及开启写入的文件流对象
videoWriter = None # 创建全局的文件流对象,用来传递
filename_ = None # 文件名,和上面的一样,创建全局的,方便值的传递
加载视频以及保存视频
def loadVideo(): # 加载视频以及保存视频
global Record, StartTime, startSave, videoWriter, filename_
ret, frame = cap.read() # 读取摄像头的数据
if ret:
waitKey(1000) # 这个不加也可以
if Record:
savePath = filepath.get() # 获取文件的保存路径
if savePath:
# print(filename_)
if startSave:
filename_ = savePath + f"/recordSaveAs{int(time() * 1000)}" + combox.get()
# 这里写入
nameLabel.config(text=f"文件名:{basename(filename_)}")
print(f"创建保存的文件对象 {filename_}")
videoWriter = VideoWriter(filename_, VideoWriter_fourcc('I', '4', '2', '0'), FPS, SIZE)
startSave = False # 更改录制的状态,不执行再次创建对象流的语句
if videoWriter: # 存在文件流就写入对象
print(f'开始循环写入:{filename_}')
videoWriter.write(frame) # 保存视频
else: # 没有获取到文件的保存路径,就调用文件选择函数
choicePath(None)
# font = cv2.FONT_HERSHEY_PLAIN # 小字体,蛮不错的
font = FONT_HERSHEY_COMPLEX_SMALL # 小字体,比上面那个字体显得大一点
thisTime = calcTime(time()) # 获取计时的时间戳
# TODO 下面两个显示提示信息,这里还可以添加人脸识别系统,检测到人脸的时候保存,没有的时候就不保存
putText(frame, f"Is recording : {basename(filename_)}", (10, 30), font, 1, (255, 255, 187), 1, LINE_AA)
putText(frame, f'Record Time: {thisTime}', (10, 60), font, 1, (255, 184, 99), 1, LINE_AA)
cv2Image = cvtColor(frame, COLOR_BGR2RGBA) # 转换成灰度图
current_img = Image.fromarray(cv2Image) # 灰度图转换成Image对象
imgtk = ImageTk.PhotoImage(image=current_img) # 创建可以添加到tk上面的对象
pannel.imgtk = imgtk
pannel.config(image=imgtk) # 设置图像
root.after(1, loadVideo) # 循环函数,每1毫秒
点击按钮的事件
def StartOrStopRecord(event): # 开始或停止的按钮
global Record, StartTime, filePathChoice, startSave, videoWriter
if filePathChoice: # 如果选择过文件的路径就开始录制
if not Record:
Record = True # 开始录制
startSave = True # 开始保存
# videoWriter = None # 释放这个对象
record.config(text="点击停止录制")
StartTime = time() # 存储时间戳
filend = combox.get() # 获取多选框中的内容
print(f'文件后缀为{filend}')
else:
Record = False
startSave = False
videoWriter = None # 释放文件流对象
record.config(text="点击开始录制")
else: # 没有选择路径就调用路径选择函数
choicePath(None)
选择文件函数,还有一个计时小方法
def choicePath(event): # TODO 选择文件的保存路径,这个窗口不咋滴好看,之后再找找好看的
global filePathChoice
filedirectory = askdirectory(title="选择视频保存的路径", initialdir=__file__.replace(basename(__file__), ""))
text.set(filedirectory) # 设置单行文本框中的内容
filePathChoice = True
def calcTime(thisTime): # 还有一种计算时间戳转时间的库 datetime
global StartTime
diffTime = thisTime - StartTime # 时间差
calTime = strftime("%H:%M:%S", localtime(diffTime)) # 当前时间 # 比正常时间多8个小时,下面进行时间调整
calTime = calTime.split(':') # 分隔为列表
calTime[0] = f'0{int(calTime[0]) - 8}'
return ":".join(calTime) # 返回时间差 时:分:秒
主函数控制区
# 控制区
FPS = 30
cap = VideoCapture(0) # 捕获摄像头
SIZE = (int(cap.get(CAP_PROP_FRAME_WIDTH)), int(cap.get(CAP_PROP_FRAME_HEIGHT))) # 获取摄像头捕获的画面的大小
root = Tk() # 创建一个窗口
root.title('录屏噻!') # 设置标题
# TODO 布局还不是很好看,组件的颜色形状啥的也不是很好
frame1 = Frame(root) # 创建一个子组件
frame1.pack(side='top') # 构建出来,位置为top
frame2 = Frame(root) # 创建第二个组件
frame2.pack(side='bottom') # 也构建出来,位置为bottom # 说明:bottom和top表示组件独占一行,left和right表示组件独占一列
# 子组件的部件
pannel = Label(frame1, padx=10, pady=10) # 继承于frame1组件,用来存放图像
pannel.pack()
# 录制按钮
record = Button(frame1, text="点击开始录制") # 创建按钮对象,继承于frame1,并设置按钮的文本内容
record.pack(side='left') # 独占一列,靠左
# 提示选择文件后缀名
tipLabel = Label(frame1, text="保存类型") # 创建一个标签对象
tipLabel.pack(side='left')
# 显示保存的文件的名称
nameLabel = Label(frame1)
nameLabel.pack(side='left')
# 多选框
combox = ttk.Combobox(frame1, width=10) # 创建一个复选框,设置宽度为10
combox.pack(side='right') # 独占一列,靠右
combox['value'] = ('.avi', '.mp4') # TODO 设置保存的文件的后缀名,opencv应该还有支持的文件后缀,再添加添加
combox.current(0) # 选择默认的文件后缀名
# 选择文件的保存路径
filepathshow = Button(frame2, text="选择文件保存路径")
filepathshow.pack(side='left', expand=True)
# 文件路径显示
text = StringVar()
filepath = Entry(frame2, textvariable=text, width=70)
filepath.pack(side='left')
# print(combox.get())
# 函数循环,事件绑定
loadVideo()
record.bind("<Button-1>", StartOrStopRecord) # 按钮绑定鼠标左键,点击运行StartOrStopRecord函数
filepathshow.bind('<Button-1>', choicePath) # 选择文件保存路径按钮,点击运行choicePath函数
root.mainloop() # 主循环
cap.release() # 释放掉摄像头捕获对象
destroyAllWindows() # 销毁所有的窗口