Skip to content

Commit

Permalink
videoanalysis: snapshot mode cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Mar 1, 2023
1 parent 80db6e5 commit fee9033
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 20 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.

3 changes: 2 additions & 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.0.92",
"version": "0.0.98",
"description": "Scrypted Video Analysis Plugin. Installed alongside a detection service like OpenCV or TensorFlow.",
"author": "Scrypted",
"license": "Apache-2.0",
Expand Down Expand Up @@ -35,6 +35,7 @@
"name": "Video Analysis Plugin",
"type": "API",
"interfaces": [
"Settings",
"MixinProvider"
],
"realfs": true
Expand Down
70 changes: 53 additions & 17 deletions plugins/objectdetector/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sdk, { Camera, DeviceState, EventListenerRegister, MediaObject, MixinDeviceBase, MixinProvider, MotionSensor, ObjectDetection, ObjectDetectionCallbacks, ObjectDetectionModel, ObjectDetectionResult, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting, Settings, VideoCamera } from '@scrypted/sdk';
import sdk, { Camera, DeviceState, EventListenerRegister, MediaObject, MixinDeviceBase, MixinProvider, MotionSensor, ObjectDetection, ObjectDetectionCallbacks, ObjectDetectionModel, ObjectDetectionResult, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk';
import { StorageSettings } from '@scrypted/sdk/storage-settings';
import crypto from 'crypto';
import cloneDeep from 'lodash/cloneDeep';
Expand Down Expand Up @@ -347,7 +347,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
}
// retain passing the second pass threshold for first time.
if (d.bestSecondPassScore < this.secondScoreThreshold && secondPassScore >= this.secondScoreThreshold) {
this.console.log('improved', d.id, d.bestSecondPassScore, d.score);
this.console.log('improved', d.id, secondPassScore, d.score);
better = true;
retainImage = true;
}
Expand Down Expand Up @@ -436,6 +436,8 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
return;

this.detectorRunning = true;
this.analyzeStop = Date.now() + this.getDetectionDuration();

while (this.detectorRunning) {
const now = Date.now();
if (now > this.analyzeStop)
Expand All @@ -445,26 +447,21 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
reason: 'event',
});
const found = await this.objectDetection.detectObjects(mo, {
detectionId: this.detectionId,
duration: this.getDetectionDuration(),
settings: this.getCurrentSettings(),
});
found.running = this.detectorRunning;
this.handleDetectionEvent(found, undefined, mo);
}, this);
}
catch (e) {
this.console.error('snapshot detection error', e);
}
// cameras tend to only refresh every 1s at best.
// maybe get this value from somewhere? or sha the jpeg?
const diff = now + 1100 - Date.now();
if (diff > 0)
await sleep(diff);
}
this.detectorRunning = false;
this.handleDetectionEvent({
detectionId: this.detectionId,
running: false,
detections: [],
timestamp: Date.now(),
}, undefined, undefined);
this.endObjectDetection();
}

async startStreamAnalysis() {
Expand Down Expand Up @@ -545,7 +542,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
applyZones(detection: ObjectsDetected) {
// determine zones of the objects, if configured.
if (!detection.detections)
return;
return [];
let copy = detection.detections.slice();
for (const o of detection.detections) {
if (!o.boundingBox)
Expand Down Expand Up @@ -829,7 +826,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
settings.push({
subgroup,
key: `zone-${name}`,
title: `Edit Zone`,
title: `Open Zone Editor`,
type: 'clippath',
value: JSON.stringify(value),
});
Expand Down Expand Up @@ -987,6 +984,8 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
}

class ObjectDetectorMixin extends MixinDeviceBase<ObjectDetection> implements MixinProvider {
currentMixins = new Set<ObjectDetectionMixin>();

constructor(mixinDevice: ObjectDetection, mixinDeviceInterfaces: ScryptedInterface[], mixinDeviceState: DeviceState, mixinProviderNativeId: ScryptedNativeId, public model: ObjectDetectionModel) {
super({ mixinDevice, mixinDeviceInterfaces, mixinDeviceState, mixinProviderNativeId });

Expand Down Expand Up @@ -1036,19 +1035,53 @@ class ObjectDetectorMixin extends MixinDeviceBase<ObjectDetection> implements Mi
const hasMotionType = this.model.classes.includes('motion');
const settings = this.model.settings;

return new ObjectDetectionMixin(mixinDevice, mixinDeviceInterfaces, mixinDeviceState, this.mixinProviderNativeId, objectDetection, this.model.name, group, hasMotionType, settings);
const ret = new ObjectDetectionMixin(mixinDevice, mixinDeviceInterfaces, mixinDeviceState, this.mixinProviderNativeId, objectDetection, this.model.name, group, hasMotionType, settings);
this.currentMixins.add(ret);
return ret;
}

async releaseMixin(id: string, mixinDevice: any) {
this.currentMixins.delete(mixinDevice);
return mixinDevice.release();
}
}

class ObjectDetectionPlugin extends AutoenableMixinProvider {
class ObjectDetectionPlugin extends AutoenableMixinProvider implements Settings {
currentMixins = new Set<ObjectDetectorMixin>();

storageSettings = new StorageSettings(this, {
activeMotionDetections: {
title: 'Active Motion Detection Sessions',
readonly: true,
mapGet: () => {
return [...this.currentMixins.values()]
.reduce((c1, v1) => c1 + [...v1.currentMixins.values()]
.reduce((c2, v2) => c2 + (v2.hasMotionType && v2.detectorRunning ? 1 : 0), 0), 0);
}
},
activeObjectDetections: {
title: 'Active Object Detection Sessions',
readonly: true,
mapGet: () => {
return [...this.currentMixins.values()]
.reduce((c1, v1) => c1 + [...v1.currentMixins.values()]
.reduce((c2, v2) => c2 + (!v2.hasMotionType && v2.detectorRunning ? 1 : 0), 0), 0);
}
}
})

constructor(nativeId?: ScryptedNativeId) {
super(nativeId);
}

getSettings(): Promise<Setting[]> {
return this.storageSettings.getSettings();
}

putSetting(key: string, value: SettingValue): Promise<void> {
return this.storageSettings.putSetting(key, value);
}

async canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise<string[]> {
if (!interfaces.includes(ScryptedInterface.ObjectDetection))
return;
Expand All @@ -1057,12 +1090,15 @@ class ObjectDetectionPlugin extends AutoenableMixinProvider {

async getMixin(mixinDevice: ObjectDetection, mixinDeviceInterfaces: ScryptedInterface[], mixinDeviceState: { [key: string]: any; }): Promise<any> {
const model = await mixinDevice.getDetectionModel();
return new ObjectDetectorMixin(mixinDevice, mixinDeviceInterfaces, mixinDeviceState, this.nativeId, model);
const ret = new ObjectDetectorMixin(mixinDevice, mixinDeviceInterfaces, mixinDeviceState, this.nativeId, model);
this.currentMixins.add(ret);
return ret;
}

async releaseMixin(id: string, mixinDevice: any): Promise<void> {
// what does this mean to make a mixin provider no longer available?
// just ignore it until reboot?
this.currentMixins.delete(mixinDevice);
}
}

Expand Down

0 comments on commit fee9033

Please sign in to comment.