AI辅助搞定视频字幕提取与视频背景替换

昨天遇到一个问题,我想把电视剧的片尾飞字字幕提取出来,然后将它贴到一张图片上,最终形成一个新的视频。

之前学过一点数字图像处理的皮毛,大概知道应该是一个怎样的处理流程。但我一行代码也不想写,于是求助于chatGPT。

image
image

我上传的图片是4:3的,而原视频是16:9的,视频左右两侧有黑边,chatGPT在后续的对话中也识别到了这一子任务,于给出了以下处理流程。

步骤1:去除黑边,生成4:3比例的视频
步骤2:提取文字并替换背景

经过几轮对话与测试,得到了以下代码,完美地解决了我的需求。

点击查看代码
import cv2
import numpy as np
from moviepy.editor import VideoFileClip, ImageSequenceClip
import os
import sys

# 确保中文显示正常
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]

def crop_black_borders(input_video_path, cropped_video_path):
    """第一步:去除视频左右黑边,生成4:3比例的视频"""
    try:
        video = VideoFileClip(input_video_path)
    except Exception as e:
        print(f"错误:无法加载原始视频 {input_video_path},错误信息:{e}")
        return False
    
    w, h = video.size
    print(f"原始视频尺寸:{w}x{h}")
    
    # 计算需要裁剪的左右黑边宽度(目标4:3比例,高度不变,调整宽度)
    # 4:3比例的宽度 = 高度 * 4 / 3
    target_width = int(h * 4 / 3)
    if target_width > w:
        print("警告:原始视频宽度小于4:3比例所需宽度,可能无黑边或黑边在上下方")
        # 若宽度不足,直接使用原始宽度(避免错误)
        crop_clip = video
    else:
        # 计算左右各裁剪的宽度(总裁剪宽度 = 原始宽度 - 目标宽度)
        crop_pixels = (w - target_width) // 2
        # 裁剪左右黑边(x1:左边界,x2:右边界)
        crop_clip = video.crop(x1=crop_pixels, x2=w - crop_pixels)
        print(f"裁剪黑边:左右各裁剪 {crop_pixels} 像素,裁剪后尺寸:{crop_clip.size[0]}x{crop_clip.size[1]}(4:3)")
    
    # 保存裁剪后的视频(中间文件)
    crop_clip.write_videofile(cropped_video_path, codec="libx264", audio_codec="aac")
    video.close()
    return True

def process_video_with_new_bg(cropped_video_path, background_img_path, output_video_path):
    """使用指定背景图像替换视频背景,并保留原视频中的红色文字(优化版)"""
    
    # 加载背景并调整尺寸
    bg = cv2.imread(background_img_path)
    if bg is None:
        print(f"错误:无法加载背景图片 {background_img_path}")
        return False
    
    bg = cv2.cvtColor(bg, cv2.COLOR_BGR2RGB)

    # 加载裁剪后的视频
    try:
        video = VideoFileClip(cropped_video_path)
    except Exception as e:
        print(f"错误:无法加载裁剪后的视频 {cropped_video_path},错误信息:{e}")
        return False
    
    fps = video.fps
    w, h = video.size
    bg = cv2.resize(bg, (w, h))

    frames_out = []
    total_frames = int(video.duration * fps)
    print(f"开始处理视频,总帧数:{total_frames}")
    
    for i, frame in enumerate(video.iter_frames()):
        # 显示进度
        if i % 10 == 0:
            progress = f"\r处理进度:{i}/{total_frames} 帧"
            sys.stdout.write(progress)
            sys.stdout.flush()
            
        # 转换为HSV颜色空间
        hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
        
        # 1. 提取红色字幕主体(扩大红色检测范围)
        lower_red1 = np.array([0, 50, 30])    # 降低饱和度和明度阈值,捕获更淡、更暗的红色
        # upper_red1 = np.array([15, 255, 255]) # 扩大低色相红色范围
        lower_red2 = np.array([165, 50, 30])  # 扩大高色相红色范围
        upper_red2 = np.array([180, 255, 255])
        
        # lower_red1 = np.array([0, 70, 50])
        upper_red1 = np.array([10, 255, 255])
        # lower_red2 = np.array([170, 70, 50])
        # upper_red2 = np.array([180, 255, 255])

        mask_red = cv2.inRange(hsv, lower_red1, upper_red1) | cv2.inRange(hsv, lower_red2, upper_red2)
        
        # 增强红色提取:修复断裂,扩大范围
        kernel_red = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
        mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel_red, iterations=1)  # 填充内部空洞
        mask_red = cv2.dilate(mask_red, kernel_red, iterations=1)  # 轻微膨胀,扩大红色范围
        
        # 2. 对红色区域进行膨胀(确定"红色周围"的范围,即可能的白边区域)
        kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))  # 增大膨胀核,捕获更粗白边
        mask_red_dilated = cv2.dilate(mask_red, kernel_dilate, iterations=2)
        
        # 3. 提取红色周围的白色
        lower_white = np.array([0, 0, 200])
        upper_white = np.array([180, 40, 255])
        mask_white = cv2.inRange(hsv, lower_white, upper_white)
        mask_white_exterior = cv2.bitwise_and(mask_white, mask_red_dilated)  # 红色周围的白色
        
        # 4. 提取文字内部的白色(解决"口"字内部白边缺失问题)
        kernel_erode = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
        mask_red_eroded = cv2.erode(mask_red, kernel_erode, iterations=1)  # 腐蚀红色,得到内部区域
        mask_text_interior = cv2.subtract(mask_red, mask_red_eroded)  # 文字内部区域
        mask_white_interior = cv2.bitwise_and(mask_white, mask_text_interior)  # 文字内部的白色
        
        # 5. 合并三种掩膜:红色主体 + 红色周围白边 + 文字内部白边
        mask = cv2.bitwise_or(mask_red, mask_white_exterior)
        mask = cv2.bitwise_or(mask, mask_white_interior)
        
        # 6. 形态学优化(清理噪点,修复边缘)
        kernel_clean = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (6, 6))
        mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_clean, iterations=1)  # 填充小空洞
        mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_clean, iterations=1)   # 去除小噪点
        
        # 7. 转换为RGB掩膜并替换背景
        mask_rgb = np.stack([mask]*3, axis=2) // 255
        out_frame = np.where(mask_rgb == 1, frame, bg)
        frames_out.append(out_frame)

    print(f"\n视频处理完成,正在合成...")
    
    # 合成最终视频(保留原音频)
    final_clip = ImageSequenceClip(frames_out, fps=fps).set_audio(video.audio)
    final_clip.write_videofile(output_video_path, codec="libx264", audio_codec="aac")
    
    print(f"视频已保存至:{output_video_path}")
    return True

if __name__ == "__main__":
    # 文件路径设置(可根据实际情况修改)
    input_video = "大结局.mp4"         # 原始视频(带黑边)
    cropped_video = "a.mp4"             # 裁剪黑边后的4:3视频(中间文件)
    background_img = "尾帧.png"         # 新背景图
    output_video = "最终输出视频(本地版).mp4"  # 最终结果
    
    # 检查原始文件是否存在
    if not os.path.exists(input_video):
        print(f"错误:找不到原始视频 {input_video}")
        exit(1)
    if not os.path.exists(background_img):
        print(f"错误:找不到背景图片 {background_img}")
        exit(1)
    
    # 步骤1:去除黑边,生成4:3比例的视频
    print("===== 步骤1:裁剪黑边,生成4:3视频 =====")
    if not crop_black_borders(input_video, cropped_video):
        print("裁剪黑边失败,程序终止")
        exit(1)
    
    # 步骤2:提取文字并替换背景
    print("\n===== 步骤2:提取文字,替换背景 =====")
    process_video_with_new_bg(cropped_video, background_img, output_video)
    
    # 清理中间文件(可选)
    if os.path.exists(cropped_video):
        os.remove(cropped_video)
        print(f"已删除中间文件:{cropped_video}")
posted @ 2025-07-14 15:17  icuic  阅读(30)  评论(0)    收藏  举报