调整视频帧率、分辨率

v1.0

问题:人脸检测太灵敏了,导致当画面中出现多个人脸时,画面反复跳各个人脸,闪动严重

python 复制代码
import cv2
import os
from tqdm import tqdm  # 进度条
import subprocess

# 加载人脸检测模型
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

def contains_face(frame):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    return len(faces) > 0

def crop_center(frame, crop_width, crop_height):
    height, width = frame.shape[:2]
    start_x = width//2 - crop_width//2
    start_y = height//2 - crop_height//2
    return frame[start_y:start_y+crop_height, start_x:start_x+crop_width]

def extract_audio(input_path, audio_path):
    subprocess.run(['ffmpeg', '-y', '-i', input_path, '-vn', '-acodec', 'copy', audio_path])

def merge_video_audio(video_path, audio_path, output_path):
    subprocess.run(['ffmpeg', '-y', '-i', video_path, '-i', audio_path, '-c:v', 'copy', '-c:a', 'aac', '-map', '0:v:0', '-map', '1:a:0', output_path])

def process_video(path, out_path, fps=25):
    print(f'[INFO] ===== process video from {path} to {out_path} =====')

    # 创建VideoCapture对象
    cap = cv2.VideoCapture(path)

    # 检查是否成功打开视频
    if not cap.isOpened():
        print("Error opening video file")
        return

    frame_rate = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # 获取视频的总帧数
    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # 获取视频的宽度
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # 获取视频的高度
    print("原视频帧率=", frame_rate, "fps")
    print("原视频帧数=", total_frames)
    print("原视频尺寸=", frame_width, "x", frame_height)
    if frame_rate != fps:
        cap.set(cv2.CAP_PROP_FPS, fps)
        frame_rate = fps

    # 创建VideoWriter对象
    fourcc = cv2.VideoWriter_fourcc(*'mp4v') 
    out = cv2.VideoWriter(out_path, fourcc, fps, (512, 512))

    frame_count = 0

    # 创建一个tqdm进度条
    pbar = tqdm(total=total_frames, ncols=70, unit='frame')

    while cap.isOpened():
        ret, frame = cap.read()
        if ret:
            if contains_face(frame) and frame_count % (frame_rate // fps) == 0:
                frame = crop_center(frame, 512, 512)
                out.write(frame)
            frame_count += 1
            pbar.update(1)  # 更新进度条
        else:
            break

    pbar.close()  # 关闭进度条

    cap.release()
    out.release()
# 你的代码中存在一些可能影响最终视频与音频同步的潜在问题:

# 当你使用cap.set(cv2.CAP_PROP_FPS, fps)尝试改变视频的帧率,这实际上并不会改变视频的内部帧率属性。CAP_PROP_FPS是一个读取属性,而不是写入属性。因此,你不能依靠这种方法来更改视频的帧率。
# 当你基于frame_rate // fps的模运算来决定是否抽取帧时,这假定了frame_rate可以被fps整除。如果frame_rate和fps不是整数倍的关系,那么视频的总时长可能会与原视频不完全匹配,这可能导致音频和视频的轻微不同步。
# 为了避免这些问题,你应该:

# 不尝试设置CAP_PROP_FPS,而是通过抽取帧来实现目标帧率。
# 计算每个帧应该抽取的时间戳,确保与原始视频的帧持续时间一致。
# def process_video(path, out_path, target_fps=25):
#     print(f'[INFO] ===== process video from {path} to {out_path} =====')

#     # 创建VideoCapture对象
#     cap = cv2.VideoCapture(path)

#     # 检查是否成功打开视频
#     if not cap.isOpened():
#         print("Error opening video file")
#         return

#     original_fps = cap.get(cv2.CAP_PROP_FPS)
#     total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
#     frame_duration = 1 / original_fps  # 原始视频每帧的持续时间

#     # 创建VideoWriter对象
#     fourcc = cv2.VideoWriter_fourcc(*'mp4v') 
#     out = cv2.VideoWriter(out_path, fourcc, target_fps, (512, 512))

#     frame_count = 0
#     target_frame_time = 0  # 目标帧的累积时间

#     # 创建一个tqdm进度条
#     pbar = tqdm(total=total_frames, ncols=70, unit='frame')

#     while cap.isOpened():
#         ret, frame = cap.read()
#         if ret:
#             current_time = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000  # 当前帧的时间戳(秒)
            
#             # 如果当前时间等于或超过目标帧时间,则写入帧
#             if current_time >= target_frame_time:
#                 if contains_face(frame):
#                     frame = crop_center(frame, 512, 512)
#                     out.write(frame)
#                 target_frame_time += frame_duration * (original_fps / target_fps)  # 下一帧的目标时间

#             frame_count += 1
#             pbar.update(1)  # 更新进度条
#         else:
#             break

#     pbar.close()  # 关闭进度条

#     cap.release()
#     out.release()
    print(f'[INFO] ===== processed video =====')

    # 打开处理后的视频,获取总帧数、帧率和视频尺寸
    cap_out = cv2.VideoCapture(out_path)
    total_frames_out = int(cap_out.get(cv2.CAP_PROP_FRAME_COUNT))
    frame_rate_out = cap_out.get(cv2.CAP_PROP_FPS)
    frame_width = int(cap_out.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap_out.get(cv2.CAP_PROP_FRAME_HEIGHT))
    print(f'处理后的视频帧率: {frame_rate_out} fps')
    print(f'处理后的视频帧数: {total_frames_out}')
    print(f'处理后的视频尺寸: {frame_width}x{frame_height}')
    cap_out.release()

def process_video_with_audio(input_path, output_path):
    audio_path = output_path.replace('.mp4', '_audio.aac')
    output_with_audio_path = output_path.replace('.mp4', '_with_audio.mp4')

    extract_audio(input_path, audio_path)
    process_video(input_path, output_path)
    merge_video_audio(output_path, audio_path, output_with_audio_path)

    # 删除临时文件
    os.remove(output_path)
    os.remove(audio_path)

    return output_with_audio_path

if __name__ == "__main__":
    for i in tqdm(range(1, 75), desc="Processing videos"):
        input_path = f"data/{i}/{i}.mp4"
        output_path = f"data/{i}/{i}_fc.mp4"

        if not os.path.isfile(input_path):
            print(f"文件 {input_path} 不存在.")
            continue
        
        final_output_path = process_video_with_audio(input_path, output_path)
        print(f"处理后的视频已保存至 {final_output_path}")

通过调整帧率和裁剪视频来处理视频,同时保留音频并保持音频和视频的同步。为了确保音频和视频的同步,关键点在于保持视频帧的持续时间和原始视频一致。当减少帧率时,重要的是要确保抽取的帧在时间上均匀分布,这样每个帧代表的时间间隔与原始视频相同,从而保持音频的同步。

在代码中,通过以下方式实现了这一点:

使用cv2.CAP_PROP_FPS属性读取原始视频的帧率。 设置目标帧率fps(例如25fps)。 在循环中,只写入那些在时间上符合目标帧率的帧。你通过frame_count % (frame_rate // fps) == 0这一条件实现,这确保了只有在正确的时间间隔下才抽取一帧。 然而,你的代码中存在一些可能影响最终视频与音频同步的潜在问题:

当使用cap.set(cv2.CAP_PROP_FPS, fps)尝试改变视频的帧率,这实际上并不会改变视频的内部帧率属性。CAP_PROP_FPS是一个读取属性,而不是写入属性。因此,你不能依靠这种方法来更改视频的帧率。 当你基于frame_rate // fps的模运算来决定是否抽取帧时,这假定了frame_rate可以被fps整除。如果frame_rate和fps不是整数倍的关系,那么视频的总时长可能会与原视频不完全匹配,这可能导致音频和视频的轻微不同步。 为了避免这些问题,你应该:

不尝试设置CAP_PROP_FPS,而是通过抽取帧来实现目标帧率。 计算每个帧应该抽取的时间戳,确保与原始视频的帧持续时间一致。

相关推荐
长和信泰光伏储能2 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂2 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study3 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境4 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境4 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1234 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102164 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb005 小时前
一个项目带你入门AI应用开发01
python
0566465 小时前
Python康复训练——常用标准库
开发语言·python·学习
weixin_495248405 小时前
短剧视频翻译配音指南:古装、甜宠与悬疑如何本地化?
音视频