Fun project!
The GitHub project page for Node-RED node with a TensorFlow.js Object Detection model cautions that their demo code only works with the Raspberry PI. However, today I got it working (quite easily) with macOS.
It's fairly accurate too! However, LOL, there was no airplane in our condo today!!!; but maybe that reading happened when I was moving the camera around, haha. Here is a quick screen grab from Telegram on the desktop.
Here is my Node-RED flow for this caper:
To build this, just follow the README.md file on the GitHub page above and here (again):
https://github.com/IBM/node-red-tensorflowjs
It might also help to watch the YT Video:
Creating A Machine Learning IoT App on Raspberry Pi with Node-RED and TensorFlow.js. Jan 7, 2020
The instructions are easy to follow, and so I will not repeat them here. However, here are the steps to get it to work on macOS:
teps:
- Install rpi-gpio into Node-RED (so we can disable those nodes easily when we import the flow from the demo code);
- Install node-red-tensorflowjs in Node-RED
- brew install imagesnap;
- audio node in Node-RED: change aplay to afplay;
- adjust full paths as needed;
I modified most of the flow to create this logic, at a high level
- Turn On and Off the WebCam using Telegram
- Turn On and Off the "nothing detected" message with Telegram (debugging when away)
- Retooled the detection logic.
New detection logic (function) as follows:
var onoff = global.get("onoff") || "on";
let newMsg = {};
var chat = "your_telegram_chat_id_here";
var type = "message";
var message = "";
var count = 0;
var score = 0.0;
var onoi = ["chair", "couch", "bench"]; // onoi = "objects not of interest"
if (onoff == "on" || onoff == "all") {
for (let object of msg.payload) {
if (object.class.length > 2) {
if (onoi.includes(object.class)) {
} else {
count++;
score = (100 * object.score).toFixed(1);
message =
'TF CamBot detected a "' +
object.class +
'" with ' +
score +
"% confidence.";
newMsg = { payload: { chatId: chat, type: type, content: message } };
//console.log(newMsg.payload);
node.send([[newMsg]]);
}
}
}
if (count < 1 && onoff == "all") {
message = "TF CamBot detected nothing!";
newMsg = { payload: { chatId: chat, type: type, content: message } };
node.send([[newMsg]]);
}
}
Here is the Node-RED flow:
[{"id":"21dff30d.cdd83c","type":"tab","label":"NEO DETECT","disabled":false,"info":""},{"id":"940a9a8d.367588","type":"inject","z":"21dff30d.cdd83c","name":"Take Photo","topic":"","payload":"","payloadType":"date","repeat":"15","crontab":"","once":true,"onceDelay":0.1,"x":130,"y":180,"wires":[["f2a273c5.fc849"]]},{"id":"71a92361.dbad0c","type":"debug","z":"21dff30d.cdd83c","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1000,"y":280,"wires":[]},{"id":"339bc42f.3b72fc","type":"tfjs-object-detection","z":"21dff30d.cdd83c","name":"","modelUrl":"","x":760,"y":180,"wires":[["71a92361.dbad0c","5ff0aa2f.76ba54"]]},{"id":"4442c40d.9bc1dc","type":"usbcamera","z":"21dff30d.cdd83c","filemode":"0","filename":"image01.jpg","filedefpath":"1","filepath":"/Users/Tim","fileformat":"jpeg","resolution":"2","name":"","x":530,"y":180,"wires":[["339bc42f.3b72fc","518f1480.b2223c"]]},{"id":"518f1480.b2223c","type":"image","z":"21dff30d.cdd83c","name":"","width":"240","data":"payload","dataType":"msg","thumbnail":true,"active":false,"x":760,"y":280,"wires":[]},{"id":"cddbc39f.fb82c","type":"comment","z":"21dff30d.cdd83c","name":"Check detected classes for specific object.","info":"","x":210,"y":280,"wires":[]},{"id":"6dc534c0.7946ac","type":"telegram sender","z":"21dff30d.cdd83c","name":"CamBot Send","bot":"995ad6d2.ff8ae8","x":1240,"y":100,"wires":[[]]},{"id":"5ff0aa2f.76ba54","type":"function","z":"21dff30d.cdd83c","name":"Detection Logic","func":"var onoff =global.get('onoff') || 'on';\nlet personDetected = false;\nlet newMsg = {};\nvar chat = 727630977;\nvar type = \"message\";\nvar message = \"\";\nvar count = 0;\nvar score = 0.0;\nvar onoi = [\"chair\", \"couch\", \"bench\"];\n\nif(onoff ==\"on\" ||onoff == \"all\")\n{\nfor (let object of msg.payload) {\n if (object.class.length > 2 ) {\n if (onoi.includes(object.class)) {\n }\n else {\n count++;\n score = (100*object.score).toFixed(1);\n message = 'TF CamBot detected a \"'+ object.class+'\" with '+score+'% confidence.';\n newMsg = {payload:{chatId:chat,type:type,content:message}};\n //console.log(newMsg.payload);\n node.send([ [ newMsg] ]);\n }\n }\n}\n\nif(count < 1 && onoff == \"all\"){\n message = 'TF CamBot detected nothing!';\n newMsg = {payload:{chatId:chat,type:type,content:message}};\n node.send([ [ newMsg] ]);\n}\n}\n","outputs":1,"noerr":0,"x":1010,"y":180,"wires":[["6dc534c0.7946ac"]]},{"id":"492a7004.57b96","type":"telegram receiver","z":"21dff30d.cdd83c","name":"CamBot In","bot":"995ad6d2.ff8ae8","saveDataDir":"","x":110,"y":100,"wires":[["f2c0ca3b.61eac8"],[]]},{"id":"f2c0ca3b.61eac8","type":"change","z":"21dff30d.cdd83c","name":"Global: On/Off/All ","rules":[{"t":"set","p":"onoff","pt":"global","to":"payload.content","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":100,"wires":[["168875ba.43431a"]]},{"id":"168875ba.43431a","type":"function","z":"21dff30d.cdd83c","name":"Confirm C2","func":"let newMsg = {};\nvar chat = 111111111;\nvar type = \"message\";\nmessage = \"TF CamBot is \"+msg.payload.content;\nnewMsg = {payload:{chatId:chat,type:type,content:message}};\nnode.send([ [ newMsg] ]);\n","outputs":1,"noerr":0,"x":530,"y":100,"wires":[["6dc534c0.7946ac"]]},{"id":"f2a273c5.fc849","type":"function","z":"21dff30d.cdd83c","name":"On || Off ?","func":"var onoff =global.get('onoff') || 'on';\nif(onoff =='on' || onoff =='all')\n{\nreturn msg;\n}\n\n","outputs":1,"noerr":0,"x":310,"y":180,"wires":[["4442c40d.9bc1dc"]]},{"id":"995ad6d2.ff8ae8","type":"telegram bot","z":"","botname":"YourCoolCambot","usernames":"","chatids":"","baseapiurl":"","updatemode":"polling","pollinterval":"300","usesocks":false,"sockshost":"","socksport":"6667","socksusername":"anonymous","sockspassword":"","bothost":"","localbotport":"8443","publicbotport":"8443","privatekey":"","certificate":"","useselfsignedcertificate":false,"sslterminated":false,"verboselogging":false}]
Works like a charm so far, just needs some fine tuning.
Enjoy!
My plan is to try to detect the seagulls which sometimes fly into our condo since the window are open most of the time! Those bird go a bit crazy when they fly into the condo (normally only one at a time) looking for food, and cannot fly out so easily!
Well, it's a good life if that is the main problem we want to detect, for sure!