前端黑科技 浏览器本地 AI 抠图 无需服务器全程隐私保护
发现一个超厉害的工具 aicut.online 不用依赖服务器 直接在浏览器里就能完成图片背景移除 所有处理都在本地进行 隐私安全拉满 研究后发现它是靠 u2net 模型加 onnxruntime-web 实现的 原理其实没那么复杂。
核心技术拆解
先搞懂三个关键技术 才能明白本地抠图怎么实现。
- WebAssembly 简称 Wasm 一种二进制指令格式 能在浏览器里以接近原生的速度运行 还支持 C C++ Rust 等多种语言编译 专门解决 JS 处理密集型任务性能不足的问题。
- onnxruntime-web 浏览器端的机器学习模型运行引擎 能加载 ONNX 格式的模型文件 支持 Wasm WebGL 等多种执行后端 让模型在本地高效推理。
- u2net 专门做目标分割的神经网络 嵌套 U 型结构能精准捕捉图像细节 就像智能剪刀手 能快速把前景主体从背景中分离出来 抠图效果超棒。
技术架构层层解析
整个工具的架构很清晰 分五层协作。
- 用户层 就是我们看到的网页界面 负责接收图片输入 调用 API 执行抠图 最后展示结果。
- 模型服务层 核心是 onnxruntime-web 库 加载 u2net 模型文件 管理内存中的张量数据 给上层提供简洁的 JS API。
- 执行引擎层 首选 Wasm 后端保障高性能 可选 WebGL 利用 GPU 加速 还有实验性的 WebNN 调用硬件加速。
- 模型层 存储 u2net 的 ONNX 格式文件 包含训练好的网络结构和权重参数 专门处理抠图任务。
- 资源层 提供模型文件的存储和加载环境 浏览器的 Wasm 引擎 WebGL API 还有 CPU GPU 等硬件资源都在这里。
核心代码直击
核心代码关键流程就三步。
第一步 加载模型 用 IndexedDB 缓存 二次使用不用重新下载
useEffect(() => {
const loadModel = async () => {
try {
setError(null);
const db = await openDB();
let modelData = await getModelFromDB(db);
if (!modelData) {
const response = await fetch('./u2net.onnx');
if (!response.ok) throw new Error(`网络请求模型失败 ${response.status} ${response.statusText}`);
modelData = await response.arrayBuffer();
await storeModelInDB(db, modelData);
}
const newSession = await ort.InferenceSession.create(modelData, {
executionProviders: ['wasm'],
graphOptimizationLevel: 'all',
});
setSession(newSession);
} catch (e) {
console.error('ONNX模型加载或初始化失败', e);
setError(`模型处理失败 ${e.message}`);
}
};
loadModel();
}, []);
第二步 图片预处理 转换成模型需要的张量格式
const preprocess = async (imgElement) => {
const canvas = document.createElement('canvas');
const modelWidth = 320;
const modelHeight = 320;
canvas.width = modelWidth;
canvas.height = modelHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(imgElement, 0, 0, modelWidth, modelHeight);
const imageData = ctx.getImageData(0, 0, modelWidth, modelHeight);
const data = imageData.data;
const float32Data = new Float32Array(1 * 3 * modelHeight * modelWidth);
const mean = [0.485, 0.456, 0.406];
const std = [0.229, 0.224, 0.225];
for (let i = 0; i < modelHeight * modelWidth; i++) {
float32Data[i] = (data[i * 4] / 255 - mean[0]) / std[0];
float32Data[i + modelHeight * modelWidth] = (data[i * 4 + 1] / 255 - mean[1]) / std[1];
float32Data[i + 2 * modelHeight * modelWidth] = (data[i * 4 + 2] / 255 - mean[2]) / std[2];
}
return new ort.Tensor('float32', float32Data, [1, 3, modelHeight, modelWidth]);
};
第三步 运行模型推理 后处理生成透明背景图
const runSegmentation = async () => {
if (!image || !session) {
setError('请先上传图片并等待模型加载完成');
return;
}
setError(null);
setOutputImage(null);
try {
const imgElement = imageRef.current;
if (!imgElement) throw new Error('图片元素未找到');
if (!imgElement.complete) await new Promise(resolve => { imgElement.onload = resolve; });
const inputTensor = await preprocess(imgElement);
const feeds = { 'input.1': inputTensor };
const results = await session.run(feeds);
const outputTensor = results[session.outputNames[0]];
const outputDataURL = postprocess(outputTensor, imgElement);
setOutputImage(outputDataURL);
} catch (e) {
console.error('抠图失败', e);
setError(`抠图处理失败 ${e.message}`);
}
};
后处理部分主要是将模型输出的掩码图 与原图合成带透明通道的图片 代码就不贴全了 核心是处理 alpha 通道并缩放回原图尺寸。
海云前端丨前端开发丨简历面试辅导丨求职陪跑