求助:使用OpenCV Python实现视频平滑缩放时出现抖动问题
问题描述
我想用OpenCV给视频添加缩放效果,因为OpenCV没有内置缩放功能,所以尝试通过插值计算裁剪的宽高和坐标,裁剪帧后再resize回原视频分辨率1920×1080。但最终渲染的视频出现抖动,无法实现特定时间段内的平滑缩放。
现有代码
import cv2 video_path = 'inputTest.mp4' cap = cv2.VideoCapture(video_path) fps = int(cap.get(cv2.CAP_PROP_FPS)) fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (1920, 1080)) initialZoomValue={ 'initialZoomWidth': 1920, 'initialZoomHeight': 1080, 'initialZoomX': 0, 'initialZoomY': 0 } desiredValues = { 'zoomWidth': 1672, 'zoomHeight': 941, 'zoomX': 200, 'zoomY': 0 } def ease_out_quart(t): return 1 - (1 - t) ** 4 async def zoomInInterpolation(initialZoomValue, desiredZoom, start, end, index): t = (index - start) / (end - start) eased_t = ease_out_quart(t) interpolatedWidth = round(initialZoomValue['initialZoomWidth'] + eased_t * (desiredZoom['zoomWidth']['width'] - initialZoomValue['initialZoomWidth']), 2) interpolatedHeight = round(initialZoomValue['initialZoomHeight'] + eased_t * (desiredZoom['zoomHeight'] - initialZoomValue['initialZoomHeight']), 2) interpolatedX = round(initialZoomValue['initialZoomX'] + eased_t * (desiredZoom['zoomX'] - initialZoomValue['initialZoomX']), 2) interpolatedY = round(initialZoomValue['initialZoomY'] + eased_t * (desiredZoom['zoomY'] - initialZoomValue['initialZoomY']), 2) return {'interpolatedWidth': int(interpolatedWidth), 'interpolatedHeight': int(interpolatedHeight), 'interpolatedX': int(interpolatedX), 'interpolatedY': int(interpolatedY)} def generate_frame(): while cap.isOpened(): code, frame = cap.read() if code: yield frame else: print("bailsdfing") break for i, frame in enumerate(generate_frame()): if i >= 1 and i <= 60: interpolatedValues = zoomInInterpolation(initialZoomValue, desiredValues, 1, 60, i) crop = frame[interpolatedValues['interpolatedY']:(interpolatedValues['interpolatedHeight'] + interpolatedValues['interpolatedY']), interpolatedValues['interpolatedX']:(interpolatedValues['interpolatedWidth'] + interpolatedValues['interpolatedX'])] zoomedFrame = cv2.resize(crop,(1920, 1080), interpolation = cv2.INTER_CUBIC) out.write(zoomedFrame) # Release the video capture and close windows cap.release() cv2.destroyAllWindows()
问题现象
最终生成的视频存在明显抖动。
插值数值说明
- 插值数值的平滑曲线图表:显示参数随帧索引的连续变化趋势
- 取整后的插值数值图表:显示参数变为整数后出现的阶梯状突变
限制条件
OpenCV的裁剪操作仅接受整数坐标和尺寸,无法使用插值得到的小数数值。
解决方案
抖动的核心原因是提前对插值参数取整,导致每帧裁剪区域出现阶梯式跳变,而非连续平滑过渡。改用浮点精度的仿射变换替代“裁剪+resize”流程,可彻底规避整数限制带来的抖动问题。
修改后的代码示例:
import cv2 import numpy as np video_path = 'inputTest.mp4' cap = cv2.VideoCapture(video_path) fps = int(cap.get(cv2.CAP_PROP_FPS)) fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (1920, 1080)) # 初始状态:无缩放,偏移为0 initial_scale = 1.0 initial_offset_x = 0.0 initial_offset_y = 0.0 # 目标状态:计算缩放系数(原尺寸/目标裁剪尺寸) target_width = 1672 target_height = 941 target_scale_x = 1920 / target_width target_scale_y = 1080 / target_height # 目标偏移:将裁剪区域的x=200对应到输出帧的x=0,偏移量为 -200 * 缩放系数 target_offset_x = -200 * target_scale_x target_offset_y = 0.0 def ease_out_quart(t): return 1 - (1 - t) ** 4 def zoomInInterpolation(start_frame, end_frame, current_frame): t = (current_frame - start_frame) / (end_frame - start_frame) eased_t = ease_out_quart(t) # 插值计算缩放系数和偏移量(保留浮点精度) scale_x = initial_scale + eased_t * (target_scale_x - initial_scale) scale_y = initial_scale + eased_t * (target_scale_y - initial_scale) offset_x = initial_offset_x + eased_t * (target_offset_x - initial_offset_x) offset_y = initial_offset_y + eased_t * (target_offset_y - initial_offset_y) return scale_x, scale_y, offset_x, offset_y while cap.isOpened(): ret, frame = cap.read() if not ret: break frame_idx = int(cap.get(cv2.CAP_PROP_POS_FRAMES)) - 1 # 从0开始计数 if 1 <= frame_idx <= 60: scale_x, scale_y, offset_x, offset_y = zoomInInterpolation(1, 60, frame_idx) # 构建仿射变换矩阵:先缩放,再平移 M = np.float32([[scale_x, 0, offset_x], [0, scale_y, offset_y]]) # 应用仿射变换,保持输出尺寸为1920x1080,使用双三次插值 zoomed_frame = cv2.warpAffine(frame, M, (1920, 1080), flags=cv2.INTER_CUBIC) out.write(zoomed_frame) else: # 非缩放时间段直接输出原帧 out.write(frame) cap.release() out.release() cv2.destroyAllWindows()
关键改进点
- 用仿射变换替代“裁剪+resize”流程,浮点精度的变换参数保证每帧过渡连续,彻底消除抖动
- 保留缓动函数逻辑,确保缩放动画曲线符合预期
- 直接对整帧进行变换,规避裁剪操作的整数限制问题
内容的提问来源于stack exchange,提问作者Zaid
相关产品推荐
相关产品推荐

