Files @ 4bab5bbce195
Branch filter:

Location: light9/web/calibrate/FindSafeExposure.ts

drewp@bigasterisk.com
show-specific changes
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;
      }
    }
    return this.expo;
  }
  
  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;
  }
}