原目的是侦测文字游戏录屏的画面的变化以推送至负责OCR和翻译的第二个程序处理为字幕文件(就是自动生成文字游戏录屏字幕的项目的一个环节),不过,或许也可以有一些别的用途?注释非常非常之详细,可以看了注释做改动
By 某不知名的B站文字游戏博主 @符若_float
**注意 默认的threshold值(0.98)非常高,如非监控文字变动等微小变化请适度调低
import cv2 #使用了这个库进行fps读取和图片的读取
import os #使用这个库进行输入和输出
import time #使用这个库对程序的运行进行计时
from skimage.metrics import structural_similarity as ssim #使用这个库进行对于生成图片的细筛
'''
Author: AleryXiao
Date: 2022.10.15
Title: 抽取视频关键帧(可设置抽样频率和相似程度要求) v1.0
Content:
0. *两个用户可设定参数*(分别位于Line27和Line28)
a. 抽帧频率targetFPS (每秒钟抽取几帧)
b. 目标相似度threshold (低于此相似度的图片将会被保存)
更高的targetFPS会消耗更多的时间, 但是会生成更精确的时间码
**注意 默认的threshold值非常高,如非监控文字变动等微小变化请适度调低
1. 通过弹出的文件资源窗口选择一个或多个视频文件
2. 对于每个视频文件:
按给定的每秒帧数进行抽帧(cv2.VideoCapture) 抽取的帧存储在temp.jpg中
对于第0个满足条件的帧, 存储至cache中
对于其它满足条件的帧, 将之与cache作比较:
如果相似度大于threshold ->储存至本地和cache, 更新cache为这一图片, 输出储存信息
如果相速度小于threshold ->输出舍弃信息
(SSIM的运行速度较为有限, 或许可以考虑使用多线程运行?)
完成后, 关闭抽帧相机, 输出完成的信息
'''
targetFPS = 3 # 目标每秒选取的帧数(越低越快,相对的,时间轴精度就比较低)
threshold = 0.98 # 目标相似度(低于此相似度的图片才会被保存)
def frames2time(frames, fps): # 给定fps,将帧数转化为指定格式的视频时间(匹配popsub,小时数是假的2333,不能超过1小时,想要什么格式可以自己改ww)
fpm = fps*60
a = int(frames/fpm) # 分钟数(取整)
frames %= fpm # 余下帧数
b = frames/fps
return "0_"+f"{str(a):0>2}"+"_"+f"{b:0>5.2f}"
def video_to_frames(video_path, outPutDirName, frame_frequency):
if not os.path.exists(outPutDirName): # 如果文件目录不存在则创建目录
os.makedirs(outPutDirName)
camera = cv2.VideoCapture(video_path) # 创建一个相机,读取视频帧
frames = -1
T = int(frame_frequency/targetFPS) # 周期每秒3帧
while True:
frames = frames + 1 # 第frames帧(0base)
res, image = camera.read()
if not res:
print('Completed!')
break
if frames % T == 0: #按照周期抽帧,不到周期就跳过
cv2.imwrite(outPutDirName + '/temp.jpg', image)
img1 = cv2.imread(outPutDirName + '/temp.jpg') # 每周期保存一个临时文件
if frames==0:
cv2.imwrite(outPutDirName + '/' +frames2time(frames, frame_frequency)+'.jpg', image)
cache = img1 #每次遇到“新图片”就存储,并刷新缓存
print('OvO '+str(frames)+' OwO')
else:
if ssim(img1, cache, multichannel=True) > threshold:
print('___ '+str(frames)+' ___')
else:
cv2.imwrite(outPutDirName + '/' +frames2time(frames, frame_frequency)+'.jpg', image)
cache = img1 #每次遇到“新图片”就存储,并刷新缓存
print('OvO '+str(frames)+' OwO')
os.remove(outPutDirName + '/temp.jpg') # 删除临时文件
camera.release() # 释放相机
if __name__ == "__main__":
start=time.time()
from tkinter import filedialog as fd # 窗口式输入
import tkinter as tk
root = tk.Tk()
root.withdraw()
input_list = fd.askopenfilenames(filetypes=[('视频文件', ['.flv', '.mp4', '.mov'])]) # 可以批量选取!结果输出在相同目录的同名文件夹
for video_name in input_list:
videoCapture = cv2.VideoCapture(video_name)
print("正在处理视频: "+video_name)
fps = videoCapture.get(cv2.CAP_PROP_FPS) # 获取帧速率(frames per second)
video_to_frames(video_name, video_name[:-4], fps) # 输出在同一目录下的同名文件夹
print("视频"+video_name+"已处理完成~\n****************")
end=time.time()
print(f"用时共计{end-start}秒")
ヽ(✿゚▽゚)ノ
《母语是C草但是因为python的优良库生态努力地学习了异国他乡的语言》的感觉……作为python萌新的我能写到这里应该给自己鼓个掌!(掌声!)
这段代码感觉比较难受的一个地方就是那个temp文件,明明都是cv2的读取格式但是不知道怎么传参image到img2……这一部分可能是有一些性能损失的,不过最卡时间的地方应该还是那个相似度判断,就看想要怎么用啦。
我个人测试了一些视频,感觉这个方法算下来耗时和效果还是比较能够兼顾的,如果后面我有学到新的方法再对它进行改进吧~