相信大家最近在B站看到了不少用1080p60帧看和用30帧看会看到不同画面的视频,其原理主要是因为B站视频的规则是遇到120帧的视频时,在生成60帧视频时,会将该视频每两帧为一组,取每组第二针作为60帧视频的其中一帧,而生成30帧视频则会将视频每四帧为一组,取每组第一帧作为30帧视频的其中一帧
知道原理了,代码自然就好实现了
import os, glob
import shutil
import cv2
from matplotlib import pyplot as plt
frame_rate2 = 0
frame_rate1 = 0
def extract_frames(video_path, fold_name):
# 使用OpenCV打开视频
the_video_fold = get_video_fold(video_path)
if not os.path.exists(the_video_fold + fold_name):
os.makedirs(the_video_fold + fold_name)
output_folder = the_video_fold + fold_name
video = cv2.VideoCapture(video_path)
# 初始化帧计数器
count = 0
# 遍历视频中的每一帧
while True:
# 读取下一帧
success, frame = video.read()
# 如果读取失败,说明已到视频末尾
if not success:
break
# 构造输出图片的文件名
frame_filename = f"{output_folder}/frame_{count:010d}.png"
# 保存帧为图片
cv2.imwrite(frame_filename, frame)
print("导出:" + frame_filename) # debug
# cel_filter(frame_filename) # 转描
# 帧计数器加1
count += 1
# 释放VideoCapture对象
video.release()
print("导出了" + str(count) + "帧。")
def images_to_video(image_folder1, image_folder2, output_video, frame_rate):
# 获取文件夹中的图片列表
images1 = glob.glob(f'{image_folder1}/*.png')
images2 = glob.glob(f'{image_folder2}/*.png')
# 按文件名排序,确保图片按正确的顺序读取
images1.sort()
images2.sort()
# 读取第一张图片以获取视频尺寸
frame1 = cv2.imread(images1[0])
height1, width1, layers1 = frame1.shape
frame2 = cv2.imread(images2[0])
height2, width2, layers2 = frame2.shape
height = max(height1, height2)
width = max(width1, width2)
# 定义视频编码和创建VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'avc1')
video = cv2.VideoWriter(output_video, fourcc, frame_rate, (width, height))
frame_num1 = 0
frame_num2 = 0
increment1 = frame_rate1 / 60 # 计算步长
increment2 = frame_rate2 / 60
while True:
if int(frame_num1) < len(images1):
video.write(cv2.imread(images1[int(frame_num1)]))
print("编码:" + images1[int(frame_num1)])
if int(frame_num2) < len(images2):
video.write(cv2.imread(images2[int(frame_num2)]))
print("编码:" + images2[int(frame_num2)])
if int(frame_num1) >= len(images1) and int(frame_num2) >= len(images2):
break
frame_num1 += increment1
frame_num2 += increment2
cv2.destroyAllWindows()
video.release()
def get_frame_rate(video_path):
# 使用OpenCV打开视频
video = cv2.VideoCapture(video_path)
# 获取视频的帧率
frame_rate = video.get(cv2.CAP_PROP_FPS)
# 释放VideoCapture对象
video.release()
return round(frame_rate, 2)
def remove_quotation_marks(the_video_path):
tmp = list(the_video_path)
if tmp[0] == '"' and tmp[len(the_video_path) - 1] == '"':
the_video_path = the_video_path[1:len(the_video_path) - 1]
return the_video_path
def get_video_fold(the_video_path):
lastSlash = 0
dir = 0
for c in the_video_path:
if c == '\\':
lastSlash = dir
dir = dir + 1
return the_video_path[:lastSlash + 1]
def is_mp4(the_video_path):
film_type = the_video_path[len(the_video_path) - 3:len(the_video_path)]
if film_type != "mp4":
print("视频格式必须是mp4格式")
# os.system("pause")
return False
return True
if __name__ == '__main__':
plt.rcParams['font.sans-serif'] = ['SimHei']
print("LoongLy大会员专属视频脚本")
video_path1 = input("输入文件路径或将视频拖拽到此处,注意,视频格式必须是mp4格式且目录中不能存在中文")
video_path1 = remove_quotation_marks(video_path1)
if not is_mp4(video_path1):
print("NO.1 Video ERROR")
exit(1)
video_path2 = input("输入文件路径或将视频拖拽到此处,注意,视频格式必须是mp4格式且目录中不能存在中文")
video_path2 = remove_quotation_marks(video_path2)
if not is_mp4(video_path2):
print("NO.2 Video ERROR")
exit(1)
video_fold1 = get_video_fold(video_path1)
video_fold2 = get_video_fold(video_path2)
frame_rate1 = get_frame_rate(video_path1)
frame_rate2 = get_frame_rate(video_path2)
print(f"'{video_path1}'\n 帧率: {frame_rate1} FPS")
print(f"'{video_path2}'\n 帧率: {frame_rate2} FPS")
print("开始导出帧到文件...")
extract_frames(video_path1, "img_fold1")
extract_frames(video_path2, "img_fold2")
images_to_video(video_fold1 + "img_fold1", video_fold2 + "img_fold2", video_fold1 + "output.mp4", 120)
print("成功导出为:" + video_fold1 + "output.mp4")
os.system('pause')
# LZX completed this script in 2024/07/23
# LZX-TC-2024-07-23-001