(1)遇到问题的现象描述
一段mp4绿幕背景视频,我想替换为其他背景图,第一步抠图的时候发现结果边缘还有绿色虚线,两手空隙直接也有少量绿幕背景图没扣掉,导致替换的背景图视频效果不佳。
(2)问题相关代码,运行结果,没有报错
import sys
import cv2
import numpy as np
import os
from pathlib import Path
from typing import Tuple, Optional
# ================== 配置常量区 ==================
DEBUG_MODE = True # 开启调试窗口和实时参数调节
GREEN_RANGE: Tuple[np.ndarray, np.ndarray] = (
np.array([25, 40, 40]), # 最低HSV阈值 (H, S, V)
np.array([100, 255, 255]) # 最高HSV阈值
)
MORPH_KERNEL_SIZE: Tuple[int, int] = (9, 9) # 形态学核尺寸
MORPH_CLOSE_ITERS: int = 3 # 闭运算迭代次数
MORPH_OPEN_ITERS: int = 2 # 开运算迭代次数
EDGE_BLUR_SIZE: int = 15 # 边缘羽化强度(必须奇数)
# ================================================
def create_debug_windows():
"""创建调试窗口和滑动条(仅在DEBUG_MODE开启时生效)"""
if DEBUG_MODE:
cv2.namedWindow('Debug Controls')
cv2.createTrackbar('H Min', 'Debug Controls', GREEN_RANGE[0][0], 180, lambda x: None)
cv2.createTrackbar('H Max', 'Debug Controls', GREEN_RANGE[1][0], 180, lambda x: None)
cv2.createTrackbar('S Min', 'Debug Controls', GREEN_RANGE[0][1], 255, lambda x: None)
cv2.createTrackbar('V Min', 'Debug Controls', GREEN_RANGE[0][2], 255, lambda x: None)
def get_dynamic_hsv_range():
"""实时获取调试参数(调试模式专用)"""
if DEBUG_MODE:
return (
np.array([
cv2.getTrackbarPos('H Min', 'Debug Controls'),
cv2.getTrackbarPos('S Min', 'Debug Controls'),
cv2.getTrackbarPos('V Min', 'Debug Controls')
]),
np.array([GREEN_RANGE[1][0], 255, 255])
)
return GREEN_RANGE
def dynamic_hsv_adjust(hsv_frame: np.ndarray) -> np.ndarray:
"""动态亮度补偿(解决阴影/高光问题)"""
v_channel = hsv_frame[:, :, 2].astype(np.float32)
v_mean = np.mean(v_channel)
contrast = 1.2 if v_mean < 100 else 0.8 # 自动对比度调整
hsv_frame[:, :, 2] = np.clip(v_channel * contrast, 0, 255).astype(np.uint8)
return hsv_frame
def apply_morphology(mask: np.ndarray) -> np.ndarray:
"""增强型形态学处理"""
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, MORPH_KERNEL_SIZE)
mask = cv2.GaussianBlur(mask, (5, 5), 0)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=MORPH_CLOSE_ITERS)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=MORPH_OPEN_ITERS)
return cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=1)
def process_frame(frame: np.ndarray) -> Tuple[np.ndarray, Optional[np.ndarray], Optional[np.ndarray]]:
"""核心处理流程(返回处理帧 + 调试数据)"""
# HSV颜色空间处理
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv = dynamic_hsv_adjust(hsv)
# 获取动态阈值
lower, upper = get_dynamic_hsv_range()
mask = cv2.inRange(hsv, lower, upper)
# 形态学优化
mask = apply_morphology(mask)
# 边缘羽化处理
alpha = cv2.merge([mask] * 3).astype(float) / 255.0
alpha = cv2.GaussianBlur(alpha, (EDGE_BLUR_SIZE, EDGE_BLUR_SIZE), 0)
# 前景合成(替换背景在此修改)
background = np.zeros_like(frame) # 替换为实际背景
foreground = cv2.multiply(frame.astype(float), 1 - alpha)
processed = cv2.add(foreground, cv2.multiply(background.astype(float), alpha)).astype(np.uint8)
# 返回处理结果和调试数据
if DEBUG_MODE:
return processed, hsv, mask
return processed, None, None
def main():
# 初始化调试窗口
if DEBUG_MODE:
create_debug_windows()
# 路径配置(根据实际路径修改)
current_dir = Path(__file__).resolve().parent
input_video = current_dir / "../../video_ppt/assets/input_videos/20250407/3283dcc22420686162e32102af8304bb.mp4"
output_video = current_dir / "../../video_ppt/output/temp/20250407/v1_6-10.mp4"
# 路径验证
if not input_video.exists():
raise FileNotFoundError(f"输入视频不存在:{input_video}")
output_video.parent.mkdir(parents=True, exist_ok=True)
# 视频流初始化
cap = cv2.VideoCapture(str(input_video))
if not cap.isOpened():
raise RuntimeError("视频打开失败")
# 获取视频参数(添加总帧数获取)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
current_frame = 0
# 进度条显示函数
def print_progress(current: int, total: int):
bar_length = 30
progress = current / total
block = int(round(bar_length * progress))
text = "\rProgress: [{}{}] {:.1f}% Frame: {}/{}".format(
"=" * block, " " * (bar_length - block),
progress * 100, current, total
)
sys.stdout.write(text)
sys.stdout.flush()
# 视频写入器配置(兼容OpenCV 4.5.x)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(str(output_video), fourcc, fps, (frame_width, frame_height), isColor=True)
try:
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
current_frame += 1 # 更新当前帧计数
# 显示进度条(每帧更新,但控制台每5%更新一次)
if total_frames > 0 and (current_frame % max(1, total_frames // 200) == 0 or current_frame == total_frames):
print_progress(current_frame, total_frames)
# 处理帧并获取调试数据
processed_frame, hsv_debug, mask_debug = process_frame(frame)
# 调试显示
if DEBUG_MODE:
cv2.imshow('Final Output', processed_frame)
if hsv_debug is not None:
cv2.imshow('HSV Debug', cv2.cvtColor(hsv_debug, cv2.COLOR_HSV2BGR))
if mask_debug is not None:
cv2.imshow('Mask Debug', mask_debug)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 写入处理后的帧
out.write(processed_frame)
# 处理完成后换行
if total_frames > 0:
print("\n处理完成!")
finally:
cap.release()
out.release()
if DEBUG_MODE:
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
(3)我的初步解决思路是
开启Debug调整HSV各通道分布值,观察效果,但是无论如何调整都没能解决问题
# ================== 配置常量区 ==================
DEBUG_MODE = True # 开启调试窗口和实时参数调节
GREEN_RANGE: Tuple[np.ndarray, np.ndarray] = (
np.array([25, 40, 40]), # 最低HSV阈值 (H, S, V)
np.array([100, 255, 255]) # 最高HSV阈值
)
MORPH_KERNEL_SIZE: Tuple[int, int] = (9, 9) # 形态学核尺寸
MORPH_CLOSE_ITERS: int = 3 # 闭运算迭代次数
MORPH_OPEN_ITERS: int = 2 # 开运算迭代次数
EDGE_BLUR_SIZE: int = 15 # 边缘羽化强度(必须奇数)
# ================================================
(4)操作系统、软件版本
操作系统:windows10
python3.9 conda环境 requirements.txt如下:
# three_nine_env
# 核心依赖
numpy==1.21.5 # 确保与 OpenCV 和 MoviePy 兼容
opencv-python==4.5.5.64 # 视频处理核心库
moviepy==1.0.3 # 视频合成库(适配低版本依赖)
python-pptx==0.6.20 # PPT文件处理库
# MoviePy 依赖链(精确版本锁定)
imageio[ffmpeg]==2.31.6 # 视频编解码支持(与 moviepy 1.0.3 兼容)
decorator==4.4.2 # 必须匹配 moviepy 1.0.3 的依赖范围(<5.0)
tqdm==4.65.0 # 进度条工具
proglog==0.1.9 # moviepy 1.x 的隐藏依赖(不指定会触发版本冲突)
# 视频识别语音转文本内容
zhconv==1.4.3
openai-whisper==20240930
# 高质量的视频输出插件
opencv-contrib-python==4.5.5.64 # 包含ximgproc模块