Skip to content

Commit

Permalink
objectdetector: add zone option to smart sensor
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Dec 7, 2023
1 parent 4d28872 commit c74be7e
Showing 4 changed files with 66 additions and 17 deletions.
4 changes: 2 additions & 2 deletions plugins/objectdetector/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugins/objectdetector/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scrypted/objectdetector",
"version": "0.1.8",
"version": "0.1.9",
"description": "Scrypted Video Analysis Plugin. Installed alongside a detection service like OpenCV or TensorFlow.",
"author": "Scrypted",
"license": "Apache-2.0",
43 changes: 30 additions & 13 deletions plugins/objectdetector/src/main.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ import { sleep } from '@scrypted/common/src/sleep';
import sdk, { Camera, DeviceCreator, DeviceCreatorSettings, DeviceProvider, DeviceState, EventListenerRegister, Image, MediaObject, MediaStreamDestination, MixinDeviceBase, MixinProvider, MotionSensor, ObjectDetection, ObjectDetectionModel, ObjectDetectionTypes, ObjectDetectionZone, ObjectDetector, ObjectsDetected, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, Settings, SettingValue, VideoCamera, VideoFrame, VideoFrameGenerator } from '@scrypted/sdk';
import { StorageSettings } from '@scrypted/sdk/storage-settings';
import crypto from 'crypto';
import os from 'os';
import { AutoenableMixinProvider } from "../../../common/src/autoenable-mixin-provider";
import { SettingsMixinDeviceBase } from "../../../common/src/settings-mixin";
import { FFmpegVideoFrameGenerator } from './ffmpeg-videoframes';
@@ -29,6 +28,7 @@ type ClipPath = [number, number][];
type Zones = { [zone: string]: ClipPath };
interface ZoneInfo {
exclusion?: boolean;
filterMode?: 'include' | 'exclude' | 'observe';
type?: 'Intersect' | 'Contain';
classes?: string[];
scoreThreshold?: number;
@@ -358,7 +358,8 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
continue;
const odz: ObjectDetectionZone = {
classes: mixin.hasMotionType ? ['motion'] : zi?.classes,
exclusion: zi?.exclusion,
exclusion: zi?.filterMode ? zi?.filterMode === 'exclude' : zi?.exclusion,
filterMode: zi?.filterMode || (zi?.exclusion ? 'exclude' : undefined),
path: zone,
type: zi?.type,
}
@@ -495,8 +496,9 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
}

const zoneInfo = this.zoneInfos[zone];
const exclusion = zoneInfo?.filterMode ? zoneInfo.filterMode === 'exclude' : zoneInfo?.exclusion;
// track if there are any inclusion zones
if (!zoneInfo?.exclusion && !included)
if (!exclusion && !included && zoneInfo.filterMode !== 'observe')
included = false;

let match = false;
@@ -516,12 +518,14 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
if (match) {
o.zones.push(zone);

if (zoneInfo?.exclusion && match) {
copy = copy.filter(c => c !== o);
break;
}
if (zoneInfo.filterMode !== 'observe') {
if (exclusion && match) {
copy = copy.filter(c => c !== o);
break;
}

included = true;
included = true;
}
}
}

@@ -690,13 +694,26 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
value: JSON.stringify(value),
});

// settings.push({
// subgroup,
// key: `zoneinfo-exclusion-${name}`,
// title: `Exclusion Zone`,
// description: 'Detections in this zone will be excluded.',
// type: 'boolean',
// value: zi?.exclusion,
// });
settings.push({
subgroup,
key: `zoneinfo-exclusion-${name}`,
title: `Exclusion Zone`,
description: 'Detections in this zone will be excluded.',
type: 'boolean',
value: zi?.exclusion,
key: `zoneinfo-filterMode-${name}`,
title: `Filter Mode`,
description: 'The filter mode used by this zone. The Default is include. Zones set to observe will not affect filtering and can be used for automations.',
choices: [
'Default',
'include',
'exclude',
'observe',
],
value: zi?.filterMode || (zi?.exclusion ? 'exclude' : undefined) || 'Default',
});

settings.push({
34 changes: 33 additions & 1 deletion plugins/objectdetector/src/smart-motionsensor.ts
Original file line number Diff line number Diff line change
@@ -28,6 +28,14 @@ export class SmartMotionSensor extends ScryptedDeviceBase implements Settings, R
type: 'number',
defaultValue: 60,
},
zones: {
title: 'Zones',
description: 'Optional: The sensor will only be triggered when an object is in any of the following zones.',
multiple: true,
combobox: true,
choices: [
],
}
});
listener: EventListenerRegister;
timeout: NodeJS.Timeout;
@@ -48,6 +56,14 @@ export class SmartMotionSensor extends ScryptedDeviceBase implements Settings, R

this.storageSettings.settings.objectDetector.onPut = () => this.rebind();

this.storageSettings.settings.zones.onPut = () => this.rebind();

this.storageSettings.settings.zones.onGet = async () => {
return {
choices: this.storageSettings.values.zones || [],
};
};

this.rebind();
}

@@ -80,14 +96,30 @@ export class SmartMotionSensor extends ScryptedDeviceBase implements Settings, R
if (!detections?.length)
return;


const console = sdk.deviceManager.getMixinConsole(objectDetector.id, this.nativeId);

this.listener = objectDetector.listen(ScryptedInterface.ObjectDetector, (source, details, data) => {
const detected: ObjectsDetected = data;
const match = detected.detections?.find(d => {
if (!detections.includes(d.className))
return false;
const zones: string[] = this.storageSettings.values.zones;
if (zones?.length) {
if (d.zones) {
let found = false;
for (const z of d.zones) {
if (zones.includes(z)) {
found = true;
break;
}
}
if (!found)
return false;
}
else {
this.console.warn('Camera does not provide Zones in detection event. Zone filter will not be applied.');
}
}
if (!d.movement)
return true;
return d.movement.moving;

0 comments on commit c74be7e

Please sign in to comment.