diff web/calibrate/FindSafeExposure.ts @ 2419:e3af0ac507c8

new exposure-finder algorithm
author drewp@bigasterisk.com
date Tue, 21 May 2024 14:08:17 -0700
parents
children d5750b2aaa9e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/calibrate/FindSafeExposure.ts	Tue May 21 14:08:17 2024 -0700
@@ -0,0 +1,82 @@
+import { clamp } from "lodash";
+import { sleep } from "./Light9Calibrate";
+import { Light9Camera } from "./Light9Camera";
+import { XyPlot } from "./XyPlot";
+
+export class FindSafeExposure {
+  expoMin: number;
+  expoMax: number;
+  expo: number;
+  expoStep: number;
+  sameStateSteps: number;
+
+  constructor(public cam: Light9Camera, public plot: XyPlot) {
+    const fixedSteps = 5;
+
+    this.plot.clear();
+    this.expoMin = 1;
+    this.expoMax = 800;
+    this.expo = this.expoMin;
+    this.expoStep = (this.expoMax - this.expoMin) / fixedSteps;
+    this.sameStateSteps = 0;
+  }
+
+  async run() {
+    while (true) {
+      const currentOverexposed = await this.gatherSample();
+      this.step(currentOverexposed);
+      if (this.plot.data.length > 50 || Math.abs(this.expoStep) < 1) {
+        this.expo = this.expoMin;
+        await this.gatherSample();
+        break;
+      }
+    }
+  }
+  
+  step(currentOverexposed: number) {
+    const maxAllowedOverexposedPixels = 5;
+    const turnaroundScale = 0.6;
+    const stepsPerSide = 3;
+
+    const overexposed = currentOverexposed > maxAllowedOverexposedPixels;
+    if (this.expoStep > 0) {
+      if (overexposed) {
+        this.sameStateSteps += 1;
+      }
+    } else {
+      if (!overexposed) {
+        this.sameStateSteps += 1;
+      }
+    }
+    this.plot.setXMarklines([
+      { txt: "min", x: this.expoMin },
+      { txt: "max", x: this.expoMax },
+      { txt: "expo", x: this.expo },
+    ]);
+    const nextExpo = clamp(this.expo + this.expoStep, this.expoMin, this.expoMax);
+    if (this.sameStateSteps > stepsPerSide || nextExpo <= this.expoMin || nextExpo >= this.expoMax) {
+      if (this.expoStep > 0) {
+        this.expoMax = this.expo;
+      } else {
+        this.expoMin = this.expo;
+      }
+      this.expoStep = this.expoStep * -1 * turnaroundScale;
+      this.sameStateSteps = 0;
+    }
+    this.expo = clamp(this.expo + this.expoStep, this.expoMin, this.expoMax);
+  }
+  
+  async gatherSample() {
+    const settleMs = 200;
+
+    await this.cam.set("exposureTime", this.expo);
+    const settleUntil = Date.now() + settleMs;
+    let miny = this.cam.saturatedPixelCount!;
+    while (Date.now() < settleUntil) {
+      await sleep(1000 / this.cam.videoSettings.frameRate);
+      miny = Math.min(miny, this.cam.saturatedPixelCount!);
+    }
+    this.plot!.insertPoint(this.expo, clamp(miny, 0, 100));
+    return miny;
+  }
+}