【剪映小助手源码精讲】27_遮罩添加服务

52 阅读6分钟

第27章 遮罩添加服务

27.1 概述

遮罩添加服务是剪映小助手的核心功能之一,主要负责为视频片段添加各种类型的遮罩效果。遮罩可以创建特殊的视觉效果,如圆形头像、渐变边缘、形状裁剪等。该服务支持多种遮罩类型,包括线性遮罩、镜面遮罩、圆形遮罩、矩形遮罩、爱心遮罩和星形遮罩等。

27.2 核心实现

27.2.1 服务入口函数

遮罩添加服务的核心实现位于 src/service/add_masks.py 文件中:

async def add_masks(request: AddMasksRequest) -> AddMasksResponse:
    """为视频片段添加遮罩"""
    logger.info(f"为草稿添加遮罩: {request.draft_url}")
    
    # 参数验证
    if not request.draft_url:
        raise ValueError("草稿URL不能为空")
    
    if not request.segment_ids:
        raise ValueError("片段ID列表不能为空")
    
    if not request.mask_type:
        raise ValueError("遮罩类型不能为空")
    
    # 获取草稿缓存
    draft = await get_draft_cache(request.draft_url)
    if not draft:
        raise INVALID_DRAFT_URL
    
    # 查找遮罩类型
    mask_type = find_mask_type(request.mask_type)
    if not mask_type:
        raise INVALID_MASK_TYPE
    
    # 为每个片段添加遮罩
    mask_ids = []
    affected_segments = []
    
    for segment_id in request.segment_ids:
        try:
            # 查找视频片段
            video_segment = find_video_segment(draft, segment_id)
            if not video_segment:
                logger.warning(f"未找到片段: {segment_id}")
                continue
            
            # 创建遮罩
            mask = create_mask(mask_type, request, segment_id)
            
            # 添加遮罩到片段
            video_segment.mask = mask
            mask_ids.append(mask.id)
            affected_segments.append(segment_id)
            
            logger.info(f"成功为片段 {segment_id} 添加遮罩")
            
        except Exception as e:
            logger.error(f"为片段 {segment_id} 添加遮罩失败: {str(e)}")
            continue
    
    # 保存草稿
    await save_draft_cache(request.draft_url, draft)
    
    return AddMasksResponse(
        draft_url=request.draft_url,
        added_count=len(mask_ids),
        affected_segments=affected_segments,
        mask_ids=mask_ids
    )

27.2.2 遮罩类型查找

系统支持多种预定义的遮罩类型:

def find_mask_type(mask_type_name: str) -> dict:
    """查找遮罩类型"""
    mask_types = {
        "linear": {
            "name": "线性遮罩",
            "type": "linear",
            "default_params": {
                "angle": 0,
                "feather": 0.1
            }
        },
        "mirror": {
            "name": "镜面遮罩", 
            "type": "mirror",
            "default_params": {
                "axis": "horizontal",
                "feather": 0.1
            }
        },
        "circle": {
            "name": "圆形遮罩",
            "type": "circle", 
            "default_params": {
                "radius": 0.5,
                "feather": 0.1,
                "inverted": False
            }
        },
        "rectangle": {
            "name": "矩形遮罩",
            "type": "rectangle",
            "default_params": {
                "width": 1.0,
                "height": 1.0,
                "corner_radius": 0,
                "feather": 0.1,
                "inverted": False
            }
        },
        "heart": {
            "name": "爱心遮罩",
            "type": "heart",
            "default_params": {
                "size": 0.5,
                "feather": 0.1,
                "inverted": False
            }
        },
        "star": {
            "name": "星形遮罩",
            "type": "star",
            "default_params": {
                "points": 5,
                "outer_radius": 0.5,
                "inner_radius": 0.2,
                "feather": 0.1,
                "inverted": False
            }
        }
    }
    
    return mask_types.get(mask_type_name.lower())

27.2.3 遮罩创建

根据遮罩类型和参数创建遮罩对象:

def create_mask(mask_type: dict, request: AddMasksRequest, segment_id: str) -> Mask:
    """创建遮罩"""
    # 基础参数
    mask_params = mask_type["default_params"].copy()
    
    # 应用自定义参数
    if request.center_x is not None:
        mask_params["center_x"] = request.center_x
    if request.center_y is not None:
        mask_params["center_y"] = request.center_y
    if request.width is not None:
        mask_params["width"] = request.width
    if request.height is not None:
        mask_params["height"] = request.height
    if request.feather is not None:
        mask_params["feather"] = request.feather
    if request.rotation is not None:
        mask_params["rotation"] = request.rotation
    if request.inverted is not None:
        mask_params["inverted"] = request.inverted
    if request.corner_radius is not None:
        mask_params["corner_radius"] = request.corner_radius
    
    # 创建遮罩ID
    mask_id = f"mask_{mask_type['type']}_{uuid.uuid4().hex}"
    
    # 创建遮罩对象
    mask = Mask(
        id=mask_id,
        type=mask_type["type"],
        name=mask_type["name"],
        params=mask_params,
        segment_id=segment_id,
        enabled=True
    )
    
    return mask

27.2.4 视频片段查找

在草稿中查找指定的视频片段:

def find_video_segment(draft, segment_id: str):
    """查找视频片段"""
    # 遍历所有轨道
    for track in draft.tracks:
        # 只查找视频轨道
        if track.type != "video":
            continue
        
        # 查找片段
        for segment in track.segments:
            if segment.id == segment_id:
                return segment
    
    return None

27.3 遮罩类型与动画效果

27.3.1 线性遮罩

线性遮罩创建线性渐变效果,支持角度调整:

# 线性遮罩参数
linear_mask_params = {
    "type": "linear",
    "angle": 45,                    # 渐变角度
    "feather": 0.2,                # 羽化程度
    "center_x": 0.5,               # 中心点X坐标
    "center_y": 0.5                  # 中心点Y坐标
}

27.3.2 镜面遮罩

镜面遮罩创建镜像效果,支持水平或垂直镜像:

# 镜面遮罩参数
mirror_mask_params = {
    "type": "mirror",
    "axis": "horizontal",           # 镜像轴:horizontal/vertical
    "feather": 0.1,                 # 羽化程度
    "center_x": 0.5,
    "center_y": 0.5
}

27.3.3 圆形遮罩

圆形遮罩创建圆形裁剪效果,支持半径和反转设置:

# 圆形遮罩参数
circle_mask_params = {
    "type": "circle",
    "radius": 0.4,                  # 圆形半径
    "feather": 0.15,                # 羽化程度
    "inverted": False,              # 是否反转
    "center_x": 0.5,
    "center_y": 0.5
}

27.3.4 矩形遮罩

矩形遮罩创建矩形裁剪效果,支持圆角设置:

# 矩形遮罩参数
rectangle_mask_params = {
    "type": "rectangle",
    "width": 0.8,                   # 矩形宽度
    "height": 0.6,                  # 矩形高度
    "corner_radius": 0.1,           # 圆角半径
    "feather": 0.1,
    "inverted": False,
    "center_x": 0.5,
    "center_y": 0.5
}

27.3.5 爱心遮罩

爱心遮罩创建爱心形状的裁剪效果:

# 爱心遮罩参数
heart_mask_params = {
    "type": "heart",
    "size": 0.5,                    # 爱心大小
    "feather": 0.2,
    "inverted": False,
    "center_x": 0.5,
    "center_y": 0.5
}

27.3.6 星形遮罩

星形遮罩创建星形裁剪效果,支持角数设置:

# 星形遮罩参数
star_mask_params = {
    "type": "star",
    "points": 5,                    # 星形角数
    "outer_radius": 0.4,            # 外半径
    "inner_radius": 0.2,            # 内半径
    "feather": 0.15,
    "inverted": False,
    "center_x": 0.5,
    "center_y": 0.5
}

27.4 数据结构定义

27.4.1 请求参数模型

遮罩添加服务的请求参数定义在 src/schemas/add_masks.py 中:

class AddMasksRequest(BaseModel):
    """添加遮罩请求参数"""
    draft_url: str = Field(..., description="草稿URL")
    segment_ids: List[str] = Field(..., description="要添加遮罩的片段ID列表")
    mask_type: str = Field(..., description="遮罩类型名称")
    center_x: Optional[float] = Field(default=None, description="中心点X坐标(0-1)")
    center_y: Optional[float] = Field(default=None, description="中心点Y坐标(0-1)")
    width: Optional[float] = Field(default=None, description="宽度(0-1)")
    height: Optional[float] = Field(default=None, description="高度(0-1)")
    feather: Optional[float] = Field(default=None, description="羽化程度(0-1)")
    rotation: Optional[float] = Field(default=None, description="旋转角度(度)")
    inverted: Optional[bool] = Field(default=None, description="是否反转遮罩")
    corner_radius: Optional[float] = Field(default=None, description="圆角半径(矩形遮罩)")

27.4.2 响应参数模型

class AddMasksResponse(BaseModel):
    """添加遮罩响应参数"""
    draft_url: str = Field(..., description="草稿URL")
    added_count: int = Field(..., description="成功添加的遮罩数量")
    affected_segments: List[str] = Field(..., description="受影响的片段ID列表")
    mask_ids: List[str] = Field(..., description="遮罩ID列表")

27.5 异常处理

遮罩添加服务定义了完善的异常处理机制:

# 无效的遮罩类型
INVALID_MASK_TYPE = HTTPException(
    status_code=400,
    detail="无效的遮罩类型"
)

# 遮罩添加失败
MASK_ADD_FAILED = HTTPException(
    status_code=500,
    detail="遮罩添加失败"
)

# 未找到视频片段
VIDEO_SEGMENT_NOT_FOUND = HTTPException(
    status_code=404,
    detail="未找到指定的视频片段"
)

27.6 API接口定义

@router.post("/addMasks", response_model=AddMasksResponse)
async def add_masks_endpoint(request: AddMasksRequest):
    """添加遮罩"""
    try:
        return await add_masks(request)
    except Exception as e:
        logger.error(f"添加遮罩失败: {str(e)}")
        raise MASK_ADD_FAILED

27.7 使用示例

27.7.1 请求示例

{
    "draft_url": "capcut://draft/123456789",
    "segment_ids": ["video_segment_001", "video_segment_002"],
    "mask_type": "circle",
    "center_x": 0.5,
    "center_y": 0.5,
    "radius": 0.4,
    "feather": 0.15,
    "inverted": false
}

27.7.2 响应示例

{
    "draft_url": "capcut://draft/123456789",
    "added_count": 2,
    "affected_segments": ["video_segment_001", "video_segment_002"],
    "mask_ids": ["mask_circle_abc123", "mask_circle_def456"]
}

27.8 动画效果支持

遮罩服务还支持为遮罩添加动画效果:

27.8.1 遮罩动画类型

# 遮罩动画类型
class MaskAnimationType:
    FADE = "fade"                    # 淡入淡出
    SCALE = "scale"                  # 缩放
    ROTATE = "rotate"                # 旋转
    SLIDE = "slide"                  # 滑动
    MORPH = "morph"                  # 形变

27.8.2 动画参数配置

# 遮罩动画参数
mask_animation_params = {
    "animation_type": "fade",
    "start_time": 0,                 # 动画开始时间
    "duration": 1000000,             # 动画持续时间(微秒)
    "start_value": 0.0,              # 动画起始值
    "end_value": 1.0,                # 动画结束值
    "easing": "ease_in_out"           # 缓动函数
}

27.9 性能优化

遮罩添加服务采用了多种性能优化策略:

  1. 批量处理:支持一次为多个片段添加遮罩
  2. 缓存机制:遮罩类型定义缓存,避免重复查找
  3. 异步处理:使用异步函数处理遮罩添加
  4. 错误隔离:单个片段处理失败不影响其他片段

27.10 扩展性设计

遮罩服务具有良好的扩展性:

  • 遮罩类型扩展:易于添加新的遮罩类型
  • 参数扩展:支持动态添加新的遮罩参数
  • 动画扩展:支持自定义遮罩动画效果
  • 组合扩展:支持多个遮罩的组合使用

附录

代码仓库地址:

  • GitHub: https://github.com/Hommy-master/capcut-mate
  • Gitee: https://gitee.com/taohongmin-gitee/capcut-mate

接口文档地址:

  • API文档地址: https://docs.jcaigc.cn