找回密码
 立即注册
首页 业界区 安全 AI辅助搞定视频字幕提取与视频背景替换 ...

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

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

2.png

我上传的图片是4:3的,而原视频是16:9的,视频左右两侧有黑边,chatGPT在后续的对话中也识别到了这一子任务,于给出了以下处理流程。
步骤1:去除黑边,生成4:3比例的视频
步骤2:提取文字并替换背景
经过几轮对话与测试,得到了以下代码,完美地解决了我的需求。
  点击查看代码
  1. import cv2
  2. import numpy as np
  3. from moviepy.editor import VideoFileClip, ImageSequenceClip
  4. import os
  5. import sys
  6. # 确保中文显示正常
  7. import matplotlib.pyplot as plt
  8. plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
  9. def crop_black_borders(input_video_path, cropped_video_path):
  10.     """第一步:去除视频左右黑边,生成4:3比例的视频"""
  11.     try:
  12.         video = VideoFileClip(input_video_path)
  13.     except Exception as e:
  14.         print(f"错误:无法加载原始视频 {input_video_path},错误信息:{e}")
  15.         return False
  16.    
  17.     w, h = video.size
  18.     print(f"原始视频尺寸:{w}x{h}")
  19.    
  20.     # 计算需要裁剪的左右黑边宽度(目标4:3比例,高度不变,调整宽度)
  21.     # 4:3比例的宽度 = 高度 * 4 / 3
  22.     target_width = int(h * 4 / 3)
  23.     if target_width > w:
  24.         print("警告:原始视频宽度小于4:3比例所需宽度,可能无黑边或黑边在上下方")
  25.         # 若宽度不足,直接使用原始宽度(避免错误)
  26.         crop_clip = video
  27.     else:
  28.         # 计算左右各裁剪的宽度(总裁剪宽度 = 原始宽度 - 目标宽度)
  29.         crop_pixels = (w - target_width) // 2
  30.         # 裁剪左右黑边(x1:左边界,x2:右边界)
  31.         crop_clip = video.crop(x1=crop_pixels, x2=w - crop_pixels)
  32.         print(f"裁剪黑边:左右各裁剪 {crop_pixels} 像素,裁剪后尺寸:{crop_clip.size[0]}x{crop_clip.size[1]}(4:3)")
  33.    
  34.     # 保存裁剪后的视频(中间文件)
  35.     crop_clip.write_videofile(cropped_video_path, codec="libx264", audio_codec="aac")
  36.     video.close()
  37.     return True
  38. def process_video_with_new_bg(cropped_video_path, background_img_path, output_video_path):
  39.     """使用指定背景图像替换视频背景,并保留原视频中的红色文字(优化版)"""
  40.    
  41.     # 加载背景并调整尺寸
  42.     bg = cv2.imread(background_img_path)
  43.     if bg is None:
  44.         print(f"错误:无法加载背景图片 {background_img_path}")
  45.         return False
  46.    
  47.     bg = cv2.cvtColor(bg, cv2.COLOR_BGR2RGB)
  48.     # 加载裁剪后的视频
  49.     try:
  50.         video = VideoFileClip(cropped_video_path)
  51.     except Exception as e:
  52.         print(f"错误:无法加载裁剪后的视频 {cropped_video_path},错误信息:{e}")
  53.         return False
  54.    
  55.     fps = video.fps
  56.     w, h = video.size
  57.     bg = cv2.resize(bg, (w, h))
  58.     frames_out = []
  59.     total_frames = int(video.duration * fps)
  60.     print(f"开始处理视频,总帧数:{total_frames}")
  61.    
  62.     for i, frame in enumerate(video.iter_frames()):
  63.         # 显示进度
  64.         if i % 10 == 0:
  65.             progress = f"\r处理进度:{i}/{total_frames} 帧"
  66.             sys.stdout.write(progress)
  67.             sys.stdout.flush()
  68.             
  69.         # 转换为HSV颜色空间
  70.         hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
  71.         
  72.         # 1. 提取红色字幕主体(扩大红色检测范围)
  73.         lower_red1 = np.array([0, 50, 30])    # 降低饱和度和明度阈值,捕获更淡、更暗的红色
  74.         # upper_red1 = np.array([15, 255, 255]) # 扩大低色相红色范围
  75.         lower_red2 = np.array([165, 50, 30])  # 扩大高色相红色范围
  76.         upper_red2 = np.array([180, 255, 255])
  77.         
  78.         # lower_red1 = np.array([0, 70, 50])
  79.         upper_red1 = np.array([10, 255, 255])
  80.         # lower_red2 = np.array([170, 70, 50])
  81.         # upper_red2 = np.array([180, 255, 255])
  82.         mask_red = cv2.inRange(hsv, lower_red1, upper_red1) | cv2.inRange(hsv, lower_red2, upper_red2)
  83.         
  84.         # 增强红色提取:修复断裂,扩大范围
  85.         kernel_red = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
  86.         mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel_red, iterations=1)  # 填充内部空洞
  87.         mask_red = cv2.dilate(mask_red, kernel_red, iterations=1)  # 轻微膨胀,扩大红色范围
  88.         
  89.         # 2. 对红色区域进行膨胀(确定"红色周围"的范围,即可能的白边区域)
  90.         kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))  # 增大膨胀核,捕获更粗白边
  91.         mask_red_dilated = cv2.dilate(mask_red, kernel_dilate, iterations=2)
  92.         
  93.         # 3. 提取红色周围的白色
  94.         lower_white = np.array([0, 0, 200])
  95.         upper_white = np.array([180, 40, 255])
  96.         mask_white = cv2.inRange(hsv, lower_white, upper_white)
  97.         mask_white_exterior = cv2.bitwise_and(mask_white, mask_red_dilated)  # 红色周围的白色
  98.         
  99.         # 4. 提取文字内部的白色(解决"口"字内部白边缺失问题)
  100.         kernel_erode = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
  101.         mask_red_eroded = cv2.erode(mask_red, kernel_erode, iterations=1)  # 腐蚀红色,得到内部区域
  102.         mask_text_interior = cv2.subtract(mask_red, mask_red_eroded)  # 文字内部区域
  103.         mask_white_interior = cv2.bitwise_and(mask_white, mask_text_interior)  # 文字内部的白色
  104.         
  105.         # 5. 合并三种掩膜:红色主体 + 红色周围白边 + 文字内部白边
  106.         mask = cv2.bitwise_or(mask_red, mask_white_exterior)
  107.         mask = cv2.bitwise_or(mask, mask_white_interior)
  108.         
  109.         # 6. 形态学优化(清理噪点,修复边缘)
  110.         kernel_clean = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (6, 6))
  111.         mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_clean, iterations=1)  # 填充小空洞
  112.         mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_clean, iterations=1)   # 去除小噪点
  113.         
  114.         # 7. 转换为RGB掩膜并替换背景
  115.         mask_rgb = np.stack([mask]*3, axis=2) // 255
  116.         out_frame = np.where(mask_rgb == 1, frame, bg)
  117.         frames_out.append(out_frame)
  118.     print(f"\n视频处理完成,正在合成...")
  119.    
  120.     # 合成最终视频(保留原音频)
  121.     final_clip = ImageSequenceClip(frames_out, fps=fps).set_audio(video.audio)
  122.     final_clip.write_videofile(output_video_path, codec="libx264", audio_codec="aac")
  123.    
  124.     print(f"视频已保存至:{output_video_path}")
  125.     return True
  126. if __name__ == "__main__":
  127.     # 文件路径设置(可根据实际情况修改)
  128.     input_video = "大结局.mp4"         # 原始视频(带黑边)
  129.     cropped_video = "a.mp4"             # 裁剪黑边后的4:3视频(中间文件)
  130.     background_img = "尾帧.png"         # 新背景图
  131.     output_video = "最终输出视频(本地版).mp4"  # 最终结果
  132.    
  133.     # 检查原始文件是否存在
  134.     if not os.path.exists(input_video):
  135.         print(f"错误:找不到原始视频 {input_video}")
  136.         exit(1)
  137.     if not os.path.exists(background_img):
  138.         print(f"错误:找不到背景图片 {background_img}")
  139.         exit(1)
  140.    
  141.     # 步骤1:去除黑边,生成4:3比例的视频
  142.     print("===== 步骤1:裁剪黑边,生成4:3视频 =====")
  143.     if not crop_black_borders(input_video, cropped_video):
  144.         print("裁剪黑边失败,程序终止")
  145.         exit(1)
  146.    
  147.     # 步骤2:提取文字并替换背景
  148.     print("\n===== 步骤2:提取文字,替换背景 =====")
  149.     process_video_with_new_bg(cropped_video, background_img, output_video)
  150.    
  151.     # 清理中间文件(可选)
  152.     if os.path.exists(cropped_video):
  153.         os.remove(cropped_video)
  154.         print(f"已删除中间文件:{cropped_video}")
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册