段干叶农 发表于 2025-9-26 10:48:57

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

昨天遇到一个问题,我想把电视剧的片尾飞字字幕提取出来,然后将它贴到一张图片上,最终形成一个新的视频。
之前学过一点数字图像处理的皮毛,大概知道应该是一个怎样的处理流程。但我一行代码也不想写,于是求助于chatGPT。


我上传的图片是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}x{crop_clip.size}(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()    # 降低饱和度和明度阈值,捕获更淡、更暗的红色
      # upper_red1 = np.array() # 扩大低色相红色范围
      lower_red2 = np.array()# 扩大高色相红色范围
      upper_red2 = np.array()
      
      # lower_red1 = np.array()
      upper_red1 = np.array()
      # lower_red2 = np.array()
      # upper_red2 = np.array()

      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()
      upper_white = np.array()
      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(*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}")
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: AI辅助搞定视频字幕提取与视频背景替换