400 lines
17 KiB
JavaScript
400 lines
17 KiB
JavaScript
// Matrix In Region Libs >
|
|
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){
|
|
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(e.details.detector_sensitivity===''){
|
|
globalSensitivity = 10
|
|
}else{
|
|
globalSensitivity = parseInt(e.details.detector_sensitivity)
|
|
}
|
|
if(e.details.detector_color_threshold===''){
|
|
globalColorThreshold = 9
|
|
}else{
|
|
globalColorThreshold = parseInt(e.details.detector_color_threshold)
|
|
}
|
|
|
|
globalThreshold = parseInt(e.details.detector_threshold) || 0
|
|
|
|
var regionJson
|
|
try{
|
|
regionJson = JSON.parse(s.group[e.ke].rawMonitorConfigurations[e.id].details.cords)
|
|
}catch(err){
|
|
regionJson = s.group[e.ke].rawMonitorConfigurations[e.id].details.cords
|
|
}
|
|
|
|
if(Object.keys(regionJson).length === 0 || e.details.detector_frame === '1'){
|
|
fullFrame = {
|
|
name:'FULL_FRAME',
|
|
sensitivity:globalSensitivity,
|
|
color_threshold:globalColorThreshold,
|
|
points:[
|
|
[0,0],
|
|
[0,height],
|
|
[width,height],
|
|
[width,0]
|
|
]
|
|
}
|
|
}
|
|
|
|
e.triggerTimer = {}
|
|
|
|
var regions = s.createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
|
|
var pamDiffOptions = {
|
|
grayscale: 'luminosity',
|
|
regions : regions.forPam
|
|
}
|
|
if(e.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()
|
|
var regionArray = Object.values(regionJson)
|
|
if(config.detectorMergePamRegionTriggers === true){
|
|
// merge pam triggers for performance boost
|
|
var buildTriggerEvent = function(trigger){
|
|
var detectorObject = {
|
|
f:'trigger',
|
|
id:e.id,
|
|
ke:e.ke,
|
|
name:trigger.name,
|
|
details:{
|
|
plug:'built-in',
|
|
name:trigger.name,
|
|
reason:'motion',
|
|
confidence:trigger.percent
|
|
},
|
|
plates:[],
|
|
imgHeight:e.details.detector_scale_y,
|
|
imgWidth:e.details.detector_scale_x
|
|
}
|
|
if(trigger.merged){
|
|
if(trigger.matrices)detectorObject.details.matrices = trigger.matrices
|
|
var filteredCount = 0
|
|
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) {
|
|
++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)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
}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) {
|
|
if(!err1 && !err2){
|
|
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
|
|
s.triggerEvent(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
|
|
Object.keys(regions.notForPam).forEach(function(name){
|
|
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
|
|
})
|
|
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
|
var filteredCount = 0
|
|
var filteredCountSuccess = 0
|
|
data.trigger.forEach(function(trigger){
|
|
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(err){
|
|
++filteredCount
|
|
if(!err)++filteredCountSuccess
|
|
if(filteredCount === data.trigger.length && filteredCountSuccess > 0){
|
|
buildTriggerEvent(s.mergePamTriggers(data))
|
|
}
|
|
})
|
|
})
|
|
})
|
|
}else{
|
|
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
|
buildTriggerEvent(s.mergePamTriggers(data))
|
|
})
|
|
}
|
|
}else{
|
|
//config.detectorMergePamRegionTriggers NOT true
|
|
//original behaviour, all regions have their own event.
|
|
var buildTriggerEvent = function(trigger){
|
|
var detectorObject = {
|
|
f:'trigger',
|
|
id:e.id,
|
|
ke:e.ke,
|
|
name:trigger.name,
|
|
details:{
|
|
plug:'built-in',
|
|
name:trigger.name,
|
|
reason:'motion',
|
|
confidence:trigger.percent
|
|
},
|
|
plates:[],
|
|
imgHeight:e.details.detector_scale_y,
|
|
imgWidth:e.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) {
|
|
if(!err1 && ! err2){
|
|
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
|
|
s.triggerEvent(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
|
|
Object.keys(regions.notForPam).forEach(function(name){
|
|
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
|
|
})
|
|
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
|
data.trigger.forEach(function(trigger){
|
|
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){
|
|
s.createMatrixFromPamTrigger(trigger)
|
|
buildTriggerEvent(trigger)
|
|
})
|
|
})
|
|
})
|
|
}else{
|
|
s.group[e.ke].activeMonitors[e.id].pamDiff.on('diff', (data) => {
|
|
data.trigger.forEach(function(trigger){
|
|
s.createMatrixFromPamTrigger(trigger)
|
|
buildTriggerEvent(trigger)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
s.createPamDiffRegionArray = function(regions,globalColorThreshold,globalSensitivity,fullFrame){
|
|
var pamDiffCompliantArray = [],
|
|
arrayForOtherStuff = [],
|
|
json
|
|
try{
|
|
json = JSON.parse(regions)
|
|
}catch(err){
|
|
json = regions
|
|
}
|
|
if(fullFrame){
|
|
json[fullFrame.name]=fullFrame;
|
|
}
|
|
Object.values(json).forEach(function(region){
|
|
if(!region)return false;
|
|
region.polygon = [];
|
|
region.points.forEach(function(points){
|
|
var x = parseFloat(points[0]);
|
|
var y = parseFloat(points[1]);
|
|
if(x < 0)x = 0;
|
|
if(y < 0)y = 0;
|
|
region.polygon.push({
|
|
x: x,
|
|
y: y
|
|
})
|
|
})
|
|
if(region.sensitivity===''){
|
|
region.sensitivity = globalSensitivity
|
|
}else{
|
|
region.sensitivity = parseInt(region.sensitivity)
|
|
}
|
|
if(region.color_threshold===''){
|
|
region.color_threshold = globalColorThreshold
|
|
}else{
|
|
region.color_threshold = parseInt(region.color_threshold)
|
|
}
|
|
pamDiffCompliantArray.push({name: region.name, difference: region.color_threshold, percent: region.sensitivity, polygon:region.polygon})
|
|
arrayForOtherStuff[region.name] = region;
|
|
})
|
|
if(pamDiffCompliantArray.length===0){pamDiffCompliantArray = null}
|
|
return {forPam:pamDiffCompliantArray,notForPam:arrayForOtherStuff};
|
|
}
|
|
|
|
s.filterTheNoise = function(e,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
|
|
if(!noiseRange || noiseRange === ''){
|
|
noiseRange = 6
|
|
}
|
|
noiseRange = parseFloat(noiseRange)
|
|
if(((trigger.percent - thePreviousTriggerPercent) < noiseRange)||(thePreviousTriggerPercent - trigger.percent) > -noiseRange){
|
|
noiseFilterArray[trigger.name].push(trigger.percent);
|
|
}
|
|
}else{
|
|
noiseFilterArray[trigger.name].push(trigger.percent);
|
|
}
|
|
if(noiseFilterArray[trigger.name].length > 10){
|
|
noiseFilterArray[trigger.name] = noiseFilterArray[trigger.name].splice(1,10)
|
|
}
|
|
var theNoise = 0;
|
|
noiseFilterArray[trigger.name].forEach(function(v,n){
|
|
theNoise += v;
|
|
})
|
|
theNoise = theNoise / noiseFilterArray[trigger.name].length;
|
|
var triggerPercentWithoutNoise = trigger.percent - theNoise;
|
|
if(triggerPercentWithoutNoise > regions.notForPam[trigger.name].sensitivity){
|
|
callback(null,trigger)
|
|
}else{
|
|
callback(true)
|
|
}
|
|
}
|
|
|
|
s.checkMaximumSensitivity = function(monitor, region, detectorObject, callback) {
|
|
var logName = detectorObject.id + ':' + detectorObject.name
|
|
var globalMaxSensitivity = parseInt(monitor.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
|
|
}
|
|
}
|
|
}
|
|
|
|
s.checkTriggerThreshold = function(monitor, 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] = {
|
|
count : threshold,
|
|
timeout : null
|
|
}
|
|
}
|
|
if (--monitor.triggerTimer[detectorObject.name].count == 0) {
|
|
callback(null)
|
|
clearTimeout(monitor.triggerTimer[detectorObject.name].timeout)
|
|
monitor.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
|
|
}, ((threshold+0.5) * 1000) / fps)
|
|
}
|
|
}
|
|
}
|
|
s.mergePamTriggers = function(data){
|
|
if(data.trigger.length > 1){
|
|
var n = 0
|
|
var sum = 0
|
|
var name = []
|
|
var matrices = []
|
|
data.trigger.forEach(function(trigger){
|
|
name.push(trigger.name + ' ('+trigger.percent+'%)')
|
|
++n
|
|
sum += trigger.percent
|
|
s.createMatrixFromPamTrigger(trigger)
|
|
if(trigger.matrix)matrices.push(trigger.matrix)
|
|
})
|
|
var average = sum / n
|
|
name = name.join(', ')
|
|
if(matrices.length === 0)matrices = null
|
|
var trigger = {
|
|
name: name,
|
|
percent: parseInt(average),
|
|
matrices: matrices,
|
|
merged: data.trigger
|
|
}
|
|
}else{
|
|
var trigger = data.trigger[0]
|
|
s.createMatrixFromPamTrigger(trigger)
|
|
trigger.matrices = [trigger.matrix]
|
|
}
|
|
return trigger
|
|
}
|
|
s.isAtleastOneMatrixInRegion = function(regions,matrices,callback){
|
|
var regionPolys = []
|
|
var matrixPoints = []
|
|
regions.forEach(function(region,n){
|
|
var polyPoints = []
|
|
region.points.forEach(function(point){
|
|
polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
|
|
})
|
|
regionPolys[n] = new P(new V(0,0), polyPoints)
|
|
})
|
|
var collisions = []
|
|
var foundInRegion = false
|
|
matrices.forEach(function(matrix){
|
|
var matrixPoints = [
|
|
new V(matrix.x,matrix.y),
|
|
new V(matrix.width,matrix.y),
|
|
new V(matrix.width,matrix.height),
|
|
new V(matrix.x,matrix.height)
|
|
]
|
|
var matrixPoly = new P(new V(0,0), matrixPoints)
|
|
regionPolys.forEach(function(region,n){
|
|
var response = new SAT.Response()
|
|
var collided = SAT.testPolygonPolygon(matrixPoly, region, response)
|
|
if(collided === true){
|
|
collisions.push({
|
|
matrix: matrix,
|
|
region: regions[n]
|
|
})
|
|
foundInRegion = true
|
|
}
|
|
})
|
|
})
|
|
if(callback)callback(foundInRegion,collisions)
|
|
return foundInRegion
|
|
}
|
|
s.createMatrixFromPamTrigger = function(trigger){
|
|
if(
|
|
trigger.minX &&
|
|
trigger.maxX &&
|
|
trigger.minY &&
|
|
trigger.maxY
|
|
){
|
|
var coordinates = [
|
|
{"x" : trigger.minX, "y" : trigger.minY},
|
|
{"x" : trigger.maxX, "y" : trigger.minY},
|
|
{"x" : trigger.maxX, "y" : trigger.maxY}
|
|
]
|
|
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))
|
|
trigger.matrix = {
|
|
x: coordinates[0].x,
|
|
y: coordinates[0].y,
|
|
width: width,
|
|
height: height,
|
|
tag: trigger.name
|
|
}
|
|
}
|
|
return trigger
|
|
}
|
|
}
|