Changeset - d5750b2aaa9e
[Not reviewed]
default
0 3 0
drewp@bigasterisk.com - 8 months ago 2024-05-21 21:50:01
drewp@bigasterisk.com
minor cam edits
3 files changed with 10 insertions and 6 deletions:
0 comments (0 inline, 0 general)
web/calibrate/FindSafeExposure.ts
Show inline comments
 
@@ -10,48 +10,49 @@ export class FindSafeExposure {
 
  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) {
web/calibrate/Light9Calibrate.ts
Show inline comments
 
@@ -60,42 +60,42 @@ export class Light9Calibrate extends Lit
 
  }
 

	
 
  async withButtonSpinner(ev: MouseEvent, fn: () => Promise<void>) {
 
    const btn = ev.target as HTMLButtonElement;
 
    try {
 
      btn.disabled = true;
 
      await fn();
 
    } finally {
 
      btn.disabled = false;
 
    }
 
  }
 
  async setToFull(ev: MouseEvent) {
 
    await this.withButtonSpinner(ev, async () => {
 
      this.collector.updateSettings([
 
        /// device,attr,value
 
        [this.device, "http://light9.bigasterisk.com/color", "#ffffff"],
 
        [this.device, "http://light9.bigasterisk.com/white", 1],
 
      ]);
 
    });
 
  }
 

	
 
  async findSafeExposure(ev: MouseEvent) {
 
    await this.withButtonSpinner(ev, async () => {
 
      const algo = new FindSafeExposure(this.cam!, this.plot!);
 
      await algo.run();
 
      const expo = await algo.run();
 
    });
 
  }
 
  async setToZero(ev: MouseEvent) {
 
    await this.withButtonSpinner(ev, async () => {
 
      this.collector.updateSettings([
 
        [this.device, "http://light9.bigasterisk.com/color", "#000000"],
 
        [this.device, "http://light9.bigasterisk.com/white", 0],
 
      ]);
 
    });
 
  }
 
  async markTare(ev: MouseEvent) {
 
    await this.withButtonSpinner(ev, async () => {});
 
  }
 
  async calibrateLoop(ev: MouseEvent) {
 
    await this.withButtonSpinner(ev, async () => {});
 
  }
 
}
web/calibrate/Light9Camera.ts
Show inline comments
 
@@ -64,54 +64,56 @@ export class Light9Camera extends LitEle
 
        <p>saturated pixels: ${saturatedCountDisplay}</p>
 
        <light9-camera-settings-table .cam=${this} .videoSettings=${this.videoSettings}></light9-camera-settings-table>
 
      </div>
 
    `;
 
  }
 

	
 
  protected async firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>) {
 
    this.videoEl = this.shadowRoot!.getElementById("video") as HTMLVideoElement;
 
    this.canvas = this.shadowRoot!.getElementById("canvas") as HTMLCanvasElement;
 
    this.ctx = this.canvas.getContext("2d", { willReadFrequently: true })!;
 

	
 
    const constraints: MediaStreamConstraints = {
 
      video: {
 
        facingMode: { ideal: "environment" },
 
        frameRate: { max: 10 },
 
        width: 640,
 
        height: 480,
 
      },
 
    };
 
    const stream = await navigator.mediaDevices.getUserMedia(constraints);
 
    const t = stream.getVideoTracks()[0];
 
    await t.applyConstraints({
 
      brightness: 0,
 
      contrast: 32,
 
      colorTemperature: 6600,
 
      colorTemperature: 4600,
 
      exposureMode: "manual",
 
      exposureTime: 250,
 
      whiteBalanceMode: "manual",
 
      focusMode: "manual",
 
      focusDistance: 235,
 
      // this could stop focus from moving around, but it also makes my cam
 
      // click on every page reload
 
      //   focusMode: "manual",
 
      //   focusDistance: 235,
 
    } as MediaTrackConstraints);
 

	
 
    this.vtrack = t;
 
    this.videoEl.srcObject = stream;
 
    this.videoEl.play();
 
    this.videoSettings = this.vtrack.getSettings();
 

	
 
    this.redrawLoop();
 
  }
 

	
 
  redrawLoop() {
 
    if (this.videoEl.videoWidth !== 0 && this.videoEl.videoHeight !== 0) {
 
      this.redraw();
 
    }
 
    // todo: video frames come slower than raf is waiting
 
    requestAnimationFrame(this.redrawLoop.bind(this));
 
  }
 

	
 
  public async set(k: string, v: any) {
 
    if (!this.vtrack) {
 
      throw new Error("vtrack");
 
    }
 
    await this.vtrack.applyConstraints({ [k]: v });
 
    this.videoSettings = this.vtrack.getSettings();
 
@@ -134,96 +136,97 @@ export class Light9Camera extends LitEle
 

	
 
        data[i + 3] = 0;
 
      }
 
    }
 
    this.saturatedPixelFraction = this.saturatedPixelCount / (data.length / 4);
 
    this.ctx.putImageData(imageData, 0, 0);
 
  }
 
}
 

	
 
@customElement("light9-camera-settings-table")
 
export class Light9CameraSettingsTable extends LitElement {
 
  static styles = [
 
    css`
 
      table {
 
        border-collapse: collapse;
 
      }
 
      td {
 
        border: 1px solid gray;
 
        padding: 1px 6px;
 
      }
 
    `,
 
  ];
 

	
 
  boring = [
 
    "autoGainControl",
 
    "aspectRatio",
 
    "backgroundBlur",
 
    "channelCount",
 
    "deviceId",
 
    "displaySurface",
 
    "echoCancellation",
 
    "eyeGazeCorrection",
 
    "faceFraming",
 
    "groupId",
 
    "latency",
 
    "noiseSuppression",
 
    "pointsOfInterest",
 
    "resizeMode",
 
    "sampleRate",
 
    "sampleSize",
 
    "suppressLocalAudioPlayback",
 
    "torch",
 
    "voiceIsolation",
 
  ];
 

	
 
  adjustable: Record<string, { min: string; max: string }> = {
 
    focusDistance: { min: "0", max: "1023" },
 
    brightness: { min: "0", max: "64" },
 
    colorTemperature: { min: "2800", max: "6500" },
 
    exposureTime: { min: "0", max: "800" },
 
  };
 

	
 
  @property() cam!: Light9Camera;
 
  @property() videoSettings: MediaTrackSettings & any = {};
 
  supportedByBrowser: MediaTrackSupportedConstraints;
 
  constructor() {
 
    super();
 
    this.supportedByBrowser = navigator.mediaDevices.getSupportedConstraints();
 
  }
 
  render() {
 
    const rows: TemplateResult<1>[] = [];
 
    for (const key of Object.keys(this.supportedByBrowser)) {
 
      if (!this.boring.includes(key)) {
 
        this.renderRow(key, rows);
 
      }
 
    }
 
    return html`<table>
 
      ${rows}
 
    </table>`;
 
  }
 

	
 
  private renderRow(key: string, rows: any[]) {
 
  private renderRow(key: string, out: TemplateResult<1>[]) {
 
    let valueDisplay = "";
 
    if (this.videoSettings[key] !== undefined) {
 
      valueDisplay = JSON.stringify(this.videoSettings[key]);
 
    }
 
    let adjuster = html``;
 
    let conf = this.adjustable[key];
 
    if (conf !== undefined) {
 
      adjuster = html`
 
        <input type="range" min="${conf.min}" max="${conf.max}" value="${this.videoSettings[key]}" data-param="${key}" @input=${this.setFromSlider} />
 
      `;
 
    }
 
    rows.push(
 
    out.push(
 
      html`<tr>
 
        <td>${key}</td>
 
        <td>${valueDisplay}</td>
 
        <td>${adjuster}</td>
 
      </tr>`
 
    );
 
  }
 

	
 
  async setFromSlider(ev: InputEvent) {
 
    const el = ev.target as HTMLInputElement;
 
    await this.cam.set(el.dataset.param as string, parseFloat(el.value));
 
  }
 
}
0 comments (0 inline, 0 general)