Can use fi in filmout_manager sequences. Can set in the UI and set the exposure times in the UI
This commit is contained in:
parent
fc8fc1b92b
commit
8547e905da
|
|
@ -51,7 +51,9 @@ interface State {
|
|||
screen? : StateDimensions,
|
||||
sequence? : SequenceState,
|
||||
statistics? : SequenceStatistics,
|
||||
exposure? : number
|
||||
mode? : any,
|
||||
exposure? : number,
|
||||
exposures? : number[]
|
||||
}
|
||||
|
||||
interface Message {
|
||||
|
|
@ -62,6 +64,7 @@ interface Message {
|
|||
width? : number;
|
||||
height? : number;
|
||||
scale? : number;
|
||||
mode? : string;
|
||||
}
|
||||
|
||||
interface Channels {
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ class Client {
|
|||
this.resetForm('manualCtrlForm');
|
||||
this.resetForm('exposureCtrlForm');
|
||||
this.resetForm('statisticsForm');
|
||||
this.resetForm('displayAdjustForm');
|
||||
|
||||
this.disableClass('sequenceCtrl');
|
||||
this.disableClass('manualCtrl');
|
||||
|
|
@ -246,6 +247,8 @@ class Client {
|
|||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.setExposure(state);
|
||||
this.setExposures(state);
|
||||
this.setImageMode(state);
|
||||
this.setDisplay(state);
|
||||
this.set('sequence', state.sequence.hash);
|
||||
this.removeClass('sequence', 'edited');
|
||||
|
|
@ -256,6 +259,8 @@ class Client {
|
|||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.setExposure(state);
|
||||
this.setExposures(state);
|
||||
this.setImageMode(state);
|
||||
this.setStatistics(state.statistics);
|
||||
this.display.updateImage();
|
||||
}
|
||||
|
|
@ -303,6 +308,41 @@ class Client {
|
|||
}
|
||||
}
|
||||
|
||||
private setExposures (state : State) {
|
||||
if (typeof state.exposures !== 'undefined') {
|
||||
const r : HTMLInputElement = document.getElementById('exposureR') as HTMLInputElement;
|
||||
const g : HTMLInputElement = document.getElementById('exposureG') as HTMLInputElement;
|
||||
const b : HTMLInputElement = document.getElementById('exposureB') as HTMLInputElement;
|
||||
|
||||
this.enableClass('exposureCtrl');
|
||||
|
||||
this.set('exposureR', `${state.exposures[0]}`);
|
||||
this.set('exposureG', `${state.exposures[1]}`);
|
||||
this.set('exposureB', `${state.exposures[2]}`);
|
||||
|
||||
this.removeClass('exposureR', 'edited');
|
||||
this.removeClass('exposureG', 'edited');
|
||||
this.removeClass('exposureB', 'edited');
|
||||
}
|
||||
}
|
||||
|
||||
private setImageMode (state : State) {
|
||||
if (typeof state.mode !== 'undefined') {
|
||||
(document.getElementById(`imageMode-${state.mode}`) as HTMLInputElement).checked = true;
|
||||
const exposureCtrl : HTMLElement = document.getElementById('exposureCtrl');
|
||||
if (state.mode === 'rgb' || state.mode === 'irgb') {
|
||||
if (exposureCtrl.classList.contains('single')) {
|
||||
exposureCtrl.classList.replace('single', 'multiple');
|
||||
}
|
||||
} else {
|
||||
if (exposureCtrl.classList.contains('multiple')) {
|
||||
exposureCtrl.classList.replace('multiple', 'single');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private setDisplay (state : State) {
|
||||
if (typeof state.display !== 'undefined') {
|
||||
this.set('displayWidth', state.display.width.toString());
|
||||
|
|
@ -497,8 +537,21 @@ class Client {
|
|||
}
|
||||
|
||||
public sendExposure () {
|
||||
const exposure : number = parseInt(this.get('exposure'));
|
||||
this.client.send(JSON.stringify({ cmd : 'exposure', state : { exposure }}));
|
||||
let exposure : number;
|
||||
let r : number;
|
||||
let g : number;
|
||||
let b : number;
|
||||
|
||||
if (document.getElementById('exposureCtrl').classList.contains('single')) {
|
||||
exposure = parseInt(this.get('exposure'));
|
||||
this.client.send(JSON.stringify({ cmd : 'exposure', state : { exposure }}));
|
||||
} else {
|
||||
r = parseInt(this.get('exposureR'));
|
||||
g = parseInt(this.get('exposureG'));
|
||||
b = parseInt(this.get('exposureB'));
|
||||
|
||||
this.client.send(JSON.stringify({ cmd : 'exposures', state : { exposures : [r, g, b] }}));
|
||||
}
|
||||
}
|
||||
|
||||
private receiveUpdate (msg : Message) {
|
||||
|
|
@ -567,10 +620,22 @@ class Client {
|
|||
this.client.send(JSON.stringify({ cmd : 'blank' }));
|
||||
}
|
||||
|
||||
public sendImageMode () {
|
||||
const radioName : string = `imageMode`;
|
||||
const checked : HTMLInputElement = document.querySelector(`input[name="${radioName}"]:checked`);
|
||||
const mode : string = checked.value;
|
||||
console.log(`sent mode: ${mode}`);
|
||||
this.send({ cmd : 'mode', mode });
|
||||
}
|
||||
|
||||
/**
|
||||
* HELPERS
|
||||
**/
|
||||
|
||||
private send (msg : any) {
|
||||
this.client.send(JSON.stringify(msg));
|
||||
}
|
||||
|
||||
public fullscreen () {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen();
|
||||
|
|
|
|||
|
|
@ -249,6 +249,9 @@ async function cmd(msg) {
|
|||
case 'exposure':
|
||||
exposureSet(msg.state.exposure);
|
||||
break;
|
||||
case 'exposures':
|
||||
exposuresSet(msg.state.exposures);
|
||||
break;
|
||||
case 'focus':
|
||||
await focus();
|
||||
break;
|
||||
|
|
@ -267,6 +270,9 @@ async function cmd(msg) {
|
|||
case 'scale':
|
||||
scale(msg);
|
||||
break;
|
||||
case 'mode':
|
||||
modeSet(msg);
|
||||
break;
|
||||
default:
|
||||
log.warn(`No matching command: ${msg.cmd}`);
|
||||
}
|
||||
|
|
@ -297,6 +303,15 @@ function exposureSet(exposure) {
|
|||
}
|
||||
sequence.setExposure(exposure);
|
||||
}
|
||||
function exposuresSet(exposures) {
|
||||
const r = exposures[0] > 0 ? exposures[0] : 1;
|
||||
const g = exposures[1] > 0 ? exposures[1] : 1;
|
||||
const b = exposures[2] > 0 ? exposures[2] : 1;
|
||||
sequence.setExposures(r, g, b);
|
||||
}
|
||||
function modeSet(msg) {
|
||||
sequence.setMode(msg.mode);
|
||||
}
|
||||
async function select(id) {
|
||||
const sequencesArr = await files_1.Files.enumerateSequences();
|
||||
const seq = sequencesArr.find(el => el.hash === id);
|
||||
|
|
@ -367,7 +382,7 @@ async function focus() {
|
|||
};
|
||||
}
|
||||
focusImage = await testimage_1.TestImage.Focus(pos.w, pos.h);
|
||||
fd.setMode(fd_1.Mode.RGB);
|
||||
fd.setMode(fd_1.Mode.RGB); // this never changes
|
||||
await fd.load(focusImage, pos.x, pos.y, pos.w, pos.h);
|
||||
await fd.display(focusImage);
|
||||
send({ cmd: 'focus' });
|
||||
|
|
@ -512,7 +527,7 @@ async function main() {
|
|||
log.info(`filmout_manager WebSocket server running on port ${wsPort}`);
|
||||
//ffmpeg.listFormats();
|
||||
//log.info(await TestImage.Focus(640, 480));
|
||||
sequence = new sequence_1.Sequence(camera, fd, display, ffprobe, send);
|
||||
sequence = new sequence_1.Sequence(camera, fd, display, ffprobe, image, send);
|
||||
setInterval(keepAlive, 30000);
|
||||
}
|
||||
main();
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -3,6 +3,7 @@ import type { FD } from '../fd';
|
|||
import type { Camera } from '../camera';
|
||||
import type { Display } from '../display';
|
||||
import type { FFPROBE } from '../ffprobe';
|
||||
import type { Image } from '../image';
|
||||
declare enum SequenceStatus {
|
||||
IDLE = 0,
|
||||
RUNNING = 1,
|
||||
|
|
@ -16,6 +17,7 @@ export declare class Sequence {
|
|||
private camera;
|
||||
private display;
|
||||
private ffprobe;
|
||||
private image;
|
||||
private fd;
|
||||
private send;
|
||||
private stats;
|
||||
|
|
@ -24,8 +26,10 @@ export declare class Sequence {
|
|||
private progress;
|
||||
private frame;
|
||||
private frames;
|
||||
private mode;
|
||||
private exposure;
|
||||
constructor(camera: Camera, fd: FD, display: Display, ffprobe: FFPROBE, send: Function);
|
||||
private exposures;
|
||||
constructor(camera: Camera, fd: FD, display: Display, ffprobe: FFPROBE, image: Image, send: Function);
|
||||
start(): void;
|
||||
stop(): void;
|
||||
isRunning(): boolean;
|
||||
|
|
@ -41,6 +45,8 @@ export declare class Sequence {
|
|||
getUpdateState(): State;
|
||||
getSequenceState(): SequenceState;
|
||||
setExposure(ms: number): void;
|
||||
setExposures(r: number, g: number, b: number): void;
|
||||
setMode(mode: string): void;
|
||||
getStatus(): SequenceStatus;
|
||||
getCurrent(): ImageObject;
|
||||
frameAdvance(frames?: number): void;
|
||||
|
|
@ -50,5 +56,6 @@ export declare class Sequence {
|
|||
updateSize(width: number, height: number): void;
|
||||
updateScale(scale: number): void;
|
||||
private frameRecord;
|
||||
private frameRecordRGB;
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -3,12 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||
exports.Sequence = void 0;
|
||||
const files_1 = require("../files");
|
||||
const log_1 = require("../log");
|
||||
const promises_1 = require("fs/promises");
|
||||
var SequenceStatus;
|
||||
(function (SequenceStatus) {
|
||||
SequenceStatus[SequenceStatus["IDLE"] = 0] = "IDLE";
|
||||
SequenceStatus[SequenceStatus["RUNNING"] = 1] = "RUNNING";
|
||||
SequenceStatus[SequenceStatus["PAUSED"] = 2] = "PAUSED";
|
||||
})(SequenceStatus || (SequenceStatus = {}));
|
||||
var ImageMode;
|
||||
(function (ImageMode) {
|
||||
ImageMode["Default"] = "8bit";
|
||||
ImageMode["Inverted"] = "inverted";
|
||||
ImageMode["RGB"] = "rgb";
|
||||
ImageMode["IRGB"] = "irgb";
|
||||
})(ImageMode || (ImageMode = {}));
|
||||
class Statistics {
|
||||
constructor(exposure, frames) {
|
||||
this.exposure = exposure;
|
||||
|
|
@ -76,7 +84,7 @@ class Statistics {
|
|||
}
|
||||
}
|
||||
class Sequence {
|
||||
constructor(camera, fd, display, ffprobe, send) {
|
||||
constructor(camera, fd, display, ffprobe, image, send) {
|
||||
this.current = null;
|
||||
this.info = null;
|
||||
this.images = [];
|
||||
|
|
@ -86,12 +94,15 @@ class Sequence {
|
|||
this.progress = 0;
|
||||
this.frame = 0;
|
||||
this.frames = 0;
|
||||
this.mode = ImageMode.Default;
|
||||
this.exposure = 1000;
|
||||
this.exposures = [1000, 1000, 1000];
|
||||
this.log = (0, log_1.createLog)('seq');
|
||||
this.camera = camera;
|
||||
this.fd = fd;
|
||||
this.display = display;
|
||||
this.ffprobe = ffprobe;
|
||||
this.image = image;
|
||||
this.send = send;
|
||||
}
|
||||
start() {
|
||||
|
|
@ -120,11 +131,22 @@ class Sequence {
|
|||
if (!this.running) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
await this.frameRecord();
|
||||
console.dir(this.mode);
|
||||
if (this.mode === ImageMode.Default || this.mode === ImageMode.Inverted) {
|
||||
try {
|
||||
await this.frameRecord(this.mode === ImageMode.Inverted);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
else if (this.mode === ImageMode.RGB || this.mode === ImageMode.IRGB) {
|
||||
try {
|
||||
await this.frameRecordRGB(this.mode === ImageMode.IRGB);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
}
|
||||
}
|
||||
this.frameAdvance();
|
||||
}
|
||||
|
|
@ -218,7 +240,9 @@ class Sequence {
|
|||
frames: this.frames,
|
||||
status: this.getStatus()
|
||||
},
|
||||
mode: this.mode,
|
||||
exposure: this.exposure,
|
||||
exposures: this.exposures,
|
||||
statistics: this.stats !== null ? this.stats.calculate(this.frame) : null
|
||||
};
|
||||
}
|
||||
|
|
@ -250,6 +274,18 @@ class Sequence {
|
|||
this.log.info(`Updated exposure: ${ms}ms`);
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
setExposures(r, g, b) {
|
||||
this.exposures = [r, g, b];
|
||||
this.log.info(`Updated exposures: ${r}ms, ${g}ms, ${b}ms`);
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
setMode(mode) {
|
||||
if (this.mode !== mode) {
|
||||
this.log.info(`Update mode: ${mode}`);
|
||||
}
|
||||
this.mode = mode;
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
getStatus() {
|
||||
if (this.running && this.paused) {
|
||||
return SequenceStatus.PAUSED;
|
||||
|
|
@ -381,7 +417,7 @@ class Sequence {
|
|||
this.display.setOffsetY(newOffsetY);
|
||||
this.updateClientsOnDisplay();
|
||||
}
|
||||
async frameRecord() {
|
||||
async frameRecord(invert = false) {
|
||||
const start = Date.now();
|
||||
let load;
|
||||
let open;
|
||||
|
|
@ -390,11 +426,21 @@ class Sequence {
|
|||
let close;
|
||||
let total;
|
||||
let result;
|
||||
let inverted;
|
||||
const img = this.images[this.frame];
|
||||
const dimensions = this.display.getOutgoingPosition();
|
||||
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
|
||||
if (invert) {
|
||||
try {
|
||||
inverted = await this.image.invert(img.path);
|
||||
this.log.info(`Inverted ${img.path}`);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error inverting image`, err);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
await this.fd.load(invert ? inverted : img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error loading image ${img.path}`, err);
|
||||
|
|
@ -421,6 +467,103 @@ class Sequence {
|
|||
catch (err) {
|
||||
this.log.error(`Error closing camera`, err);
|
||||
}
|
||||
if (invert) {
|
||||
await (0, promises_1.unlink)(inverted);
|
||||
}
|
||||
close = Date.now() - start - load - open - exposureElapsed;
|
||||
total = Date.now() - start;
|
||||
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
|
||||
}
|
||||
async frameRecordRGB(invert = false) {
|
||||
const start = Date.now();
|
||||
let load;
|
||||
let open;
|
||||
let exposureElapsed = 0;
|
||||
let exposureReported = 0;
|
||||
let close;
|
||||
let total;
|
||||
let result;
|
||||
let channels;
|
||||
const img = this.images[this.frame];
|
||||
const dimensions = this.display.getOutgoingPosition();
|
||||
this.log.info(`RGB: ${img.name} ${this.frame} / ${this.images.length}`);
|
||||
try {
|
||||
channels = await this.image.rgb(img.path, invert);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error getting temp files for channels`, err);
|
||||
}
|
||||
try {
|
||||
await this.camera.open();
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error opening camera`, err);
|
||||
}
|
||||
open = Date.now() - start - load;
|
||||
try {
|
||||
await this.fd.load(channels.red, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error loading red channel for display`, err);
|
||||
}
|
||||
load = Date.now() - start;
|
||||
try {
|
||||
await this.camera.open();
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error opening camera`, err);
|
||||
}
|
||||
this.log.info(`Displaying RED channel for ${img.name}`);
|
||||
try {
|
||||
result = await this.fd.display(channels.red, [this.exposures[0]]);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error displaying red channel`, err);
|
||||
}
|
||||
exposureReported += result.reported;
|
||||
try {
|
||||
await this.fd.load(channels.green, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error loading green channel`, err);
|
||||
}
|
||||
this.log.info(`Displaying GREEN channel for ${img.name}`);
|
||||
try {
|
||||
result = await this.fd.display(channels.green, [this.exposures[1]]);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error displaying green channel`, err);
|
||||
}
|
||||
exposureReported += result.reported;
|
||||
try {
|
||||
await this.fd.load(channels.blue, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error loading blue channel`, err);
|
||||
}
|
||||
this.log.info(`Displaying BLUE channel for ${img.name}`);
|
||||
try {
|
||||
result = await this.fd.display(channels.blue, [this.exposures[2]]);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error displaying blue channel`, err);
|
||||
}
|
||||
exposureReported += result.reported;
|
||||
try {
|
||||
await (0, promises_1.unlink)(channels.red);
|
||||
await (0, promises_1.unlink)(channels.green);
|
||||
await (0, promises_1.unlink)(channels.blue);
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error cleaning up temp channel files`, err);
|
||||
}
|
||||
exposureElapsed = Date.now() - start - load - open;
|
||||
try {
|
||||
await this.camera.close();
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error closing camera`, err);
|
||||
}
|
||||
close = Date.now() - start - load - open - exposureElapsed;
|
||||
total = Date.now() - start;
|
||||
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -96,6 +96,19 @@ html, body{
|
|||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#exposureCtrl{
|
||||
&.single{
|
||||
.multiple{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.multiple{
|
||||
.single{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.half{
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 50%;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ interface State {
|
|||
screen? : StateDimensions,
|
||||
sequence? : SequenceState,
|
||||
statistics? : SequenceStatistics,
|
||||
exposure? : number
|
||||
mode? : any,
|
||||
exposure? : number,
|
||||
exposures? : number[]
|
||||
}
|
||||
|
||||
interface Message {
|
||||
|
|
@ -62,6 +64,7 @@ interface Message {
|
|||
width? : number;
|
||||
height? : number;
|
||||
scale? : number;
|
||||
mode? : string;
|
||||
}
|
||||
|
||||
interface Channels {
|
||||
|
|
|
|||
21
src/index.ts
21
src/index.ts
|
|
@ -236,6 +236,9 @@ async function cmd (msg : Message) {
|
|||
case 'exposure' :
|
||||
exposureSet(msg.state.exposure);
|
||||
break;
|
||||
case 'exposures' :
|
||||
exposuresSet(msg.state.exposures);
|
||||
break;
|
||||
case 'focus' :
|
||||
await focus();
|
||||
break;
|
||||
|
|
@ -254,6 +257,9 @@ async function cmd (msg : Message) {
|
|||
case 'scale' :
|
||||
scale(msg);
|
||||
break;
|
||||
case 'mode' :
|
||||
modeSet(msg);
|
||||
break;
|
||||
default :
|
||||
log.warn(`No matching command: ${msg.cmd}`);
|
||||
}
|
||||
|
|
@ -291,6 +297,17 @@ function exposureSet (exposure : number) {
|
|||
sequence.setExposure(exposure);
|
||||
}
|
||||
|
||||
function exposuresSet (exposures : number[]) {
|
||||
const r : number = exposures[0] > 0 ? exposures[0] : 1;
|
||||
const g : number = exposures[1] > 0 ? exposures[1] : 1;
|
||||
const b : number = exposures[2] > 0 ? exposures[2] : 1;
|
||||
sequence.setExposures(r, g, b);
|
||||
}
|
||||
|
||||
function modeSet (msg : Message) {
|
||||
sequence.setMode(msg.mode);
|
||||
}
|
||||
|
||||
async function select (id : string) : Promise<boolean> {
|
||||
const sequencesArr : SequenceObject[] = await Files.enumerateSequences();
|
||||
const seq : SequenceObject = sequencesArr.find(el => el.hash === id);
|
||||
|
|
@ -374,7 +391,7 @@ async function focus () {
|
|||
|
||||
focusImage = await TestImage.Focus(pos.w, pos.h);
|
||||
|
||||
fd.setMode(Mode.RGB);
|
||||
fd.setMode(Mode.RGB); // this never changes
|
||||
await fd.load (focusImage, pos.x, pos.y, pos.w, pos.h);
|
||||
await fd.display(focusImage);
|
||||
send({ cmd : 'focus' });
|
||||
|
|
@ -525,7 +542,7 @@ async function main () {
|
|||
|
||||
//ffmpeg.listFormats();
|
||||
//log.info(await TestImage.Focus(640, 480));
|
||||
sequence = new Sequence(camera, fd, display, ffprobe, send);
|
||||
sequence = new Sequence(camera, fd, display, ffprobe, image, send);
|
||||
setInterval(keepAlive, 30000);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { Files } from '../files';
|
||||
import { createLog } from '../log';
|
||||
import { delay } from '../delay';
|
||||
import { unlink } from 'fs/promises';
|
||||
import type { Logger } from 'winston';
|
||||
import type { SequenceObject, ImageObject } from '../files';
|
||||
import type { FD, fdOutgoingPosition, fdResult } from '../fd';
|
||||
import type { Camera } from '../camera';
|
||||
import type { Display, Dimensions, Offset } from '../display';
|
||||
import type { FFPROBE, VideoInfo } from '../ffprobe';
|
||||
import type { Image } from '../image';
|
||||
|
||||
enum SequenceStatus {
|
||||
IDLE,
|
||||
|
|
@ -14,6 +16,13 @@ enum SequenceStatus {
|
|||
PAUSED
|
||||
}
|
||||
|
||||
enum ImageMode {
|
||||
Default = '8bit',
|
||||
Inverted = 'inverted',
|
||||
RGB = 'rgb',
|
||||
IRGB = 'irgb'
|
||||
}
|
||||
|
||||
class Statistics {
|
||||
private frameLoad : number[];
|
||||
private frameOpen : number[];
|
||||
|
|
@ -113,6 +122,7 @@ export class Sequence {
|
|||
private camera : Camera;
|
||||
private display : Display;
|
||||
private ffprobe : FFPROBE;
|
||||
private image : Image;
|
||||
private fd : FD;
|
||||
private send : Function;
|
||||
private stats : Statistics = null;
|
||||
|
|
@ -123,14 +133,17 @@ export class Sequence {
|
|||
private frame : number = 0;
|
||||
private frames : number = 0;
|
||||
|
||||
private mode : ImageMode = ImageMode.Default;
|
||||
private exposure : number = 1000;
|
||||
private exposures : number[] = [ 1000, 1000, 1000 ];
|
||||
|
||||
constructor (camera : Camera, fd : FD, display: Display, ffprobe : FFPROBE, send : Function) {
|
||||
constructor (camera : Camera, fd : FD, display: Display, ffprobe : FFPROBE, image : Image, send : Function) {
|
||||
this.log = createLog('seq');
|
||||
this.camera = camera;
|
||||
this.fd = fd;
|
||||
this.display = display;
|
||||
this.ffprobe = ffprobe;
|
||||
this.image = image;
|
||||
this.send = send;
|
||||
}
|
||||
|
||||
|
|
@ -164,11 +177,19 @@ export class Sequence {
|
|||
if (!this.running) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.frameRecord();
|
||||
} catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
console.dir(this.mode)
|
||||
if (this.mode === ImageMode.Default || this.mode === ImageMode.Inverted) {
|
||||
try {
|
||||
await this.frameRecord(this.mode === ImageMode.Inverted);
|
||||
} catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
}
|
||||
} else if (this.mode === ImageMode.RGB || this.mode === ImageMode.IRGB) {
|
||||
try {
|
||||
await this.frameRecordRGB(this.mode === ImageMode.IRGB);
|
||||
} catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
}
|
||||
}
|
||||
|
||||
this.frameAdvance();
|
||||
|
|
@ -274,7 +295,9 @@ export class Sequence {
|
|||
frames : this.frames,
|
||||
status : this.getStatus() as SequenceStatus
|
||||
},
|
||||
mode : this.mode,
|
||||
exposure : this.exposure,
|
||||
exposures : this.exposures,
|
||||
statistics : this.stats !== null ? this.stats.calculate(this.frame) : null
|
||||
}
|
||||
}
|
||||
|
|
@ -310,6 +333,20 @@ export class Sequence {
|
|||
this.updateClientsOnState();
|
||||
}
|
||||
|
||||
public setExposures(r : number, g: number, b: number) {
|
||||
this.exposures = [r, g, b];
|
||||
this.log.info(`Updated exposures: ${r}ms, ${g}ms, ${b}ms`);
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
|
||||
public setMode (mode : string) {
|
||||
if (this.mode !== mode) {
|
||||
this.log.info(`Update mode: ${mode}`);
|
||||
}
|
||||
this.mode = mode as ImageMode;
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
|
||||
public getStatus () : SequenceStatus {
|
||||
if (this.running && this.paused) {
|
||||
return SequenceStatus.PAUSED;
|
||||
|
|
@ -447,7 +484,7 @@ export class Sequence {
|
|||
this.updateClientsOnDisplay();
|
||||
}
|
||||
|
||||
private async frameRecord () {
|
||||
private async frameRecord (invert : boolean = false) {
|
||||
const start : number = Date.now();
|
||||
let load : number;
|
||||
let open : number;
|
||||
|
|
@ -456,13 +493,23 @@ export class Sequence {
|
|||
let close : number;
|
||||
let total : number;
|
||||
let result : fdResult;
|
||||
let inverted : string;
|
||||
const img : ImageObject = this.images[this.frame];
|
||||
const dimensions : fdOutgoingPosition = this.display.getOutgoingPosition();
|
||||
|
||||
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
|
||||
|
||||
if (invert) {
|
||||
try {
|
||||
inverted = await this.image.invert(img.path);
|
||||
this.log.info(`Inverted ${img.path}`);
|
||||
} catch (err) {
|
||||
this.log.error(`Error inverting image`, err);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
await this.fd.load(invert ? inverted : img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
} catch (err) {
|
||||
this.log.error(`Error loading image ${img.path}`, err);
|
||||
}
|
||||
|
|
@ -492,6 +539,114 @@ export class Sequence {
|
|||
this.log.error(`Error closing camera`, err);
|
||||
}
|
||||
|
||||
if (invert) {
|
||||
await unlink(inverted);
|
||||
}
|
||||
|
||||
close = Date.now() - start - load - open - exposureElapsed;
|
||||
total = Date.now() - start;
|
||||
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
|
||||
}
|
||||
|
||||
private async frameRecordRGB (invert : boolean = false) {
|
||||
const start : number = Date.now();
|
||||
let load : number;
|
||||
let open : number;
|
||||
let exposureElapsed : number = 0;
|
||||
let exposureReported : number = 0;
|
||||
let close : number;
|
||||
let total : number;
|
||||
let result : fdResult;
|
||||
let channels : Channels
|
||||
const img : ImageObject = this.images[this.frame];
|
||||
|
||||
const dimensions : fdOutgoingPosition = this.display.getOutgoingPosition();
|
||||
|
||||
this.log.info(`RGB: ${img.name} ${this.frame} / ${this.images.length}`);
|
||||
|
||||
try {
|
||||
channels = await this.image.rgb(img.path, invert);
|
||||
} catch (err) {
|
||||
this.log.error(`Error getting temp files for channels`, err);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.camera.open();
|
||||
} catch (err) {
|
||||
this.log.error(`Error opening camera`, err);
|
||||
}
|
||||
|
||||
open = Date.now() - start - load;
|
||||
|
||||
try {
|
||||
await this.fd.load(channels.red, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
} catch (err) {
|
||||
this.log.error(`Error loading red channel for display`, err);
|
||||
}
|
||||
|
||||
load = Date.now() - start;
|
||||
|
||||
try {
|
||||
await this.camera.open();
|
||||
} catch (err) {
|
||||
this.log.error(`Error opening camera`, err);
|
||||
}
|
||||
|
||||
this.log.info(`Displaying RED channel for ${img.name}`);
|
||||
try {
|
||||
result = await this.fd.display(channels.red, [ this.exposures[0] ] );
|
||||
} catch (err) {
|
||||
this.log.error(`Error displaying red channel`, err);
|
||||
}
|
||||
|
||||
exposureReported += result.reported;
|
||||
|
||||
try {
|
||||
await this.fd.load(channels.green, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
} catch (err) {
|
||||
this.log.error(`Error loading green channel`, err);
|
||||
}
|
||||
|
||||
this.log.info(`Displaying GREEN channel for ${img.name}`);
|
||||
try {
|
||||
result = await this.fd.display(channels.green, [ this.exposures[1] ] );
|
||||
} catch (err) {
|
||||
this.log.error(`Error displaying green channel`, err);
|
||||
}
|
||||
|
||||
exposureReported += result.reported;
|
||||
|
||||
try {
|
||||
await this.fd.load(channels.blue, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
} catch (err) {
|
||||
this.log.error(`Error loading blue channel`, err);
|
||||
}
|
||||
|
||||
this.log.info(`Displaying BLUE channel for ${img.name}`);
|
||||
try {
|
||||
result = await this.fd.display(channels.blue, [ this.exposures[2] ] );
|
||||
} catch (err) {
|
||||
this.log.error(`Error displaying blue channel`, err);
|
||||
}
|
||||
|
||||
exposureReported += result.reported;
|
||||
|
||||
try {
|
||||
await unlink(channels.red);
|
||||
await unlink(channels.green);
|
||||
await unlink(channels.blue);
|
||||
} catch (err) {
|
||||
this.log.error(`Error cleaning up temp channel files`, err);
|
||||
}
|
||||
|
||||
exposureElapsed = Date.now() - start - load - open;
|
||||
|
||||
try {
|
||||
await this.camera.close();
|
||||
} catch (err) {
|
||||
this.log.error(`Error closing camera`, err);
|
||||
}
|
||||
|
||||
close = Date.now() - start - load - open - exposureElapsed;
|
||||
total = Date.now() - start;
|
||||
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,12 @@ body {
|
|||
display: inline-block;
|
||||
padding-right: 3px;
|
||||
}
|
||||
#exposureCtrl.single .multiple {
|
||||
display: none;
|
||||
}
|
||||
#exposureCtrl.multiple .single {
|
||||
display: none;
|
||||
}
|
||||
.half {
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 50%;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ declare class Client {
|
|||
private setProgress;
|
||||
private setFrame;
|
||||
private setExposure;
|
||||
private setExposures;
|
||||
private setImageMode;
|
||||
private setDisplay;
|
||||
edited(el: HTMLElement): void;
|
||||
private setStatistics;
|
||||
|
|
@ -100,6 +102,8 @@ declare class Client {
|
|||
sendSize(width: number, height: number): void;
|
||||
sendScale(scale: number): void;
|
||||
sendBlank(): void;
|
||||
sendImageMode(): void;
|
||||
private send;
|
||||
fullscreen(): void;
|
||||
exitFullscreen(): void;
|
||||
private active;
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ class Client {
|
|||
this.resetForm('manualCtrlForm');
|
||||
this.resetForm('exposureCtrlForm');
|
||||
this.resetForm('statisticsForm');
|
||||
this.resetForm('displayAdjustForm');
|
||||
this.disableClass('sequenceCtrl');
|
||||
this.disableClass('manualCtrl');
|
||||
this.disableClass('exposureCtrl');
|
||||
|
|
@ -195,6 +196,8 @@ class Client {
|
|||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.setExposure(state);
|
||||
this.setExposures(state);
|
||||
this.setImageMode(state);
|
||||
this.setDisplay(state);
|
||||
this.set('sequence', state.sequence.hash);
|
||||
this.removeClass('sequence', 'edited');
|
||||
|
|
@ -204,6 +207,8 @@ class Client {
|
|||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.setExposure(state);
|
||||
this.setExposures(state);
|
||||
this.setImageMode(state);
|
||||
this.setStatistics(state.statistics);
|
||||
this.display.updateImage();
|
||||
}
|
||||
|
|
@ -246,6 +251,36 @@ class Client {
|
|||
this.removeClass('exposure', 'edited');
|
||||
}
|
||||
}
|
||||
setExposures(state) {
|
||||
if (typeof state.exposures !== 'undefined') {
|
||||
const r = document.getElementById('exposureR');
|
||||
const g = document.getElementById('exposureG');
|
||||
const b = document.getElementById('exposureB');
|
||||
this.enableClass('exposureCtrl');
|
||||
this.set('exposureR', `${state.exposures[0]}`);
|
||||
this.set('exposureG', `${state.exposures[1]}`);
|
||||
this.set('exposureB', `${state.exposures[2]}`);
|
||||
this.removeClass('exposureR', 'edited');
|
||||
this.removeClass('exposureG', 'edited');
|
||||
this.removeClass('exposureB', 'edited');
|
||||
}
|
||||
}
|
||||
setImageMode(state) {
|
||||
if (typeof state.mode !== 'undefined') {
|
||||
document.getElementById(`imageMode-${state.mode}`).checked = true;
|
||||
const exposureCtrl = document.getElementById('exposureCtrl');
|
||||
if (state.mode === 'rgb' || state.mode === 'irgb') {
|
||||
if (exposureCtrl.classList.contains('single')) {
|
||||
exposureCtrl.classList.replace('single', 'multiple');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (exposureCtrl.classList.contains('multiple')) {
|
||||
exposureCtrl.classList.replace('multiple', 'single');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setDisplay(state) {
|
||||
if (typeof state.display !== 'undefined') {
|
||||
this.set('displayWidth', state.display.width.toString());
|
||||
|
|
@ -409,8 +444,20 @@ class Client {
|
|||
this.client.send(JSON.stringify({ cmd: 'stop' }));
|
||||
}
|
||||
sendExposure() {
|
||||
const exposure = parseInt(this.get('exposure'));
|
||||
this.client.send(JSON.stringify({ cmd: 'exposure', state: { exposure } }));
|
||||
let exposure;
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
if (document.getElementById('exposureCtrl').classList.contains('single')) {
|
||||
exposure = parseInt(this.get('exposure'));
|
||||
this.client.send(JSON.stringify({ cmd: 'exposure', state: { exposure } }));
|
||||
}
|
||||
else {
|
||||
r = parseInt(this.get('exposureR'));
|
||||
g = parseInt(this.get('exposureG'));
|
||||
b = parseInt(this.get('exposureB'));
|
||||
this.client.send(JSON.stringify({ cmd: 'exposures', state: { exposures: [r, g, b] } }));
|
||||
}
|
||||
}
|
||||
receiveUpdate(msg) {
|
||||
this.setUpdate(msg.state);
|
||||
|
|
@ -463,6 +510,16 @@ class Client {
|
|||
sendBlank() {
|
||||
this.client.send(JSON.stringify({ cmd: 'blank' }));
|
||||
}
|
||||
sendImageMode() {
|
||||
const radioName = `imageMode`;
|
||||
const checked = document.querySelector(`input[name="${radioName}"]:checked`);
|
||||
const mode = checked.value;
|
||||
console.log(`sent mode: ${mode}`);
|
||||
this.send({ cmd: 'mode', mode });
|
||||
}
|
||||
send(msg) {
|
||||
this.client.send(JSON.stringify(msg));
|
||||
}
|
||||
fullscreen() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen();
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -88,11 +88,18 @@
|
|||
<button id="select" onclick="client.sendSelect();">Select</button>
|
||||
</form>
|
||||
</fieldset>
|
||||
<fieldset id="exposureCtrl" class="inline half">
|
||||
<fieldset id="exposureCtrl" class="inline half single">
|
||||
<legend>Exposure</legend>
|
||||
<form id="exposureCtrlForm" onsubmit="return false;">
|
||||
<input id="exposure" class="large exposureCtrl" type="number" value="0" oninput="client.edited(this);" disabled />
|
||||
<span>ms </span>
|
||||
<input id="exposure" class="large exposureCtrl single" type="number" value="0" oninput="client.edited(this);" disabled />
|
||||
<span class="single">ms </span>
|
||||
|
||||
<input id="exposureR" class="large exposureCtrl multiple" type="number" value="0" oninput="client.edited(this);" disabled />
|
||||
<span class="multiple">ms </span>
|
||||
<input id="exposureG" class="large exposureCtrl multiple" type="number" value="0" oninput="client.edited(this);" disabled />
|
||||
<span class="multiple">ms </span>
|
||||
<input id="exposureB" class="large exposureCtrl multiple" type="number" value="0" oninput="client.edited(this);" disabled />
|
||||
<span class="multiple">ms </span>
|
||||
<button id="exposureUpdate" class="exposureCtrl" onclick="client.sendExposure();">Update</button>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
|
@ -193,21 +200,21 @@
|
|||
<button class="small manualCtrl" id="scaleMinus" onclick="client.sendScale(-2);" disabled>S -</button>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<span style="margin-left: 15px;">
|
||||
<div class="check">
|
||||
<input type="radio" name="imageMode" id="imageMode-8bit" value="8bit" checked />
|
||||
<input type="radio" name="imageMode" id="imageMode-8bit" value="8bit" checked onclick="client.sendImageMode();" />
|
||||
<label for="imageMode-8bit">Positive 8bit</label>
|
||||
</div>
|
||||
<div class="check">
|
||||
<input type="radio" name="imageMode" id="imageMode-inverted" value="inverted" />
|
||||
<input type="radio" name="imageMode" id="imageMode-inverted" value="inverted" onclick="client.sendImageMode();" />
|
||||
<label for="imageMode-inverted">Negative 8bit</label>
|
||||
</div>
|
||||
<div class="check">
|
||||
<input type="radio" name="imageMode" id="imageMode-rgb" value="rgb" />
|
||||
<input type="radio" name="imageMode" id="imageMode-rgb" value="rgb" onclick="client.sendImageMode();" />
|
||||
<label for="imageMode-rgb">Positive RGB</label>
|
||||
</div>
|
||||
<div class="check">
|
||||
<input type="radio" name="imageMode" id="imageMode-irgb" value="irgb" />
|
||||
<input type="radio" name="imageMode" id="imageMode-irgb" value="irgb" onclick="client.sendImageMode();" />
|
||||
<label for="imageMode-irgb">Negative RGB</label>
|
||||
</div>
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Reference in New Issue