- 人工智能
- 机器学习
- 深度学习
- 计算机视觉
- NLP
【免费下载链接】ml5-library
Friendly machine learning for the web! 🤖
BodyPix 是 ml5.js 提供的人像分割(person segmentation)模型封装,它基于 TensorFlow.js 在浏览器端运行,可把图像像素划分为“人”与“背景”两大类,并进一步将人体像素细分为 24 个身体部位。本文以 docs/reference/bodypix.md 为核心脉络,结合 src/BodyPix 源码与 examples/p5js/BodyPix 示例,完整讲解初始化参数、segment()与segmentWithParts()的调用方式与返回结构、调色板定制技巧,以及模型与训练数据的来源,读完后你可以在自己的 p5.js 或原生 JavaScript 项目中直接落地人像抠图、背景替换、身体部位可视化等能力。
BodyPix 是什么:在浏览器中做像素级人像分割
按 BodyPix 开发者的原话:"BodyPix 是一个开源机器学习模型,允许在浏览器中通过 TensorFlow.js 进行人物与身体部位分割。在计算机视觉中,图像分割(image segmentation)是指将图像中的像素按语义区域分组的技术,通常用于定位物体与边界。BodyPix 模型经过训练,可以对一个人以及二十四个身体部位(如左手、右前小腿外侧、躯干背部)完成这项任务。换句话说,BodyPix 可以把图像的像素分为两类:1)代表人的像素;2)代表背景的像素;它还可以把代表人的像素进一步归类到 24 个身体部位中的任意一个。"
从 ml5.js 的实现看,这一点被封装在 src/BodyPix/index.js 中:它直接导入@tensorflow-models/body-pix包(import * as bp from '@tensorflow-models/body-pix';),模型加载后即可调用底层estimatePersonSegmentation(人像分割)与estimatePartSegmentation(身体部位分割)两个 API。也就是说,你不需要直接接触 TensorFlow.js 的底层接口,ml5.js 已经替你完成了模型加载、参数传递与结果格式转换。
快速开始:5 行代码完成第一次分割
把下面的代码放入你的 p5.js sketch 或普通 HTML 页面中,即可体验 BodyPix 的基础流程:
const bodypix = ml5.bodyPix(modelReady); function modelReady() { // 对给定的图片执行分割 bodypix.segment(img, gotResults); } function gotResults(error, result) { if (error) { console.log(error); return; } // 输出结果 console.log(result.backgroundMask); }流程很直观:先通过ml5.bodyPix(modelReady)创建实例并传入模型就绪回调;模型就绪后在回调里调用bodypix.segment(img, gotResults)对图像执行分割;最终结果在gotResults(error, result)中接收。注意这里img可以是HTMLImageElement、HTMLVideoElement、ImageData或HTMLCanvasElement。
初始化与配置参数
构造函数签名
const bodyPix = new ml5.bodyPix(?video, ?options, ?callback);三个参数中,video与options是可选的,callback是模型加载完成后执行的回调函数:
| 参数 | 必填 | 说明 |
|---|---|---|
video | 可选 | 一个HTMLVideoElement,传入后可作为后续segment()/segmentWithParts()的默认输入 |
callback | 必填 | 模型加载完成后执行的回调函数 |
options | 可选 | 用于改变默认行为的配置对象,可用项见下文 |
构造函数内部的处理可参考 src/BodyPix/index.js:它把video存入this.video,用传入的options覆盖默认值生成this.config,并调用this.loadModel()异步加载模型,最后通过callCallback(src/utils/callcallback.js)把回调接入加载流程。加载完成后this.modelReady被置为true(src/BodyPix/index.js)。
options 完整配置项
文档给出的默认配置如下:
{ multiplier: 0.75, // 1.0, 0.75, 或 0.50, 0.25 outputStride: 16, // 8, 16, 或 32,默认是 16 segmentationThreshold: 0.5, // 0 - 1,默认是 0.5 palette: { /* 24 个身体部位的颜色映射,见下文 */ }, }对照 src/BodyPix/index.js 中的DEFAULTS,还可以确认一个文档中未列出的隐藏配置项——returnTensors:
| 配置项 | 取值 | 默认值 | 作用 |
|---|---|---|---|
multiplier | 1.0、0.75、0.50、0.25 | 0.75 | MobileNet 的宽度乘子,控制模型大小与精度之间的权衡,数值越小模型越快但精度越低 |
outputStride | 8、16、32 | 16 | 输出步长,影响分割结果的像素精度与计算量,8最精细 |
segmentationThreshold | 0-1 | 0.5 | 判定像素属于“人”的置信度阈值,越低越容易把人更多地纳入分割结果 |
palette | 对象 | BODYPIX_PALETTE | 24 个身体部位各自的id与 RGBcolor映射 |
returnTensors | 布尔值 | false | 是否在结果中保留 TensorFlow 张量,用于底层调试或进一步张量运算 |
其中palette定义在独立的 src/BodyPix/BODYPIX_PALETTE.js 中,对应 24 个身体部位的完整颜色表。文档原样给出了这份调色板,整理成表格如下(ml5.js 默认即为这些颜色):
| 部位 | id | RGB 颜色 |
|---|---|---|
leftFace | 0 | [110, 64, 170] |
rightFace | 1 | [106, 72, 183] |
rightUpperLegFront | 2 | [100, 81, 196] |
rightLowerLegBack | 3 | [92, 91, 206] |
rightUpperLegBack | 4 | [84, 101, 214] |
leftLowerLegFront | 5 | [75, 113, 221] |
leftUpperLegFront | 6 | [66, 125, 224] |
leftUpperLegBack | 7 | [56, 138, 226] |
leftLowerLegBack | 8 | [48, 150, 224] |
rightFeet | 9 | [40, 163, 220] |
rightLowerLegFront | 10 | [33, 176, 214] |
leftFeet | 11 | [29, 188, 205] |
torsoFront | 12 | [26, 199, 194] |
torsoBack | 13 | [26, 210, 182] |
rightUpperArmFront | 14 | [28, 219, 169] |
rightUpperArmBack | 15 | [33, 227, 155] |
rightLowerArmBack | 16 | [41, 234, 141] |
leftLowerArmFront | 17 | [51, 240, 128] |
leftUpperArmFront | 18 | [64, 243, 116] |
leftUpperArmBack | 19 | [79, 246, 105] |
leftLowerArmBack | 20 | [96, 247, 97] |
rightHand | 21 | [115, 246, 91] |
rightLowerArmFront | 22 | [134, 245, 88] |
leftHand | 23 | [155, 243, 88] |
值得注意:当在 p5.js 环境中传入调色板时,ml5.js 还支持直接用 p5 的颜色对象(p5.Color)。src/BodyPix/index.js 中的bodyPartsSpec()会先检查传入的调色板是否达到 24 个部位,再通过p5Color2RGB()把p5.Color转回[r, g, b]数组,保证内部始终以 RGB 数组工作。这一特性在 examples/p5js/BodyPix/BodyPix_Webcam_Parts/sketch.js 中被用于实现随机 HSB/RGB 调色板生成。
运行时参数覆盖
除了构造时设置options,segment()与segmentWithParts()也接受一个options参数,用于在单次调用中临时修改outputStride与segmentationThreshold(segmentWithParts()还额外支持palette)。从源码看,这些运行时选项会被写入this.config再传给底层 API(src/BodyPix/index.js):
this.config.palette = segmentationOptions.palette || this.config.palette; this.config.outputStride = segmentationOptions.outputStride || this.config.outputStride; this.config.segmentationThreshold = segmentationOptions.segmentationThreshold || this.config.segmentationThreshold;属性(Properties)
创建实例后,你可以访问以下公开属性:
| 属性 | 类型 | 说明 |
|---|---|---|
.video | Object | 构造时传入的HTMLVideoElement,未传则为null |
.model | Object | 已加载的 BodyPix 底层模型 |
.modelReady | Boolean | 模型是否加载完成的标志(truthy 表示已就绪) |
.modelPath | String | 模型路径 |
.config | Object | 当前生效的 BodyPix 配置对象(包含multiplier、outputStride、segmentationThreshold、palette、returnTensors) |
其中.config尤其有用:在 examples/p5js/BodyPix/BodyPix_Webcam_Parts/sketch.js 中可以看到,自定义调色板正是通过读取bodypix.config.palette并改写各部位的color实现的。
方法详解
.segment():人像与背景分割
允许你把人物从背景中分割出来。
bodyPix.segment(?input, ?options, callback);输入:
input:HTMLImageElement、HTMLVideoElement、ImageData、HTMLCanvasElement之一。注意:视频也可以在构造函数中传入,作为默认输入。options:Object,可修改outputStride与segmentationThreshold。callback:Function,用于处理.segment()结果,通常用来对分割后的图像做后续处理。
输出:
返回一个对象;如果环境中存在 p5.js,掩码会是p5.Image,否则是像素数组(UInt8):
{ segmentation, raw: { personMask: null, backgroundMask: null, }, tensor: { personMask: null, backgroundMask: null, }, personMask: null, backgroundMask: null, }内部实现:segment()的参数解析依赖 src/utils/handleArguments.js 中的handleArguments()——它支持任意顺序的可选参数组合,会自动识别图像/视频元素、配置对象与回调(src/BodyPix/index.js)。若既没有传入图像也没有在构造时传入视频,会抛出错误:"No input image provided..."。真正的工作在segmentInternal()(src/BodyPix/index.js)中完成:
- 等待模型就绪(
await this.ready)并等待媒体可读(mediaReady); - 调用底层
this.model.estimatePersonSegmentation(imgToSegment, outputStride, segmentationThreshold)得到像素级分割结果segmentation; - 用
bp.toMaskImageData(segmentation, true)生成背景掩码、bp.toMaskImageData(segmentation, false)生成人物掩码,存入raw; - 在
tf.tidy中把原图与分割掩码做张量乘法,得到带 alpha 通道的人物/背景掩码张量; - 通过 src/utils/generatedImageResult.js 把张量转成
raw像素数组、blob与(如果存在 p5)p5.Image; - 若
config.returnTensors为true,同时把张量放入result.tensor,否则张量会被dispose()释放。
这也解释了为什么结果里同时出现raw、tensor与顶层personMask/backgroundMask三套字段:raw是ImageData级别的像素数据,tensor是 TensorFlow 张量(仅returnTensors: true时),顶层字段是便于直接绘制的p5.Image(p5 环境下)或像素数组。
.segmentWithParts():身体部位分割
允许你获取人物的身体部位分割结果。
bodyPix.segmentWithParts(?input, ?options, callback);输入:
input:HTMLImageElement、HTMLVideoElement、ImageData、HTMLCanvasElement之一。视频同样可在构造函数中传入。options:Object,可修改outputStride、segmentationThreshold与palette。callback:Function,处理分割结果的回调。
输出:
{ segmentation: *ImageData*, raw: { personMask: *ImageData*, backgroundMask: *ImageData*, partMask: *ImageData* }, tensor: { personMask: *Tensor*, backgroundMask: *Tensor*, partMask: *Tensor*, }, personMask: *P5Image*, backgroundMask: *P5Image*, partMask: *P5Image*, bodyParts: *JSONObject* }与segment()相比,这里多了partMask(身体部位着色掩码)和bodyParts(部位元数据)。在segmentWithPartsInternal()(src/BodyPix/index.js)中,底层调用的是estimatePartSegmentation,并通过bp.toColoredPartImageData(segmentation, colorsArray)结合调色板数组生成彩色部位掩码;bodyParts则来自bodyPartsSpec(),包含每个部位的id与color。
完整可运行示例
示例一:静态图片分割(p5.js)
完整代码见 examples/p5js/BodyPix/BodyPix_Image,核心逻辑如下(节选自其 sketch.js):
let bodypix; let segmentation; let img; function preload() { img = loadImage('data/harriet.jpg'); bodypix = ml5.bodyPix(); } function setup() { createCanvas(480, 560); bodypix.segment(img, gotResults); } function gotResults(err, result) { if (err) { console.log(err); return; } segmentation = result; background(0); image(segmentation.backgroundMask, 0, 0, width, height); }这里segmentation.backgroundMask是 p5.Image,可以直接用image()绘制到画布上,实现“只保留背景、人物变黑”的视觉效果。
示例二:网络摄像头实时分割(p5.js)
见 examples/p5js/BodyPix/BodyPix_Webcam,节选自其 sketch.js:
const options = { outputStride: 8, // 8, 16, 或 32,默认是 16 segmentationThreshold: 0.3, // 0 - 1,默认是 0.5 }; function preload() { bodypix = ml5.bodyPix(options); } function setup() { createCanvas(320, 240); video = createCapture(VIDEO, videoReady); video.size(width, height); } function videoReady() { bodypix.segment(video, gotResults); } function draw() { background(0); if (segmentation) { image(segmentation.backgroundMask, 0, 0, width, height); } } function gotResults(error, result) { if (error) { console.log(error); return; } segmentation = result; bodypix.segment(video, gotResults); // 在回调中再次调用,形成连续分割循环 }注意两个实用技巧:一是把outputStride调低到8、segmentationThreshold调低到0.3以获得更精细的实时分割;二是在gotResults回调里递归调用bodypix.segment(video, gotResults),让每一帧分割完成后立即处理下一帧,形成流畅的连续分割管线。此外视频可以只作为输入,通过隐藏原始视频元素、只绘制掩码画布实现“虚拟背景”。
示例三:身体部位实时着色(p5.js)
见 examples/p5js/BodyPix/BodyPix_Webcam_Parts,节选自其 sketch.js:
function videoReady() { bodypix.segmentWithParts(video, gotResults, options); } function gotResults(err, result) { if (err) { console.log(err); return; } segmentation = result; background(255, 0, 0); image(segmentation.partMask, 0, 0, width, height); bodypix.segmentWithParts(video, gotResults, options); }segmentation.partMask就是 24 个身体部位按调色板着色后的彩色掩码,直接绘制即可看到身体各部位被不同颜色标注。同一示例中还演示了自定义调色板:createSimplePalette()随机为每个部位分配 RGB 颜色,createHSBPalette()用 HSB 色彩模式生成随机色,createRGBPalette()生成随机 RGB 色(见 examples/p5js/BodyPix/BodyPix_Webcam_Parts/sketch.js),都通过改写options.palette实现。
示例四:纯 JavaScript(无 p5.js)
如果不想依赖 p5.js,可以参考 examples/javascript/BodyPix/BodyPix_Image 的 sketch.js,它使用await ml5.bodyPix()与await bodypix.segment(img)的 Promise 风格调用,并把segmentation.raw.backgroundMask.data转成ImageData后绘制到<canvas>上。示例目录中还有同名的 Webcam 与 Webcam Parts 版本,以及对应的 HTML 入口 examples/javascript/BodyPix/BodyPix_Image/index.html(通过<script src="http://localhost:8080/ml5.js">引入本地构建的 ml5.js)。
所有示例在仓库中均有对应 HTML 入口与素材:图片版使用data/harriet.jpg、data/ada.jpg(见 examples/p5js/BodyPix/BodyPix_Image 与 examples/javascript/BodyPix/BodyPix_Image)。运行这些示例时,你需要先构建 ml5.js 并在本地启动一个静态服务器(参考 docs/tutorials/local-web-server.md),因为浏览器环境加载本地文件与模型资源通常需要 HTTP 服务。
模型与数据来源
该项目由 Ellen Nickles 发起。
MobileNetV1 模型传记
- 描述:MobileNet 描述了一类为算力受限平台(如移动设备、嵌入式设备)优化而设计的机器学习模型架构,可用于图像分类、目标检测与图像分割等任务。ml5.js 使用的这个 MobileNet 模型属于卷积神经网络(CNN),经过训练可检测图像或视频中的人与 24 个身体大致区域。CNN 在图像像素中寻找模式(如水平或垂直边缘),并通过逐层计算组合出更复杂的模式(如角或圆),最终识别出它预测属于某个类别的精细模式(如左脸或右大臂前侧)——具体类别取决于模型训练数据集中图像的标注方式。
- ml5.js 默认模型:使用基于 TensorFlow.js 创建的 MobileNet。TensorFlow 是由 Google 开发的开源机器学习平台。
- 开发者与年份:Google 的 TensorFlow.js 团队。TensorFlow.js BodyPix 模型由纽约大学的 Dan Oved 与 Google Research 的 Tyler Zhu 及更多贡献者于 2019 年开发。
- 用途与目标用户:TensorFlow 是一个开源机器学习平台,拥有全面而灵活的工具、库与社区资源生态。该模型以 Apache License 2.0 许可提供给 ml5.js 使用。
- 托管位置:截至 2019 年 6 月,ml5.js 从 TensorFlow 导入 MobileNetV1,模型托管在 NPM 数据库上,这意味着你的 ml5 sketch 会自动使用 NPM 上分发的最新版本。
- ml5.js 贡献者与年份:由 Joey Lee 于 2019 年移植到 ml5.js。
MobileNetV1 数据传记
- 描述:根据官方文章,模型训练数据包含两部分:(1)来自 COCO 数据集的图像;(2)数字生成的“模拟数据”。
- 来源:COCO 数据集由多个学术与商业机构的合作者共同维护,用于“大规模目标检测、分割与图像描述”,根据论文其图像来自 Flickr;Google Research 的 Per Karlsson 渲染了数据集中其余部分的模拟图像。
- 采集者与年份:COCO 数据库始于 2014 年。
- 采集方法:COCO 采集图像并将像素标注为分割区域的方法在论文中有详细描述。
- 用途与目标用户:COCO 数据集旨在推进计算机视觉研究。使用混合数据集的原因正如官方文章所述:"为像素级分割任务手动标注大量训练数据(把图像像素分成 24 个身体部位区域)非常耗时。作为替代,我们在内部使用计算机图形渲染带有 ground truth 身体部位分割标注的图像。训练时,我们把渲染图像与真实 COCO 图像(带 2D 关键点与实例分割标注)混合在一起。"
源码结构与测试验证
BodyPix 模块的源码组织如下:
- src/BodyPix/index.js:核心实现,包含
BodyPix类、默认配置、参数解析、分割与部位分割两条完整管线; - src/BodyPix/BODYPIX_PALETTE.js:24 个身体部位的默认调色板;
- src/BodyPix/tests/BodyPix.test.js:测试用例,验证默认配置值(
multiplier: 0.75、outputStride: 16、segmentationThreshold: 0.5)、segment()对ImageData的输入支持,以及对 128x128 测试图片的分割尺寸正确性; - src/utils/handleArguments.js:通用参数解析器,识别图像/视频/
ImageData/p5 元素/配置/回调; - src/utils/generatedImageResult.js:张量到
raw/blob/p5.Image的结果转换; - src/utils/p5Utils.js:p5 环境检测与 p5 图像转换工具。
测试代码(src/BodyPix/tests/BodyPix.test.js)还提供了一种无须浏览器 UI 即可验证 BodyPix 行为的方式:直接await bodyPix()创建实例,再对随机ImageData或固定图片调用bp.segment(img)并断言返回的segmentation.width/segmentation.height。这印证了ml5.bodyPix()在没有显式回调时会返回一个 Promise(见 src/BodyPix/index.js),因此你完全可以用 async/await 风格替代回调风格编写代码。
小结
BodyPix 是 ml5.js 中最适合快速上手的人像分割模型:ml5.bodyPix()一行完成初始化,segment()与segmentWithParts()分别提供人/背景二分类分割与 24 个身体部位细分,返回结果同时覆盖raw像素数据、p5.Image与可选张量,天然适配 p5.js 绘图与纯 JavaScript 两种场景。无论是做虚拟背景、人像抠图、身体部位着色还是交互艺术装置,配合 examples/p5js/BodyPix 与 examples/javascript/BodyPix 下的四个示例都能快速起步。如果你想在运行前进一步了解模型加载与回调机制,可继续阅读 docs/tutorials/promises-and-callbacks.md。
- 人工智能
- 机器学习
- 深度学习
- 计算机视觉
- NLP
【免费下载链接】ml5-library
Friendly machine learning for the web! 🤖
相关推荐
BodyPix 人体分割详解:在 TF.js 的 body-segmentation 中使用 BodyPix 实现人像与 24 部位语义分割
BodyPix 人体分割详解:在 TF.js 的 body segmentation 中使用 BodyPix 实现人像与 24 部位语义分割 BodyPix 是
人工智能深度学习计算机视觉NLP语音BodyPix 2.0 浏览器端人体与身体部位分割实战指南:TensorFlow.js 模型加载、多人体推理与可视化渲染
BodyPix 2.0 浏览器端人体与身体部位分割实战指南:TensorFlow.js 模型加载、多人体推理与可视化渲染 BodyPix 是 TensorFlo
人工智能深度学习计算机视觉NLP语音BodyPix 浏览器端人体与身体部位分割实战指南:基于 TensorFlow.js 的模型加载、推理与图像合成
BodyPix 浏览器端人体与身体部位分割实战指南:基于 TensorFlow.js 的模型加载、推理与图像合成 BodyPix 是 TensorFlow.js
人工智能深度学习计算机视觉NLP语音
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考