The Nodes I used for this:
- node-red-contrib-indigo. https://flows.nodered.org/node/node-red-contrib-indigo
node-red-contrib-facial-recognition https://flows.nodered.org/node/node-red-contrib-facial-recognition
node-red-contrib-image-output https://flows.nodered.org/node/node-red-contrib-image-output
node-red-node-annotate-image https://flows.nodered.org/node/node-red-node-annotate-image
On the backend, you'll need:
- 1 action to start the node-red flow.
6 variables for flow data to go into
1 "KnownFaces" folder on your desktop. The subfolders are labeled "Firstname Lastname" with pictures of those individuals faces inside the corresponding folder.
1 "UnknownFaces" folder on your desktop
Some other mechanism of your choosing to capture an image and save as "UKF.jpg" into the "UnknownFaces" folder
The process:
- IP Camera script saves a snapshot and puts a copy "UKF.jpg" into my "UnknownFaces" folder.
An action group updates the HTTP In node in my Facial Recognition Flow. The action just executes this script:
- Code: Select all
import requests
r = requests.get('http://192.168.1.10:1880/FaceRecognizer?recognize=all')
r.raise_for_status()
Running the action starts the Node-Red flow.
The facial recognizer node does its magic and then does a couple things.
- 1) annotates the image with a box around each face and a name of all recognized faces.
2) saves a copy of the annotated image into the "UnknownFaces" folder with the name "Found.jpg" <-- overwritten whenever this is run.
3) splits out the message for the first two people recognized and updates Indigo Variables with that information.
If you import this into your node-red, that should give you a leg up on the process if you are going to set it up the way I did:
- Code: Select all
[{"id":"83d559af.a0ed6","type":"tab","label":"Facial Recognition","disabled":false,"info":""},{"id":"dd4c55d0.245","type":"http in","z":"83d559af.a0ed6","name":"192.168.1.10:1880/FaceRecognizer","url":"/FaceRecognizer","method":"get","upload":false,"swaggerDoc":"","x":160,"y":240,"wires":[["9c39ee04.5a9a7","d640e2e5.927328"]]},{"id":"9c39ee04.5a9a7","type":"http response","z":"83d559af.a0ed6","name":"","statusCode":"","headers":{},"x":390,"y":300,"wires":[]},{"id":"d640e2e5.927328","type":"json","z":"83d559af.a0ed6","name":"","property":"payload","action":"","pretty":false,"x":390,"y":260,"wires":[["3fce7ac2.2026be"]]},{"id":"3fce7ac2.2026be","type":"switch","z":"83d559af.a0ed6","name":"","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"all","vt":"str"},{"t":"cont","v":"off","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":510,"y":260,"wires":[["a1fcdc7f.dafe8"],["376864a.dc6831c"]]},{"id":"a1fcdc7f.dafe8","type":"change","z":"83d559af.a0ed6","name":"ON","rules":[{"t":"set","p":"payload","pt":"msg","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":220,"wires":[["f363ff4e.11ea58"]]},{"id":"376864a.dc6831c","type":"change","z":"83d559af.a0ed6","name":"OFF","rules":[{"t":"set","p":"payload","pt":"msg","to":"OFF","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":300,"wires":[[]]},{"id":"f363ff4e.11ea58","type":"file in","z":"83d559af.a0ed6","name":"","filename":"/users/yourname/Desktop/UnknownFaces/UKF.jpg","format":"","chunk":false,"sendError":false,"encoding":"none","x":300,"y":460,"wires":[["da7daaf8.264a38"]]},{"id":"d394509a.939b3","type":"function","z":"83d559af.a0ed6","name":"Prep rects for annotate image node","func":"//global vars\nvar the_rects;\n\n//was detectAllFaces or detectSingleFace used\n//check to see if payload.Result is an array (detectAllFaces)\nvar Result = msg.payload.Result;\nif ( Array.isArray(Result) ) {\n // get just the rect values and place in array\n the_rects = Result.map(x => {\n //check for label from FaceRecognition\n var match_label;\n if ( x.match ) {\n match_label = x.match._label;\n }\n else {\n match_label = \"\";\n }\n var result = {\n type: \"rect\",\n x: x.detection._box._x,\n y: x.detection._box._y,\n w: x.detection._box._width,\n h: x.detection._box._height,\n label: match_label\n }\n return result;\n });\n msg.annotations = the_rects;\n}\n//else detectSingleFace was used\nelse {\n //check for label from FaceRecognition\n var match_label;\n if ( Result.match ) {\n match_label = Result.match._label;\n }\n else {\n match_label = \"\";\n }\n the_rects = [{\n type: \"rect\",\n x: Result.detection._box._x,\n y: Result.detection._box._y,\n w: Result.detection._box._width,\n h: Result.detection._box._height,\n label: match_label.match._label\n }]\n msg.annotations = the_rects;\n}\n\n\n\n//var xx = msg.payload.Result[0].detection._box._x;\n//var yy = msg.payload.Result[0].detection._box._y;\n//var ww = msg.payload.Result[0].detection._box._width;\n//var hh = msg.payload.Result[0].detection._box._height;\n\n//msg.annotations = [ {\n// type: \"rect\",\n// //bbox: [ xx, yy, ww, hh],\n// x: xx, y: yy, w: ww, h: hh,\n// //bbox: [ 100, 100, 100, 100],\n// label: \"Tara Sanford\"\n//}]\n\nmsg.payload = msg.payload.OriginalBufferedImg;\n\nreturn msg;\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":980,"y":340,"wires":[["a704c0e3.d1086"]]},{"id":"a704c0e3.d1086","type":"annotate-image","z":"83d559af.a0ed6","name":"","fill":"","stroke":"#0070c0","lineWidth":"20","fontSize":"48","fontColor":"#0070c0","x":900,"y":420,"wires":[["7c0b0a99.fba74c","d4d0bc7f.43516"]]},{"id":"da7daaf8.264a38","type":"facial-recognition","z":"83d559af.a0ed6","image":"payload","settings":"settings","name":"","bindings":"CPU","FaceDetector":"SsdMobilenetv1","FaceDetector_SsdMobilenetv1_maxResults":"5","FaceDetector_SsdMobilenetv1_minConfidence":"0.6","FaceDetector_tinyFaceDetector_inputSize":"416","FaceDetector_tinyFaceDetector_scoreThreshold":"0.5","Tasks":"detectAllFaces","FaceLandmarks":true,"FaceExpressions":true,"AgeAndGender":true,"FaceDescriptors":true,"Face_Recognition":"Face_Recognition_enabled","Face_Recognition_enabled_path":"/users/williammoore/Desktop/KnownFaces","Face_Recognition_distanceThreshold":0.7,"x":630,"y":460,"wires":[["d394509a.939b3","fa677f40.aa8388","72039a5d.15b924","fcbfdb46.c7f768","f6133bfe.14f7d","28c8f510.8dd42a","5018bb5.11054c4"]]},{"id":"7c0b0a99.fba74c","type":"file","z":"83d559af.a0ed6","name":"Save Processed Image","filename":"/users/yourname/Desktop/UnknownFaces/Found.jpg","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":940,"y":480,"wires":[[]]},{"id":"541278cb.d7866","type":"inject","z":"83d559af.a0ed6","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":380,"wires":[["f363ff4e.11ea58"]]},{"id":"82c1232.b9d616","type":"comment","z":"83d559af.a0ed6","name":"Launched From Indigo Action","info":"","x":140,"y":200,"wires":[]},{"id":"d4d0bc7f.43516","type":"image","z":"83d559af.a0ed6","name":"","width":"800","data":"payload","dataType":"msg","thumbnail":false,"active":true,"pass":true,"outputs":1,"x":840,"y":540,"wires":[[]]},{"id":"2cf91b2f.11b27c","type":"indigo-set-variable","z":"83d559af.a0ed6","name":"Person 0 Name","controller":"2387b996.34c33e","itemname":"Person0MatchLable","x":480,"y":580,"wires":[[]]},{"id":"fa677f40.aa8388","type":"change","z":"83d559af.a0ed6","name":"Person 0 Name","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Result[0].match._label","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":580,"wires":[["2cf91b2f.11b27c"]]},{"id":"c7279453.56e3f","type":"indigo-set-variable","z":"83d559af.a0ed6","name":"Person 0 Gender","controller":"2387b996.34c33e","itemname":"Person0Gender","x":490,"y":620,"wires":[[]]},{"id":"602c2ef5.0e903","type":"indigo-set-variable","z":"83d559af.a0ed6","name":"Person 0 Age","controller":"2387b996.34c33e","itemname":"Person0Age","x":470,"y":660,"wires":[[]]},{"id":"72039a5d.15b924","type":"change","z":"83d559af.a0ed6","name":"Person 0 Gender","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Result[0].gender","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":620,"wires":[["c7279453.56e3f"]]},{"id":"fcbfdb46.c7f768","type":"change","z":"83d559af.a0ed6","name":"Person 0 Age","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Result[0].age","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":660,"wires":[["602c2ef5.0e903"]]},{"id":"dff3f647.fdb778","type":"indigo-set-variable","z":"83d559af.a0ed6","name":"Person 1 Name","controller":"2387b996.34c33e","itemname":"Person1MatchLable","x":480,"y":740,"wires":[[]]},{"id":"f6133bfe.14f7d","type":"change","z":"83d559af.a0ed6","name":"Person 1 Name","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Result[1].match._label","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":740,"wires":[["dff3f647.fdb778"]]},{"id":"3befb188.164216","type":"indigo-set-variable","z":"83d559af.a0ed6","name":"Person 1 Gender","controller":"2387b996.34c33e","itemname":"Person1Gender","x":490,"y":780,"wires":[[]]},{"id":"2df1b09.f9a1d5","type":"indigo-set-variable","z":"83d559af.a0ed6","name":"Person 1 Age","controller":"2387b996.34c33e","itemname":"Person1Age","x":470,"y":820,"wires":[[]]},{"id":"28c8f510.8dd42a","type":"change","z":"83d559af.a0ed6","name":"Person 1 Gender","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Result[1].gender","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":780,"wires":[["3befb188.164216"]]},{"id":"5018bb5.11054c4","type":"change","z":"83d559af.a0ed6","name":"Person 1 Age","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.Result[1].age","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":820,"wires":[["2df1b09.f9a1d5"]]},{"id":"d7d6e5fd.570ab8","type":"comment","z":"83d559af.a0ed6","name":"Use Timestamp to test your work","info":"","x":170,"y":340,"wires":[]},{"id":"bd48f09a.94a22","type":"comment","z":"83d559af.a0ed6","name":"Set location folder of Known Images and file path for unknown","info":"","x":580,"y":500,"wires":[]},{"id":"554249d3.e22a3","type":"comment","z":"83d559af.a0ed6","name":"Image preview isn't needed, good for testing","info":"","x":1090,"y":380,"wires":[]},{"id":"2387b996.34c33e","type":"indigo-controller","name":"INDIGOSERVER","protocol":"http","host":"HOSTNAME.local.","port":"8176","username":"","password":""}]
Happy Tinkering!!