最近,我在 Timeline Studio 中实现了实物描边能力:识别画面中的商品、鞋子、玩具等物体,并在浏览器本地生成可编辑的描边效果。
这项功能的核心并不是“找出画面边缘”,而是稳定地回答三个问题:
目标检测擅长回答“物体在哪里”,但只能得到矩形框;提示分割可以生成像素级蒙版,却需要一个可靠的目标提示。
因此,整个流程拆成了两步:
NanoDet 检测候选物体
↓
选择主要编辑对象
↓
根据目标中心生成提示点
↓
MagicTouch 生成物体蒙版
↓
质量检查
↓
生成并缓存 Alpha检测放在独立 Worker 中运行,避免阻塞编辑器界面。推理时优先使用 WebGPU,不支持时回退到 WASM:
async function createSession(model) {
if (navigator.gpu) {
try {
return await ort.InferenceSession.create(model, {
executionProviders: ["webgpu"],
graphOptimizationLevel: "all",
});
} catch (error) {
console.warn("WebGPU 不可用,回退 WASM", error);
}
}
return ort.InferenceSession.create(model, {
executionProviders: ["wasm"],
graphOptimizationLevel: "all",
});
}一张图中可能同时存在多个物体,因此不能简单选择置信度最高的检测框。
项目会综合置信度、面积、画面中心距离,以及与上一帧目标的重叠程度进行排序:
function rankObject(item, previousSubject) {
const area = getBoxArea(item.box);
const centered = getCenterScore(item.box);
const previousIoU = boxIoU(item.box, previousSubject?.box);
return (
item.score * 0.55 +
Math.min(0.25, area) +
centered * 0.2 +
previousIoU * 1.2
);
}视频处理中,上一帧目标的 IoU 权重更高。这样即使画面中出现新的物体,也不会轻易切换编辑对象。
检测到目标后,将目标中心转换为归一化提示点,交给提示式分割模型:
const result = segmenter.segment(image, {
keypoint: {
x: Math.max(0, Math.min(1, point.x)),
y: Math.max(0, Math.min(1, point.y)),
},
});
const probabilities = result.confidenceMasks[1]
.getAsFloat32Array();
const alpha = new Uint8ClampedArray(probabilities.length);
for (let i = 0; i < probabilities.length; i += 1) {
const value = probabilities[i];
alpha[i] = value >= threshold
? Math.round(value * 255)
: 0;
}这里不会直接使用模型输出,而是继续检查蒙版面积、边界、连通区域和目标框关系。
如果蒙版包含背景、其他物体或发生异常面积跳变,该帧会被拒绝。宁可暂时不显示效果,也不传播错误的物体蒙版。
如果每一帧都执行目标检测和提示分割,浏览器推理成本会很高。
项目采用“稀疏锚点+光流传播”的方式:
const needsAnchor =
frameIndex === 0 ||
frameIndex - lastAnchorIndex >= anchorInterval ||
!trackedMaskBox;
if (needsAnchor) {
currentAlpha = await analyzeObjectAnchor(frame);
lastAnchorIndex = frameIndex;
} else {
currentAlpha = await propagateWithOpticalFlow(
previousFrame,
currentFrame,
currentAlpha,
);
}稳定追踪期间,通过光流将上一帧 Alpha 传播到当前帧;达到锚点间隔、目标丢失或质量检查失败时,再执行完整检测和分割。
如果传播后的蒙版面积变化过大,就会立即失效:
const areaRatio = nextArea / previousArea;
if (!nextBox || areaRatio < 0.35 || areaRatio > 2.8) {
currentAlpha = null;
trackedMaskBox = null;
}目标分析是比较耗时的步骤,而颜色、宽度、阴影和材质调整应该即时响应。
因此,项目会缓存分析生成的 Alpha,描边参数单独保存在片段状态中:
const effect = {
enabled: true,
targetKind: "object",
outline: {
color: "#f3efe4",
width: 12,
opacity: 1,
softness: 0,
glow: 0.35,
},
material: {
id: "paper",
edgeDensity: 0.5,
shadowDepth: 0.32,
},
};用户调整样式时只重新合成画面,不重新运行模型。预览和导出也使用同一套 Alpha 与效果状态,避免最终导出和编辑器显示不一致。
完整项目和代码可以在 GitHub 查看:
https://github.com/MartinDelophy/ai-video-editor
实物描边的关键不只是模型精度,而是把检测、分割、质量门槛、目标身份和渲染状态组合成一条可靠的浏览器处理链路。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。