btsimonh's node-opencv motion detection (2017-11-02)

This flow shows use of node-opencv (from https://github.com/peterbraden/node-opencv) used for motion detection on RPi3 at 5fps at 640x480. At 5fps, it uses about 25% of the RPi. Ensure you install from gihub - npm install peterbraden/node-opencv from ~/.node-red

You will need to install opencv 3.3.0 (see https://btsimonh.wordpress.com/node-opencv-with-node-red/).

You will need https://flows.nodered.org/node/node-red-contrib-multipart-stream-encoder

You will need 'require' in globals.

Notes:

Press start to start the flow.

Press stop to stop the flow. make sure you stop before re-deploying, else you may find you can't restart (unable to open video).

Point a browser at /test2 for a mjpeg stream of the input video with boxes drawn for motion detected.

Point a browser at /mvmt for a mjpeg stream of the output of the opencv background extractor.

FPS, Motion detection frame size is set using 'width' in 'Variables'. It will work at higher framerates with smaller image sizes, and because some of the processing is Async, you can occupy more than one core of the RPi. Images should be re-ordered to correct order at output.

Updated to allow global or NR local node-opencv install.

[{"id":"a1ec0d46.3fb21","type":"inject","z":"2038339d.f16dfc","name":"Start","topic":"","payload":"1","payloadType":"num","repeat":"","crontab":"","once":false,"x":130,"y":40,"wires":[["444593e4.1e35bc"]]},{"id":"91c9965b.8a7898","type":"function","z":"2038339d.f16dfc","name":"LoadCV","func":"var require = global.get('require');\nvar cv = null;\ntry{\n    cv = require('opencv');\n} catch (e){\n    cv = require.main.require('opencv');\n}\nvar cvdesc = Object.keys(cv);\nnode.send([null, {payload:cvdesc}]);\nflow.set('cv', cv);\n\n var bg = flow.get('bg');\n if (bg){\n     delete bg;\n     bg = null;\n }\n \n bg = cv.BackgroundSubtractor.createMOG2();\n node.warn(\"made bg \"+util.inspect(bg));\n flow.set('bg', bg);\n \n\nnode.send({payload:1});\nnode.send({payload:'next'});\n","outputs":"2","noerr":0,"x":420,"y":40,"wires":[["ab72f37b.94ad7"],[]]},{"id":"ab72f37b.94ad7","type":"function","z":"2038339d.f16dfc","name":"GetImage","func":"\n\nif (msg.payload === 0){\n    flow.set('state', 'stop');\n    \n    // in case we never hit if(stop), delete in 200ms\n    // else can't open camera ever again. \n    setTimeout(function(){\n        var vid = flow.get('cvvid');\n        if (vid){\n            // stopping, so delete vid whilst we're not using it.\n            node.warn(util.inspect(vid));\n            vid.release();\n            flow.set('cvvid', null);\n            delete vid;\n        }\n    }, 200);\n}\n\n\nif (msg.payload === 1){\n try{\n     flow.set('state', 'run');\n     flow.set('start', null);\n     flow.set('count', null);\n     flow.set('last_s', null);\n    \n     var cv = flow.get('cv');\n     var timings = flow.get('timings') || {};\n     timings.startup = {};\n     timings.startup.start = Date.now();\n     var vid = new cv.VideoCapture(0);\n     var fps = vid.setFPS(flow.get('fps'));\n     node.warn(fps);\n     timings.startup.end = Date.now();\n     timings.startup.diff = timings.startup.end - timings.startup.start;\n     \n     node.warn(vid);\n     flow.set('cvvid', vid);\n } catch (e){\n     node.warn(e);\n }\n}\n\nif (msg.payload === 'ack'){\n var timings = flow.get('timings');\n timings.imagecidiff = context.imageci - msg.imageci;\n return;\n}\n\n\nif (msg.payload === 'next'){\n var vid = flow.get('cvvid');\n \n if (vid){\n     try{\n         //node.warn(\"grabbed \" + util.inspect(err) + \" \"+util.inspect(im));\n         vid.read(function(err, im){\n             try{\n                var state = flow.get(\"state\");\n                switch(state){\n                    case 'stop':\n                        // stopping, so delete vid whilst we're not using it.\n                        node.warn(util.inspect(vid));\n                        vid.release();\n                        flow.set('cvvid', null);\n                        delete vid;\n                        return;\n                    break;\n                    default:\n                        break\n                }\n                \n                if (err) {\n                    node.warn(\"read \" + util.inspect(err) + \" \"+util.inspect(im));\n                    return;\n                }\n                 \n                if ((im.size()[0] === 0) && (im.size()[1] === 0)){\n                    node.warn(\"image has zero width or height\");\n                    return;    \n                }\n             \n                msg.camera = im;\n                msg.flowstarttime = new Date();\n                \n                msg.framenumber = context.framenum || 0;\n                context.framenum = msg.framenumber + 1;\n\n                context.lasttime = context.lasttime || 0;\n\n                node.send([null, {payload:0}, null]);\n\n                var frametime = Date.now();\n                if (context.lasttime){\n                    node.send([null, null, {payload:frametime - context.lasttime, topic:\"inputframeinterval\"}]);\n                }\n                context.lasttime = frametime;\n                \n                node.send(msg);\n             } catch(e){\n                node.warn(e);\n             }\n         });\n     } catch (e){\n         node.warn(e);\n     }\n }\n}\n","outputs":"3","noerr":0,"x":320,"y":140,"wires":[["23f78acd.d78c66"],["fb976227.4659e"],["8dc10c1c.1e0ff"]]},{"id":"f7b41010.97451","type":"inject","z":"2038339d.f16dfc","name":"Stop","topic":"","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"x":130,"y":80,"wires":[["ab72f37b.94ad7"]]},{"id":"6873c74.548ce38","type":"function","z":"2038339d.f16dfc","name":"BackgroundSubtraction","func":"var cv = flow.get('cv');\n\ntry {\n var bg = flow.get('bg');\n if (!bg){\n }\n \n if (bg){\n    var bgstart = (new Date()).valueOf();\n    \n    //node.warn(msg.img.type());\n    //node.warn(utils.inspect(msg.img))\n    //node.warn(\"refcountbefore addref \"+msg.img.getrefCount());\n    //msg.img.addref();\n    //node.warn(\"refcountbefore fn \"+msg.img.getrefCount());\n    bg.apply(msg.img, function(err, mat){\n        //node.warn(\"did mog \"+err+\" \"+util.inspect(mat));\n        try{\n            var bgend = (new Date()).valueOf();\n            var bgtime = bgend - bgstart;\n            node.send([null, {payload:bgtime, topic:\"bgtime\"}]);\n            \n            msg.out = mat;\n            //node.warn(\"did mog:\"+util.inspect(mat));\n \n            node.send(msg);\n            //msg.avg.release(); \n            return;\n        } catch(e){\n            node.warn(\"exception\"+e);\n        }\n    });\n    \n    ///********************** test to break it\n    //node.warn(\"refcountbefore release2 \"+msg.img.getrefCount());\n    msg.img.release();\n    delete msg.img;\n }\n \n} catch(e){\n node.warn(e);\n}\n\nreturn;\n\nreturn msg;","outputs":"2","noerr":0,"x":910,"y":100,"wires":[["c5b16a4b.8a7818"],["8dc10c1c.1e0ff"]]},{"id":"fb976227.4659e","type":"function","z":"2038339d.f16dfc","name":"GetNextFrame","func":"setTimeout(function(){\n node.send({payload:'next'});\n}, 10);\nreturn;","outputs":1,"noerr":0,"x":320,"y":220,"wires":[["ab72f37b.94ad7"]]},{"id":"cfd82a72.82b288","type":"multipart-encoder","z":"2038339d.f16dfc","name":"","statusCode":"","ignoreMessages":true,"outputOneNew":true,"outputOneClosed":false,"outputAllClosed":false,"globalHeaders":{"Content-Type":"multipart/x-mixed-replace;boundary=myboundary","Connection":"keep-alive","Expires":"Fri, 01 Jan 1990 00:00:00 GMT","Cache-Control":"no-cache, no-store, max-age=0, must-revalidate","Pragma":"no-cache"},"partHeaders":{"Content-Type":"image/jpeg"},"destination":"all","x":920,"y":520,"wires":[["33c1bb6.75dfa44"]]},{"id":"31e6b5c5.433c0a","type":"http in","z":"2038339d.f16dfc","name":"","url":"/test2","method":"get","upload":false,"swaggerDoc":"","x":720,"y":520,"wires":[["cfd82a72.82b288"]]},{"id":"33c1bb6.75dfa44","type":"function","z":"2038339d.f16dfc","name":"","func":"flow.set('enableavg', msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":1070,"y":520,"wires":[[]]},{"id":"a4ee4265.a400d","type":"function","z":"2038339d.f16dfc","name":"encodetojpg","func":"var enable = flow.get('enableavg');\n\nif (1){\n    var av = msg.camera;\n    //node.warn(util.inspect(av));\n    var d = av.toBuffer(function(err, d){\n        av.release();\n        delete av;\n        delete msg.camera;\n        var newmsg = {\n            payload: d,\n            framenumber: msg.framenumber\n        };\n        delete msg;\n        node.send(newmsg);\n    });\n} else {\n    msg.camera.release();\n    delete msg.camera;\n}\n","outputs":1,"noerr":0,"x":710,"y":440,"wires":[["a58c25b3.b31858"]]},{"id":"c5b16a4b.8a7818","type":"function","z":"2038339d.f16dfc","name":"GetContours","func":"var cv = flow.get('cv');\n\nvar starttime = new Date();\nvar cnts = msg.out.findContours(cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE);\nmsg.cnts = cnts;\n\nnode.send([null, {payload:(new Date()) - starttime, topic:\"findcontours\"}]);\n\nreturn msg;","outputs":"2","noerr":0,"x":890,"y":220,"wires":[["dfd68181.234c4"],["8dc10c1c.1e0ff"]]},{"id":"dfd68181.234c4","type":"function","z":"2038339d.f16dfc","name":"Draw Boxes","func":"\nvar numcnts = msg.cnts.size();\n//var area = flow.get(\"area\");\n\nvar movementfound = false;\nvar requiredarea = flow.get('area');\n\nvar starttime = new Date();\nif (numcnts){\n    var scalex = msg.camera.width()/msg.out.width();\n    var scaley = msg.camera.height()/msg.out.height();;\n    var areas = [];\n    for (var c = 0; c < numcnts; c++){\n        var area = { area:msg.cnts.area(c), index:c };\n        areas.push(area);\n    }\n    var fn = function(a, b){\n        return (b.area - a.area);\n    }\n    areas.sort(fn);\n    //node.warn(numcnts + ' ' + util.inspect(areas));    \n    \n    // draw only the three biggest\n    for (var i = 0; ((i < 3) && (i < numcnts)); i++){\n        var c = areas[i].index;\n        //node.error(areas[i].index + ' '+areas[i].area);\n        if (areas[i].area > requiredarea){\n            var bounding = msg.cnts.boundingRect(c);\n            //node.error(bounding);\n            \n            // draw onto original camera image\n            msg.camera.rectangle([bounding.x*scalex, bounding.y*scaley], [bounding.width*scalex, bounding.height*scaley], [0, 0, 255], 2);\n            movementfound = true;\n        }\n    }\n}\nnode.send([null, null, {payload:(new Date()) - starttime, topic:\"drawboxes\"}]);\n\ndelete msg.cnts;\n\nmsg.movement = movementfound;\n\nvar prevmv = flow.get('prevmv');\nif(movementfound){\n    node.send([null, {payload:100, topic:'movement'}]);\n    if (!prevmv){\n        node.send([null, null, null, {payload:100, topic:'movement'}]);\n        node.warn(\"movement\");\n    }\n} else {\n    if (prevmv){\n        node.send([null, null, null, {payload:0, topic:'movement'}]);\n        node.warn(\"no movement\");\n    }\n    node.send([null, {payload:0, topic:'movement'}]);\n}\n\nflow.set('prevmv', movementfound);\n\nreturn msg;\n","outputs":"4","noerr":0,"x":890,"y":280,"wires":[["aacfff54.2fdee"],["8dc10c1c.1e0ff"],["8dc10c1c.1e0ff"],["f340dcf5.b7835"]],"outputLabels":["","movement","",""]},{"id":"7ac8ccbf.574b04","type":"ui_chart","z":"2038339d.f16dfc","name":"","group":"85a18620.7d76d8","order":0,"width":"6","height":"5","label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"200","removeOlder":"2","removeOlderPoints":"100","removeOlderUnit":"60","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":470,"y":420,"wires":[[],[]]},{"id":"8dc10c1c.1e0ff","type":"function","z":"2038339d.f16dfc","name":"","func":"context.values = context.values || {};\ncontext.avgs = context.avgs || {};\n\nif (msg.topic === 'reset'){\n    context.avgs = {};\n    return;\n}\n\nif (msg.topic){\n    context.values[msg.topic] = msg.payload;\n    if (!context.avgs[msg.topic+'avg']){\n        context.avgs[msg.topic+'avg'] = msg.payload;\n    } else {\n        context.avgs[msg.topic+'avg'] *= 0.999;\n        context.avgs[msg.topic+'avg'] += msg.payload*0.001;\n    }\n    return;\n}\n\nvar keys = Object.keys(context.values);\nfor (var i=0; i < keys.length; i++){\n    var newmsg = {\n        payload:context.values[keys[i]],\n        topic:keys[i]\n    };\n    node.send([newmsg, null]);\n    \n}\n\nvar keys = Object.keys(context.avgs);\nfor (var i=0; i < keys.length; i++){\n    var newmsg = {\n        payload:context.avgs[keys[i]],\n        topic:keys[i]\n    };\n    node.send([null, newmsg]);\n    \n}","outputs":"2","noerr":0,"x":300,"y":440,"wires":[["7ac8ccbf.574b04"],["96e4f92a.d3a5e8"]]},{"id":"b98efa63.7c9988","type":"inject","z":"2038339d.f16dfc","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"x":150,"y":440,"wires":[["8dc10c1c.1e0ff"]]},{"id":"444593e4.1e35bc","type":"function","z":"2038339d.f16dfc","name":"Variables","func":"\n\nflow.set(\"writerframes\", 200);\nflow.set(\"writerframe\", -1);\n\nflow.set(\"fps\", 30);\nflow.set(\"area\", 200);\nflow.set(\"width\", 350);\nflow.set(\"height\", ((flow.get('width')*3/4/2)>>0)*2);\n\nvar timings = {};\nflow.set('timings', timings);\n\nreturn msg;","outputs":1,"noerr":0,"x":280,"y":40,"wires":[["91c9965b.8a7898"]]},{"id":"23f78acd.d78c66","type":"function","z":"2038339d.f16dfc","name":"resize","func":"\nvar starttime = new Date();\nvar AfterResize = function(err, img){\n    try{\n    var resizetime = (new Date()) - starttime;\n    node.send([null, {payload:resizetime, topic:\"resizetime\"}]);\n    msg.img = img;\n    node.send([msg, null]);\n    } catch (e) {\n        node.warn(e);\n    }\n};\n\n    \n\ntry{\nvar Async = true;\nif (Async){\n    // note - generates a new image\n    msg.camera.resize(flow.get('width'), flow.get('height'), AfterResize);\n} else {\n    // sync - note - modifies the input image\n    msg.camera.resize(flow.get('width'), flow.get('height'));\n    AfterResize(null, msg.camera);\n}\n} catch (e) {\n    node.warn(e);\n}\n\n","outputs":"2","noerr":0,"x":490,"y":120,"wires":[["72e7f9a5.396508"],["8dc10c1c.1e0ff"]]},{"id":"1951b990.48edc6","type":"function","z":"2038339d.f16dfc","name":"split","func":"node.send([msg, null]);\nnode.send([null, msg]);\n","outputs":"2","noerr":0,"x":730,"y":400,"wires":[["a4ee4265.a400d"],["a92355b1.261b38"]]},{"id":"ba6daa76.7bd578","type":"multipart-encoder","z":"2038339d.f16dfc","name":"","statusCode":"","ignoreMessages":true,"outputOneNew":true,"outputOneClosed":false,"outputAllClosed":false,"globalHeaders":{"Content-Type":"multipart/x-mixed-replace;boundary=myboundary","Connection":"keep-alive","Expires":"Fri, 01 Jan 1990 00:00:00 GMT","Cache-Control":"no-cache, no-store, max-age=0, must-revalidate","Pragma":"no-cache"},"partHeaders":{"Content-Type":"image/jpeg"},"destination":"all","x":920,"y":620,"wires":[["b730beb9.488e7"]]},{"id":"ec18119.26142f","type":"http in","z":"2038339d.f16dfc","name":"","url":"/mvmt","method":"get","upload":false,"swaggerDoc":"","x":720,"y":620,"wires":[["ba6daa76.7bd578"]]},{"id":"b730beb9.488e7","type":"function","z":"2038339d.f16dfc","name":"","func":"flow.set('enablepic', msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":1070,"y":620,"wires":[[]]},{"id":"2558fee6.bd3f22","type":"function","z":"2038339d.f16dfc","name":"encodetojpg","func":"var enable = flow.get('enablepic');\n\nif (1){\n    //var av = flow.get('mask1');//msg.out;\n    var av = msg.out;\n    //node.warn(util.inspect(av));\n    var d = av.toBuffer(function(err, d){\n        delete av;\n        delete msg.out;\n        var newmsg = {\n            payload: d\n        };\n        node.send(newmsg);\n    });\n} else {\n    delete msg.out;\n}\n","outputs":1,"noerr":0,"x":790,"y":560,"wires":[["ba6daa76.7bd578"]]},{"id":"96e4f92a.d3a5e8","type":"ui_chart","z":"2038339d.f16dfc","name":"","group":"96dbe569.b5e008","order":0,"width":"6","height":"5","label":"avgs","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"200","removeOlder":"2","removeOlderPoints":"100","removeOlderUnit":"60","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":470,"y":460,"wires":[[],[]]},{"id":"aacfff54.2fdee","type":"function","z":"2038339d.f16dfc","name":"split","func":"node.send([msg, null]);\nnode.send([null, msg]);\n","outputs":"2","noerr":0,"x":730,"y":360,"wires":[["2ac933df.20042c"],["1951b990.48edc6"]]},{"id":"2ac933df.20042c","type":"function","z":"2038339d.f16dfc","name":"endtime","func":"var endtime = new Date();\n\nvar newmsg = {\n    payload:endtime - msg.flowstarttime,\n    topic:'flowtime'\n}\n\nreturn newmsg;","outputs":1,"noerr":0,"x":930,"y":380,"wires":[["8dc10c1c.1e0ff"]]},{"id":"4b3e964c.137d58","type":"function","z":"2038339d.f16dfc","name":"VideoWriter","func":"\nvar writerframe = flow.get('writerframe');\nvar writerframes = flow.get('writerframes');\n\nif (writerframe >= 0){\n    //node.warn(\"writerframe \"+ writerframe);\n    \n    var writer = flow.get('writer');\n    //node.warn(writerframe);\n    \n    if (writerframe === 0){\n        // start writer\n        var cv = flow.get('cv');\n        var filename = '/home/pi/output-'+new Date().getTime()+'.avi';\n        var FPS = 5;\n        writer = new cv.VideoWriter(filename, 'MP42' /*'DIVX'*/, FPS, msg.camera.size(), true);    \n        flow.set('writer', writer);\n        node.warn(\"started writer\"+util.inspect(writer));\n    }\n\n    if (writer){\n        writerframe++;\n        flow.set('writerframe', writerframe);\n        writer.writeSync(msg.camera);\n    }\n\n    if (writerframe >= writerframes){\n        // stop writer\n        if (writer){\n            writer.release();\n            flow.set('writer', null);\n            node.warn(\"stopped writer\");\n        }\n        flow.set('writerframe', -1);\n    }\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":1050,"y":460,"wires":[[]]},{"id":"a92355b1.261b38","type":"function","z":"2038339d.f16dfc","name":"split","func":"node.send([msg, null]);\nnode.send([null, msg]);\n","outputs":"2","noerr":0,"x":850,"y":420,"wires":[["2558fee6.bd3f22"],[]]},{"id":"f340dcf5.b7835","type":"function","z":"2038339d.f16dfc","name":"start-stop-VideoRecord","func":"if (msg.payload){\n    var writerframe = flow.get('writerframe');\n    node.warn(\"movement started \"+writerframe);\n    if (writerframe < 0) {\n        // start writer\n        flow.set('writerframe', 0);\n        writerframe = flow.get('writerframe');\n        node.warn(\"set writerframe to \"+writerframe);\n    }\n} else {\n    var writerframe = flow.get('writerframe');\n    node.warn(\"movement stopped \"+writerframe);\n    if (writerframe >= 0) {\n        // start writer\n        var writerframes = flow.get('writerframes');\n        flow.set('writerframe', writerframes);\n        writerframe = flow.get('writerframe');\n        node.warn(\"set writerframe to \"+writerframe);\n    }\n    \n}\nreturn msg;","outputs":1,"noerr":0,"x":1140,"y":320,"wires":[[]]},{"id":"b051acc0.7a02d","type":"inject","z":"2038339d.f16dfc","name":"","topic":"reset","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"x":130,"y":480,"wires":[["8dc10c1c.1e0ff"]]},{"id":"72e7f9a5.396508","type":"function","z":"2038339d.f16dfc","name":"FrameOrder","func":"\nvar framestore = flow.get('framestore') || {};\nvar currentframe = flow.get('currentframe') || 0;\n\n// keep the input msg\nif (msg.framenumber){\n    var framediff = (msg.framenumber - currentframe)\n    //node.warn(\"framediff \"+framediff);\n\n    if (framediff > 5){\n        framestore = {};\n        currentframe = msg.framenumber;\n        node.warn(\"**** reset framediff \"+framediff + \" len \"+Object.keys(framestore).length);\n    } else {\n        if (framediff > 1){\n            node.warn(\"framediff \"+framediff + \" len \"+Object.keys(framestore).length);\n        }\n    }\n\n    framestore['frame'+msg.framenumber] = msg;\n    flow.set('framestore', framestore);\n}\n\n\nvar moreframes = 3;\nvar framessent = 0;\ndo {\n    if (framestore['frame'+currentframe] != undefined){\n        var newmsg = framestore['frame'+currentframe];\n        delete framestore['frame'+currentframe];\n        currentframe++;\n        flow.set('currentframe', currentframe);\n        //node.warn();\n        node.send(newmsg);\n        framessent++;\n        moreframes --;\n    } else {\n        moreframes = 0;\n    }\n} while (moreframes);\n\nif (framessent > 2){\n    node.warn(\"frames sent \"+framessent);\n}","outputs":1,"noerr":0,"x":690,"y":100,"wires":[["6873c74.548ce38"]]},{"id":"a58c25b3.b31858","type":"function","z":"2038339d.f16dfc","name":"FrameOrder2","func":"\nvar framestore = flow.get('framestore2') || {};\nvar currentframe = flow.get('currentframe2') || 0;\n\n// keep the input msg\nif (msg.framenumber){\n    var framediff = (msg.framenumber - currentframe)\n    //node.warn(\"framediff \"+framediff);\n\n    if (framediff > 5){\n        framestore = {};\n        currentframe = msg.framenumber;\n        node.warn(\"**** reset framediff2 \"+framediff + \" len \"+Object.keys(framestore).length);\n    } else {\n        if (framediff > 1){\n            node.warn(\"framediff2 \"+framediff + \" len \"+Object.keys(framestore).length);\n        }\n    }\n\n    framestore['frame'+msg.framenumber] = msg;\n    flow.set('framestore2', framestore);\n}\n\n\nvar moreframes = 3;\nvar framessent = 0;\ndo {\n    if (framestore['frame'+currentframe] != undefined){\n        var newmsg = framestore['frame'+currentframe];\n        delete framestore['frame'+currentframe];\n        currentframe++;\n        flow.set('currentframe2', currentframe);\n        //node.warn();\n        var timetosend = (newmsg.time_in + 500) - (new Date());\n        if (timetosend < 0)\n            timetosend = 0;\n            \n        setTimeout(function(){node.send(newmsg);}, timetosend);\n        \n        framessent++;\n        moreframes --;\n    } else {\n        moreframes = 0;\n    }\n} while (moreframes);\n\nif (framessent > 2){\n    node.warn(\"frames2 sent \"+framessent);\n}","outputs":1,"noerr":0,"x":860,"y":480,"wires":[["cfd82a72.82b288"]]},{"id":"85a18620.7d76d8","type":"ui_group","z":"","name":"Default","tab":"82abaa61.322da8","disp":true,"width":"6"},{"id":"96dbe569.b5e008","type":"ui_group","z":"","name":"avgs","tab":"82abaa61.322da8","disp":true,"width":"6"},{"id":"82abaa61.322da8","type":"ui_tab","z":"","name":"newstuff","icon":"dashboard"}]

Flow Info

Created 7 years ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • function (x21)
  • http in (x2)
  • inject (x4)
Other

Tags

  • opencv
  • node-opencv
  • video
  • motion
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option