$yolo 模型调用
更新: 2026/3/2 字数: 0 字 时长: 0 分钟
$yolo 模块提供了基于 YOLO 模型的实时目标检测、实例分割、姿态估计、分类和旋转框检测等功能。支持 ONNX 和 NCNN 两种模型格式,适用于多种计算机视觉任务。
$yolo.load(options[, callback])
options{Object} 模型配置选项binPath{string} NCNN 模型的权重文件路径(.bin 文件)paramPath{string} NCNN 模型的结构文件路径(.param 文件)onnxPath{string} ONNX 模型文件路径(.onnx 文件)classesPath{string} 类别标签文件路径(.txt、.yaml 文件)classes{Array<string>} 直接传入类别数组version{number} YOLO 输入输出层解析版本:- 通常情况下,对应模型的 YOLO 实际版本号,例如:0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 26(0 代表 YOLOX)
- 由于不同 YOLO 版本在不同导出方式下导出为 NCNN 模型时,输入层和输出层的名称、数量及结构可能不同,因此
version实际取决于模型的输入输出层结构,不完全等同于训练版本。 - 请参考 NCNN 输入输出层名称 选择与你模型匹配的版本。
- 如果选择错误,可能导致推理闪退或始终无法检测到目标,具体可参阅常见问题。
task{number} 任务类型:0-检测, 1-分割, 2-分类, 3-姿态, 4-旋转框, 5-跟踪device{number} 推理设备:0-CPU, 1-GPU, 2-Turnip
callback{Object} 加载回调(可选)onload(yolo, success)模型加载完成时回调
- 返回 Yolo 对象,失败返回
null
加载 YOLO 模型并创建推理实例。
类别配置方式(二选一)
- 使用
classesPath从文件加载类别 - 或使用
classes直接传入数组
注意事项
- 必须在不需要时调用 Yolo.release() 释放模型资源,否则会导致内存泄漏
let yolo = $yolo.load({
binPath: "/sdcard/yolo/yolo26n.ncnn.bin",
paramPath: "/sdcard/yolo/yolo26n.ncnn.param",
// 类别来源(二选一)
classesPath: "/sdcard/yolo/det.txt",
// classes: ["人","自行车","汽车","摩托车","飞机","公交车","火车","卡车","船","红绿灯","消防栓","停止标志","停车计时器","长椅","鸟","猫","狗","马","羊","牛","大象","熊","斑马","长颈鹿","背包","雨伞","手提包","领带","行李箱","飞盘","滑雪板","单板滑雪板","球","风筝","棒球棒","棒球手套","滑板","冲浪板","网球拍","瓶子","酒杯","杯子","叉子","刀","勺子","碗","香蕉","苹果","三明治","橙子","西兰花","胡萝卜","热狗","披萨","甜甜圈","蛋糕","椅子","沙发","盆栽","床","餐桌","马桶","电视","笔记本电脑","鼠标","遥控器","键盘","手机","微波炉","烤箱","烤面包机","水槽","冰箱","书","时钟","花瓶","剪刀","泰迪熊","吹风机","牙刷"],
version: 26, //YOLO26版本
task: 0, // 目标检测
device: 0 // CPU
}, {
onload: function(yolo, success) {
console.log(success ? "NCNN 模型加载成功" : "NCNN 模型加载失败");
}
});
if (!yolo) {
console.error("加载失败");
exit();
}
// 当脚本正常或者异常退出时会触发该事件
events.on("exit", function () {
try {
if (yolo) {
yolo.release();
}
} catch (error) {
console.error(error);
}
});
//在不需要时记得释放模型资源
yolo.release();let yolo = $yolo.load({
onnxPath: "/sdcard/yolo/yolo11n.onnx",
classesPath: "/sdcard/yolo/det.txt",
version: 11,
task: 0, // 目标检测
device: 0 // CPU
}, {
onload: function(yolo, success) {
console.log(success ? "ONNX 模型加载成功" : "ONNX 模型加载失败");
}
});
if (!yolo) {
console.error("加载失败");
exit();
}
// 当脚本正常或者异常退出时会触发该事件
events.on("exit", function () {
try {
if (yolo) {
yolo.release();
}
} catch (error) {
console.error(error);
}
});
//在不需要时记得释放模型资源
yolo.release();$yolo.load 支持使用 相对路径 来加载模型文件,相对路径是相对于当前工作目录或应用安装目录的路径。在 Bot.js 项目中,尤其是在打包应用后,使用相对路径可以提高项目的可移植性,确保在不同设备或环境下都能正常工作。
项目目录结构
├─📁 MyProject/ # 项目文件夹
│ ├─📄 main.js # 主脚本文件
│ ├─📁 assets/ # 存储模型文件的文件夹
│ │ ├─📄 yolo26n.ncnn.bin # NCNN 格式权重文件
│ │ ├─📄 yolo26n.ncnn.param # NCNN 格式结构文件
│ │ └─📄 det.txt # 类别标签文件
│ └─📄 project.json # 项目配置文件加载相对路径示例
// 使用相对路径加载 NCNN 格式的 YOLO 模型
let yolo = $yolo.load({
binPath: "./assets/yolo26n.ncnn.bin", // 相对路径
paramPath: "./assets/yolo26n.ncnn.param", // 相对路径
classesPath: "./assets/det.txt", // 相对路径
version: 26, // YOLO26版本
task: 0, // 目标检测
device: 0 // CPU
}, {
onload: function(yolo, success) {
console.log(success ? "NCNN 模型加载成功" : "NCNN 模型加载失败");
}
});Yolo
由 $yolo.load 返回的 YOLO 推理对象。
Yolo.getInfo()
var info = yolo.getInfo();
if (info) {
console.log("模型信息:", JSON.stringify(info));
}Yolo.setConfig(modelOptions[, drawOptions])
modelOptions{Object} 推理选项width{number} 输入图像宽度,默认640height{number} 输入图像高度,默认640meanVals{Array<number>} 均值归一化值,默认[0, 0, 0]normVals{Array<number>} 方差归一化值,默认[1/255, 1/255, 1/255]conf{number} 置信度阈值,默认0.25iou{number} IOU 阈值,默认0.7sort{number} 排序方式,默认0,具体参阅识别结果排序规则show{boolean} 是否显示结果,默认falsesave{boolean} 是否保存结果,默认falsesaveTxt{boolean} 是否保存标签文件,默认falsesaveConf{boolean} 是否保存置信度,默认falseverbose{boolean} 是否输出详细信息,默认falsepath{string} 输出路径,默认nullproject{string} 项目名称,默认nullname{string} 结果名称,默认nullvidStride{number} 视频帧采样间隔或帧跳过因子,默认1,在 YOLO 视频推理中,它控制视频处理时跳过的帧数
drawOptions{Object} 绘制选项(可选)lineWidth{number} 边框线宽,默认0fontSize{number} 字体大小,默认0kptRadius{number} 关键点半径,默认5showBoxes{boolean} 是否显示边界框,默认trueshowLabels{boolean} 是否显示标签,默认trueshowConf{boolean} 是否显示置信度,默认trueshowMasks{boolean} 是否显示掩码,默认trueshowKptLine{boolean} 是否显示骨骼连线,默认trueclassPalette{Array<Array<number>> | Array<number|string>} 类别颜色调色板,可以是二维数组的ARGB、RGB 或是一维数组的颜色值posePalette{Array<Array<number>> | Array<number|string>} 姿态颜色调色板,可以是二维数组的ARGB、RGB 或是一维数组的颜色值skeleton{Array<Array<number>>} 骨架连接关系limbColor{Array<number>} 骨骼颜色索引kptColor{Array<number>} 关键点颜色索引
- 返回 {boolean} 是否设置成功
设置全局推理和绘制选项参数。
yolo.setConfig({
width: 640,
height: 640,
meanVals: [0, 0, 0],
normVals: [1/255, 1/255, 1/255],
conf: 0.25,
iou: 0.7,
sort: 0,
show: false,
save: false,
saveTxt: false,
saveConf: false,
verbose: true
}, {
lineWidth: 2,
fontSize: 14,
kptRadius: 5,
showBoxes: true,
showLabels: true,
showConf: true,
showMasks: true,
showKptLine: true,
classPalette: [
//[A, R, G, B] 或者[R, G, B] 或者 colors.rgb(4, 42, 255) 或者 colors.argb(255, 4, 42, 255) 或者 #FF042AFF
[255, 4, 42, 255],
[255, 11, 219, 235],
[255, 243, 243, 243],
[255, 0, 223, 183],
[255, 17, 31, 104],
[255, 255, 111, 221],
[255, 255, 68, 79],
[255, 204, 237, 0],
[255, 0, 243, 68],
[255, 189, 0, 255],
[255, 0, 180, 255],
[255, 221, 0, 186],
[255, 0, 255, 255],
[255, 38, 192, 0],
[255, 1, 255, 179],
[255, 125, 36, 255],
[255, 123, 0, 104],
[255, 255, 27, 108],
[255, 252, 109, 47],
[255, 162, 255, 11]
// ... 更多颜色
],
posePalette: [
//[A, R, G, B] 或者[R, G, B] 或者 colors.rgb(4, 42, 255) 或者 colors.argb(255, 4, 42, 255) 或者 #FF042AFF
[255, 128, 0],
[255, 153, 51],
[255, 178, 102],
[230, 230, 0],
[255, 153, 255],
[153, 204, 255],
[255, 102, 255],
[255, 51, 255],
[102, 178, 255],
[51, 153, 255],
[255, 153, 153],
[255, 102, 102],
[255, 51, 51],
[153, 255, 153],
[102, 255, 102],
[51, 255, 51],
[0, 255, 0],
[0, 0, 255],
[255, 0, 0],
[255, 255, 255]
// ... 更多颜色
],
skeleton: [
// 下半身连接(注意:这里的索引从1开始,与COCO标准0-16不同)
[16, 14], // 右踝(16) -> 右膝(14):右小腿
[14, 12], // 右膝(14) -> 右髋(12):右大腿
[17, 15], // 左踝(17) -> 左膝(15):左小腿
[15, 13], // 左膝(15) -> 左髋(13):左大腿
[12, 13], // 右髋(12) -> 左髋(13):连接髋部,形成腰线
// 躯干连接
[6, 12], // 右肩(6) -> 右髋(12):身体右侧线
[7, 13], // 左肩(7) -> 左髋(13):身体左侧线
[6, 7], // 右肩(6) -> 左肩(7):连接双肩,形成肩线
// 右臂连接
[6, 8], // 右肩(6) -> 右肘(8):右上臂
[8, 10], // 右肘(8) -> 右腕(10):右前臂
[10, 11], // 右腕(10) -> 右手(11):右手(如果有手部关键点)
// 左臂连接
[7, 9], // 左肩(7) -> 左肘(9):左上臂
[9, 11], // 左肘(9) -> 左腕(11):左前臂
// [11, 12] // 注释掉的:可能之前尝试过连接左手到某处
// 面部连接
[2, 3], // 右眼(2) -> 鼻子(3)?
[1, 2], // 左眼(1) -> 右眼(2):连接双眼
[1, 3], // 左眼(1) -> 鼻子(3)?
[2, 4], // 右眼(2) -> 右耳(4)
[3, 5], // 鼻子(3) -> 左耳(5)?
[4, 6], // 右耳(4) -> 右肩(6):头部到肩膀的斜方肌
[5, 7], // 左耳(5) -> 左肩(7):头部到肩膀的斜方肌
// ... 更多骨骼连接
],
limbColor: [9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16],
kptColor: [16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]
});Yolo.getConfig()
- 返回 {object} 配置对象,包含推理选项和绘制选项
获取当前配置参数。
var config = yolo.getConfig();
console.log(config);Yolo.predict(image[, options])
image{Image | Bitmap | File | string} 输入源:- Image / Bitmap: 图像对象
- File: 图像文件
- string: 图像文件路径或URL
options{Object} 推理选项,可选,参见推理选项- 返回 YOLOResults 对象
执行图像推理预测。
注意事项
- 除了
images.captureScreen()截屏得到的Image对象除外(自动回收),其他必须在不需要时调用YOLOResults.recycleOrig()回收源图 Image 对象资源,否则会导致内存泄漏 - 当推理选项中
show: true或save: true时,方法内部会生成带有绘制结果的Image对象资源- 此
Image可通过YOLOResults.plot()获取 - 必须在不需要时调用
YOLOResults.recycle()释放绘制的Image对象资源,否则会导致内存泄漏
- 此
// 请求截图
if(!requestScreenCapture()){
toastLog("请求截图失败");
exit();
}
// 延迟 5000 毫秒
sleep(5000)
// 截屏 返回Image对象 会自动回收,不要手动回收 否则可能导致captureScreen()返回的是 null
var img = captureScreen();
var yoloResults = yolo.predict(img)
console.log(yoloResults);
// 回收当前结果绘制的Image对象资源
yoloResults.recycle();var img = images.read("/sdcard/test.jpg");
var yoloResults = yolo.predict(img, {
width: 320,
height: 320,
conf: 0.3,
save: true
});
console.log(yoloResults);
// 回收源图的Image对象资源
img.recycle();
// 回收当前结果绘制的Image对象资源
yoloResults.recycle();
// var yoloResults = yolo.predict(new java.io.File("/sdcard/test.jpg"), {
// width: 320,
// height: 320
// });
var yoloResults = yolo.predict("/sdcard/test.jpg", {
width: 320,
height: 320
});
console.log(yoloResults);
// 回收源帧的Image对象资源
yoloResults.recycleOrig();
// 回收当前结果绘制的Image对象资源
yoloResults.recycle();var yoloResults = yolo.predict("https://docs.botjs.org/yolo/bus.jpg");
console.log(yoloResults);
// 回收源帧的Image对象资源
yoloResults.recycleOrig();
// 回收当前结果绘制的Image对象资源
yoloResults.recycle();Yolo.predict(video[, options])
video{File | string} 输入源:- File: 视频文件
- string: 视频文件路径或URL
options{Object} 推理选项,可选,参见推理选项- 返回 {Array<YOLOResults>}
执行视频推理预测,如果视频文件偏大,源帧和绘制帧没有释放可能导致内存泄漏,推荐使用下面回调函数的 API。
注意事项
- 必须在不需要时调用
YOLOResults.recycleOrig()回收源图 Image 对象资源,否则会导致内存泄漏- 此
Image可通过YOLOResults.orig()获取
- 此
- 当推理选项中
show: true或save: true时,方法内部会生成带有绘制结果的Image对象资源- 此
Image可通过YOLOResults.plot()获取 - 必须在不需要时调用
YOLOResults.recycle()释放绘制的Image对象资源,否则会导致内存泄漏
- 此
- 输入为网络URL时,会先下载视频到临时目录再进行推理
var yoloResultsList = yolo.predict("/storage/emulated/0/assets/videos/04滑冰.mp4", {
width: 320,
height: 320,
save: true,
show: true,
saveTxt: true,
saveConf: false,
verbose: true,
vidStride: 10,
project: "runs",
name: "video_predict",
});
// 检查是否成功获取结果
if (!yoloResultsList) {
console.error("视频推理失败或返回空结果");
exit();
}
// 遍历数组并处理每个结果
for (var i = 0; i < yoloResultsList.length; i++) {
var yoloResults = yoloResultsList[i];
console.log("第", i + 1, "帧结果:", yoloResults);
// 可以进一步处理结果
var summary = yoloResults.summary();
console.log("检测到", yoloResults.size(), "个目标");
console.log("摘要信息:", summary);
console.log(yoloResults.orig());
console.log(yoloResults.plot());
// 回收源帧的Image对象资源
yoloResults.recycleOrig();
// 回收当前结果绘制的Image对象资源
yoloResults.recycle();
console.log("已释放第", i + 1, "帧资源");
}
console.log("视频处理完成,共处理", yoloResultsList.length, "帧");Yolo.predict(video, options, callback)
video{File | string} 输入源:- File: 视频文件
- string: 视频文件路径或URL
options{Object} 推理选项,可选,参见推理选项callback{Object} 视频推理回调,可选,用于监听视频解码、逐帧推理进度、保存状态等生命周期事件:onReady(width, height, fps, durationUs, totalFrames)
视频初始化完成时触发。width{number} 视频宽度height{number} 视频高度fps{number} 帧率durationUs{number} 视频总时长(微秒)totalFrames{number} 总帧数
onFrame(results, ptsUs, index)
每一帧推理完成时触发。results{YOLOResults} 当前帧检测结果ptsUs{number} 当前帧时间戳(微秒)index{number} 当前帧索引
onProgress(percent, index, total)
推理进度回调。percent{float} 当前进度(0~100)index{number} 当前处理帧total{number} 总帧数
onComplete(frameCount, costMs)
全部视频帧推理完成时触发。frameCount{number} 实际处理帧数costMs{number} 总耗时(毫秒)
onError(error)
推理或解码过程中发生错误时触发。error{Error} 错误信息
onSaveStart()
开始保存推理结果(视频或图片)时触发。onSaveProgress(percent, index, total)
保存进度回调。percent{float} 保存进度(0~100)index{number} 当前保存帧total{number} 总保存项帧
onSaveCompleted()
所有结果保存完成时触发。
返回
VideoPredict对象
执行视频推理预测。
注意事项
- 必须在不需要时调用
YOLOResults.recycleOrig()回收源图 Image 对象资源,否则会导致内存泄漏- 此
Image可通过YOLOResults.orig()获取
- 此
- 当推理选项中
show: true或save: true时,方法内部会生成带有绘制结果的Image对象资源- 此
Image可通过YOLOResults.plot()获取 - 必须在不需要时调用
YOLOResults.recycle()释放绘制的Image对象资源,否则会导致内存泄漏
- 此
- 输入为网络URL时,会先下载视频到临时目录再进行推理
var videoPredict = yolo.predict(
"/storage/emulated/0/assets/videos/04滑冰.mp4",
{
width: 640,
height: 640,
save: true,
saveTxt: true,
saveConf: false,
verbose: true,
vidStride: 10,
project: "runs",
name: "video_predict",
},
{
onReady: function (width, height, fps, durationUs, totalFrames) {
console.log(
"视频准备完成: width=" +
width +
", height=" +
height +
", fps=" +
fps +
", durationUs=" +
durationUs +
", totalFrames=" +
totalFrames
);
},
onFrame: function (yoloResults, ptsUs, index) {
console.log("处理帧 index=" + index + ", ptsUs=" + ptsUs);
console.log("推理结果: " + yoloResults);
// 回收源帧 Image 对象资源,防止内存泄漏
yoloResults.recycleOrig();
// 回收当前结果绘制的 Image 对象资源
yoloResults.recycle();
},
onProgress: function (percent, index, total) {
console.log("推理进度: " + percent + "%, index=" + index + ", totalFrames=" + total);
},
onComplete: function (frameCount, costMs) {
console.log("推理完成: 总帧数=" + frameCount + ", 总耗时=" + costMs + "ms");
},
onError: function (error) {
console.error("推理错误: " + error);
},
onSaveStart: function () {
console.log("视频保存开始");
},
onSaveProgress: function (percent, index, total) {
console.log("保存进度: " + percent + "%, index=" + index + ", total=" + total);
},
onSaveCompleted: function () {
console.log("视频保存完成");
},
}
);
// -------------------- 执行视频推理 --------------------
// 开始解码视频并对每帧进行推理
console.log("开始解码并推理视频");
videoPredict.start();
// 等待解码与推理完成
console.log("等待解码/推理完成...");
while (videoPredict.isProcessing()) {
sleep(100); // 轮询等待,每 100ms 检查一次
}
// 等待视频帧合并完成(保存视频)
console.log("等待帧合并完成...");
while (videoPredict.isMerging()) {
sleep(100);
}
// 停止解码器
console.log("停止视频解码器");
videoPredict.stop();
// 释放解码器资源,防止内存泄漏
console.log("释放解码器资源");
videoPredict.release();
console.log("视频处理全流程完成");Yolo.predict(facing[, options])
facing{number} 相机方向:0: 后置摄像头1: 前置摄像头
options{Object} 推理选项,可选,参见推理选项- 返回 {FloatCamera}类型的对象,提供相机推理控制
打开摄像头推理预测,打开摄像头进行推理预测需要悬浮窗权限,并且必须在应用处于前台时调用,否则无法正常显示摄像头悬浮窗。
var floatCamera = yolo.predict(0, {
width: 320,
height: 320,
conf: 0.55,
iou: 0.5,
verbose: true,
saveTxt: false,
});
// 启动相机预览
floatCamera.show();
// 当脚本退出时,确保关闭悬浮摄像头
events.on("exit", function () {
try {
if (floatCamera) {
floatCamera.close(); // 安全关闭悬浮摄像头
}
} catch (error) {
console.error(error); // 捕获异常并输出
}
});
// 等待 20 秒后手动关闭悬浮摄像头(可选)
sleep(20000);
floatCamera.close();
//在不需要时记得释放模型资源
yolo.release();Yolo.predictCamera(facing, options, callback)
facing{number} 相机方向:0:后置摄像头1:前置摄像头
options{Object} 推理选项,可选,参见 推理选项callback{Object} 相机推理回调:needImage(){Function} 返回是否需要生成Image对象(源帧和绘制后的帧)。- 默认返回
false,此时不会生成 Image,对性能和内存更友好。 - 如果设置为
true,推理过程中会生成源帧和绘制后的 Image 对象。使用完必须在onResults回调里YOLOResults.recycleOrig() 和 YOLOResults.recycle() 回收,否则会造成内存泄漏。
- 默认返回
onResults(yoloResults){Function} 推理结果回调函数,可在这里处理检测到的目标。yoloResults{YOLOResults} 推理结果- 如果
needImage = true,可在此回调中访问和处理源帧或绘制后的帧,并调用回收方法释放资源。
返回 {FloatCamera} 类型对象,提供悬浮摄像头控制功能。
打开摄像头推理预测,打开摄像头进行推理预测需要悬浮窗权限,并且必须在应用处于前台时调用,否则无法正常显示摄像头悬浮窗。
用途说明
如果你需要保存源帧或绘制后的结果,或在推理过程中对图像做额外处理,可以使用带回调函数的
predictCamera。但是一定要记住:生成的 Image 对象必须在
onResults中回收,否则会造成内存泄漏。如果不需要处理 Image 或保存帧,可以设置
needImage = false,这样性能更高,内存压力更低。
var floatCamera = yolo.predictCamera(
1,
{
width: 320,
height: 320,
verbose: true,
saveTxt: false,
},
{
needImage: function () {
return false; // 是否需要返回 Image 对象;false 表示不生成 Image,可提高推理效率并减少内存占用
},
onResults: function (yoloResults) {
console.log("检测到目标数:", yoloResults.size());
// 如果 needImage 设置为 true:
// 推理过程中会生成原始帧 Image 对象和绘制后的 Image 对象。
// 使用完毕后必须手动释放或回收,否则可能导致内存泄漏。
// var origImg = yoloResults.orig();
// var plotImg = yoloResults.plot();
// if (plotImg) {
// plotImg.saveTo("/sdcard/plotImg.png");
// }
// yoloResults.recycleOrig();
// yoloResults.recycle();
},
}
);
// 启动相机预览
floatCamera.setTitle("我的相机推理"); //设置标题
floatCamera.show(); //显示悬浮
floatCamera.setPosition(100, 100);
floatCamera.setSize(device.width * 0.8, device.height * 0.8);
// 当脚本退出时,确保关闭悬浮摄像头
events.on("exit", function () {
try {
if (floatCamera) {
floatCamera.close(); // 安全关闭悬浮摄像头
}
} catch (error) {
console.error(error); // 捕获异常并输出
}
try {
if (yolo) {
// 脚本退出时执行模型资源清理
// 用于处理异常退出(报错、中断、强制停止等)场景,避免资源泄漏
// 注意:业务逻辑中应主动调用 release(),此处仅作为兜底机制
yolo.release();
}
} catch (error) {
console.error(error);
}
});
// 等待 20 秒后手动关闭悬浮摄像头(可选)
sleep(20000);
floatCamera.close();
//在不需要时记得释放模型资源
yolo.release();Yolo.release()
释放模型及其相关资源。
在模型不再使用时,应主动调用 Yolo.release() 以释放内存资源,避免长期占用内存或导致性能下降。
yolo.release();示例:脚本退出时的兜底释放
在实际开发中,建议在脚本退出时增加资源清理逻辑,用于处理异常退出(报错、中断、强制停止等)场景,防止模型资源未正确释放。
⚠ 注意:此方式属于兜底机制,正常业务流程中仍应主动调用 Yolo.release()。
events.on("exit", function () {
try {
if (yolo) {
// 脚本退出时执行模型资源清理
// 处理异常退出场景,避免资源泄漏
// 正常使用中应主动调用 release()
yolo.release();
}
} catch (error) {
console.error(error);
}
});VideoPredict
VideoPredict 表示一个视频解码与推理控制对象,用于解码视频文件、执行逐帧推理以及控制保存和资源释放。
通过 Yolo.predict(video, options, callback) 获取实例。
VideoPredict.start()
开始解码视频并执行推理。
如果未调用,视频不会开始解码和推理。
VideoPredict.isProcessing()
- 返回 {boolean}
true:正在解码或推理中false:未开始或已完成
返回是否正在解码并推理视频帧。
VideoPredict.isMerging()
- 返回 {boolean}
true:正在合并视频false:未合并或已完成
返回是否正在合并视频帧(生成保存的视频文件)。
仅在 options.save = true 时有效。
VideoPredict.stop()
停止视频解码和推理。
停止后当前处理将终止,不会继续推理剩余帧。
VideoPredict.release()
释放解码器相关资源。
释放后对象不可再次使用,否则可能抛出异常。
建议在视频处理完成后调用,以防止内存泄漏。
FloatCamera
FloatCamera 表示一个悬浮摄像头窗口对象,用于显示摄像头预览并执行实时推理。
通过 Yolo.predictCamera(facing, options, callback) 获取实例。
FloatCamera.show()
显示悬浮摄像头窗口。如果未调用,摄像头不会显示。
FloatCamera.setTitle(title)
title{string} 标题文本
设置悬浮窗标题。
FloatCamera.setPosition(x, y)
x{number} X 坐标(像素)y{number} Y 坐标(像素)
设置悬浮窗位置。基于屏幕左上角坐标。
FloatCamera.setSize(width, height)
width{number} 宽度(像素)height{number} 高度(像素)
设置悬浮窗大小。
FloatCamera.close()
关闭悬浮摄像头并释放相关资源。关闭后不可再次使用。
推理选项
options {object} 包括:
width{number} 输入图像宽度,默认640height{number} 输入图像高度,默认640meanVals{Array<number>} 均值归一化值,默认[0, 0, 0]normVals{Array<number>} 方差归一化值,默认[1/255, 1/255, 1/255]conf{number} 置信度阈值,默认0.25iou{number} IOU 阈值,默认0.7sort{number} 排序方式,默认0,具体参阅识别结果排序规则show{boolean} 是否显示结果,默认falsesave{boolean} 是否保存结果,默认falsesaveTxt{boolean} 是否保存标签文件,默认falsesaveConf{boolean} 是否保存置信度,默认falseverbose{boolean} 是否输出详细信息,默认falsepath{string} 输出路径,默认nullproject{string} 项目名称,默认nullname{string} 结果名称,默认nullvidStride{number} 视频帧采样间隔或帧跳过因子,默认1,在 YOLO 视频推理中,它控制视频处理时跳过的帧数
绘制选项
用于控制识别结果的绘制样式。
所有参数的默认值均可通过 Yolo.getConfig() 获取。
options {object} 包括:
lineWidth{number} 边框线宽,默认0fontSize{number} 字体大小,默认0kptRadius{number} 关键点半径,默认5showBoxes{boolean} 是否显示边界框,默认trueshowLabels{boolean} 是否显示标签,默认trueshowConf{boolean} 是否显示置信度,默认trueshowMasks{boolean} 是否显示掩码,默认trueshowKptLine{boolean} 是否显示骨骼连线,默认trueclassPalette{Array<Array<number>> | Array<number|string>} 类别颜色调色板,可以是二维数组的ARGB、RGB 或是一维数组的颜色值posePalette{Array<Array<number>> | Array<number|string>} 姿态颜色调色板,可以是二维数组的ARGB、RGB 或是一维数组的颜色值skeleton{Array<Array<number>>} 骨架连接关系limbColor{Array<number>} 骨骼颜色索引kptColor{Array<number>} 关键点颜色索引
识别结果排序规则
用于控制识别结果集合的返回顺序。
默认值为 0,表示不进行排序。
| 值 | 排序方式 | 描述 |
|---|---|---|
| 0(默认) | 不排序 | 不对识别结果进行排序,保持模型原始输出顺序(性能最佳) |
| 1 | 从左到右 | 按边界框左上角 X 坐标升序排列(X 越小,越靠左) |
| 2 | 从右到左 | 按边界框左上角 X 坐标降序排列(X 越大,越靠右) |
| 3 | 从上到下 | 按边界框左上角 Y 坐标升序排列(Y 越小,越靠上) |
| 4 | 从下到上 | 按边界框左上角 Y 坐标降序排列(Y 越大,越靠下) |
| 5 | 从左上到右下 | 按 (X + Y) 升序排列(越靠左上越优先) |
| 6 | 从右上到左下 | 按 (X − Y) 降序排列(越靠右上越优先) |
| 7 | 从左下到右上 | 按 (X − Y) 升序排列(越靠左下越优先) |
| 8 | 从右下到左上 | 按 (X + Y) 降序排列(越靠右下越优先) |
| 9 | 从左到右,从上到下 | 优先按 X 升序,X 相同时按 Y 升序 |
| 10 | 从右到左,从上到下 | 优先按 X 降序,X 相同时按 Y 升序 |
| 11 | 从左到右,从下到上 | 优先按 X 升序,X 相同时按 Y 降序 |
| 12 | 从右到左,从下到上 | 优先按 X 降序,X 相同时按 Y 降序 |
| 13 | 按面积从大到小 | 按边界框面积降序排列(面积越大越优先) |
| 14 | 按面积从小到大 | 按边界框面积升序排列(面积越小越优先) |
推理速度
YOLO 版本选择
不同版本的 YOLO 模型在性能和精度上表现差异很大:
性能不一定随版本升高而变快,高版本模型通常更复杂,推理耗时可能更长;低版本模型可能精度不够。
推荐做法:
- 在训练阶段尝试多版本模型,例如 YOLO26、YOLO11、YOLOv8,具体参阅批量训练不同版本。
- 在目标手机上进行 推理测试,评估速度和精度综合表现。
- 最终选择 最适合该设备的 YOLO 版本。
💡 注意:手机 CPU/GPU 性能差异会显著影响模型推理速度,选择模型时务必考虑目标设备。
模型导出与轻量化优化
从 PyTorch 导出 ONNX 或 NCNN 时,可以进行 轻量化优化:
- 剔除冗余节点
- 量化(FP16、INT8)
- 使用 NCNN 优化工具链进行优化
优化效果:
- 推理速度可提升 10% ~ 30%,具体提升与模型结构、输入尺寸和设备性能有关。
- 优化后的模型在精度上通常影响较小,但仍需验证。
详细说明参阅:模型优化
模型输入尺寸优化
输入宽高必须是 32 的倍数。
尺寸影响推理速度与精度:
- 尺寸越大 → 精度越高,但推理速度下降
- 尺寸越小 → 推理速度加快,但精度下降
选择输入尺寸的原则:
根据推理图片区域比例选择输入尺寸:
- 如果仅需要手机屏幕的一部分区域 → 按区域比例训练模型
- 如果需要全屏推理 → 按屏幕纵横比例选择最佳输入尺寸
常见屏幕比例参考:
- 竖屏:高宽比约 2:1 → 推荐输入尺寸
640×320 - 横屏:高宽比约 1:2 → 推荐输入尺寸
320×640
- 竖屏:高宽比约 2:1 → 推荐输入尺寸
输入尺寸参考训练参数
imgsz
💡 建议在训练阶段结合目标屏幕比例进行尺寸选择,以兼顾精度和速度。
后台推理限制
Android 系统会对后台应用进行 CPU/GPU 限频、线程调度降级和 Doze 节流,导致后台推理速度比前台慢 5~10 倍。
优化方法:
将 App 添加到 电池优化白名单,或请求用户忽略电池优化。
开启 前台服务。
效果:可让后台推理速度接近前台,同时保证线程安全和资源稳定。
常见问题
⚠ 重要说明 绝大多数模型加载或推理异常,通常由以下原因导致:
- 模型导出方式或工具使用不正确
- 导出工具版本不同,导致生成的输入输出层名称或结构发生变化
- 加载时指定的
version与实际模型结构不匹配
需要特别注意:
- 即使是同一 YOLO 版本
- 即使使用的是同一个导出工具
只要导出方式或导出工具版本不同,生成的 NCNN 模型输入输出层结构都可能发生变化。
因此:YOLO 版本 ≠ 模型实际输出层结构
加载模型时,必须根据实际输入输出层名称选择匹配的 version,否则可能出现:
- 加载闪退
- 加载报错
- 推理闪退
- 无检测结果
- 检测框异常或数量异常
建议使用 Netron 查看模型结构,并结合「输入输出层对照表」确认后再选择合适的 version。
例如:YOLOv8 使用新版 pnnx 导出后,通常需使用 version: 13 或 version: 26 进行加载。
加载模型问题
1️⃣ 加载模型闪退
请优先检查:
- 输入输出层名称是否匹配
version是否正确
参考:
👉 输入输出层对照表
若以上排查后仍无法解决,可能是模型导出方式或导出工具版本选择不当,建议重新确认导出流程。
2️⃣ 加载模型报错
请根据报错检查 $yolo.load 的各项参数是否正确,比如模型文件路径(bin、param 或 onnx)、类别文件或数组、任务类型、版本号和推理设备,并确保相关文件存在且完整。
3️⃣ 系统架构不支持
主流手机架构均支持:
arm64-v8aarmeabi-v7ax86_64x86
部分虚拟机或非主流设备可能存在 ABI 不匹配问题。
若设备架构与 SDK 编译架构不一致,可能导致模型加载失败或程序崩溃。
推理问题
1️⃣ 推理闪退
常见原因
模型导出方式或导出工具版本不同,可能导致输入输出层名称或结构发生变化。
当加载时指定的 version 与模型实际结构不匹配时, 可能在推理阶段直接崩溃。
排查步骤
- 使用 Netron 查看模型输入输出层名称
- 对照「输入输出层对照表」确认结构
- 重新选择匹配的
version进行加载
若以上排查后仍无法解决,可能是模型导出方式或导出工具版本选择不当,建议重新确认导出流程。
2️⃣ 多实例 GPU 推理闪退
原因: 部分设备 GPU 不支持同时加载多个模型实例进行推理。 在多实例场景下可能导致程序崩溃。
解决方案: 如需同时加载多个模型,请使用 CPU 推理模式:
$yolo.load({
modelPath: "...",
paramPath: "...",
classPath: "...",
task: 0,
version: 13,
device: 0, // 用CPU,别用GPU
});3️⃣ 始终没有推理结果
请优先确认:
version是否与模型实际结构匹配- 输入输出层名称是否正确
- 导出方式是否正确
这是最常见原因。
参考: 👉 输入输出层对照表
4️⃣ 推理结果异常或检测框过多
可能原因包括:
- 输入尺寸设置不正确(参考 输入尺寸说明)
- 置信度阈值设置过高或过低
- 加载时指定的
version与模型实际结构不匹配 - 模型导出方式或导出工具版本不同,导致输出层结构发生变化
建议:
- 使用 Netron 查看模型输入输出层名称
- 对照「输入输出层对照表」确认结构
- 重新选择匹配的
version进行加载
推理一段时间后闪退
在推理过程中,未及时释放占用内存的对象可能导致程序崩溃,主要包括以下两类:
图片对象未释放
在推理过程中,会生成大量 Image 对象:
- 原图:屏幕截图、本地图片、网络图片、视频
- 绘制图:推理过程中生成的标注或绘制结果
回收方法:
images.captureScreen()截屏方式由系统自动管理,无需手动回收。- 其他截屏方式需要手动回收原图,参考:
- 推理过程中生成的绘制图也需手动回收,参考:
YOLO 模型对象未释放
当不再使用 YOLO 对象时,如果未及时释放模型和相关资源,也可能导致内存泄漏和性能下降。
- 请调用
Yolo.release()释放模型及资源。
建议:
- 在长时间或循环推理中,务必对所有生成的图片对象及时回收。
- 使用完 YOLO 对象后,及时调用
Yolo.release()释放资源,避免内存泄漏和性能下降。
示例
屏幕实时目标检测
// 加载模型
var yolo = $yolo.load({
binPath: "/sdcard/yolov8n.ncnn.bin",
paramPath: "/sdcard/yolov8n.ncnn.param",
classes: ["人","自行车","汽车","摩托车","飞机","公交车","火车","卡车","船","红绿灯","消防栓","停止标志","停车计时器","长椅","鸟","猫","狗","马","羊","牛","大象","熊","斑马","长颈鹿","背包","雨伞","手提包","领带","行李箱","飞盘","滑雪板","单板滑雪板","球","风筝","棒球棒","棒球手套","滑板","冲浪板","网球拍","瓶子","酒杯","杯子","叉子","刀","勺子","碗","香蕉","苹果","三明治","橙子","西兰花","胡萝卜","热狗","披萨","甜甜圈","蛋糕","椅子","沙发","盆栽","床","餐桌","马桶","电视","笔记本电脑","鼠标","遥控器","键盘","手机","微波炉","烤箱","烤面包机","水槽","冰箱","书","时钟","花瓶","剪刀","泰迪熊","吹风机","牙刷"],
version: 8,
task: 0,
device: 0,
});
if (!yolo) {
console.error("模型加载失败");
exit();
}
// 当脚本正常或者异常退出时会触发该事件
events.on("exit", function () {
try {
if (yolo) {
yolo.release();
}
} catch (error) {
console.error(error);
}
});
// 配置参数
yolo.setConfig({
width: 320,
height: 320,
conf: 0.25,
iou: 0.7,
verbose: true,
});
// 请求屏幕截图权限(首次使用需要授权)
requestScreenCapture();
// 循环执行 100 次检测
for (var i = 0; i < 100; i++) {
console.log("第", i + 1, "次检测");
// 获取当前屏幕图像
var img = captureScreen();
// 推理
var yoloResults = yolo.predict(img);
// 输出结果
console.log("检测到", yoloResults.size(), "个目标");
// 获取摘要
var summary = yoloResults.summary();
for (var i = 0; i < summary.length; i++) {
var obj = summary[i];
console.log(
obj.name +
" 置信度:" +
obj.confidence +
" 左上:(" +
obj.box.x1 +
"," +
obj.box.y1 +
")" +
" 右下:(" +
obj.box.x2 +
"," +
obj.box.y2 +
")"
);
}
// 绘制检测框到屏幕
$draw.yolo(yoloResults);
sleep(1000);
// 清除绘制内容
$draw.clearSync();
// 释放本次推理结果对象(重要)
yoloResults.recycle();
}
// 主动释放模型资源
yolo.release();相机检测物体
本功能可在检测到目标后,将图片保存到本地,也可根据需要发送邮件通知。
例如,可用于检测异常事件或安全相关场景,如火焰、烟雾、人员进入指定区域等。
⚠ 注意事项:
前台运行
- 需在前台运行,否则悬浮相机弹窗可能无法正常显示。
权限要求
- 必须授予相机权限,否则无法打开相机。
- 如果保存到本地,还需授予存储权限。
var yolo = $yolo.load(
{
binPath: "/sdcard/yolo11s.ncnn.bin",
paramPath: "/sdcard/yolo11s.ncnn.param",
task: 0,
classes: ["人","自行车","汽车","摩托车","飞机","公交车","火车","卡车","船","红绿灯","消防栓","停止标志","停车计时器","长椅","鸟","猫","狗","马","羊","牛","大象","熊","斑马","长颈鹿","背包","雨伞","手提包","领带","行李箱","飞盘","滑雪板","单板滑雪板","球","风筝","棒球棒","棒球手套","滑板","冲浪板","网球拍","瓶子","酒杯","杯子","叉子","刀","勺子","碗","香蕉","苹果","三明治","橙子","西兰花","胡萝卜","热狗","披萨","甜甜圈","蛋糕","椅子","沙发","盆栽","床","餐桌","马桶","电视","笔记本电脑","鼠标","遥控器","键盘","手机","微波炉","烤箱","烤面包机","水槽","冰箱","书","时钟","花瓶","剪刀","泰迪熊","吹风机","牙刷"],
version: 11,
device: 0,
},
{
onload: function (yolo, success) {
console.log(success ? "NCNN 模型加载成功" : "NCNN 模型加载失败");
},
}
);
if (!yolo) {
console.error("加载失败");
exit();
}
var floatCamera = yolo.predictCamera(
0,
{
conf: 0.25,
iou: 0.7,
width: 320,
height: 320,
// verbose: true,
saveTxt: false,
saveConf: false,
},
{
needImage: function () {
return true;
},
onResults: function (yoloResults) {
if (yoloResults.size() > 0) {
// console.log("检测到目标数:", yoloResults.size());
var yoloObjects = yoloResults.getYoloObjects();
var hasPerson = yoloObjects.some(function (obj) {
return obj.cls === 0; //判断是否检测到“人”(cls = 0)
});
if (hasPerson) {
console.log("检测到人员,保存图片");
var plotImg = yoloResults.plot();
if (plotImg) {
// 确保目录存在
var saveDir = "/sdcard/cloud/yolo/";
files.ensureDir(saveDir);
var filePath = saveDir + generateFileName();
plotImg.saveTo(filePath);
}
// var origImg = yoloResults.orig();
// if (origImg) {
// var filePath = "/sdcard/cloud/yolo/orig" + generateFileName();
// origImg.saveTo(filePath);
// }
}
}
// 回收原图
yoloResults.recycleOrig();
// 回收绘制图
yoloResults.recycle();
},
}
);
floatCamera.setTitle("我的相机推理");
floatCamera.show(); //显示悬浮
// 当脚本退出时,确保关闭悬浮摄像头和释放模型资源
events.on("exit", function () {
try {
if (floatCamera) {
floatCamera.close(); // 安全关闭悬浮摄像头
}
} catch (error) {
console.error(error); // 捕获异常并输出
}
try {
if (yolo) {
yolo.release();
}
} catch (error) {
console.error(error);
}
});
/**
* 生成带日期时间和毫秒的图片文件名
*
* 格式示例: IMG_20260228_154523_037.png
* 符合手机相册常用命名规范,便于排序和识别拍摄时间
*
* @returns {string} 带日期时间和毫秒的图片文件名
*/
function generateFileName() {
var now = new Date();
var yyyy = now.getFullYear();
var MM = pad(now.getMonth() + 1);
var dd = pad(now.getDate());
var HH = pad(now.getHours());
var mm = pad(now.getMinutes());
var ss = pad(now.getSeconds());
var SSS = padMs(now.getMilliseconds());
return "IMG_" + yyyy + MM + dd + "_" + HH + mm + ss + "_" + SSS + ".png";
}
// 内部工具函数:补零到两位数
function pad(num) {
return num < 10 ? "0" + num : num;
}
// 内部工具函数:补零到三位数
function padMs(num) {
if (num < 10) return "00" + num;
if (num < 100) return "0" + num;
return num;
}
// 放在脚本结尾,保持脚本运行
setInterval(() => {}, 1000);