perf: prepare the lighting-corrected image once per photo
Threshold, click, scale and margin changes now reuse it, so each update skips the ~200 ms grayscale and lighting fit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8ea525a1e8
commit
077c3cc31d
3 files changed
+60
-15
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { detect, otsuThreshold } from './detect';
|
||||
import { detectPrepared, prepareImage, preparedThreshold, type PreparedImage } from './detect';
|
||||
import { prepareForExport } from './geometry';
|
||||
import { loadPhoto } from './load-image';
|
||||
import { shapeFromPolygons } from './margin';
|
||||
@@ -16,6 +16,8 @@ export class AppState {
|
||||
cv = $state.raw<CV | null>(null);
|
||||
cvError = $state<string | null>(null);
|
||||
image = $state.raw<RasterImage | null>(null);
|
||||
/** Lighting-corrected grayscale of `image`, computed once per photo; owns WASM memory. */
|
||||
prepared = $state.raw<PreparedImage | null>(null);
|
||||
loadError = $state<string | null>(null);
|
||||
|
||||
tool = $state<Tool>('none');
|
||||
@@ -38,12 +40,13 @@ export class AppState {
|
||||
marginPx = $derived(this.pxPerMm ? this.marginMm * this.pxPerMm : 0);
|
||||
|
||||
shapePx = $derived.by((): Shape | null => {
|
||||
const { cv, image } = this;
|
||||
const { cv, image, prepared } = this;
|
||||
if (!cv || !image) return null;
|
||||
if (this.mode === 'manual') {
|
||||
return shapeFromPolygons(cv, image.width, image.height, { outer: this.manualOuter, holes: this.manualHoles }, this.marginPx);
|
||||
}
|
||||
return detect(cv, image, {
|
||||
if (!prepared) return null;
|
||||
return detectPrepared(cv, prepared, {
|
||||
threshold: this.threshold, seed: this.seed, marginPx: this.marginPx,
|
||||
minHolePx: this.pxPerMm ? MIN_HOLE_MM * this.pxPerMm : 0,
|
||||
});
|
||||
@@ -53,7 +56,7 @@ export class AppState {
|
||||
|
||||
setCv(cv: CV) {
|
||||
this.cv = cv;
|
||||
if (this.image) this.threshold = otsuThreshold(cv, this.image);
|
||||
this.prepare();
|
||||
}
|
||||
|
||||
setImage(image: RasterImage) {
|
||||
@@ -62,7 +65,14 @@ export class AppState {
|
||||
this.scalePoints = [];
|
||||
this.seed = null;
|
||||
this.clearManual();
|
||||
this.threshold = this.cv ? otsuThreshold(this.cv, image) : DEFAULT_THRESHOLD;
|
||||
this.prepare();
|
||||
}
|
||||
|
||||
private prepare() {
|
||||
this.prepared?.gray.delete();
|
||||
const { cv, image } = this;
|
||||
this.prepared = cv && image ? prepareImage(cv, image) : null;
|
||||
this.threshold = cv && this.prepared ? preparedThreshold(cv, this.prepared) : DEFAULT_THRESHOLD;
|
||||
}
|
||||
|
||||
async loadFile(file: Blob) {
|
||||
@@ -75,7 +85,7 @@ export class AppState {
|
||||
}
|
||||
|
||||
autoThreshold() {
|
||||
if (this.cv && this.image) this.threshold = otsuThreshold(this.cv, this.image);
|
||||
if (this.cv && this.prepared) this.threshold = preparedThreshold(this.cv, this.prepared);
|
||||
}
|
||||
|
||||
handleImageClick(p: Point) {
|
||||
|
||||
Reference in new issue
Block a user