|
@@ -0,0 +1,119 @@
|
|
|
|
|
+#define MAX_RESOLUTION_VGA
|
|
|
|
|
+
|
|
|
|
|
+#include "esp32cam.h"
|
|
|
|
|
+#include "esp32cam/JpegDecoder.h"
|
|
|
|
|
+#include "esp32cam/motion/Detector.h"
|
|
|
|
|
+#include "esp32cam/motion/SimpleChange.h"
|
|
|
|
|
+
|
|
|
|
|
+Eloquent::Esp32cam::Cam cam;
|
|
|
|
|
+Eloquent::Esp32cam::JpegDecoder decoder;
|
|
|
|
|
+Eloquent::Esp32cam::Motion::SimpleChange algorithm;
|
|
|
|
|
+Eloquent::Esp32cam::Motion::Detector detector(algorithm);
|
|
|
|
|
+
|
|
|
|
|
+void motion_detection_setup() {
|
|
|
|
|
+ cam.aithinker();
|
|
|
|
|
+ cam.highQuality();
|
|
|
|
|
+ cam.vga();
|
|
|
|
|
+ cam.highestSaturation();
|
|
|
|
|
+ /**
|
|
|
|
|
+ * For motion detection to perform well, I suggest
|
|
|
|
|
+ * you disable automatic controls, otherwise
|
|
|
|
|
+ * the camera sensor will artificially alter the
|
|
|
|
|
+ * pixels and those will be mis-labelled as foreground
|
|
|
|
|
+ */
|
|
|
|
|
+ cam.disableAutomaticWhiteBalance();
|
|
|
|
|
+ cam.disableAutomaticExposureControl();
|
|
|
|
|
+ cam.disableGainControl();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Configure the detector
|
|
|
|
|
+ */
|
|
|
|
|
+ /**
|
|
|
|
|
+ * use the first N frames to train the algorithm
|
|
|
|
|
+ */
|
|
|
|
|
+ detector.trainFor(30);
|
|
|
|
|
+ /**
|
|
|
|
|
+ * re-run the training aftern N frames
|
|
|
|
|
+ * (at 33 FPS, 33 * 600 = 10 minutes)
|
|
|
|
|
+ */
|
|
|
|
|
+ detector.retrainAfter(33ULL * 600);
|
|
|
|
|
+ /**
|
|
|
|
|
+ * detection motion when 20% or more pixels of the frame
|
|
|
|
|
+ * are labelled as background
|
|
|
|
|
+ */
|
|
|
|
|
+ detector.triggerAbove(0.2);
|
|
|
|
|
+ /**
|
|
|
|
|
+ * try to remove spurious foreground pixels
|
|
|
|
|
+ */
|
|
|
|
|
+ detector.denoise();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Configure algorithm
|
|
|
|
|
+ * (each algorithm has its own set of parameters)
|
|
|
|
|
+ */
|
|
|
|
|
+ /**
|
|
|
|
|
+ * label pixel as foreground if its value changed
|
|
|
|
|
+ * by more than 20 (in a range from 0 to 255)
|
|
|
|
|
+ */
|
|
|
|
|
+ algorithm.differBy(20);
|
|
|
|
|
+ /**
|
|
|
|
|
+ * when updating the pixel value, how much shall we
|
|
|
|
|
+ * take into consideration its previous value?
|
|
|
|
|
+ * The higher this value, the stronger influence
|
|
|
|
|
+ * the pixel history has w.r.t. its current value
|
|
|
|
|
+ * The update formula is
|
|
|
|
|
+ * updated value = a * old value + (1 - a) * new value
|
|
|
|
|
+ * Where a in in the range 0 - 1 (1 excluded)
|
|
|
|
|
+ */
|
|
|
|
|
+ algorithm.smooth(0.9);
|
|
|
|
|
+ /**
|
|
|
|
|
+ * when a pixel is labelled as foreground, should we
|
|
|
|
|
+ * update its value or not?
|
|
|
|
|
+ * It is updated by default, so if that's what you want,
|
|
|
|
|
+ * remove the following line.
|
|
|
|
|
+ */
|
|
|
|
|
+ algorithm.onlyUpdateBackground();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ while (!cam.begin())
|
|
|
|
|
+ Serial.println(cam.getErrorMessage());
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void motion_detection_loop() {
|
|
|
|
|
+ if (!cam.capture()) {
|
|
|
|
|
+ Serial.println(cam.getErrorMessage());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!decoder.decode(cam)) {
|
|
|
|
|
+ Serial.println(decoder.getErrorMessage());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Update the background model
|
|
|
|
|
+ * If there's an error, print it
|
|
|
|
|
+ *
|
|
|
|
|
+ * Note: while training, `update()` will return False
|
|
|
|
|
+ * even if it cannot really considered an error.
|
|
|
|
|
+ * If you want to check if the error is due to training or not,
|
|
|
|
|
+ * you can check for `detector.isTraining()`
|
|
|
|
|
+ */
|
|
|
|
|
+ if (!detector.update(decoder.luma)) {
|
|
|
|
|
+ Serial.println(detector.getErrorMessage());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Test if motion was detected
|
|
|
|
|
+ */
|
|
|
|
|
+ if (detector.triggered()) {
|
|
|
|
|
+ Serial.println("Motion!");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * After the call to `triggered()`, you can debug the internal
|
|
|
|
|
+ * status of the detector if you want to find out why it triggered or not
|
|
|
|
|
+ */
|
|
|
|
|
+ Serial.println(detector.getTriggerStatus());
|
|
|
|
|
+}
|