标题:
在 Timeline Studio 中,我实现了一套浏览器本地实物描边功能。它可以识别商品、鞋子、玩具等物体,生成 Alpha 蒙版,并在图片或视频中渲染可调节的描边效果。
项目完整代码已开源:
https://github.com/MartinDelophy/ai-video-editor
整个过程在浏览器内完成,不需要把用户素材上传到服务器。
整体实现思路
实物描边不能只依靠边缘检测。边缘检测会同时提取人物、背景和纹理,无法确定用户真正想处理的物体。
因此,项目将处理流程拆成四个阶段:
目标检测
↓
目标身份锁定
↓
提示式物体分割
↓
描边材质渲染
目标检测负责找到候选物体,提示式分割负责生成像素级蒙版。对于视频,再通过光流传播和稀疏锚点分析保持前后帧稳定。
使用 Worker 执行目标检测
模型推理放在 Web Worker 中,避免阻塞 React 界面和时间线交互。
主线程通过消息发送待分析画面:
worker.postMessage({
requestId,
type: "detect",
blob,
scoreThreshold: 0.24,
});
Worker 初始化 ONNX Runtime 会话时优先使用 WebGPU,并提供 WASM 回退:
async function createSession(modelBytes) {
if (navigator.gpu) {
try {
return await ort.InferenceSession.create(modelBytes, {
executionProviders: ["webgpu"],
graphOptimizationLevel: "all",
});
} catch (error) {
console.warn("WebGPU 初始化失败,回退 WASM");
}
}
return ort.InferenceSession.create(modelBytes, {
executionProviders: ["wasm"],
graphOptimizationLevel: "all",
});
}
这样既可以利用支持 WebGPU 的设备,也能兼容普通浏览器环境。
锁定用户真正想处理的物体
检测模型可能同时返回多个候选框。项目不会直接选择置信度最高的结果,而是综合计算:
- 检测置信度
- 物体面积
- 物体是否靠近画面中心
- 与上一帧目标的重叠程度
- 前后帧类别是否一致
简化后的排序代码如下:
function calculateRank(item, previous) {
const area = getArea(item.box);
const centerScore = getCenterScore(item.box);
const previousIoU = boxIoU(item.box, previous?.box);
const identityBonus =
previous?.label === item.label ? 0.5 : 0;
return (
item.score * 0.55 +
Math.min(area, 0.25) +
centerScore * 0.2 +
previousIoU * 1.2 +
identityBonus
);
}
在视频中,上一帧目标拥有更高权重,从而避免其他物体进入画面后发生错误切换。
从检测框生成物体 Alpha
目标确定后,系统根据物体中心生成提示点,再调用提示式分割模型:
const result = segmenter.segment(image, {
keypoint: {
x: clamp(point.x, 0, 1),
y: clamp(point.y, 0, 1),
},
});
const mask = result.confidenceMasks.at(-1);
const probabilities = mask.getAsFloat32Array();
置信度结果会转换为 0~255 的 Alpha:
const alpha = new Uint8ClampedArray(probabilities.length);
for (let i = 0; i < probabilities.length; i++) {
const probability = clamp(probabilities[i], 0, 1);
alpha[i] = probability >= threshold
? Math.round(probability * 255)
: 0;
}
得到 Alpha 后,还需要进行保守的质量检查。如果蒙版出现以下情况,就不会直接接受:
- 蒙版越过目标区域
- 出现多个显著连通区域
- 混入其他物体或背景
- 面积相对上一帧异常变化
- 目标身份发生跳变
相比修复一个已经错误的蒙版,直接跳过不可靠帧更加安全。
视频使用稀疏锚点和光流传播
逐帧执行完整检测和分割会产生较高开销,因此视频分析采用稀疏锚点策略。
const needsAnchor =
index === 0 ||
index - lastAnchorIndex >= anchorInterval ||
!trackedMaskBox;
if (needsAnchor) {
currentAlpha = await analyzeObjectAnchor(frame);
lastAnchorIndex = index;
} else {
currentAlpha = await propagateAlphaWithFlow(
previousFrame,
currentFrame,
currentAlpha,
);
}
只有首帧、固定间隔或目标丢失时才重新执行完整分析。中间帧通过光流传播 Alpha。
传播完成后还会检查面积变化:
const ratio = nextArea / trackedArea;
if (!nextBox || ratio < 0.35 || ratio > 2.8) {
currentAlpha = null;
trackedMaskBox = null;
}
一旦传播结果不可信,系统会重新检测目标,而不是继续传播错误蒙版。
分离 AI 分析与视觉样式
模型分析只负责生成可复用 Alpha。描边颜色、宽度、阴影和材质属于独立的效果状态:
const objectOutline = {
targetKind: "object",
outline: {
color: "#f3efe4",
width: 12,
opacity: 1,
glow: 0.35,
},
material: {
id: "paper",
edgeDensity: 0.5,
shadowDepth: 0.32,
},
};
用户调整样式时只重新合成画面,不需要再次运行检测和分割模型。
同一份 Alpha 同时用于编辑器预览和最终导出,确保两条渲染链路的效果一致。
总结
浏览器本地实物描边的核心并不是某一个模型,而是整套处理策略:
- Worker 隔离模型推理
- WebGPU 与 WASM 兼容回退
- 检测与提示式分割组合
- 目标身份锁定
- 保守的蒙版质量门槛
- 稀疏锚点与光流传播
- AI 分析和视觉渲染解耦