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

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

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


当然,这篇文章并不是 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

尝试使用 Python 截屏并录屏的更多相关文章

  1. Android 禁止截屏、录屏 — 解决PopupWindow无法禁止录屏问题

    项目开发中,为了用户信息的安全,会有禁止页面被截屏.录屏的需求. 这类资料,在网上有很多,一般都是通过设置Activity的Flag解决,如: //禁止页面被截屏.录屏 getWindow().add ...

  2. 搭建前端监控系统(六)JS截屏和录屏篇

    怎样定位前端线上问题,一直以来,都是很头疼的问题,因为它发生于用户的一系列操作之后.错误的原因可能源于机型,网络环境,接口请求,复杂的操作行为等等,在我们想要去解决的时候很难复现出来,自然也就无法解决 ...

  3. android手机截屏、录屏

    1. 手动截屏,通过其他第三方软件发送截图,或者从手机取出截图 2. 使用命令截图,将截图保存到手机,再拉取到电脑 #!/bin/sh #运行 sh screenshot name picName=$ ...

  4. Chrome截图截满滑动栏,QQ截长屏,录屏

    1.Chrome截图截满滑动栏 一般我们截图都是用QQ的Ctrl+shift+A,但是网页不好截,这里我们可以用Chrome控制台来截全网页: F12或Ctrl+shift+i打开控制台: 点击一下控 ...

  5. react-native项目中禁止截屏与录屏

    在android/app/src/main/java/com/projname/MainActivity.java文件中的onCreate方法添加一下代码即可 import android.view. ...

  6. Android5.0免Root截屏,录屏

    http://blog.csdn.net/wds1181977/article/details/52174840 MediaProjection介绍 MediaProjection可以用来捕捉屏幕,具 ...

  7. 新手学习FFmpeg - 调用API完成录屏并进行H.264编码

    Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...

  8. Camtasia 录屏说明

    准备好要录制的屏幕或网页,在即将播放的位置暂停住. 从开始菜单位置“TechSmith”启动Camtasia Recorder 8,其界面如下所示: 注意,要录制系统声音,须在Recorded inp ...

  9. iOS ReplayKit 录屏 框架的使用

    在需要使用录屏的 地方 引入 头文件 #import <ReplayKit/ReplayKit.h> 添加代理 RPPreviewViewControllerDelegate 因为 iOS ...

  10. 再见,付费录屏软件!我用70行Python代码打造免费版!

随机推荐

  1. IM跨平台技术学习(四):蘑菇街基于Electron开发IM客户端的技术实践

    本文由蘑菇街前端技术团队分享,原题"Electron 从零到一",有修订和改动. 1.引言 本系列文章的前面几篇主要是从Electron技术本身进行了讨论(包括:第1篇初步了解El ...

  2. Solution -「AGC 031E」Snuke the Phantom Thief

    \(\mathscr{Description}\)   Link.   在一个网格图内有 \(n\) 个格子有正价值,给出四种限制:横 / 纵坐标不大于 / 不小于 \(a\) 的格子不能选超过 \( ...

  3. 《深入理解Mybatis原理》Mybatis中的缓存实现原理

    一级缓存实现 什么是一级缓存? 为什么使用一级缓存? 每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话. 在对数据库的一次会话中, ...

  4. 项目PMP之七项目成本管理

    项目PMP之七--项目成本管理   一.定义:在预算内管理成本:预测项目成本 核心理念:重点关注项目活动的成本:同时决策的影响.相关方的不同时间不同方法的测算 趋势:挣值进度(ES)逻辑:敏捷的方式则 ...

  5. Redis五种数据结构及真实应用场景

    前言 如果问你redis有哪些数据结构,你肯定可以一口气说出五种基本数据结构: String(字符串).Hash(哈希).List(列表).Set(集合).zset(有序集合) 你或许还知道它还有三种 ...

  6. SM9-密钥封装

    算法过程 代码实现 ///************************************************************************ // File name: ...

  7. java中的接口和枚举

    接口:可以简单的理解为规则 接口的基本语法: interface 接口名称 { 规则属性, 规则行为} 接口 接口其实是抽象的 接口的属性必须是固定的值,并且不能够修改的 属性和行为的访问权限必须是公 ...

  8. Luogu P11628 WC2025 猫粮 题解 [ 绿 ] [ 贪心 ] [ adhoc ] [ 鸽巢原理 ]

    猫粮:WC 诈骗题.我竟然能切 WC 的 T3 也是逆天了. 话说切了猫粮能变成猫娘吗 qwq. 思路 首先题目里有下面几点关键的性质: 所有猫粮质量总和等于所有猫要吃的质量总和. 优质的有 \(n\ ...

  9. led色块是什么,bin指值是什么

    色块其实就是色温的区域范围. LED的色温按标准是分段的, 既然是分段,就有一个最大值和最小值,在色坐标系中是一个,X和Y的坐标对应的也就有一个最大值和最小值,这样一个色温段就是以小块的区域,即所谓的 ...

  10. mysql where条件:某时间字段为今天的sql语句

    1.查询:注册时间为今天的所有用户数: select count(*) from customer where TO_DAYS(createtime) = TO_DAYS(NOW()) 2.获取当前时 ...