You've already forked FrameTour-RenderWorker
refactor(video): 重构视频裁切功能实现
- 将 crop_size 字段替换为 crop_scale 浮点数字段,支持缩放倍率控制 - 将 face_pos 字段重命名为 crop_pos,统一裁切位置控制 - 移除 zoom_cut 和 crop_size 字段,简化裁切参数 - 新增 _build_crop_filter 静态方法,统一构建裁切滤镜逻辑 - 优化裁切算法,支持按目标比例和倍率进行精确裁切 - 统一处理图像和视频的裁切逻辑,消除代码重复 - 添加 cropScale 参数的安全解析,防止非法数值导致错误 - 改进裁切位置解析,支持浮点数坐标并添加异常处理
This commit is contained in:
@@ -325,6 +325,43 @@ class RenderSegmentTsHandler(BaseHandler):
|
||||
finally:
|
||||
self.cleanup_work_dir(work_dir)
|
||||
|
||||
@staticmethod
|
||||
def _build_crop_filter(
|
||||
render_spec: 'RenderSpec',
|
||||
width: int,
|
||||
height: int,
|
||||
task_id: str = ''
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
构建裁切滤镜
|
||||
|
||||
crop_enable 时:以目标比例为基准,按 crop_scale 倍率裁切,crop_pos 控制位置(默认居中)。
|
||||
|
||||
Returns:
|
||||
crop 滤镜字符串,无需裁切时返回 None
|
||||
"""
|
||||
if render_spec.crop_enable:
|
||||
scale = render_spec.crop_scale
|
||||
target_ratio = width / height
|
||||
|
||||
# 解析裁切位置,默认居中
|
||||
fx, fy = 0.5, 0.5
|
||||
if render_spec.crop_pos:
|
||||
try:
|
||||
fx, fy = map(float, render_spec.crop_pos.split(','))
|
||||
except ValueError:
|
||||
logger.warning(f"[task:{task_id}] Invalid crop position: {render_spec.crop_pos}, using center")
|
||||
fx, fy = 0.5, 0.5
|
||||
|
||||
# 基准:源中最大的目标比例矩形,再除以倍率
|
||||
return (
|
||||
f"crop='min(iw,ih*{target_ratio})/{scale}':'min(ih,iw/{target_ratio})/{scale}':"
|
||||
f"'(iw-min(iw,ih*{target_ratio})/{scale})*{fx}':"
|
||||
f"'(ih-min(ih,iw/{target_ratio})/{scale})*{fy}'"
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def _convert_image_to_video(
|
||||
self,
|
||||
image_file: str,
|
||||
@@ -373,23 +410,10 @@ class RenderSegmentTsHandler(BaseHandler):
|
||||
# 构建滤镜:缩放填充到目标尺寸
|
||||
filters = []
|
||||
|
||||
# 裁切处理(与视频相同逻辑)
|
||||
if render_spec.crop_enable and render_spec.face_pos:
|
||||
try:
|
||||
fx, fy = map(float, render_spec.face_pos.split(','))
|
||||
target_ratio = width / height
|
||||
filters.append(
|
||||
f"crop='min(iw,ih*{target_ratio})':'min(ih,iw/{target_ratio})':"
|
||||
f"'(iw-min(iw,ih*{target_ratio}))*{fx}':"
|
||||
f"'(ih-min(ih,iw/{target_ratio}))*{fy}'"
|
||||
)
|
||||
except (ValueError, ZeroDivisionError):
|
||||
logger.warning(f"[task:{task_id}] Invalid face position: {render_spec.face_pos}")
|
||||
elif render_spec.zoom_cut:
|
||||
target_ratio = width / height
|
||||
filters.append(
|
||||
f"crop='min(iw,ih*{target_ratio})':'min(ih,iw/{target_ratio})'"
|
||||
)
|
||||
# 裁切处理
|
||||
crop_filter = self._build_crop_filter(render_spec, width, height, task_id)
|
||||
if crop_filter:
|
||||
filters.append(crop_filter)
|
||||
|
||||
# 缩放填充
|
||||
filters.append(
|
||||
@@ -711,26 +735,9 @@ class RenderSegmentTsHandler(BaseHandler):
|
||||
filters.append(f"lut3d='{lut_path}'")
|
||||
|
||||
# 3. 裁切处理
|
||||
if render_spec.crop_enable and render_spec.face_pos:
|
||||
# 根据人脸位置进行智能裁切
|
||||
try:
|
||||
fx, fy = map(float, render_spec.face_pos.split(','))
|
||||
# 计算裁切区域(保持输出比例)
|
||||
target_ratio = width / height
|
||||
# 假设裁切到目标比例
|
||||
filters.append(
|
||||
f"crop='min(iw,ih*{target_ratio})':'min(ih,iw/{target_ratio})':"
|
||||
f"'(iw-min(iw,ih*{target_ratio}))*{fx}':"
|
||||
f"'(ih-min(ih,iw/{target_ratio}))*{fy}'"
|
||||
)
|
||||
except (ValueError, ZeroDivisionError):
|
||||
logger.warning(f"Invalid face position: {render_spec.face_pos}")
|
||||
elif render_spec.zoom_cut:
|
||||
# 中心缩放裁切
|
||||
target_ratio = width / height
|
||||
filters.append(
|
||||
f"crop='min(iw,ih*{target_ratio})':'min(ih,iw/{target_ratio})'"
|
||||
)
|
||||
crop_filter = self._build_crop_filter(render_spec, width, height)
|
||||
if crop_filter:
|
||||
filters.append(crop_filter)
|
||||
|
||||
# 4. 缩放和填充
|
||||
scale_filter = (
|
||||
|
||||
Reference in New Issue
Block a user