Multi Threaded Camera Spawn (Alpha)

pushover
Moe 2019-12-05 13:25:13 +06:00
parent c943eedddc
commit da53108ba9
6 changed files with 179 additions and 779 deletions

View File

@ -59,8 +59,6 @@ loadLib('ffmpeg')(s,config,lang,function(ffmpeg){
loadLib('monitor')(s,config,lang)
//event functions : motion, object matrix handler
loadLib('events')(s,config,lang)
//built-in detector functions : pam-diff..
loadLib('detector')(s,config)
//recording functions
loadLib('videos')(s,config,lang)
//branding functions and config defaults

View File

@ -1,46 +1,57 @@
// Matrix In Region Libs >
var fs = require('fs')
var SAT = require('sat')
var V = SAT.Vector;
var P = SAT.Polygon;
// Matrix In Region Libs />
var P2P = require('pipe2pam')
// pamDiff is based on https://www.npmjs.com/package/pam-diff
var PamDiff = require('pam-diff')
module.exports = function(s,config){
s.createPamDiffEngine = function(e){
module.exports = function(jsonData,pamDiffResponder){
var s = {};
var noiseFilterArray = {};
const groupKey = jsonData.rawMonitorConfig.ke
const monitorId = jsonData.rawMonitorConfig.mid
const triggerTimer = {}
var pamDiff
var p2p
var writeToStderr = function(text){
fs.appendFileSync('/home/Shinobi/test.log',text + '\n','utf8')
}
createPamDiffEngine = function(){
var width,
height,
globalSensitivity,
globalColorThreshold,
fullFrame = false
if(s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x===''||s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y===''){
width = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_x;
height = s.group[e.ke].rawMonitorConfigurations[e.id].details.detector_scale_y;
}else{
width = e.width
height = e.height
if(jsonData.rawMonitorConfig.details.detector_scale_x===''||jsonData.rawMonitorConfig.details.detector_scale_y===''){
width = jsonData.rawMonitorConfig.details.detector_scale_x;
height = jsonData.rawMonitorConfig.details.detector_scale_y;
}
if(e.details.detector_sensitivity===''){
else{
width = jsonData.rawMonitorConfig.width
height = jsonData.rawMonitorConfig.height
}
if(jsonData.rawMonitorConfig.details.detector_sensitivity===''){
globalSensitivity = 10
}else{
globalSensitivity = parseInt(e.details.detector_sensitivity)
globalSensitivity = parseInt(jsonData.rawMonitorConfig.details.detector_sensitivity)
}
if(e.details.detector_color_threshold===''){
if(jsonData.rawMonitorConfig.details.detector_color_threshold===''){
globalColorThreshold = 9
}else{
globalColorThreshold = parseInt(e.details.detector_color_threshold)
globalColorThreshold = parseInt(jsonData.rawMonitorConfig.details.detector_color_threshold)
}
globalThreshold = parseInt(e.details.detector_threshold) || 0
globalThreshold = parseInt(jsonData.rawMonitorConfig.details.detector_threshold) || 0
var regionJson
try{
regionJson = JSON.parse(s.group[e.ke].rawMonitorConfigurations[e.id].details.cords)
regionJson = JSON.parse(jsonData.rawMonitorConfig.details.cords)
}catch(err){
regionJson = s.group[e.ke].rawMonitorConfigurations[e.id].details.cords
regionJson = jsonData.rawMonitorConfig.details.cords
}
if(Object.keys(regionJson).length === 0 || e.details.detector_frame === '1'){
if(Object.keys(regionJson).length === 0 || jsonData.rawMonitorConfig.details.detector_frame === '1'){
fullFrame = {
name:'FULL_FRAME',
sensitivity:globalSensitivity,
@ -54,26 +65,27 @@ module.exports = function(s,config){
}
}
e.triggerTimer = {}
var regions = s.createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
var regions = createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
writeToStderr(JSON.stringify(regions,null,3))
var pamDiffOptions = {
grayscale: 'luminosity',
regions : regions.forPam
}
if(e.details.detector_show_matrix==='1'){
writeToStderr(JSON.stringify(pamDiffOptions,null,3))
if(jsonData.rawMonitorConfig.details.detector_show_matrix==='1'){
pamDiffOptions.response = 'bounds'
}
s.group[e.ke].activeMonitors[e.id].pamDiff = new PamDiff(pamDiffOptions);
s.group[e.ke].activeMonitors[e.id].p2p = new P2P()
pamDiff = new PamDiff(pamDiffOptions)
p2p = new P2P()
var regionArray = Object.values(regionJson)
if(config.detectorMergePamRegionTriggers === true){
if(jsonData.globalInfo.config.detectorMergePamRegionTriggers === true){
// merge pam triggers for performance boost
var buildTriggerEvent = function(trigger){
var detectorObject = {
f:'trigger',
id:e.id,
ke:e.ke,
id:monitorId,
ke:groupKey,
name:trigger.name,
details:{
plug:'built-in',
@ -82,8 +94,8 @@ module.exports = function(s,config){
confidence:trigger.percent
},
plates:[],
imgHeight:e.details.detector_scale_y,
imgWidth:e.details.detector_scale_x
imgHeight:jsonData.rawMonitorConfig.details.detector_scale_y,
imgWidth:jsonData.rawMonitorConfig.details.detector_scale_x
}
if(trigger.merged){
if(trigger.matrices)detectorObject.details.matrices = trigger.matrices
@ -91,13 +103,13 @@ module.exports = function(s,config){
var filteredCountSuccess = 0
trigger.merged.forEach(function(triggerPiece){
var region = regionArray.find(x => x.name == triggerPiece.name)
s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
checkMaximumSensitivity(region, detectorObject, function(err1) {
checkTriggerThreshold(region, detectorObject, function(err2) {
++filteredCount
if(!err1 && !err2)++filteredCountSuccess
if(filteredCount === trigger.merged.length && filteredCountSuccess > 0){
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
s.triggerEvent(detectorObject)
detectorObject.doObjectDetection = (jsonData.globalInfo.isAtleatOneDetectorPluginConnected && jsonData.rawMonitorConfig.details.detector_use_detect_object === '1')
pamDiffResponder.write(Buffer.from(JSON.stringify(detectorObject)))
}
})
})
@ -105,38 +117,36 @@ module.exports = function(s,config){
}else{
if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
var region = regionArray.find(x => x.name == detectorObject.name)
s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
checkMaximumSensitivity(region, detectorObject, function(err1) {
checkTriggerThreshold(region, detectorObject, function(err2) {
if(!err1 && !err2){
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
s.triggerEvent(detectorObject)
detectorObject.doObjectDetection = (jsonData.globalInfo.isAtleatOneDetectorPluginConnected && jsonData.rawMonitorConfig.details.detector_use_detect_object === '1')
pamDiffResponder.write(Buffer.from(JSON.stringify(detectorObject)))
}
})
})
}
}
if(e.details.detector_noise_filter==='1'){
if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
if(jsonData.rawMonitorConfig.details.detector_noise_filter==='1'){
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
})
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
pamDiff.on('diff', (data) => {
var filteredCount = 0
var filteredCountSuccess = 0
data.trigger.forEach(function(trigger){
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(err){
filterTheNoise(noiseFilterArray,regions,trigger,function(err){
++filteredCount
if(!err)++filteredCountSuccess
if(filteredCount === data.trigger.length && filteredCountSuccess > 0){
buildTriggerEvent(s.mergePamTriggers(data))
buildTriggerEvent(mergePamTriggers(data))
}
})
})
})
}else{
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
buildTriggerEvent(s.mergePamTriggers(data))
pamDiff.on('diff', (data) => {
buildTriggerEvent(mergePamTriggers(data))
})
}
}else{
@ -145,8 +155,8 @@ module.exports = function(s,config){
var buildTriggerEvent = function(trigger){
var detectorObject = {
f:'trigger',
id:e.id,
ke:e.ke,
id: monitorId,
ke: groupKey,
name:trigger.name,
details:{
plug:'built-in',
@ -155,38 +165,36 @@ module.exports = function(s,config){
confidence:trigger.percent
},
plates:[],
imgHeight:e.details.detector_scale_y,
imgWidth:e.details.detector_scale_x
imgHeight:jsonData.rawMonitorConfig.details.detector_scale_y,
imgWidth:jsonData.rawMonitorConfig.details.detector_scale_x
}
if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
var region = Object.values(regionJson).find(x => x.name == detectorObject.name)
s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
checkMaximumSensitivity(region, detectorObject, function(err1) {
checkTriggerThreshold(region, detectorObject, function(err2) {
if(!err1 && ! err2){
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
s.triggerEvent(detectorObject)
detectorObject.doObjectDetection = (jsonData.globalInfo.isAtleatOneDetectorPluginConnected && jsonData.rawMonitorConfig.details.detector_use_detect_object === '1')
}
})
})
}
if(e.details.detector_noise_filter==='1'){
if(!s.group[e.ke].activeMonitors[e.id].noiseFilterArray)s.group[e.ke].activeMonitors[e.id].noiseFilterArray = {}
var noiseFilterArray = s.group[e.ke].activeMonitors[e.id].noiseFilterArray
if(jsonData.rawMonitorConfig.details.detector_noise_filter==='1'){
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
})
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
pamDiff.on('diff', (data) => {
data.trigger.forEach(function(trigger){
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){
s.createMatrixFromPamTrigger(trigger)
filterTheNoise(noiseFilterArray,regions,trigger,function(){
createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
})
})
})
}else{
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
pamDiff.on('diff', (data) => {
data.trigger.forEach(function(trigger){
s.createMatrixFromPamTrigger(trigger)
createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
})
})
@ -194,7 +202,7 @@ module.exports = function(s,config){
}
}
s.createPamDiffRegionArray = function(regions,globalColorThreshold,globalSensitivity,fullFrame){
createPamDiffRegionArray = function(regions,globalColorThreshold,globalSensitivity,fullFrame){
var pamDiffCompliantArray = [],
arrayForOtherStuff = [],
json
@ -236,11 +244,11 @@ module.exports = function(s,config){
return {forPam:pamDiffCompliantArray,notForPam:arrayForOtherStuff};
}
s.filterTheNoise = function(e,noiseFilterArray,regions,trigger,callback){
filterTheNoise = function(noiseFilterArray,regions,trigger,callback){
if(noiseFilterArray[trigger.name].length > 2){
var thePreviousTriggerPercent = noiseFilterArray[trigger.name][noiseFilterArray[trigger.name].length - 1];
var triggerDifference = trigger.percent - thePreviousTriggerPercent;
var noiseRange = e.details.detector_noise_filter_range
var noiseRange = jsonData.rawMonitorConfig.details.detector_noise_filter_range
if(!noiseRange || noiseRange === ''){
noiseRange = 6
}
@ -267,48 +275,48 @@ module.exports = function(s,config){
}
}
s.checkMaximumSensitivity = function(monitor, region, detectorObject, callback) {
checkMaximumSensitivity = function(region, detectorObject, callback) {
var logName = detectorObject.id + ':' + detectorObject.name
var globalMaxSensitivity = parseInt(monitor.details.detector_max_sensitivity) || undefined
var globalMaxSensitivity = parseInt(jsonData.rawMonitorConfig.details.detector_max_sensitivity) || undefined
var maxSensitivity = parseInt(region.max_sensitivity) || globalMaxSensitivity
if (maxSensitivity === undefined || detectorObject.details.confidence <= maxSensitivity) {
callback(null)
} else {
callback(true)
if (monitor.triggerTimer[detectorObject.name] !== undefined) {
clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
monitor.triggerTimer[detectorObject.name] = undefined
if (triggerTimer[detectorObject.name] !== undefined) {
clearTimeout(triggerTimer[detectorObject.name].timeout)
triggerTimer[detectorObject.name] = undefined
}
}
}
s.checkTriggerThreshold = function(monitor, region, detectorObject, callback){
checkTriggerThreshold = function(region, detectorObject, callback){
var threshold = parseInt(region.threshold) || globalThreshold
if (threshold <= 1) {
callback(null)
} else {
if (monitor.triggerTimer[detectorObject.name] === undefined) {
monitor.triggerTimer[detectorObject.name] = {
if (triggerTimer[detectorObject.name] === undefined) {
triggerTimer[detectorObject.name] = {
count : threshold,
timeout : null
}
}
if (--monitor.triggerTimer[detectorObject.name].count == 0) {
if (--triggerTimer[detectorObject.name].count == 0) {
callback(null)
clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
monitor.triggerTimer[detectorObject.name] = undefined
clearTimeout(triggerTimer[detectorObject.name].timeout)
triggerTimer[detectorObject.name] = undefined
} else {
callback(true)
var fps = parseFloat(monitor.details.detector_fps) || 2
if (monitor.triggerTimer[detectorObject.name].timeout !== null)
clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
monitor.triggerTimer[detectorObject.name].timeout = setTimeout(function() {
monitor.triggerTimer[detectorObject.name] = undefined
var fps = parseFloat(jsonData.rawMonitorConfig.details.detector_fps) || 2
if (triggerTimer[detectorObject.name].timeout !== null)
clearTimeout(triggerTimer[detectorObject.name].timeout)
triggerTimer[detectorObject.name].timeout = setTimeout(function() {
triggerTimer[detectorObject.name] = undefined
}, ((threshold+0.5) * 1000) / fps)
}
}
}
s.mergePamTriggers = function(data){
mergePamTriggers = function(data){
if(data.trigger.length > 1){
var n = 0
var sum = 0
@ -318,7 +326,7 @@ module.exports = function(s,config){
name.push(trigger.name + ' ('+trigger.percent+'%)')
++n
sum += trigger.percent
s.createMatrixFromPamTrigger(trigger)
createMatrixFromPamTrigger(trigger)
if(trigger.matrix)matrices.push(trigger.matrix)
})
var average = sum / n
@ -332,7 +340,7 @@ module.exports = function(s,config){
}
}else{
var trigger = data.trigger[0]
s.createMatrixFromPamTrigger(trigger)
createMatrixFromPamTrigger(trigger)
trigger.matrices = [trigger.matrix]
}
return trigger
@ -372,7 +380,7 @@ module.exports = function(s,config){
if(callback)callback(foundInRegion,collisions)
return foundInRegion
}
s.createMatrixFromPamTrigger = function(trigger){
createMatrixFromPamTrigger = function(trigger){
if(
trigger.minX &&
trigger.maxX &&
@ -396,4 +404,14 @@ module.exports = function(s,config){
}
return trigger
}
return function(cameraProcess){
if(jsonData.rawMonitorConfig.details.detector === '1' && jsonData.rawMonitorConfig.coProcessor === false){
//frames from motion detect
if(jsonData.rawMonitorConfig.details.detector_pam === '1'){
createPamDiffEngine()
cameraProcess.stdio[3].pipe(p2p).pipe(pamDiff)
}
}
};
}

View File

@ -0,0 +1,57 @@
const fs = require('fs')
const spawn = require('child_process').spawn
const logIt = function(text){
stdioWriters[i].write(text)
}
process.send = process.send || function () {};
process.on('uncaughtException', function (err) {
writeToStderr('Uncaught Exception occured!');
writeToStderr(err.stack);
});
// [CTRL] + [C] = exit
process.on('SIGINT', function() {
cameraProcess.kill('SIGTERM')
});
if(!process.argv[2] || !process.argv[3]){
return writeToStderr('Missing FFMPEG Command String or no command operator')
}
var jsonData = JSON.parse(fs.readFileSync(process.argv[3],'utf8'))
const ffmpegAbsolutePath = process.argv[2].trim()
const ffmpegCommandString = jsonData.cmd
const stdioPipes = jsonData.pipes || []
var newPipes = []
var stdioWriters = [];
// var writeToStderr = function(text){
// process.stderr.write(Buffer.from(text))
// }
fs.appendFileSync('/home/Shinobi/test.log','---------------' + '\n','utf8')
fs.appendFileSync('/home/Shinobi/test.log','---------------' + '\n','utf8')
fs.appendFileSync('/home/Shinobi/test.log','---------------' + '\n','utf8')
var writeToStderr = function(text){
fs.appendFileSync('/home/Shinobi/test.log',text + '\n','utf8')
}
for(var i=0; i < stdioPipes; i++){
switch(i){
case 3:
newPipes.push('pipe')
stdioWriters[i] = fs.createWriteStream(null, {fd: i});
break;
default:
stdioWriters[i] = fs.createWriteStream(null, {fd: i});
newPipes.push(stdioWriters[i])
break;
}
}
var cameraProcess = spawn(ffmpegAbsolutePath,ffmpegCommandString,{detached: true,stdio:newPipes})
setTimeout(function(){
writeToStderr('Start Process Now')
try{
const attachDetector = require(__dirname + '/detector.js')(jsonData,stdioWriters[3])
attachDetector(cameraProcess)
}catch(err){
writeToStderr(err.stack)
}
},3000)

View File

@ -1,692 +0,0 @@
//
// Shinobi - fork of pam-diff
// Copyright (C) 2018 Kevin Godell
// Author : Kevin Godell, https://github.com/kevinGodell
// npmjs : https://www.npmjs.com/package/pam-diff
// Github : https://github.com/kevinGodell/pam-diff
//
'use strict';
const { Transform } = require('stream');
const PP = require('polygon-points');
/**
*
* @param chunk
* @private
*/
var _getMatrixFromPoints = function(thisRegion) {
var coordinates = [
thisRegion.topLeft,
{"x" : thisRegion.bottomRight.x, "y" : thisRegion.topLeft.y},
thisRegion.bottomRight
]
var width = Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2));
var height = Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2))
return {
x: coordinates[0].x,
y: coordinates[0].y,
width: width,
height: height,
tag: thisRegion.name
}
}
class PamDiff extends Transform {
/**
*
* @param [options] {Object}
* @param [callback] {Function}
*/
constructor(options, callback) {
super(options);
Transform.call(this, {objectMode: true});
this.difference = PamDiff._parseOptions('difference', options);//global option, can be overridden per region
this.percent = PamDiff._parseOptions('percent', options);//global option, can be overridden per region
this.regions = PamDiff._parseOptions('regions', options);//can be no regions or a single region or multiple regions. if no regions, all pixels will be compared.
this.drawMatrix = PamDiff._parseOptions('drawMatrix', options);//can be no regions or a single region or multiple regions. if no regions, all pixels will be compared.
this.callback = callback;//callback function to be called when pixel difference is detected
this._parseChunk = this._parseFirstChunk;//first parsing will be reading settings and configuring internal pixel reading
}
/**
*
* @param option {String}
* @param options {Object}
* @return {*}
* @private
*/
static _parseOptions(option, options) {
if (options && options.hasOwnProperty(option)) {
return options[option];
}
return null;
}
/**
*
* @param number {Number}
* @param def {Number}
* @param low {Number}
* @param high {Number}
* @return {Number}
* @private
*/
static _validateNumber(number, def, low, high) {
if (isNaN(number)) {
return def;
} else if (number < low) {
return low;
} else if (number > high) {
return high;
} else {
return number;
}
}
/**
*
* @deprecated
* @param string {String}
*/
setGrayscale(string) {
console.warn('grayscale option has been removed, "average" has proven to most accurate and is the default');
}
/**
*
* @param number {Number}
*/
set difference(number) {
this._difference = PamDiff._validateNumber(parseInt(number), 5, 1, 255);
}
/**
*
* @return {Number}
*/
get difference() {
return this._difference;
}
/**
*
* @param number {Number}
* @return {PamDiff}
*/
setDifference(number) {
this.difference = number;
return this;
}
/**
*
* @param number {Number}
*/
set percent(number) {
this._percent = PamDiff._validateNumber(parseInt(number), 5, 1, 100);
}
/**
*
* @return {Number}
*/
get percent() {
return this._percent;
}
/**
*
* @param number {Number}
* @return {PamDiff}
*/
setPercent(number) {
this.percent = number;
return this;
}
/**
*
* @param array {Array}
*/
set regions(array) {
if (!array) {
if (this._regions) {
delete this._regions;
delete this._regionsLength;
delete this._minDiff;
}
this._diffs = 0;
} else if (!Array.isArray(array) || array.length < 1) {
throw new Error(`Regions must be an array of at least 1 region object {name: 'region1', difference: 10, percent: 10, polygon: [[0, 0], [0, 50], [50, 50], [50, 0]]}`);
} else {
this._regions = [];
this._minDiff = 255;
for (const region of array) {
if (!region.hasOwnProperty('name') || !region.hasOwnProperty('polygon')) {
throw new Error('Region must include a name and a polygon property');
}
const polygonPoints = new PP(region.polygon);
const difference = PamDiff._validateNumber(parseInt(region.difference), this._difference, 1, 255);
const percent = PamDiff._validateNumber(parseInt(region.percent), this._percent, 1, 100);
this._minDiff = Math.min(this._minDiff, difference);
this._regions.push(
{
name: region.name,
polygon: polygonPoints,
difference: difference,
percent: percent,
diffs: 0
}
);
}
this._regionsLength = this._regions.length;
this._createPointsInPolygons(this._regions, this._width, this._height);
}
}
/**
*
* @return {Array}
*/
get regions() {
return this._regions;
}
/**
*
* @param array {Array}
* @return {PamDiff}
*/
setRegions(array) {
this.regions = array;
return this;
}
/**
*
* @param func {Function}
*/
set callback(func) {
if (!func) {
delete this._callback;
} else if (typeof func === 'function' && func.length === 1) {
this._callback = func;
} else {
throw new Error('Callback must be a function that accepts 1 argument.');
}
}
/**
*
* @return {Function}
*/
get callback() {
return this._callback;
}
/**
*
* @param func {Function}
* @return {PamDiff}
*/
setCallback(func) {
this.callback = func;
return this;
}
/**
*
* @return {PamDiff}
*/
resetCache() {
//delete this._oldPix;
//delete this._newPix;
//delete this._width;
//delete this._length;
this._parseChunk = this._parseFirstChunk;
return this;
}
/**
*
* @param regions {Array}
* @param width {Number}
* @param height {Number}
* @private
*/
_createPointsInPolygons(regions, width, height) {
if (regions && width && height) {
this._pointsInPolygons = [];
for (const region of regions) {
const bitset = region.polygon.getBitset(this._width, this._height);
region.pointsLength = bitset.count;
this._pointsInPolygons.push(bitset.buffer);
}
}
}
/**
*
* @param chunk
* @private
*/
_blackAndWhitePixelDiff(chunk) {
this._newPix = chunk.pixels;
for (let y = 0, i = 0; y < this._height; y++) {
for (let x = 0; x < this._width; x++, i++) {
const diff = this._oldPix[i] !== this._newPix[i];
if (this._regions && diff === true) {
for (let j = 0; j < this._regionsLength; j++) {
if (this._pointsInPolygons[j][i]) {
this._regions[j].diffs++;
}
}
} else if (diff === true) {
this._diffs++;
}
}
}
if (this._regions) {
const regionDiffArray = [];
for (let i = 0; i < this._regionsLength; i++) {
const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
if (percent >= this._regions[i].percent) {
regionDiffArray.push({name: this._regions[i].name, percent: percent});
}
this._regions[i].diffs = 0;
}
if (regionDiffArray.length > 0) {
const data = {trigger: regionDiffArray, pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
} else {
const percent = Math.floor(100 * this._diffs / this._length);
if (percent >= this._percent) {
const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
this._diffs = 0;
}
this._oldPix = this._newPix;
}
/**
*
* @param chunk
* @private
*/
_grayScalePixelDiffWithMatrices(chunk) {
this._newPix = chunk.pixels;
for (let j = 0; j < this._regionsLength; j++) {
this._regions[j].topLeft = {
x: this._width,
y: this._height
}
this._regions[j].bottomRight = {
x: 0,
y: 0
}
}
this.topLeft = {
x: this._width,
y: this._height
}
this.bottomRight = {
x: 0,
y: 0
}
for (let y = 0, i = 0; y < this._height; y++) {
for (let x = 0; x < this._width; x++, i++) {
if (this._oldPix[i] !== this._newPix[i]) {
const diff = Math.abs(this._oldPix[i] - this._newPix[i]);
if (this._regions && diff >= this._minDiff) {
for (let j = 0; j < this._regionsLength; j++) {
if (this._pointsInPolygons[j][i] && diff >= this._regions[j].difference) {
var theRegion = this._regions[j]
theRegion.diffs++;
if(theRegion.topLeft.x > x && theRegion.topLeft.y > y){
theRegion.topLeft.x = x
theRegion.topLeft.y = y
}
if(theRegion.bottomRight.x < x && theRegion.bottomRight.y < y){
theRegion.bottomRight.x = x
theRegion.bottomRight.y = y
}
}
}
} else if (diff >= this._difference) {
this._diffs++;
if(this.topLeft.x > x && this.topLeft.y > y){
this.topLeft.x = x
this.topLeft.y = y
}
if(this.bottomRight.x < x && this.bottomRight.y < y){
this.bottomRight.x = x
this.bottomRight.y = y
}
}
}
}
}
if (this._regions) {
const regionDiffArray = [];
for (let i = 0; i < this._regionsLength; i++) {
var thisRegion = this._regions[i]
const percent = Math.floor(100 * thisRegion.diffs / thisRegion.pointsLength);
if (percent >= thisRegion.percent) {
// create matrix from points >>
thisRegion._matrix = _getMatrixFromPoints(thisRegion)
// create matrix from points />>
regionDiffArray.push({name: thisRegion.name, percent: percent, matrix: thisRegion._matrix});
}
thisRegion.diffs = 0;
}
if (regionDiffArray.length > 0) {
this._matrix = _getMatrixFromPoints(this)
const data = {trigger: regionDiffArray, pam: chunk.pam, matrix: this._matrix};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
} else {
const percent = Math.floor(100 * this._diffs / this._length);
if (percent >= this._percent) {
this._matrix = _getMatrixFromPoints(this)
const data = {trigger: [{name: 'percent', percent: percent, matrix: this._matrix}], pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
this._diffs = 0;
}
this._oldPix = this._newPix;
}
/**
*
* @param chunk
* @private
*/
_grayScalePixelDiff(chunk) {
this._newPix = chunk.pixels;
for (let y = 0, i = 0; y < this._height; y++) {
for (let x = 0; x < this._width; x++, i++) {
if (this._oldPix[i] !== this._newPix[i]) {
const diff = Math.abs(this._oldPix[i] - this._newPix[i]);
if (this._regions && diff >= this._minDiff) {
for (let j = 0; j < this._regionsLength; j++) {
if (this._pointsInPolygons[j][i] && diff >= this._regions[j].difference) {
this._regions[j].diffs++;
}
}
} else {
if (diff >= this._difference) {
this._diffs++;
}
}
}
}
}
if (this._regions) {
const regionDiffArray = [];
for (let i = 0; i < this._regionsLength; i++) {
const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
if (percent >= this._regions[i].percent) {
regionDiffArray.push({name: this._regions[i].name, percent: percent});
}
this._regions[i].diffs = 0;
}
if (regionDiffArray.length > 0) {
const data = {trigger: regionDiffArray, pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
} else {
const percent = Math.floor(100 * this._diffs / this._length);
if (percent >= this._percent) {
const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
this._diffs = 0;
}
this._oldPix = this._newPix;
}
/**
*
* @param chunk
* @private
*/
_rgbPixelDiff(chunk) {
this._newPix = chunk.pixels;
for (let y = 0, i = 0, p = 0; y < this._height; y++) {
for (let x = 0; x < this._width; x++, i += 3, p++) {
if (this._oldPix[i] !== this._newPix[i] || this._oldPix[i + 1] !== this._newPix[i + 1] || this._oldPix[i + 2] !== this._newPix[i + 2]) {
const diff = Math.abs(this._oldPix[i] + this._oldPix[i + 1] + this._oldPix[i + 2] - this._newPix[i] - this._newPix[i + 1] - this._newPix[i + 2])/3;
if (this._regions && diff >= this._minDiff) {
for (let j = 0; j < this._regionsLength; j++) {
if (this._pointsInPolygons[j][p] && diff >= this._regions[j].difference) {
this._regions[j].diffs++;
}
}
} else {
if (diff >= this._difference) {
this._diffs++;
}
}
}
}
}
if (this._regions) {
const regionDiffArray = [];
for (let i = 0; i < this._regionsLength; i++) {
const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
if (percent >= this._regions[i].percent) {
regionDiffArray.push({name: this._regions[i].name, percent: percent});
}
this._regions[i].diffs = 0;
}
if (regionDiffArray.length > 0) {
const data = {trigger: regionDiffArray, pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
} else {
const percent = Math.floor(100 * this._diffs / this._length);
if (percent >= this._percent) {
const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
this._diffs = 0;
}
this._oldPix = this._newPix;
}
/**
*
* @param chunk
* @private
*/
_rgbAlphaPixelDiff(chunk) {
this._newPix = chunk.pixels;
for (let y = 0, i = 0, p = 0; y < this._height; y++) {
for (let x = 0; x < this._width; x++, i += 4, p++) {
if (this._oldPix[i] !== this._newPix[i] || this._oldPix[i + 1] !== this._newPix[i + 1] || this._oldPix[i + 2] !== this._newPix[i + 2]) {
const diff = Math.abs(this._oldPix[i] + this._oldPix[i + 1] + this._oldPix[i + 2] - this._newPix[i] - this._newPix[i + 1] - this._newPix[i + 2])/3;
if (this._regions && diff >= this._minDiff) {
for (let j = 0; j < this._regionsLength; j++) {
if (this._pointsInPolygons[j][p] && diff >= this._regions[j].difference) {
this._regions[j].diffs++;
}
}
} else {
if (diff >= this._difference) {
this._diffs++;
}
}
}
}
}
if (this._regions) {
const regionDiffArray = [];
for (let i = 0; i < this._regionsLength; i++) {
const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
if (percent >= this._regions[i].percent) {
regionDiffArray.push({name: this._regions[i].name, percent: percent});
}
this._regions[i].diffs = 0;
}
if (regionDiffArray.length > 0) {
const data = {trigger: regionDiffArray, pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
} else {
const percent = Math.floor(100 * this._diffs / this._length);
if (percent >= this._percent) {
const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
if (this._callback) {
this._callback(data);
}
if (this._readableState.pipesCount > 0) {
this.push(data);
}
if (this.listenerCount('diff') > 0) {
this.emit('diff', data);
}
}
this._diffs = 0;
}
this._oldPix = this._newPix;
}
/**
*
* @param chunk
* @private
*/
_parseFirstChunk(chunk) {
this._width = parseInt(chunk.width);
this._height = parseInt(chunk.height);
this._oldPix = chunk.pixels;
this._length = this._width * this._height;
this._createPointsInPolygons(this._regions, this._width, this._height);
switch (chunk.tupltype) {
case 'blackandwhite' :
this._parseChunk = this._blackAndWhitePixelDiff;
break;
case 'grayscale' :
if(this.drawMatrix === "1"){
this._parseChunk = this._grayScalePixelDiffWithMatrices;
}else{
this._parseChunk = this._grayScalePixelDiff;
}
break;
case 'rgb' :
this._parseChunk = this._rgbPixelDiff;
//this._increment = 3;//future use
break;
case 'rgb_alpha' :
this._parseChunk = this._rgbAlphaPixelDiff;
//this._increment = 4;//future use
break;
default :
throw Error(`Unsupported tupltype: ${chunk.tupltype}. Supported tupltypes include grayscale(gray), blackandwhite(monob), rgb(rgb24), and rgb_alpha(rgba).`);
}
}
/**
*
* @param chunk
* @param encoding
* @param callback
* @private
*/
_transform(chunk, encoding, callback) {
this._parseChunk(chunk);
callback();
}
/**
*
* @param callback
* @private
*/
_flush(callback) {
this.resetCache();
callback();
}
}
/**
*
* @type {PamDiff}
*/
module.exports = PamDiff;
//todo get bounding box of all regions combined to exclude some pixels before checking if they exist inside specific regions

View File

@ -1017,7 +1017,23 @@ module.exports = function(s,config,lang,onFinish){
//clean the string of spatial impurities and split for spawn()
x.ffmpegCommandString = s.splitForFFPMEG(x.ffmpegCommandString)
//launch that bad boy
return spawn(config.ffmpegDir,x.ffmpegCommandString,{detached: true,stdio:x.stdioPipes})
// return spawn(config.ffmpegDir,x.ffmpegCommandString,{detached: true,stdio:x.stdioPipes})
fs.writeFileSync(e.sdir + 'cmd.txt',JSON.stringify({
cmd: x.ffmpegCommandString,
pipes: x.stdioPipes.length,
rawMonitorConfig: s.group[e.ke].rawMonitorConfigurations[e.id],
globalInfo: {
config: config,
isAtleatOneDetectorPluginConnected: s.isAtleatOneDetectorPluginConnected
}
},null,3),'utf8')
var cameraCommandParams = [
s.mainDirectory + '/libs/cameraThread/singleCamera.js',
config.ffmpegDir,
e.sdir + 'cmd.txt'
]
console.log(cameraCommandParams.join(' '))
return spawn('node',cameraCommandParams,{detached: true,stdio:x.stdioPipes})
}
if(!config.ffmpegDir){
ffmpeg.checkForWindows(function(){

View File

@ -1035,8 +1035,11 @@ module.exports = function(s,config,lang){
s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke})
//frames from motion detect
if(e.details.detector_pam === '1'){
s.createPamDiffEngine(e)
s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].pipe(s.group[e.ke].activeMonitors[e.id].p2p).pipe(s.group[e.ke].activeMonitors[e.id].pamDiff)
// s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].pipe(s.group[e.ke].activeMonitors[e.id].p2p).pipe(s.group[e.ke].activeMonitors[e.id].pamDiff)
s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].on('data',function(buf){
var data = JSON.parse(buf)
s.triggerEvent(data)
})
if(e.details.detector_use_detect_object === '1'){
s.group[e.ke].activeMonitors[e.id].spawn.stdio[4].on('data',function(data){
s.onMonitorDetectorDataOutputSecondary(e,data)