@scanmate/image-fix 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -201
- package/dist/index.esm.js +3 -2785
- package/dist/index.esm.js.map +1 -1
- package/dist/src/index.d.ts +17 -51
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +5 -7
- package/dist/src/lib/align.d.ts +0 -136
- package/dist/src/lib/align.d.ts.map +0 -1
- package/dist/src/lib/analysis/content.d.ts +0 -50
- package/dist/src/lib/analysis/content.d.ts.map +0 -1
- package/dist/src/lib/analysis/score.d.ts +0 -18
- package/dist/src/lib/analysis/score.d.ts.map +0 -1
- package/dist/src/lib/estimate/coarse.d.ts +0 -43
- package/dist/src/lib/estimate/coarse.d.ts.map +0 -1
- package/dist/src/lib/estimate/features.d.ts +0 -72
- package/dist/src/lib/estimate/features.d.ts.map +0 -1
- package/dist/src/lib/estimate/match.d.ts +0 -38
- package/dist/src/lib/estimate/match.d.ts.map +0 -1
- package/dist/src/lib/estimate/models.d.ts +0 -41
- package/dist/src/lib/estimate/models.d.ts.map +0 -1
- package/dist/src/lib/estimate/phaseCorrelation.d.ts +0 -34
- package/dist/src/lib/estimate/phaseCorrelation.d.ts.map +0 -1
- package/dist/src/lib/estimate/ransac.d.ts +0 -36
- package/dist/src/lib/estimate/ransac.d.ts.map +0 -1
- package/dist/src/lib/image/codec.d.ts +0 -30
- package/dist/src/lib/image/codec.d.ts.map +0 -1
- package/dist/src/lib/image/gray.d.ts +0 -67
- package/dist/src/lib/image/gray.d.ts.map +0 -1
- package/dist/src/lib/image/raster.d.ts +0 -19
- package/dist/src/lib/image/raster.d.ts.map +0 -1
- package/dist/src/lib/image/resize.d.ts +0 -32
- package/dist/src/lib/image/resize.d.ts.map +0 -1
- package/dist/src/lib/image/warp.d.ts +0 -28
- package/dist/src/lib/image/warp.d.ts.map +0 -1
- package/dist/src/lib/math/fft.d.ts +0 -15
- package/dist/src/lib/math/fft.d.ts.map +0 -1
- package/dist/src/lib/math/linalg.d.ts +0 -34
- package/dist/src/lib/math/linalg.d.ts.map +0 -1
- package/dist/src/lib/math/matrix.d.ts +0 -75
- package/dist/src/lib/math/matrix.d.ts.map +0 -1
- package/dist/src/lib/math/random.d.ts +0 -13
- package/dist/src/lib/math/random.d.ts.map +0 -1
- package/dist/src/lib/regions.d.ts +0 -75
- package/dist/src/lib/regions.d.ts.map +0 -1
- package/dist/src/lib/testing/synthetic.d.ts +0 -73
- package/dist/src/lib/testing/synthetic.d.ts.map +0 -1
- package/dist/src/lib/types.d.ts +0 -86
- package/dist/src/lib/types.d.ts.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ransac.d.ts","sourceRoot":"","sources":["../../../../src/lib/estimate/ransac.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9C;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAW,cAAc,CAAA;IAC9B,gFAAgF;IAChF,SAAS,EAAO,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,qFAAqF;IACrF,UAAU,CAAC,EAAK,MAAM,CAAA;IACtB,+DAA+D;IAC/D,UAAU,CAAC,EAAK,MAAM,CAAA;IACtB,IAAI,CAAC,EAAW,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAO,OAAO,CAAA;IACpB,oCAAoC;IACpC,OAAO,EAAM,MAAM,EAAE,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAG,MAAM,CAAA;IACnB,2DAA2D;IAC3D,KAAK,EAAQ,MAAM,CAAA;CACpB;AAED,wBAAgB,MAAM,CACpB,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,OAAO,EAAE,aAAa,GACrB,YAAY,GAAG,IAAI,CAmErB;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAChB,MAAM,EAAE,CAMV"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { ImageInput, Raster } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Encoding and decoding, in pure JavaScript on purpose.
|
|
4
|
-
*
|
|
5
|
-
* `sharp` would be faster, and it is the wrong choice here: it is a native
|
|
6
|
-
* binding to libvips, so the bytes that work on your laptop are not the bytes
|
|
7
|
-
* that run in the function app, and a deployment that skips the rebuild fails
|
|
8
|
-
* at *import* time — after the cold start, in production, with a stack trace
|
|
9
|
-
* about a missing `.node` file. `pngjs` and `jpeg-js` are slower and they are
|
|
10
|
-
* the same JavaScript everywhere, which is the whole point of the constraint
|
|
11
|
-
* this library was written under.
|
|
12
|
-
*/
|
|
13
|
-
export type ImageFormat = 'png' | 'jpeg';
|
|
14
|
-
export interface EncodeOptions {
|
|
15
|
-
format?: ImageFormat;
|
|
16
|
-
/** JPEG only, 1-100. Ignored for PNG. */
|
|
17
|
-
quality?: number;
|
|
18
|
-
}
|
|
19
|
-
/** Identify a buffer by its magic bytes. Returns `null` when it is neither PNG nor JPEG. */
|
|
20
|
-
export declare function sniffFormat(bytes: Uint8Array): ImageFormat | null;
|
|
21
|
-
/**
|
|
22
|
-
* Decode PNG or JPEG bytes to RGBA, or pass a {@link Raster} straight through.
|
|
23
|
-
*
|
|
24
|
-
* Passing a raster through untouched is what makes it cheap to align a page
|
|
25
|
-
* against several scans: decode once, reuse.
|
|
26
|
-
*/
|
|
27
|
-
export declare function decodeImage(input: ImageInput): Raster;
|
|
28
|
-
/** Encode a raster. PNG by default, because a scan re-encoded as JPEG is a scan with new artefacts. */
|
|
29
|
-
export declare function encodeImage(image: Raster, options?: EncodeOptions): Uint8Array;
|
|
30
|
-
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../../../src/lib/image/codec.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAGlD;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,CAAA;AAExC,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAG,WAAW,CAAA;IACrB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAKD,4FAA4F;AAC5F,wBAAgB,WAAW,CAAE,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAKlE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAoBtD;AAED,uGAAuG;AACvG,wBAAgB,WAAW,CAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,UAAU,CAgBnF"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { BinaryImage, GrayImage, Raster } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Turning a photograph of paper into something two images can be compared on.
|
|
4
|
-
*
|
|
5
|
-
* A scan differs from its source in ways that have nothing to do with where
|
|
6
|
-
* the page is: the lamp is brighter in the middle, the phone cast a shadow
|
|
7
|
-
* down one side, the JPEG quantiser smeared the strokes. Comparing raw
|
|
8
|
-
* greyscale means comparing all of that too. So every stage below the codec
|
|
9
|
-
* works on **ink**: greyscale divided by its own slowly-varying background and
|
|
10
|
-
* inverted, which is near zero on paper and near one on print no matter what
|
|
11
|
-
* the lighting did.
|
|
12
|
-
*
|
|
13
|
-
* Think of it as reading a page through a sheet of tracing paper — you lose
|
|
14
|
-
* the tint of the paper and the angle of the lamp, and keep the writing.
|
|
15
|
-
*/
|
|
16
|
-
/** Rec. 601 luminance, alpha composited over white, scaled to `[0, 1]`. */
|
|
17
|
-
export declare function toGrayscale(image: Raster): GrayImage;
|
|
18
|
-
/** Render a single channel back to RGBA, for debugging and diff overlays. */
|
|
19
|
-
export declare function grayToRaster(image: GrayImage): Raster;
|
|
20
|
-
/** Summed-area table with a zero first row and column, so a window sum is four lookups. */
|
|
21
|
-
export declare function integralImage(image: GrayImage): Float64Array;
|
|
22
|
-
/**
|
|
23
|
-
* Mean over a `(2 * radius + 1)` square, in time independent of the radius.
|
|
24
|
-
*
|
|
25
|
-
* Border windows are clipped and divided by their real area rather than padded,
|
|
26
|
-
* so the blur never invents dark paper outside the page.
|
|
27
|
-
*/
|
|
28
|
-
export declare function boxBlur(image: GrayImage, radius: number): GrayImage;
|
|
29
|
-
export interface InkOptions {
|
|
30
|
-
/**
|
|
31
|
-
* Background window as a fraction of the shorter side. The window has to be
|
|
32
|
-
* wide enough that no glyph can fill it — otherwise a bold heading becomes
|
|
33
|
-
* its own background and disappears — and narrow enough to follow a shadow.
|
|
34
|
-
*/
|
|
35
|
-
backgroundFraction?: number;
|
|
36
|
-
/** Ink below this fraction of full black is treated as paper noise and zeroed. */
|
|
37
|
-
floor?: number;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Greyscale to ink: divide out the local background, invert, clip the noise floor.
|
|
41
|
-
*
|
|
42
|
-
* Division rather than subtraction because illumination is multiplicative —
|
|
43
|
-
* a shadow halves what reaches the sensor, it does not subtract a constant —
|
|
44
|
-
* so dividing restores the same contrast in the shadow as in the light.
|
|
45
|
-
*/
|
|
46
|
-
export declare function inkMap(gray: GrayImage, options?: InkOptions): GrayImage;
|
|
47
|
-
/** Otsu's threshold over a 256-bin histogram of `[0, 1]` values. */
|
|
48
|
-
export declare function otsuThreshold(image: GrayImage): number;
|
|
49
|
-
/**
|
|
50
|
-
* Ink to a binary mask.
|
|
51
|
-
*
|
|
52
|
-
* `threshold` defaults to Otsu's, with a floor: a page that is genuinely blank
|
|
53
|
-
* has no bimodal split to find, and Otsu will happily cut its noise in half
|
|
54
|
-
* and report that 50% of the paper is ink.
|
|
55
|
-
*/
|
|
56
|
-
export declare function binarize(image: GrayImage, threshold?: number): BinaryImage;
|
|
57
|
-
/**
|
|
58
|
-
* Morphological dilation by a square, done as two 1D max passes.
|
|
59
|
-
*
|
|
60
|
-
* Used to give the original's ink a tolerance band before asking what is new
|
|
61
|
-
* in the scan: without it, alignment that is half a pixel off reports the edge
|
|
62
|
-
* of every printed character as freshly written.
|
|
63
|
-
*/
|
|
64
|
-
export declare function dilate(mask: BinaryImage, radius: number): BinaryImage;
|
|
65
|
-
/** Fraction of pixels set in `mask`, restricted to a rectangle when one is given. */
|
|
66
|
-
export declare function coverage(mask: BinaryImage, x0?: number, y0?: number, x1?: number, y1?: number): number;
|
|
67
|
-
//# sourceMappingURL=gray.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gray.d.ts","sourceRoot":"","sources":["../../../../src/lib/image/gray.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAG9D;;;;;;;;;;;;;GAaG;AAEH,2EAA2E;AAC3E,wBAAgB,WAAW,CAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAcrD;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAWtD;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,CAAE,KAAK,EAAE,SAAS,GAAG,YAAY,CAiB7D;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAwBpE;AAED,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,kFAAkF;IAClF,KAAK,CAAC,EAAe,MAAM,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAE,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,UAAe,GAAG,SAAS,CAc5E;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAmCvD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAE,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAM3E;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAsCtE;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAE,IAAI,EAAE,WAAW,EAAE,EAAE,SAAI,EAAE,EAAE,SAAI,EAAE,EAAE,SAAa,EAAE,EAAE,SAAc,GAAG,MAAM,CActG"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { BinaryImage, GrayImage, ImageInput, Raster } from '../types.js';
|
|
2
|
-
/** Allocate an opaque RGBA raster, filled with `fill` (white by default). */
|
|
3
|
-
export declare function createRaster(width: number, height: number, fill?: [number, number, number, number]): Raster;
|
|
4
|
-
export declare function createGray(width: number, height: number): GrayImage;
|
|
5
|
-
export declare function createBinary(width: number, height: number): BinaryImage;
|
|
6
|
-
export declare function cloneRaster(image: Raster): Raster;
|
|
7
|
-
/**
|
|
8
|
-
* True when the value is already a decoded raster.
|
|
9
|
-
*
|
|
10
|
-
* The check is structural rather than `instanceof` because a raster is a plain
|
|
11
|
-
* object on purpose: callers should be able to hand us a canvas `ImageData`,
|
|
12
|
-
* or something they built themselves, without importing anything from here.
|
|
13
|
-
*/
|
|
14
|
-
export declare function isRaster(value: unknown): value is Raster;
|
|
15
|
-
/** Narrow any accepted input to the bytes of an encoded image, or `null` if it is already decoded. */
|
|
16
|
-
export declare function toBytes(input: ImageInput): Uint8Array | null;
|
|
17
|
-
/** Wrap bytes as a `Uint8ClampedArray` without copying when the alignment allows it. */
|
|
18
|
-
export declare function asClamped(data: Uint8Array): Uint8ClampedArray;
|
|
19
|
-
//# sourceMappingURL=raster.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"raster.d.ts","sourceRoot":"","sources":["../../../../src/lib/image/raster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAE1E,6EAA6E;AAC7E,wBAAgB,YAAY,CAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAwB,GAAG,MAAM,CAYlI;AAED,wBAAgB,UAAU,CAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAIpE;AAED,wBAAgB,YAAY,CAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAIxE;AAED,wBAAgB,WAAW,CAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAUzD;AAED,sGAAsG;AACtG,wBAAgB,OAAO,CAAE,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAM7D;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAE,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAI9D"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { GrayImage, Raster } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Separable resampling: area-average going down, bilinear going up.
|
|
4
|
-
*
|
|
5
|
-
* Going down matters more than it sounds. Point-sampling a 300 dpi scan to
|
|
6
|
-
* half size drops every other row, and on a page of 9pt text that deletes
|
|
7
|
-
* roughly half the strokes — the thumbnail the matcher sees is not a smaller
|
|
8
|
-
* version of the page, it is a different page. Averaging over the exact source
|
|
9
|
-
* footprint of each destination pixel is what keeps the ink where it was.
|
|
10
|
-
*/
|
|
11
|
-
/** Resize a single channel image to exactly `width x height`. */
|
|
12
|
-
export declare function resizeGray(src: GrayImage, width: number, height: number): GrayImage;
|
|
13
|
-
/**
|
|
14
|
-
* Shrink so the longer side is at most `maxDimension`.
|
|
15
|
-
*
|
|
16
|
-
* Returns the factor applied, because every coordinate the caller recovers in
|
|
17
|
-
* this smaller frame has to be scaled back up by it.
|
|
18
|
-
*/
|
|
19
|
-
export declare function downscaleGray(src: GrayImage, maxDimension: number): {
|
|
20
|
-
image: GrayImage;
|
|
21
|
-
scale: number;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Box blur an RGBA raster with two sliding-window passes.
|
|
25
|
-
*
|
|
26
|
-
* Lives here rather than with the other blurs because its only caller is the
|
|
27
|
-
* warp prefilter, and because it has to work in bytes: a summed-area table over
|
|
28
|
-
* four channels of a 12 megapixel scan is 400 MB, which is not a thing to
|
|
29
|
-
* allocate inside a function app.
|
|
30
|
-
*/
|
|
31
|
-
export declare function boxBlurRaster(src: Raster, radius: number): Raster;
|
|
32
|
-
//# sourceMappingURL=resize.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../../../src/lib/image/resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAGjD;;;;;;;;GAQG;AAEH,iEAAiE;AACjE,wBAAgB,UAAU,CAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAUpF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAE,GAAG,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAUxG;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAsDlE"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { GrayImage, Matrix3, Raster } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Resampling one image onto another image's grid.
|
|
4
|
-
*
|
|
5
|
-
* The matrix maps **destination coordinates to source coordinates** — we stand
|
|
6
|
-
* on each output pixel and reach back into the scan for its colour. Doing it
|
|
7
|
-
* the other way, pushing scan pixels forward, leaves the output full of pinholes
|
|
8
|
-
* wherever the transform stretches, for the same reason a rotated stencil
|
|
9
|
-
* sprays gaps.
|
|
10
|
-
*/
|
|
11
|
-
export type Interpolation = 'nearest' | 'bilinear' | 'bicubic';
|
|
12
|
-
export interface WarpOptions {
|
|
13
|
-
/** RGBA fill for destination pixels that fall outside the source. Opaque white by default. */
|
|
14
|
-
background?: [number, number, number, number];
|
|
15
|
-
interpolation?: Interpolation;
|
|
16
|
-
/**
|
|
17
|
-
* Low-pass the source before minifying, so shrinking a 300 dpi scan does not
|
|
18
|
-
* alias the text into stripes. On by default; it costs one blur.
|
|
19
|
-
*/
|
|
20
|
-
prefilter?: boolean;
|
|
21
|
-
}
|
|
22
|
-
/** Warp an RGBA raster onto a `width x height` canvas. */
|
|
23
|
-
export declare function warpRaster(source: Raster, matrix: Matrix3, width: number, height: number, options?: WarpOptions): Raster;
|
|
24
|
-
/** Warp a single channel image. Used for scoring, where colour is noise. */
|
|
25
|
-
export declare function warpGray(source: GrayImage, matrix: Matrix3, width: number, height: number, fill?: number): GrayImage;
|
|
26
|
-
/** Bilinear read at a fractional index, returning `fill` outside the image. */
|
|
27
|
-
export declare function sampleGrayBilinear(image: GrayImage, u: number, v: number, fill?: number): number;
|
|
28
|
-
//# sourceMappingURL=warp.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"warp.d.ts","sourceRoot":"","sources":["../../../../src/lib/image/warp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAI1D;;;;;;;;GAQG;AAEH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAA;AAE9D,MAAM,WAAW,WAAW;IAC1B,8FAA8F;IAC9F,UAAU,CAAC,EAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAChD,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAM,OAAO,CAAA;CACxB;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB,MAAM,CAsCR;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CACtB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,SAAI,GACP,SAAS,CAuBX;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,MAAM,CA2B5F"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-place radix-2 Cooley-Tukey FFT, real and imaginary parts in separate arrays.
|
|
3
|
-
*
|
|
4
|
-
* Only used by phase correlation, which needs a *global* translation estimate
|
|
5
|
-
* that no amount of local feature matching can produce on a page with almost
|
|
6
|
-
* nothing printed on it. Sizes must be powers of two; {@link nextPowerOfTwo}
|
|
7
|
-
* and the caller's zero padding see to that.
|
|
8
|
-
*/
|
|
9
|
-
export declare function nextPowerOfTwo(n: number): number;
|
|
10
|
-
export declare function isPowerOfTwo(n: number): boolean;
|
|
11
|
-
/** Transform `re`/`im` of length `n` in place. `inverse` also divides by `n`. */
|
|
12
|
-
export declare function fft1d(re: Float64Array, im: Float64Array, inverse?: boolean): void;
|
|
13
|
-
/** 2D transform of a `width x height` row-major complex image, rows then columns. */
|
|
14
|
-
export declare function fft2d(re: Float64Array, im: Float64Array, width: number, height: number, inverse?: boolean): void;
|
|
15
|
-
//# sourceMappingURL=fft.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fft.d.ts","sourceRoot":"","sources":["../../../../src/lib/math/fft.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,cAAc,CAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,wBAAgB,YAAY,CAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,iFAAiF;AACjF,wBAAgB,KAAK,CAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,UAAQ,GAAG,IAAI,CA0DhF;AAED,qFAAqF;AACrF,wBAAgB,KAAK,CACnB,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,YAAY,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,UAAQ,GACd,IAAI,CAyBN"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The two dense solvers the estimators need, and nothing more.
|
|
3
|
-
*
|
|
4
|
-
* Both work on plain row-major `Float64Array`s of a fixed, tiny size (n <= 9),
|
|
5
|
-
* so there is no pivoting strategy worth agonising over and no allocation
|
|
6
|
-
* pressure worth caring about.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Solve `A x = b` by Gaussian elimination with partial pivoting.
|
|
10
|
-
*
|
|
11
|
-
* `a` is destroyed. Returns `null` when the system is singular, which for the
|
|
12
|
-
* affine fit means the sample points were collinear — a real, common case, not
|
|
13
|
-
* an exceptional one, so it is a return value rather than a throw.
|
|
14
|
-
*/
|
|
15
|
-
export declare function solve(a: Float64Array, b: Float64Array, n: number): Float64Array | null;
|
|
16
|
-
/**
|
|
17
|
-
* Eigen-decompose a symmetric matrix with the cyclic Jacobi method.
|
|
18
|
-
*
|
|
19
|
-
* Jacobi is the right tool at this size: it is a dozen lines, it is
|
|
20
|
-
* unconditionally stable for symmetric input, and it gives eigenvectors for
|
|
21
|
-
* free. The homography fit needs the eigenvector of `A^T A` belonging to the
|
|
22
|
-
* smallest eigenvalue — the direction the data constrains least, which is the
|
|
23
|
-
* null-space direction we are after.
|
|
24
|
-
*
|
|
25
|
-
* @param input Row-major, `n * n`, symmetric. Not modified.
|
|
26
|
-
* @returns `values[i]` paired with column `i` of `vectors` (`vectors[row * n + i]`).
|
|
27
|
-
*/
|
|
28
|
-
export declare function jacobiEigen(input: Float64Array, n: number, maxSweeps?: number): {
|
|
29
|
-
values: Float64Array;
|
|
30
|
-
vectors: Float64Array;
|
|
31
|
-
};
|
|
32
|
-
/** The unit eigenvector of a symmetric `n x n` matrix belonging to its smallest eigenvalue. */
|
|
33
|
-
export declare function smallestEigenvector(input: Float64Array, n: number): Float64Array;
|
|
34
|
-
//# sourceMappingURL=linalg.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"linalg.d.ts","sourceRoot":"","sources":["../../../../src/lib/math/linalg.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA2CvF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EACnB,CAAC,EAAE,MAAM,EACT,SAAS,SAAK,GACb;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAgDjD;AAED,+FAA+F;AAC/F,wBAAgB,mBAAmB,CAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,YAAY,CAgBjF"}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { Matrix3, Point, Rect, TransformModel, TransformSummary } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* 3x3 homogeneous matrix helpers.
|
|
4
|
-
*
|
|
5
|
-
* ## The one convention that matters
|
|
6
|
-
*
|
|
7
|
-
* Every matrix in this library maps **original coordinates to scanned
|
|
8
|
-
* coordinates**, never the other way round. That reads backwards the first
|
|
9
|
-
* time: we are producing an image on the original's canvas, so we walk the
|
|
10
|
-
* output pixel by pixel and ask "where in the scan does this come from?".
|
|
11
|
-
* Inverse mapping is what stops the output having holes — forward-splatting a
|
|
12
|
-
* rotated source leaves gaps between the splats, like spray-painting through a
|
|
13
|
-
* rotated stencil.
|
|
14
|
-
*
|
|
15
|
-
* Coordinates are continuous, with the centre of pixel `(i, j)` at
|
|
16
|
-
* `(i + 0.5, j + 0.5)`. Sticking to that is what makes {@link conjugateScale}
|
|
17
|
-
* a plain scale conjugation instead of a scale plus a half-pixel fudge.
|
|
18
|
-
*/
|
|
19
|
-
export declare const IDENTITY: Matrix3;
|
|
20
|
-
/** `a * b` — the transform that applies `b` first, then `a`. */
|
|
21
|
-
export declare function multiply(a: Matrix3, b: Matrix3): Matrix3;
|
|
22
|
-
export declare function determinant(m: Matrix3): number;
|
|
23
|
-
/** Throws when `m` is singular — a transform that collapses the page to a line is never a usable answer. */
|
|
24
|
-
export declare function invert(m: Matrix3): Matrix3;
|
|
25
|
-
/** Divide through by `m8` so two matrices describing the same transform compare equal. */
|
|
26
|
-
export declare function normalize(m: Matrix3): Matrix3;
|
|
27
|
-
export declare function applyPoint(m: Matrix3, x: number, y: number): Point;
|
|
28
|
-
export declare function translation(tx: number, ty: number): Matrix3;
|
|
29
|
-
export declare function scaling(sx: number, sy?: number): Matrix3;
|
|
30
|
-
/**
|
|
31
|
-
* Scale `s` and rotation `angleRad` about `pivot`, then land that pivot on `target`.
|
|
32
|
-
*
|
|
33
|
-
* This is the shape the coarse stage produces: "the middle of the original's
|
|
34
|
-
* printed content is the middle of the scan's printed content, turned by this
|
|
35
|
-
* much and this many times bigger".
|
|
36
|
-
*/
|
|
37
|
-
export declare function similarity(s: number, angleRad: number, pivot: Point, target: Point): Matrix3;
|
|
38
|
-
/**
|
|
39
|
-
* Re-express `m` in a coordinate frame scaled by `k`.
|
|
40
|
-
*
|
|
41
|
-
* Fitting runs on downscaled copies because matching 3000x4000 images is a
|
|
42
|
-
* waste; the matrix that comes back speaks in those small pixels. `k` is
|
|
43
|
-
* `working / full`, and the result speaks in full-resolution pixels.
|
|
44
|
-
*/
|
|
45
|
-
export declare function conjugateScale(m: Matrix3, k: number): Matrix3;
|
|
46
|
-
/**
|
|
47
|
-
* Re-express a matrix whose two frames were scaled by different factors.
|
|
48
|
-
*
|
|
49
|
-
* The coarse stage measures on two independently shrunk copies - the original
|
|
50
|
-
* and the scan rarely have the same pixel count, so they rarely shrink by the
|
|
51
|
-
* same factor. `sourceScale` and `targetScale` are each `working / full` for
|
|
52
|
-
* their own side, and the result speaks full-resolution pixels on both.
|
|
53
|
-
*/
|
|
54
|
-
export declare function rebase(m: Matrix3, sourceScale: number, targetScale: number): Matrix3;
|
|
55
|
-
/** The four corners of `rect` mapped through `m`, clockwise from the top-left. */
|
|
56
|
-
export declare function mapRectCorners(m: Matrix3, rect: Rect): Point[];
|
|
57
|
-
/**
|
|
58
|
-
* Pull a matrix apart into scale, rotation and shear.
|
|
59
|
-
*
|
|
60
|
-
* The 2x2 linear part is factored as `R(theta) * [[sx, k], [0, sy]]`, which is
|
|
61
|
-
* the order a scanner actually applies them: the page is stretched on the
|
|
62
|
-
* glass, then the whole thing sits at an angle.
|
|
63
|
-
*/
|
|
64
|
-
export declare function decompose(m: Matrix3, model: TransformModel): TransformSummary;
|
|
65
|
-
/** Euclidean distance between `m * source` and `target`, in target pixels. */
|
|
66
|
-
export declare function reprojectionError(m: Matrix3, source: Point, target: Point): number;
|
|
67
|
-
/**
|
|
68
|
-
* True when `m` is a plausible page-to-page transform rather than numerical debris.
|
|
69
|
-
*
|
|
70
|
-
* RANSAC on a minimal sample of near-collinear points loves to return a
|
|
71
|
-
* matrix that folds the page in half. Cheaper to reject it here than to
|
|
72
|
-
* discover it in the output.
|
|
73
|
-
*/
|
|
74
|
-
export declare function isPlausible(m: Matrix3, maxScaleRatio?: number): boolean;
|
|
75
|
-
//# sourceMappingURL=matrix.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"matrix.d.ts","sourceRoot":"","sources":["../../../../src/lib/math/matrix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEtF;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,QAAQ,EAAE,OAAqC,CAAA;AAE5D,gEAAgE;AAChE,wBAAgB,QAAQ,CAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAczD;AAED,wBAAgB,WAAW,CAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAM/C;AAED,4GAA4G;AAC5G,wBAAgB,MAAM,CAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAoB3C;AAED,0FAA0F;AAC1F,wBAAgB,SAAS,CAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAK9C;AAED,wBAAgB,UAAU,CAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAKnE;AAED,wBAAgB,WAAW,CAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,OAAO,CAAE,EAAE,EAAE,MAAM,EAAE,EAAE,SAAK,GAAG,OAAO,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,OAAO,CAS7F;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAE,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAErF;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,KAAK,EAAE,CAS/D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAiB9E;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,MAAM,CAInF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAE,CAAC,EAAE,OAAO,EAAE,aAAa,SAAI,GAAG,OAAO,CAenE"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A seeded PRNG, so that two runs on the same bytes give the same matrix.
|
|
3
|
-
*
|
|
4
|
-
* RANSAC samples at random and the BRIEF pattern is drawn at random; with
|
|
5
|
-
* `Math.random` the library would return a slightly different answer every
|
|
6
|
-
* time, which makes a regression test a coin toss and a production bug
|
|
7
|
-
* impossible to reproduce from the inputs alone. mulberry32 is 32 bits of
|
|
8
|
-
* state and passes the statistical tests that matter at this scale.
|
|
9
|
-
*/
|
|
10
|
-
export declare function createRandom(seed: number): () => number;
|
|
11
|
-
/** Box-Muller, used to draw the BRIEF sampling pattern from a Gaussian around the patch centre. */
|
|
12
|
-
export declare function gaussian(random: () => number): number;
|
|
13
|
-
//# sourceMappingURL=random.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../../../../src/lib/math/random.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAE,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAWxD;AAED,mGAAmG;AACnG,wBAAgB,QAAQ,CAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAKtD"}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { InkOptions } from './image/gray.js';
|
|
2
|
-
import type { ImageInput, Raster, Rect } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* What changed, and where.
|
|
5
|
-
*
|
|
6
|
-
* Once the scan sits on the original's canvas, "was this box signed?" stops
|
|
7
|
-
* being an image problem and becomes arithmetic: count the ink inside the
|
|
8
|
-
* rectangle that is present in the scan and absent from the original.
|
|
9
|
-
*
|
|
10
|
-
* The one subtlety is the tolerance band. Alignment is good to a pixel or so,
|
|
11
|
-
* never to zero, and printed text is mostly edges — so a half-pixel shift
|
|
12
|
-
* lights up the outline of every character as "new ink". Dilating the
|
|
13
|
-
* original's mask first (fattening every stroke by a couple of pixels) absorbs
|
|
14
|
-
* that, the way a proofreader ignores a letter sitting a hair off the baseline.
|
|
15
|
-
* What it cannot absorb is a signature, which is ink in places the original has
|
|
16
|
-
* none.
|
|
17
|
-
*/
|
|
18
|
-
export interface Region {
|
|
19
|
-
id: string;
|
|
20
|
-
/** In the *original's* pixel coordinates - the whole point of aligning first. */
|
|
21
|
-
rect: Rect;
|
|
22
|
-
/** Fraction of the region that must be new ink before `filled` is true. Overrides the global default. */
|
|
23
|
-
threshold?: number;
|
|
24
|
-
}
|
|
25
|
-
export interface RegionOptions {
|
|
26
|
-
ink?: InkOptions;
|
|
27
|
-
/**
|
|
28
|
-
* Radius, in pixels, that the original's ink is fattened by before diffing.
|
|
29
|
-
* Raise it if alignment is loose; lower it to catch very fine additions.
|
|
30
|
-
*/
|
|
31
|
-
tolerance?: number;
|
|
32
|
-
/** Default fraction of new ink that counts as filled. */
|
|
33
|
-
threshold?: number;
|
|
34
|
-
}
|
|
35
|
-
export interface RegionReport {
|
|
36
|
-
id: string;
|
|
37
|
-
rect: Rect;
|
|
38
|
-
/** Ink coverage of the region in the original, in `[0, 1]`. */
|
|
39
|
-
originalInk: number;
|
|
40
|
-
/** Ink coverage of the region in the aligned scan. */
|
|
41
|
-
scanInk: number;
|
|
42
|
-
/** Coverage that is ink in the scan and not within `tolerance` of ink in the original. */
|
|
43
|
-
added: number;
|
|
44
|
-
/** Coverage that is ink in the original and missing from the scan. Mostly a faint-scan warning. */
|
|
45
|
-
removed: number;
|
|
46
|
-
filled: boolean;
|
|
47
|
-
/** `added` as a multiple of the threshold, clamped to `[0, 1]`. A reportable confidence. */
|
|
48
|
-
score: number;
|
|
49
|
-
}
|
|
50
|
-
export interface DocumentDiff {
|
|
51
|
-
/** Page-wide version of {@link RegionReport.added}. */
|
|
52
|
-
added: number;
|
|
53
|
-
removed: number;
|
|
54
|
-
regions: RegionReport[];
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Compare an aligned scan against its original over a set of known rectangles.
|
|
58
|
-
*
|
|
59
|
-
* `aligned` must be the output of `alignScan` - or anything else already on the
|
|
60
|
-
* original's canvas. Feeding a raw scan in produces confident nonsense, because
|
|
61
|
-
* every rectangle then names a different part of the page in each image.
|
|
62
|
-
*/
|
|
63
|
-
export declare function compareRegions(original: ImageInput, aligned: ImageInput, regions: readonly Region[], options?: RegionOptions): RegionReport[];
|
|
64
|
-
/** Page-wide added/removed ink, plus per-region detail for any regions supplied. */
|
|
65
|
-
export declare function diffDocument(original: ImageInput, aligned: ImageInput, regions?: readonly Region[], options?: RegionOptions): DocumentDiff;
|
|
66
|
-
/**
|
|
67
|
-
* An RGBA overlay of the comparison, for looking at with your own eyes.
|
|
68
|
-
*
|
|
69
|
-
* Red is ink the scan added, blue is ink it lost, grey is ink both agree on.
|
|
70
|
-
* A correctly aligned pair of a signed form is almost entirely grey with a red
|
|
71
|
-
* signature; a misaligned one is red and blue confetti along every stroke,
|
|
72
|
-
* which is the fastest way to tell the two failures apart.
|
|
73
|
-
*/
|
|
74
|
-
export declare function renderDiff(original: ImageInput, aligned: ImageInput, options?: RegionOptions): Raster;
|
|
75
|
-
//# sourceMappingURL=regions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../../src/lib/regions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEpE;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,MAAM;IACrB,EAAE,EAAU,MAAM,CAAA;IAClB,iFAAiF;IACjF,IAAI,EAAQ,IAAI,CAAA;IAChB,yGAAyG;IACzG,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAQ,UAAU,CAAA;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAW,MAAM,CAAA;IACnB,IAAI,EAAS,IAAI,CAAA;IACjB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAA;IACnB,sDAAsD;IACtD,OAAO,EAAM,MAAM,CAAA;IACnB,0FAA0F;IAC1F,KAAK,EAAQ,MAAM,CAAA;IACnB,mGAAmG;IACnG,OAAO,EAAM,MAAM,CAAA;IACnB,MAAM,EAAO,OAAO,CAAA;IACpB,4FAA4F;IAC5F,KAAK,EAAQ,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,KAAK,EAAI,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,GAAE,aAAkB,GAC1B,YAAY,EAAE,CAKhB;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,SAAS,MAAM,EAAO,EAC/B,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAWd;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAoCR"}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type { Matrix3, Raster, Rect } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Synthetic pages, and synthetic scans of them.
|
|
4
|
-
*
|
|
5
|
-
* Alignment is awkward to test honestly: with two real images you have no
|
|
6
|
-
* ground truth, only an opinion about whether the output looks right. Generate
|
|
7
|
-
* the page and then the distortion yourself and you know the exact matrix the
|
|
8
|
-
* estimator is supposed to recover, so a test can assert a number of pixels
|
|
9
|
-
* instead of a feeling.
|
|
10
|
-
*
|
|
11
|
-
* It is exported rather than kept in the test folder because the same trick is
|
|
12
|
-
* how you smoke-test a deployment: generate, distort, align, check the error is
|
|
13
|
-
* small, all without shipping sample scans.
|
|
14
|
-
*/
|
|
15
|
-
export interface DocumentOptions {
|
|
16
|
-
width?: number;
|
|
17
|
-
height?: number;
|
|
18
|
-
seed?: number;
|
|
19
|
-
/** Where a signature would go. Left empty by {@link createSyntheticDocument}. */
|
|
20
|
-
signatureBox?: Rect;
|
|
21
|
-
}
|
|
22
|
-
export interface SyntheticDocument {
|
|
23
|
-
raster: Raster;
|
|
24
|
-
/** Rectangles a caller may want to inspect later: the signature box, the tick boxes. */
|
|
25
|
-
regions: Record<string, Rect>;
|
|
26
|
-
}
|
|
27
|
-
export interface ScanOptions {
|
|
28
|
-
rotationDeg?: number;
|
|
29
|
-
/** Size of the scan relative to the page. `1.5` is roughly 300 dpi against a 200 dpi render. */
|
|
30
|
-
scale?: number;
|
|
31
|
-
translateX?: number;
|
|
32
|
-
translateY?: number;
|
|
33
|
-
/** Standard deviation of additive sensor noise, in `[0, 1]` units. */
|
|
34
|
-
noise?: number;
|
|
35
|
-
/** Box blur radius, standing in for an out-of-focus or low-quality scan. */
|
|
36
|
-
blur?: number;
|
|
37
|
-
/** Strength of a diagonal lighting ramp, in `[0, 1]`. `0.3` is a pronounced shadow. */
|
|
38
|
-
illumination?: number;
|
|
39
|
-
/** Scan canvas. Defaults to the page scaled by `scale`, so the whole page fits. */
|
|
40
|
-
canvas?: {
|
|
41
|
-
width: number;
|
|
42
|
-
height: number;
|
|
43
|
-
};
|
|
44
|
-
seed?: number;
|
|
45
|
-
}
|
|
46
|
-
export interface SimulatedScan {
|
|
47
|
-
raster: Raster;
|
|
48
|
-
/** Ground truth: maps original coordinates to scanned coordinates. */
|
|
49
|
-
matrix: Matrix3;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* A plausible printed form: header rule, paragraphs, a table, tick boxes, a
|
|
53
|
-
* signature box. Deterministic for a given seed.
|
|
54
|
-
*/
|
|
55
|
-
export declare function createSyntheticDocument(options?: DocumentOptions): SyntheticDocument;
|
|
56
|
-
/** Scribble inside a rectangle, the way a signature crosses a signature box. */
|
|
57
|
-
export declare function drawSignature(page: Raster, box: Rect, seed?: number): void;
|
|
58
|
-
/** Fill a tick box, the way a pen does. */
|
|
59
|
-
export declare function drawTick(page: Raster, box: Rect): void;
|
|
60
|
-
/**
|
|
61
|
-
* Put a page through everything a scanner does to it, and report the matrix used.
|
|
62
|
-
*
|
|
63
|
-
* Order matters and mirrors the physical one: the page is placed on the glass
|
|
64
|
-
* somewhere, at some angle, and sampled at some resolution (the geometry);
|
|
65
|
-
* then the lamp falls off towards one corner, the optics blur, and the sensor
|
|
66
|
-
* adds noise (the photometry). Estimators that only ever see clean geometric
|
|
67
|
-
* distortion pass tests and fail on real scans.
|
|
68
|
-
*/
|
|
69
|
-
export declare function simulateScan(page: Raster, options?: ScanOptions): SimulatedScan;
|
|
70
|
-
export declare function fillRect(page: Raster, rect: Rect, value: number): void;
|
|
71
|
-
export declare function strokeRect(page: Raster, rect: Rect, thickness: number, value: number): void;
|
|
72
|
-
export declare function drawLine(page: Raster, x0: number, y0: number, x1: number, y1: number, thickness: number, value: number): void;
|
|
73
|
-
//# sourceMappingURL=synthetic.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"synthetic.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/synthetic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAErD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAS,MAAM,CAAA;IACrB,MAAM,CAAC,EAAQ,MAAM,CAAA;IACrB,IAAI,CAAC,EAAU,MAAM,CAAA;IACrB,iFAAiF;IACjF,YAAY,CAAC,EAAE,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAG,MAAM,CAAA;IACf,wFAAwF;IACxF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAG,MAAM,CAAA;IACrB,gGAAgG;IAChG,KAAK,CAAC,EAAS,MAAM,CAAA;IACrB,UAAU,CAAC,EAAI,MAAM,CAAA;IACrB,UAAU,CAAC,EAAI,MAAM,CAAA;IACrB,sEAAsE;IACtE,KAAK,CAAC,EAAS,MAAM,CAAA;IACrB,4EAA4E;IAC5E,IAAI,CAAC,EAAU,MAAM,CAAA;IACrB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mFAAmF;IACnF,MAAM,CAAC,EAAQ;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,IAAI,CAAC,EAAU,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAE,OAAO,GAAE,eAAoB,GAAG,iBAAiB,CAmFzF;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,SAAI,GAAG,IAAI,CActE;AAED,2CAA2C;AAC3C,wBAAgB,QAAQ,CAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAIvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CA+CpF;AAkDD,wBAAgB,QAAQ,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAevE;AAED,wBAAgB,UAAU,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAM5F;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,IAAI,CAYN"}
|
package/dist/src/lib/types.d.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core data types.
|
|
3
|
-
*
|
|
4
|
-
* Everything in this library speaks two image shapes and nothing else:
|
|
5
|
-
*
|
|
6
|
-
* - {@link Raster} — what you get in and out: 8-bit RGBA, the same memory
|
|
7
|
-
* layout a `<canvas>` `ImageData` uses, so it needs no conversion to be
|
|
8
|
-
* re-encoded, handed to an OCR engine, or drawn.
|
|
9
|
-
* - {@link GrayImage} — what the algorithms work on: one float per pixel.
|
|
10
|
-
* Float, not byte, because the pipeline divides by an estimated background
|
|
11
|
-
* and then correlates the result; doing that in 8 bits throws away the
|
|
12
|
-
* faint strokes that OCR cares about.
|
|
13
|
-
*/
|
|
14
|
-
/** A decoded image: 8-bit RGBA, row-major, 4 bytes per pixel, no padding. */
|
|
15
|
-
export interface Raster {
|
|
16
|
-
width: number;
|
|
17
|
-
height: number;
|
|
18
|
-
/** `width * height * 4` bytes, in R, G, B, A order. */
|
|
19
|
-
data: Uint8ClampedArray;
|
|
20
|
-
}
|
|
21
|
-
/** A single-channel image. Values are normally in `[0, 1]` but are not clamped. */
|
|
22
|
-
export interface GrayImage {
|
|
23
|
-
width: number;
|
|
24
|
-
height: number;
|
|
25
|
-
data: Float32Array;
|
|
26
|
-
}
|
|
27
|
-
/** A single-channel mask. Every value is exactly `0` or `1`. */
|
|
28
|
-
export interface BinaryImage {
|
|
29
|
-
width: number;
|
|
30
|
-
height: number;
|
|
31
|
-
data: Uint8Array;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* A row-major 3x3 matrix in homogeneous coordinates:
|
|
35
|
-
*
|
|
36
|
-
* ```text
|
|
37
|
-
* [ m0 m1 m2 ]
|
|
38
|
-
* [ m3 m4 m5 ]
|
|
39
|
-
* [ m6 m7 m8 ]
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
export type Matrix3 = readonly [number, number, number, number, number, number, number, number, number];
|
|
43
|
-
export interface Point {
|
|
44
|
-
x: number;
|
|
45
|
-
y: number;
|
|
46
|
-
}
|
|
47
|
-
/** An axis-aligned rectangle in pixel coordinates. `x`/`y` are the top-left corner. */
|
|
48
|
-
export interface Rect {
|
|
49
|
-
x: number;
|
|
50
|
-
y: number;
|
|
51
|
-
width: number;
|
|
52
|
-
height: number;
|
|
53
|
-
}
|
|
54
|
-
/** Anything the library will accept as an image: an encoded PNG/JPEG, or an already-decoded raster. */
|
|
55
|
-
export type ImageInput = Raster | Uint8Array | ArrayBuffer;
|
|
56
|
-
/** Which family of transform to fit. Fewer degrees of freedom is more robust; more is more expressive. */
|
|
57
|
-
export type TransformModel =
|
|
58
|
-
/** 4 DOF: uniform scale, rotation, translation. A flatbed scan of a flat page. */
|
|
59
|
-
'similarity' |
|
|
60
|
-
/** 6 DOF: adds non-uniform scale and shear. A scan whose feed stretched one axis. */
|
|
61
|
-
'affine' |
|
|
62
|
-
/** 8 DOF: full projective warp. A photograph taken off-axis. */
|
|
63
|
-
'homography';
|
|
64
|
-
/** The geometric meaning of a fitted matrix, pulled apart into numbers a human can sanity-check. */
|
|
65
|
-
export interface TransformSummary {
|
|
66
|
-
model: TransformModel;
|
|
67
|
-
/** Scale along the scan's x axis. `1` means the scan matches the original's pixel scale. */
|
|
68
|
-
scaleX: number;
|
|
69
|
-
scaleY: number;
|
|
70
|
-
/** Rotation in degrees, counter-clockwise positive in image coordinates. */
|
|
71
|
-
rotationDeg: number;
|
|
72
|
-
/** Residual shear in degrees. Non-zero only for `affine` and `homography`. */
|
|
73
|
-
shearDeg: number;
|
|
74
|
-
/** Where the original's top-left corner lands in the scan. */
|
|
75
|
-
translation: Point;
|
|
76
|
-
/** Perspective terms (`m6`, `m7`). Non-zero only for `homography`. */
|
|
77
|
-
perspective: Point;
|
|
78
|
-
}
|
|
79
|
-
/** One `(original, scanned)` correspondence produced by feature matching. */
|
|
80
|
-
export interface PointMatch {
|
|
81
|
-
source: Point;
|
|
82
|
-
target: Point;
|
|
83
|
-
/** Hamming distance between the two descriptors. Lower is a better match. */
|
|
84
|
-
distance: number;
|
|
85
|
-
}
|
|
86
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,KAAK,EAAG,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,IAAI,EAAI,iBAAiB,CAAA;CAC1B;AAED,mFAAmF;AACnF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAG,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAI,YAAY,CAAA;CACrB;AAED,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAG,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAI,UAAU,CAAA;CACnB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEvG,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,uFAAuF;AACvF,MAAM,WAAW,IAAI;IACnB,CAAC,EAAO,MAAM,CAAA;IACd,CAAC,EAAO,MAAM,CAAA;IACd,KAAK,EAAG,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAA;AAE1D,0GAA0G;AAC1G,MAAM,MAAM,cAAc;AACxB,kFAAkF;AAChF,YAAY;AACd,qFAAqF;AACrF,QAAQ;AACR,gEAAgE;AAChE,YAAY,CAAA;AAEd,oGAAoG;AACpG,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAQ,cAAc,CAAA;IAC3B,4FAA4F;IAC5F,MAAM,EAAO,MAAM,CAAA;IACnB,MAAM,EAAO,MAAM,CAAA;IACnB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,QAAQ,EAAK,MAAM,CAAA;IACnB,8DAA8D;IAC9D,WAAW,EAAE,KAAK,CAAA;IAClB,sEAAsE;IACtE,WAAW,EAAE,KAAK,CAAA;CACnB;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAI,KAAK,CAAA;IACf,MAAM,EAAI,KAAK,CAAA;IACf,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAA;CACjB"}
|