• 调整视频帧率、分辨率


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

  • 相关阅读:
    Android—百度地图的简单使用
    【C++】一文带你吃透string的模拟实现 (万字详解)
    核医学重点归纳
    回调函数方式方法完整学习
    Dlang 并行化
    解决requests库中UnicodeError异常的问题
    Python 竟然可以绘制3D图像!
    云数据库 GaussDB(for Influx) 解密第十一期:让智能电网中时序数据处理更高效
    虹科动态 | 8月23日,虹科诚邀您参加上海电子设计创新大会(EDICON Across China 2022上海站)
    怎样防止缓存击穿?
  • 原文地址:https://blog.csdn.net/Ppandaer/article/details/140357686