尝试使用 Python 截屏并录屏

( 本文的完整版地址在 https://www.ccgxk.com/?post=494

我在去年,曾经尝试过一个大胆的东西,就是使用 Python 写了个程序来录屏,以此给自己一种“期待感”,当时有没有效果我忘了,但是现在我又将这个项目捡了起来。

img

界面是长上面那个样子,集成了项目名设置、开始录制、结束录制、转换视频的功能,看起来没有那么潦草了,这个是一种录屏软件,当然,之后我估计还会把删除截的图片那一功能给搞一下,因为转换成视频后,那一大堆临时图片确实也没有什么必要存在了。


当然,这篇文章并不是 2022 年写的,而是 2024年06月27日 写的,之所以这样搞,是因为我不想通过 RSS 来惊扰人,毕竟我曾经搞过太多让自己牛逼的想法了,但无一例外以失败告终。如果这个尝试如之前一大堆被喧闹地宣布过的尝试一样,那肯定又是一个打击了。

哈哈,回想过去的几个月的落魄操作,我似乎感觉,如果我不是选择去进工厂来个日夜生死 10 小时的两班倒,或者找个工作,那么整个世界都在我的脑门上深深的蒙上一层令人窒息的惩罚。

没事,还有丘吉尔,丘吉尔的 Never give up 一直在鼓励我,甚至和丘吉尔和谈这种事情也不同意,就是一条路走到黑,赢得地铁上百余人的热心支持和整个礼堂百千人,整个国家千万人的异口同声,那种感觉实在是妙极了!!! 终有一天,我会一天就把这些我欠下的帐一并偿还!从此人生结束那数十年载的废物状态,迎接心里期望已久的大理石、花园、雕塑、果冻那种梦幻成功人士之生活!

厄,跑题有点远了哈。

然后,当时是认为,我一直是只有在感到自己在被监视的情况下、或者更准确一点,我认为我不是孤单一人,我是对某物有责任感时,我就会搞起更多的注意力来行我的事情。

举个例子,我经常使用摄像头来录“日志”,以便于我能持续的说话。为什么?因为如果我是对着一片空气,或者说是对着一面白墙来说话的话,那么我肯定是马上跑神,两分钟我都觉得悬,这也是我为什么在搞很多正业,尤其是学生时代搞作业和其他学习活动的时候不断的跑神。这些事情的原因是一样的,没有责任感,也没有另一种学者口中所述的互动感的话,那么我将没有任何的执行力。

当然,即便我现在是多么相信这个方法可以带给我比较强的执行力,但我的历史告诉我,执行力没有这么容易被解决,再过几日,这个方法和过去千千万万方法一样,会被扫入脑海里的垃圾堆里,以及给我自信上再划上一道不忍直视的裂痕。

但是,使用摄像头又有点过于麻烦,所以,我还是想起使用 Python 来一张一张的截图,最后再通过 OpenCV 合成一个视频。听起来很牛逼!

那么今天下午搞了一下午,终于把东西搞出来了,一个初步能使用的家伙,不知道会不会有效呢?现在我把代码给粘贴到下面,然后这种东西,也上传到 github 上吧,好久没有往我的 github 上搞过任何东西了。

代码如下,Python :

from PIL import ImageGrab, Image
import os
import time
import tkinter as tk
from tkinter import messagebox 
from datetime import datetime
import cv2

global_folder = 'myscreen'  # 文件夹
global_pic_name_i = 100000000  # 截屏图像名称序列
global_pic_time = 5000  # 截屏的时间差(单位为秒,但似乎并不准确)
global_afterid = None  # after id
global_unixtime = None  # unix 时间戳

# 截屏并保存的函数
def takeScrPic(folder, picName):
    screenshot_folder = folder  # 文件夹
    screenshot_name = picName  # 截屏图像名称
    new_width = 1080  # 新的宽度
    pic_quality = 45 # 截图的图片质量(1~95)
    if not os.path.exists(screenshot_folder):  # 文件夹不存在就建立一个
        os.makedirs(screenshot_folder)
    screenshot = ImageGrab.grab()  # 截屏命令 
    width, height = screenshot.size  # 宽高计算
    new_height = int(height * (new_width / width))   
    resized_screenshot = screenshot.resize((new_width, new_height))  # 根据新宽高来转换图片 
    resized_screenshot = resized_screenshot.convert('RGB')  # rgba 变成 rgb
    screenshot_path = os.path.join(screenshot_folder, screenshot_name)   
    resized_screenshot.save(screenshot_path, 'JPEG', quality=pic_quality)  # 保存到目录
    print(f'The resized screenshot is {new_width} wide x {new_height} tall and saved as {screenshot_path}')  # 打印结果

# 程序的界面
def makeUI():
    root = tk.Tk()
    root.title('screenRec')
    root.attributes('-topmost', True) # 将窗口置顶
    width = 200
    height = 170
    screenwidth = root.winfo_screenwidth()
    screenheight = root.winfo_screenheight()
    size_geo = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2 - 70)
    root.geometry(size_geo)
    # root.resizable(False, False) 
    label = tk.Label(root, text='输入项目名称,点击开始录制')  
    label.pack()
    itemNameInput = tk.Entry(root)  
    itemNameInput.pack(pady=10)  
    startButton = tk.Button(root, text='开始录制', command=clickDef)
    startButton.pack()
    endButton = tk.Button(root, text='停止录制', command=stopAfter, state='disabled')
    endButton.pack()
    convertButton = tk.Button(root, text='转换成视频', command=item2video, state='disabled')
    convertButton.pack()
    return root, label, itemNameInput, startButton, endButton, convertButton

# 开始录制的点击事件
def clickDef():
    global global_pic_name_i, global_afterid, global_unixtime
    itemProjName = itemNameInput.get()
    if not itemProjName:
        messagebox.showerror('error', '项目名称不能为空!')
        return 0
    if not global_unixtime:
        global_unixtime = int(time.time())
    takeScrPic(itemProjName, itemProjName + str(global_pic_name_i) + '.jpg')
    global_pic_name_i = global_pic_name_i + 1
    label.config(text='已截 ' + str(global_pic_name_i - 100000000) + ' 张,' + '已录制' + toGoodTime( int(time.time()) - global_unixtime ))
    global_afterid = root.after(global_pic_time, clickDef)
    endButton.config(state='normal')
    startButton.config(state='disabled')

# 将秒转化为分秒格式
def toGoodTime(sec):
    minutes = str(sec // 60)
    remaining_seconds = str(sec % 60)
    return str(' ' + minutes+' 分 '+ remaining_seconds +' 秒')

# 停止录制
def stopAfter():
    root.after_cancel(global_afterid)
    endButton.config(state='disabled')
    convertButton.config(state='normal')

# 点击 转化成视频 按钮
def item2video():
    label.config(text = '转化中,请稍后...')
    itemProjName = itemNameInput.get()
    convertButton.config(state='disabled')
    convert2Video(itemProjName, itemProjName + '.mp4')

# 将序列转化为视频
def convert2Video(picdir, outputname, myfps=25):
    im_dir = picdir
    save_video_dir = './video_output/'
    if not os.path.exists(save_video_dir):
        os.makedirs(save_video_dir)
    fps = myfps
    frames = sorted(os.listdir(im_dir))  # 提取序列
    img = cv2.imread(os.path.join(im_dir, frames[0]))
    img_size = (img.shape[1], img.shape[0])
    seq_name = outputname
    video_dir = os.path.join(save_video_dir, seq_name)
    # fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', 'V')
    fourcc = cv2.VideoWriter_fourcc(*'avc1')
    videowriter = cv2.VideoWriter(video_dir, fourcc, fps, img_size)
 
    for frame in frames:
        f_path = os.path.join(im_dir, frame)
        image = cv2.imread(f_path)
        videowriter.write(image)
        print(frame + ' has been written!')
    videowriter.release()
    label.config(text = '转化完成')
    # ↑↑↑↑↑ convert2Video('myscreen', 'out.mp4') 本函数的示例用法 ↑↑↑↑↑

root, label, itemNameInput, startButton, endButton, convertButton = makeUI()
root.mainloop()

平时不怎么用 Python,语法也不是很懂,在 AI 语言大模型的帮助下,现学现卖把东西搞了出来。

至于需要安装的第三方库,也就一个 cv2 我感觉。

pip3 install opencv-python
或
pip install opencv-python

也就那样吧。其实这个程序特别适合使用 命令行 来搞,但上一次我确实是使用的命令行,可惜可能因为会显得这玩意儿很无聊,所以当时有放弃了,这次我把写项目名称、录制、停止、转换成视频都集成到一个界面里了,这样应该也不会有什么奇怪的理由来拒绝了吧?我也不知道。

简单说一下程序吧。

其实也没什么可说的,截图程序是 文心一言 给我写的,我那按钮点点变成可用还是不可用的逻辑也是很明了的写在那里。

文件名使用的是 100000000 去加,是因为我不知道怎么写一个能自动 01、02、.... 11、12 这样前面自动补零的算法,所以偷了个懒,直接使用一个大数相加了,为什么要前面补零呢?如果不补零的话,后期它无法识别这个序列的顺序,它会从1、10、11、....、19、2、20、21.... 这种迷之排序。

root, label, itemNameInput, startButton, endButton, convertButton = makeUI()  

这一行的 Python 独家操作,我当时还是震撼了好久。这一行,makeUI() 这个程序最后,把这些元素给 return 出来了,然后在外面接受一下,就整个程序可访问了。

另外我也发现,Python 也没那么智能,在 JavaScript 里我直接把数字和字符串相加,就能直接连接到一起,而在 Python 里,必须用 str() 来字符串化一下数字才能用。其实我觉得 JavaScript 已经证明了这样的无必要性,Python 到底是在图啥???

另外还有一点想要吐槽的是:

fourcc = cv2.VideoWriter_fourcc(*'avc1')
videowriter = cv2.VideoWriter(video_dir, fourcc, fps, img_size)

我乒乒乓乓搞了一大堆对于最后输出视频的视频配置,最后告诉我不能设置比特率和压缩质量。OpenCV 了呀,最后生成的视频,体积还是那么大。不过没事,到时候我再搞个 ffmpeg 搞里头,让它转义一下,把已经没用的图片文件、大视频删除了即可。

别的就没有了。我把它也上传到我的 github 里吧。

https://github.com/kohunglee/rec_screen_py

posted @ 2024-06-27 23:14  独元殇  阅读(9)  评论(0编辑  收藏  举报