起源
事情的起因是这样的,每年的结婚纪念日我们都有拼乐高的传统。
但是拼的时候积木块又小又多经常都找不到对应的积木兼职,太痛苦了。😖
于是心生一计,想通过手机识别到我要找的积木,然后直接用框给我标出来,省时省力不费眼,岂不美哉。😎
恰巧之前写过浏览器上运行识别狗的一个功能 "🚫 为了防止狗上沙发,写了一个浏览器实时识别目标功能 📷",想着拿来改造一下应该就行了。但是 coco-ssd 只能识别出日常的 80 多种物体。所以需要自己训练一个,或者找一个训练好的“识别积木模型”。 🤖
要找可直接运行的最终代码请直接拉到文末
步骤
[ol]
[/ol]
解决思路 📚:
[ol]
[/ol]
这里我使用的是p5.js 一个流行的 JavaScript 库,简化了视觉和交互体验的创建,提供了易于使用的 API 用于绘图、处理用户输入以及处理如视频等媒体。
其中 setup() 和 draw() 是内置函数 😊 不需要调用
/**
* 初始化函数,设置画布大小并配置视频捕获属性。
* 该函数不接受参数,也不返回任何值。
*/
function setup() {
// 创建画布并设置其尺寸
canvas = createCanvas(640, 480);
// 计算水平和垂直缩放因子,以保持捕获的视频与画布尺寸一致
scaleX = 640 / +canvas.canvas.width || 640;
scaleY = 480 / +canvas.canvas.height || 480;
// 定义视频捕获的约束,指定使用后置摄像头
let constraints = {
video: {
facingMode: "environment",
},
};
// 创建视频元素并配置其大小,注册视频准备就绪的回调函数
video = createCapture(constraints, videoReady);
video.size(640, 480);
video.hide(); // 隐藏视频元素,仅使用其捕获的视频数据
}
2. 🔍 加载对应的识别乐高的模型
原本想要使用ml5.js 但是发现需要自己再训练乐高的模型且训练速度很慢,限制很多,作罢 😕。
目前使用的是 roboflow.js 同样是基于 tensorFlow.js 但是社区中有很多的训练好可直接使用的模型。
这里模型配置可信值我降低到了 0.15 ,因为发现高可信值的模型识别率太低了 😏。
/**
* 异步函数 videoReady ,初始化视频处理模型并准备就绪。
*/
async function videoReady() {
console.log("videoReady");
// 等待模型加载
model = await getModel();
// 配置模型的阈值
model.configure({ threshold: 0.15 });
// 更新 UI ,表示模型已准备好
loadText.innerHTML = "modelReady";
console.log("modelReady");
// 选择要识别的目标
processSelect();
// 开始检测
detect();
}
/**
* 异步函数 getModel ,从 roboflow 服务加载指定的模型。
*/
async function getModel() {
return await roboflow
.auth({
publishable_key: "rf_lOpB8GQvE5Uwp6BAu66QfHyjPA13", // 使用 API 密钥进行授权
})
.load({
model: "hex-lego", // 指定要加载的模型名称
version: 3, // 指定要加载的模型版本
});
}
3. 🧱 选择要识别的“目标积木”类型
绑定要选择的“目标积木”
function processSelect() {
const { classes } = model.getMetadata();
console.log("classes", classes);
classes.forEach((className) => {
const option = document.createElement("option");
option.value = className;
option.text = className;
selectRef.appendChild(option);
});
}
4. 🔎 让该模型识别图像并返回出识别对象的信息
调用模型的 API 进行识别,以便于后续的绘制
const detect = async () => {
if (!play || !model) {
console.log("model is not available");
timer = setTimeout(() => {
requestAnimationFrame(detect);
clearTimeout(timer);
}, 2000);
return;
}
detections = await model.detect(canvas.canvas);
console.log("detections", detections);
requestAnimationFrame(detect);
};
5. 🎨 通过识别出的对象信息在 canvas 上进行绘制
获取到模型返回的信息保存并将识别到的信息都用 canvas 绘制出来
function draw() {
image(video, 0, 0);
for (let i = 0; i
6. 📲 部署在手机上
最终代码
find Lego
model is loading... please wait.
start
stop
总结 🎓
[ol]
[/ol]
[ol]
[/ol]
[ol]
[/ol]
改进方向 🚀
写在最后 😅
大家如果还有什么好方法的话可以一起分享一下 😊
还没等摸鱼的时候写好功能,老婆已经拼完了。。。