IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> Python opencv库 tkinter 设计屏幕录制工具 -> 正文阅读

[人工智能]Python opencv库 tkinter 设计屏幕录制工具

有时, 需要通过录屏, 演示电脑端的操作。如何使用Python来实现?

1.使用cv2库和生成视频

首先需要使用opencv和PIL库。使用pip install opencv-python pillow可同时安装opencv-pythonpillow两个库。
程序的步骤是:
先调用PIL的ImageGrab模块截取屏幕指定区域的图片, 再转换为cv2的视频帧, 然后写入VideoWriter对象。接着, 程序延时一段时间(1 ÷ fps)。
最后调用VideoWriterrelease()方法, 释放videoWriter。

import tkinter as tk
import tkinter.filedialog as dialog
import time
from PIL import Image,ImageGrab
import numpy as np
import cv2

def _stop(): # 停止录制
    global flag;flag=True
    btn_stop['state']=tk.DISABLED
    root.title('录制已结束')

root=tk.Tk()
root.title('录屏工具')
tk.Button(root,text='停止',command=_stop).pack()

filename="test.avi"

# 获取屏幕大小
area=[0,0,root.winfo_screenwidth(),root.winfo_screenheight()]

root.title('录制中')
fps=10;flag=False
# 创建fourcc对象, 指定视频编码格式
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
# 创建VideoWriter对象, 处理视频
videoWriter = cv2.VideoWriter(filename, fourcc, fps,
                              (area[2]-area[0],area[3]-area[1]))

while not flag:
    image=ImageGrab.grab(area) # 截取屏幕指定区域的图片
    # 将PIL的image对象转换为cv2的视频帧
    frame = cv2.cvtColor(np.asarray(image),cv2.COLOR_RGB2BGR)
    videoWriter.write(frame)
    root.update() # 避免Tk窗口卡住
    time.sleep(1/fps)

videoWriter.release()

2.使用tkinter实现选择录制区域

程序使用tkinterCanvas画布, 绘制文字和图形。
create_text(x坐标, y坐标, text=文字) : 创建文本。
create_rectangle(左上角x坐标, 左上角y, 终止点x, 右下角y) : 创建矩形。

注意: 在Canvas中, 每创建一个图形对象, 如create_xxx(), 方法都会返回一个id, 作为这个图形对象的唯一标识
cv.delete(id)可删除已创建的图形。

def select_area():
    area=[0,0,0,0]
    rectangle_id=None
    def _press(event):
        area[0],area[1]=event.x,event.y
    def _move_label(event,text=None): # 在鼠标处显示文字提示
        nonlocal tip_id,rectangle_id
        text=text or "拖曳选择录制区域(%d, %d)"%(event.x,event.y)
        cv.delete(tip_id)
        tip_id=cv.create_text((event.x+8,event.y),
                              text=text, anchor = tk.W,justify = tk.LEFT)
    def _drag(event):
        nonlocal rectangle_id
        if rectangle_id is not None:cv.delete(rectangle_id)
        rectangle_id=cv.create_rectangle(area[0],area[1],
                                         event.x,event.y)
        _move_label(event)
    def _release(event):
        area[2],area[3]=event.x,event.y
        _move_label(event,"按Enter键接受, 拖曳可重新选择")
        window.bind_all('<Key-Return>',_accept)
    def _accept(event):
        window.destroy()

    window=tk.Tk()
    window.title("选择录制区域")
    window.protocol("WM_DELETE_WINDOW",lambda:None) # 不允许窗口被异常关闭
    cv=tk.Canvas(window,bg='white',cursor="target") # cursor设置控件的鼠标光标
    cv.pack(expand=True,fill=tk.BOTH)
    tip_id=cv.create_text((cv.winfo_screenwidth()//2,
                           cv.winfo_screenheight()//2),
                          text="拖曳选择录制区域",
                            anchor = tk.W,justify = tk.LEFT)
    window.attributes("-alpha",0.6) # 使窗口透明, 置顶, 全屏
    window.attributes("-topmost",True)
    window.attributes("-fullscreen",True)
    window.bind('<Button-1>',_press)
    window.bind('<Motion>',_move_label)
    window.bind('<B1-Motion>',_drag,)
    window.bind('<B1-ButtonRelease>',_release)

    while 1: # 循环, 等待窗口关闭
        try:
            window.update()
            time.sleep(0.01) # 减少cpu占用
        except tk.TclError:break # 窗口已关闭
    return area

将原先的代码

area=[0,0,root.winfo_screenwidth(),root.winfo_screenheight()]

改成:

area=select_area()

3.再次实现

上述程序有不少bug, 如time.sleep()固定延时0.1秒, 而写入帧的步骤消耗了一定时间, 导致了视频回放的速度比实际操作的速度要, 尤其在配置较低, 或录制区域较大时较明显。
改进程序
所以需要改进程序, 程序如下:

# --snip-- 之前部分省略
fps=10;flag=False
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
videoWriter = cv2.VideoWriter(filename, fourcc, fps,
                              (area[2]-area[0],area[3]-area[1]))
start=last=time.perf_counter() # 记录开始录制时时间
count=0
while not flag:
    image=ImageGrab.grab(area)
    frame = cv2.cvtColor(np.asarray(image),cv2.COLOR_RGB2BGR)
    videoWriter.write(frame)
    count+=1
    end=time.perf_counter()
    time.sleep(max(count/fps-(end-start),0)) # 计算延时
    print('fps:',str(1/(end-last)))
    last = end
    root.update()
videoWriter.release()

4.最终的程序

import tkinter as tk
import tkinter.filedialog as dialog
import time
from PIL import Image,ImageGrab
import numpy as np
import cv2

def select_area():
    area=[0,0,0,0]
    rectangle_id=None
    def _press(event):
        area[0],area[1]=event.x,event.y
    def _move_label(event,text=None):
        nonlocal tip_id,rectangle_id
        text=text or "拖曳选择录制区域(%d, %d)"%(event.x,event.y)
        cv.delete(tip_id)
        tip_id=cv.create_text((event.x+8,event.y),
                              text=text, anchor = tk.W,justify = tk.LEFT)
    def _drag(event):
        nonlocal rectangle_id
        if rectangle_id is not None:cv.delete(rectangle_id)
        rectangle_id=cv.create_rectangle(area[0],area[1],
                                         event.x,event.y)
        _move_label(event)
    def _release(event):
        area[2],area[3]=event.x,event.y
        _move_label(event,"按Enter键接受, 拖曳可重新选择")
        window.bind_all('<Key-Return>',_accept)
    def _accept(event):
        window.destroy()

    window=tk.Tk()
    window.title("选择录制区域")
    window.protocol("WM_DELETE_WINDOW",lambda:None)# 防止窗口被异常关闭
    cv=tk.Canvas(window,bg='white',cursor="target")
    cv.pack(expand=True,fill=tk.BOTH)
    tip_id=cv.create_text((cv.winfo_screenwidth()//2,
                           cv.winfo_screenheight()//2),
                          text="拖曳选择录制区域",
                            anchor = tk.W,justify = tk.LEFT)
    window.attributes("-alpha",0.6)
    window.attributes("-topmost",True)
    window.attributes("-fullscreen",True)
    window.bind('<Button-1>',_press)
    window.bind('<Motion>',_move_label)
    window.bind('<B1-Motion>',_drag,)
    window.bind('<B1-ButtonRelease>',_release)

    while 1:
        try:
            window.update()
            time.sleep(0.01)
        except tk.TclError:break # 窗口已关闭
    return area

def main():
    def _stop():
        nonlocal flag
        flag=True
        btn_stop['state']=tk.DISABLED
        root.title('录制已结束')

    root=tk.Tk()
    root.title('录屏工具')
    btn_stop=tk.Button(root,text='停止',command=_stop)
    btn_stop.pack()
    lbl_fps=tk.Label(root,text='fps:0')
    lbl_fps.pack(fill=tk.X)
    # 新增:使用户选择文件名
    filename=dialog.asksaveasfilename(master=root,
                filetypes=[("avi视频","*.avi"),("所有文件","*.*")],
                defaultextension='.avi')
    if not filename.strip():return

    area=select_area()

    root.title('录制中')
    fps=10;flag=False
    fourcc = cv2.VideoWriter_fourcc(*"mp4v")
    videoWriter = cv2.VideoWriter(filename, fourcc, fps,
                                  (area[2]-area[0],area[3]-area[1]))
    start=last=time.perf_counter()
    count=0
    while not flag:
        image=ImageGrab.grab(area)
        frame = cv2.cvtColor(np.asarray(image),cv2.COLOR_RGB2BGR)
        videoWriter.write(frame)
        count+=1
        end=time.perf_counter()
        time.sleep(max(count/fps-(end-start),0))
        try:
            lbl_fps['text']='fps:'+str(1/(end-last))
            last = end
            root.update()
        except tk.TclError:flag=True
    videoWriter.release()

if __name__=="__main__":main()

5.拓展: 创建gif动画

Python中创建gif动画, 使用了另一个图像处理库 – imageio
在原最终程序的基础上修改:

import imageio
# --snip-- 省略
def main():
    # --snip--
    filename=dialog.asksaveasfilename(master=root,
                filetypes=[("gif动画","*.gif"),("所有文件","*.*")],
                defaultextension='.gif')

    # --snip--
    lst_image=[] # 使用列表缓存帧, 提高速度 (也需要足够的内存空间)
    while not flag:
        image=ImageGrab.grab(area)
        lst_image.append(image)
        end=time.perf_counter()
        time.sleep(max(len(lst_image)/fps-(end-start),0))
        try:
            lbl_fps['text']='fps:'+str(1/(end-last))
            last=end
            root.update()
        except tk.TclError:flag=True
    imageio.mimsave(filename,lst_image,'GIF',
                    duration=(end-start)/len(lst_image))
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:11:32  更:2022-04-04 12:14:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/8 4:35:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码