Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anomaly detection #34

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions examples/Anomaly_Detection/Anomaly_Detection.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Anomaly detection
* Detect when the frame changes by a reasonable amount
*
* BE SURE TO SET "TOOLS > CORE DEBUG LEVEL = DEBUG"
* to turn on debug messages
*/
#include <eloquent_esp32cam.h>
#include <eloquent_esp32cam/anomaly/detection.h>

using eloq::camera;
using eloq::anomaly::detection;

/**
*
*/
void setup() {
delay(3000);
Serial.begin(115200);
Serial.println("___ANOMALY DETECTION___");

// camera settings
// replace with your own model!
camera.pinout.xiao();
camera.brownout.disable();
camera.resolution.vga();
camera.quality.high();

// configure anomaly detection
detection.skip(4);
// the higher the stride, the faster the detection
// the higher the stride, the less the granularity
detection.stride(1);
// the higher the threshold, the less the sensitivity
// (at pixel level)
detection.threshold(5);
// the higher the detectionRatio, the less the sensitivity
// (at image level, from 0 to 1)
detection.detectionRatio(0.5);
// the higher the referenceRatio, the more the reference image can change over time
// (at image level, from 0 to 1)
detection.referenceRatio(0.2);
// optionally, you can enable rate limiting (aka debounce)
// anomaly won't trigger more often than the specified frequency
//detection.rate.atMostOnceEvery(5).seconds();

// init camera
while (!camera.begin().isOk())
Serial.println(camera.exception.toString());

Serial.println("Camera OK");
Serial.println("Awaiting anomaly...");
}

/**
*
*/
void loop() {

// Don't run more often than the time for an anomaly to come into view as the reference image can 'drift' away from 'normal'
delay(1000);
// capture picture
if (!camera.capture().isOk()) {
Serial.println(camera.exception.toString());
return;
}

// run anomaly detection
if (!detection.run().isOk()) {
Serial.println(detection.exception.toString());
return;
}

// on anomaly, perform action
if (detection.triggered()) {
Serial.print("Anomaly detected: "); Serial.println(detection.movingRatio);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Run anomaly detection at low resolution.
* On anomaly, capture frame at higher resolution
* for SD storage.
*
* BE SURE TO SET "TOOLS > CORE DEBUG LEVEL = INFO"
* to turn on debug messages
*/
#include <eloquent_esp32cam.h>
#include <eloquent_esp32cam/anomaly/detection.h>

using eloq::camera;
using eloq::anomaly::detection;


/**
*
*/
void setup() {
delay(3000);
Serial.begin(115200);
Serial.println("___ANOMALY DETECTION + SWITCH RESOLUTION___");

// camera settings
// replace with your own model!
camera.pinout.freenove_s3();
camera.brownout.disable();
camera.resolution.vga();
camera.quality.high();

// see example of anomaly detection for config values
detection.skip(5);
detection.stride(1);
detection.threshold(5);
// the higher the detectionRatio, the less the sensitivity
// (at image level, from 0 to 1)
detection.detectionRatio(0.5);
// the higher the referenceRatio, the more the reference image can change over time
// (at image level, from 0 to 1)
detection.referenceRatio(0.2);

// init camera
while (!camera.begin().isOk())
Serial.println(camera.exception.toString());

Serial.println("Camera OK");
Serial.println("Awaiting for anomaly...");
}

/**
*
*/
void loop() {
// Don't run more often than the time for an anomaly to come into view as the reference image can 'drift' away from 'normal'
delay(1000);
// capture picture
if (!camera.capture().isOk()) {
Serial.println(camera.exception.toString());
return;
}

// run anomaly detection
if (!detection.run().isOk()) {
Serial.println(detection.exception.toString());
return;
}

// on anomaly, perform action
if (detection.triggered()) {
Serial.printf(
"Anomaly of %.2f detected on frame of size %dx%d (%d bytes)\n",
detection.movingRatio,
camera.resolution.getWidth(),
camera.resolution.getHeight(),
camera.getSizeInBytes()
);

Serial.println("Taking photo of anomaly at higher resolution");

camera.resolution.at(FRAMESIZE_UXGA, []() {
Serial.printf(
"Switched to higher resolution: %dx%d. It took %d ms to switch\n",
camera.resolution.getWidth(),
camera.resolution.getHeight(),
camera.resolution.benchmark.millis()
);

camera.capture();

Serial.printf(
"Frame size is now %d bytes\n",
camera.getSizeInBytes()
);

// save to SD...
});

Serial.println("Resolution switched back to VGA");
}
}
91 changes: 91 additions & 0 deletions src/eloquent_esp32cam/anomaly/daemon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#ifndef ELOQUENT_ESP32CAM_ANOMALY_DAEMON_H
#define ELOQUENT_ESP32CAM_ANOMALY_DAEMON_H

#include <functional>
#include "../camera/camera.h"
#include "../extra/esp32/multiprocessing/thread.h"

using eloq::camera;
using Eloquent::Extra::Esp32::Multiprocessing::Thread;
using OnAnomalyCallback = std::function<void(void)>;


namespace Eloquent {
namespace Esp32cam {
namespace Anomaly {
/**
* Run anomaly detection in a task
*
* @class Daemon
* @author jksemple
* @date 11/07/2024
* @file daemon.h
* @brief
*/
template<typename T>
class Daemon {
public:
Thread thread;

/**
* Constructor
*
* @brief
*/
Daemon(T* detection) :
thread("AnomalyDetection"),
_detection(detection) {

}

/**
* Run function when a difference from 'normal' is detected
*
* @brief
* @param callback
*/
void onAnomaly(OnAnomalyCallback callback) {
_onAnomaly = callback;
}

/**
* Start anomaly detection in background
*
* @brief
*/
void start() {
thread
.withArgs((void*) this)
.withStackSize(5000)
.run([](void *args) {
Daemon *self = (Daemon*) args;

delay(3000);

while (true) {
yield();
delay(1);

if (!camera.capture().isOk())
continue;

if (!self->_detection->run().isOk())
continue;

if (!self->_detection->triggered())
continue;

self->_onAnomaly();
}
});
}

protected:
T *_detection;
OnAnomalyCallback _onAnomaly;
};
}
}
}

#endif
Loading