Skip to content

Commit

Permalink
Add support for custom Deepstack models (#417)
Browse files Browse the repository at this point in the history
* Initial implementation

* Schema update

* Fix name of property in schema

* More schema experiments

* Update changelog
  • Loading branch information
neilenns authored Jan 18, 2021
1 parent 189132b commit b8761a0
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## Unreleased

- Add support for `customEndpoint` on a trigger to support custom Deepstack models. Resolves [issue 416](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/416).

## 5.6.1

- Address a minor security vulnerability in a dependency. Resolves [issue 407](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/407).
Expand Down
8 changes: 6 additions & 2 deletions src/DeepStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import * as Settings from "./Settings";
import request from "request-promise-native";
import IDeepStackResponse from "./types/IDeepStackResponse";

export default async function analyzeImage(fileName: string): Promise<IDeepStackResponse> {
export default async function analyzeImage(fileName: string, endpoint?: string): Promise<IDeepStackResponse> {
// This method of calling DeepStack comes from https://nodejs.deepstack.cc/
const imageStream = fs.createReadStream(fileName);
const form = { image: imageStream };

const deepstackUri = endpoint
? new URL(endpoint, Settings.deepstackUri)
: new URL("/v1/vision/detection", Settings.deepstackUri);

const rawResponse = await request
.post({
formData: form,
uri: new URL("/v1/vision/detection", Settings.deepstackUri).toString(),
uri: deepstackUri.toString(),
})
.catch(e => {
throw Error(`Failed to call DeepStack at ${Settings.deepstackUri}: ${e.error}`);
Expand Down
13 changes: 12 additions & 1 deletion src/Trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default class Trigger {
* Use the incrementAnalyzedFiles() method to update the total.
*/
public analyzedFilesCount = 0;
public customEndpoint?: string;
public cooldownTime: number;
public enabled = true;
public name: string;
Expand Down Expand Up @@ -76,7 +77,7 @@ export default class Trigger {
private async analyzeImage(fileName: string): Promise<IDeepStackPrediction[] | undefined> {
log.verbose(`Trigger ${this.name}`, `${fileName}: Analyzing`);
const startTime = new Date();
const analysis = await analyzeImage(fileName).catch(e => {
const analysis = await analyzeImage(fileName, this.customEndpoint).catch(e => {
log.warn(`Trigger ${this.name}`, e);
return undefined;
});
Expand Down Expand Up @@ -283,6 +284,16 @@ export default class Trigger {
* @returns True if the trigger is activated by the label
*/
public isRegisteredForObject(fileName: string, label: string): boolean {
// If a custom endpoint is specified then watchObjects don't apply and it is assumed
// the trigger is always registered for the detected object.
if (this.customEndpoint) {
log.verbose(`Trigger ${this.name}`, `${fileName}: Custom endpoint matched triggering object ${label}`);
return true;
}

// In the far more common, normal, case where no custom endpoint is specified
// check and see if the detected object is in the list of registered objects
// to watch.
const isRegistered = this.watchObjects?.some(watchLabel => {
return watchLabel.toLowerCase() === label?.toLowerCase();
});
Expand Down
1 change: 1 addition & 0 deletions src/TriggerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export function loadConfiguration(configurations: IConfiguration[]): IConfigurat
triggers = triggerConfigJson.triggers.map(triggerJson => {
log.info("Triggers", `Loaded configuration for ${triggerJson.name}`);
const configuredTrigger = new Trigger({
customEndpoint: triggerJson.customEndpoint,
cooldownTime: triggerJson.cooldownTime,
enabled: triggerJson.enabled ?? true, // If it isn't specified then enable the camera
name: triggerJson.name,
Expand Down
12 changes: 10 additions & 2 deletions src/schemas/triggerConfiguration.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
"title": "Trigger",
"description": "A trigger that responds to DeepStack predictions.",
"type": "object",
"anyOf": [{ "required": ["watchPattern"] }, { "required": ["snapshotUri"] }],
"required": ["handlers", "name", "watchObjects"],
"allOf": [
{ "anyOf": [{ "required": ["watchPattern"] }, { "required": ["snapshotUri"] }] },
{ "oneOf": [{ "required": ["watchObjects"] }, { "required": ["customEndpoint"] }] },
{ "required": ["handlers", "name"] }
],
"additionalProperties": false,
"properties": {
"name": {
Expand All @@ -45,6 +48,11 @@
"maximum": 600,
"type": "integer"
},
"customEndpoint": {
"description": "Custom endpoint for DeepStack, used to call a custom model. When specified watchObjects is ignored.",
"examples": ["/v1/vision/custom/my_custom_model_name?image"],
"type": "string"
},
"enabled": {
"description": "Set to true to enable the trigger. If false the trigger will be ignored.",
"examples": [true],
Expand Down
1 change: 1 addition & 0 deletions src/types/ITriggerJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import IPushbulletConfigJson from "../handlers/pushbulletManager/IPushoverConfig

export default interface ITriggerJson {
cooldownTime: number;
customEndpoint?: string;
enabled: boolean;
name: string;
snapshotUri: string;
Expand Down

0 comments on commit b8761a0

Please sign in to comment.