调整视频帧率、分辨率

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

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,而是通过抽取帧来实现目标帧率。 计算每个帧应该抽取的时间戳,确保与原始视频的帧持续时间一致。

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值