如何将16:9视频转为9:16并确保说话人始终在画面内?
问题分析与修复方案
原代码核心问题
- 使用通用目标检测模型
yolov8n.pt,未针对人脸/人物优化,容易误识别其他高置信度物体 - 中心点计算错误:
x_center = x2 - x1、y_center = y2 - y1实际是计算目标宽度/高度,而非中心坐标 - 未过滤目标类别,可能选中非人物/人脸物体作为ROI
- 逐帧裁剪无平滑处理,画面易出现抖动
- 未处理检测失效场景,导致部分帧丢失
修复后的代码
from ultralytics import YOLO from moviepy.editor import VideoFileClip, concatenate_videoclips from moviepy.video.fx.crop import crop import numpy as np # 加载YOLOv8人脸专用检测模型(比通用模型更精准) model = YOLO("yolov8n-face.pt") # 加载输入视频 clip = VideoFileClip("short_test.mp4") original_w, original_h = clip.size target_aspect_ratio = 9 / 16 # 目标裁剪比例 target_w = int(original_h * target_aspect_ratio) target_h = original_h tacked_clips = [] prev_x_center = original_w // 2 # 初始化上一帧中心点,防止检测失效时画面失控 prev_y_center = original_h // 2 smooth_factor = 0.7 # 平滑系数,越小画面越稳定(0-1之间) for frame_no, frame in enumerate(clip.iter_frames()): results = model(frame) current_x_center = prev_x_center current_y_center = prev_y_center # 只保留人脸检测结果,过滤其他类别 if results[0].boxes: # 筛选人脸(YOLOv8-face的类别id为0) face_boxes = [box for box in results[0].boxes if box.cls == 0] if face_boxes: # 取置信度最高的人脸 main_face = max(face_boxes, key=lambda x: x.conf) x1, y1, x2, y2 = [int(val) for val in main_face.xyxy[0].tolist()] # 计算人脸中心点,同时调整y轴位置预留身体空间(比如把中心点上移,让画面包含更多身体) face_x_center = (x1 + x2) / 2 face_y_center = (y1 + y2) / 2 - (y2 - y1) * 0.3 # 上移人脸高度的30%,留出身体区域 # 用移动平均平滑中心点,避免画面抖动 current_x_center = smooth_factor * face_x_center + (1 - smooth_factor) * prev_x_center current_y_center = smooth_factor * face_y_center + (1 - smooth_factor) * prev_y_center # 限制中心点在合法范围内,防止裁剪区域超出原视频边界 current_x_center = np.clip(current_x_center, target_w/2, original_w - target_w/2) current_y_center = np.clip(current_y_center, target_h/2, original_h - target_h/2) # 获取当前帧的时间片段 start_time = frame_no / clip.fps end_time = (frame_no + 1) / clip.fps subclip = clip.subclip(start_time, end_time) # 应用裁剪 cropped_clip = crop( subclip, x_center=int(current_x_center), y_center=int(current_y_center), width=target_w, height=target_h ) tacked_clips.append(cropped_clip) # 更新上一帧中心点 prev_x_center, prev_y_center = current_x_center, current_y_center # 拼接所有裁剪后的片段 reframed_clip = concatenate_videoclips(tacked_clips, method="compose") reframed_clip.write_videofile("output_video.mp4", codec="libx264")
关键修改说明
- 替换专用模型:使用
yolov8n-face.pt,专门针对人脸优化,检测精度更高,避免误识别其他物体 - 修正中心点计算:改为
(x1+x2)/2和(y1+y2)/2,并调整y轴位置预留身体空间 - 添加平滑处理:用移动平均算法稳定中心点,解决逐帧裁剪的抖动问题
- 边界限制:用
np.clip确保裁剪区域不会超出原视频范围 - 失效处理:检测不到人脸时沿用前一帧的中心点,避免画面跳变
- 类别过滤:只保留人脸检测结果,确保ROI始终指向说话人
内容的提问来源于stack exchange,提问作者user16638844
相关产品推荐
相关产品推荐

