@scanmate/ocr 0.0.3 → 0.1.0
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 +23 -3
- package/dist/index.esm.js +743 -25
- package/dist/src/index.d.ts +4 -2
- package/dist/src/page-reading/index.d.ts +2 -2
- package/dist/src/page-reading/match-words.use-case.d.ts +6 -1
- package/dist/src/page-reading/ocr-report.contract.d.ts +23 -0
- package/dist/src/page-reading/recheck-run.use-case.d.ts +24 -8
- package/dist/src/print-verification/glyph-cells.use-case.d.ts +86 -0
- package/dist/src/print-verification/glyph-templates.use-case.d.ts +40 -0
- package/dist/src/print-verification/index.d.ts +10 -0
- package/dist/src/print-verification/merge-reading.mapper.d.ts +19 -0
- package/dist/src/print-verification/print-polarity.policy.d.ts +11 -0
- package/dist/src/print-verification/verify-print.use-case.d.ts +122 -0
- package/package.json +4 -4
package/dist/src/index.d.ts
CHANGED
|
@@ -30,8 +30,10 @@ export { compareTexts } from './text-similarity/index.js';
|
|
|
30
30
|
export type { ScoreMetric, TextMetrics } from './text-similarity/index.js';
|
|
31
31
|
export { DEFAULT_NORMALISE, normaliseText, tokenise } from './text-normalisation/index.js';
|
|
32
32
|
export type { NormaliseOptions } from './text-normalisation/index.js';
|
|
33
|
-
export {
|
|
34
|
-
export type {
|
|
33
|
+
export { collectTemplates, FIGURE_CHARACTERS, glyphCells, glyphWords, placeGlyphs, printPolarity, templateKey, TEXT_CHARACTERS, verifyPrintedRun } from './print-verification/index.js';
|
|
34
|
+
export type { Box, CellOptions, PrintedRun, PrintPolarity, PrintVerification, Templates, VerifyOptions } from './print-verification/index.js';
|
|
35
|
+
export { claimWords, DEFAULT_RECHECK_PASSES, judgeRun, judgeRuns, matchWords, readRun, recheckRun } from './page-reading/index.js';
|
|
36
|
+
export type { Claims, MatchOptions, Recheck, RecheckOptions, RecheckPass, Reference, Verdict, WordMatch } from './page-reading/index.js';
|
|
35
37
|
export { cosine, dice, jaccard, jaroWinkler, levenshtein, levenshteinSimilarity, wordDistance, wordRecall } from './text-similarity/index.js';
|
|
36
38
|
export { diacriticsMap, foldConfusables, foldDiacritics } from './text-normalisation/index.js';
|
|
37
39
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -3,6 +3,6 @@ export { claimWords, judgeRun, judgeRuns, matchWords } from './match-words.use-c
|
|
|
3
3
|
export type { Claims, MatchOptions, Reference, Verdict, WordMatch } from './match-words.use-case.js';
|
|
4
4
|
export { ocrPages } from './ocr-pages.use-case.js';
|
|
5
5
|
export type { OcrOptions, OcrReport, PageOcr, PlacedText, PositionedText, ReadablePage, RunReading, SideText, TextDifference } from './ocr-report.contract.js';
|
|
6
|
-
export { DEFAULT_RECHECK_PASSES,
|
|
7
|
-
export type {
|
|
6
|
+
export { DEFAULT_RECHECK_PASSES, readRun, recheckRun } from './recheck-run.use-case.js';
|
|
7
|
+
export type { Recheck, RecheckOptions, RecheckPass } from './recheck-run.use-case.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -59,7 +59,12 @@ export declare function claimWords(references: readonly Reference[], words: read
|
|
|
59
59
|
* and read within the similarity threshold.
|
|
60
60
|
*/
|
|
61
61
|
export declare function judgeRun(expected: string, found: string, options: MatchOptions): Verdict;
|
|
62
|
-
|
|
62
|
+
/**
|
|
63
|
+
* @param verified - Runs the print check matched glyph by glyph and found
|
|
64
|
+
* changed. Their differences are evidence in themselves; every other one is
|
|
65
|
+
* a reading, which the pixels still have to agree with.
|
|
66
|
+
*/
|
|
67
|
+
export declare function judgeRuns(references: readonly Reference[], claims: Claims, options: MatchOptions, verified?: ReadonlySet<number>): WordMatch;
|
|
63
68
|
/** Claim and judge in one go, for a page read once. */
|
|
64
69
|
export declare function matchWords(references: readonly Reference[], words: readonly PlacedText[], lineOf: readonly number[], options: MatchOptions): WordMatch;
|
|
65
70
|
//# sourceMappingURL=match-words.use-case.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AlignedPage, PageImage, ProgressCallback } from '@scanmate/ink';
|
|
2
2
|
import type { OcrEngine, TesseractEngineOptions } from '../ocr-engine/index.js';
|
|
3
|
+
import type { VerifyOptions } from '../print-verification/index.js';
|
|
3
4
|
import type { RecheckOptions } from './recheck-run.use-case.js';
|
|
4
5
|
import type { NormaliseOptions } from '../text-normalisation/index.js';
|
|
5
6
|
import type { ScoreMetric, TextMetrics } from '../text-similarity/index.js';
|
|
@@ -11,6 +12,9 @@ export interface PositionedText {
|
|
|
11
12
|
width: number;
|
|
12
13
|
height: number;
|
|
13
14
|
endsLine?: boolean;
|
|
15
|
+
/** How the run is set, when the text layer says: a figure is matched against glyphs of the same face and size. */
|
|
16
|
+
fontName?: string;
|
|
17
|
+
fontSize?: number;
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* What `ocrPages` reads: an aligned page, and optionally its enhanced image
|
|
@@ -56,6 +60,13 @@ export interface OcrOptions {
|
|
|
56
60
|
* time; `false` reports the page reading as it is. See `recheckRun`.
|
|
57
61
|
*/
|
|
58
62
|
recheck?: RecheckOptions | false;
|
|
63
|
+
/**
|
|
64
|
+
* Match every printed figure against the original's own glyphs instead of
|
|
65
|
+
* trusting what the scan reads there; `false` leaves figures to the reading.
|
|
66
|
+
* See `verifyPrintedRun`. On by default, and only possible when the original
|
|
67
|
+
* has a text layer.
|
|
68
|
+
*/
|
|
69
|
+
printCheck?: VerifyOptions | false;
|
|
59
70
|
onProgress?: ProgressCallback;
|
|
60
71
|
}
|
|
61
72
|
/** A word or line, placed on the page in points from the top-left. */
|
|
@@ -94,6 +105,13 @@ export interface TextDifference {
|
|
|
94
105
|
* the match threshold (`'text'`). Always `'text'` for missing and added.
|
|
95
106
|
*/
|
|
96
107
|
reason: 'numbers' | 'text';
|
|
108
|
+
/**
|
|
109
|
+
* The scan's ink here was matched against the original's own glyphs, and they
|
|
110
|
+
* are not the same glyphs. A difference carrying this was *seen* rather than
|
|
111
|
+
* read, so it stands on its own; one without it is a reading, and a reading
|
|
112
|
+
* disagreeing with identical ink is a misreading.
|
|
113
|
+
*/
|
|
114
|
+
verified?: boolean;
|
|
97
115
|
/** Where on the page, in points from the top-left. */
|
|
98
116
|
x: number;
|
|
99
117
|
y: number;
|
|
@@ -137,6 +155,11 @@ export interface PageOcr {
|
|
|
137
155
|
attempted: number;
|
|
138
156
|
cleared: number;
|
|
139
157
|
};
|
|
158
|
+
/** Printed figures matched against the original's own glyphs, and how many read as something else. */
|
|
159
|
+
printChecks: {
|
|
160
|
+
checked: number;
|
|
161
|
+
different: number;
|
|
162
|
+
};
|
|
140
163
|
warnings: string[];
|
|
141
164
|
}
|
|
142
165
|
export interface OcrReport {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Raster } from '@scanmate/ink';
|
|
2
2
|
import type { OcrEngine } from '../ocr-engine/index.js';
|
|
3
|
+
import type { PrintPolarity } from '../print-verification/index.js';
|
|
3
4
|
import type { MatchOptions, Reference } from './match-words.use-case.js';
|
|
4
5
|
/**
|
|
5
6
|
* A second look at one run the page reading got wrong, before calling it a
|
|
@@ -37,8 +38,6 @@ export interface RecheckOptions {
|
|
|
37
38
|
agree?: number;
|
|
38
39
|
}
|
|
39
40
|
export declare const DEFAULT_RECHECK_PASSES: readonly RecheckPass[];
|
|
40
|
-
/** Which way the original prints a run: dark text on light, or light text on a dark bar. */
|
|
41
|
-
export type PrintPolarity = 'dark-on-light' | 'light-on-dark';
|
|
42
41
|
export interface Recheck {
|
|
43
42
|
cleared: boolean;
|
|
44
43
|
/** An agreeing reading when cleared; `null` otherwise. */
|
|
@@ -47,14 +46,31 @@ export interface Recheck {
|
|
|
47
46
|
passes: number;
|
|
48
47
|
agreed: number;
|
|
49
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Reads one run's crop once per pass, and says what each pass read - without
|
|
51
|
+
* judging it against anything.
|
|
52
|
+
*
|
|
53
|
+
* `recheckRun` asks "does this still say what the original prints?", which is
|
|
54
|
+
* the right question while reading a page. It is the wrong question once the
|
|
55
|
+
* pixels have disagreed with the reading, because a systematic misreading -
|
|
56
|
+
* a face, a size, a resolution the engine handles badly - misreads the
|
|
57
|
+
* *original* just as surely as the scan. Reading both sides the same way and
|
|
58
|
+
* comparing the two readings to each other cancels exactly that error, and
|
|
59
|
+
* needs the readings themselves rather than a verdict.
|
|
60
|
+
*
|
|
61
|
+
* @param engine - The engine to read with.
|
|
62
|
+
* @param image - The page to crop from, and its resolution.
|
|
63
|
+
* @param run - The run to read, placed on that page.
|
|
64
|
+
* @param options - Which passes to try; the matching rules pick the charset.
|
|
65
|
+
* @param polarity - Whether the original prints this run light on dark.
|
|
66
|
+
* @returns What each pass read, in order; empty when the run cannot be cropped.
|
|
67
|
+
*/
|
|
68
|
+
export declare function readRun(engine: OcrEngine, image: {
|
|
69
|
+
raster: Raster;
|
|
70
|
+
dpi: number;
|
|
71
|
+
}, run: Reference, options: MatchOptions & RecheckOptions, polarity?: PrintPolarity): Promise<string[]>;
|
|
50
72
|
export declare function recheckRun(engine: OcrEngine, image: {
|
|
51
73
|
raster: Raster;
|
|
52
74
|
dpi: number;
|
|
53
75
|
}, run: Reference, options: MatchOptions & RecheckOptions, polarity?: PrintPolarity): Promise<Recheck>;
|
|
54
|
-
/**
|
|
55
|
-
* Which way the original prints a run, read off its own crisp rendering: the
|
|
56
|
-
* glyphs are the pixels far from the background, and the background is most of
|
|
57
|
-
* the box.
|
|
58
|
-
*/
|
|
59
|
-
export declare function printPolarity(raster: Raster, dpi: number, run: Reference): PrintPolarity;
|
|
60
76
|
//# sourceMappingURL=recheck-run.use-case.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { GrayImage } from '@scanmate/ink';
|
|
2
|
+
/**
|
|
3
|
+
* Where each printed character sits inside a run, found in the original's own
|
|
4
|
+
* rendering rather than guessed from font metrics.
|
|
5
|
+
*
|
|
6
|
+
* A generated PDF renders its glyphs cleanly: between two characters there is a
|
|
7
|
+
* column of paper - whatever the page puts behind the run, white or the colour
|
|
8
|
+
* of a total bar - and the only thing that varies is how wide. So the run's box
|
|
9
|
+
* is read as a column profile - how much ink stands in each column - and the
|
|
10
|
+
* groups of inked columns are the characters, in order. Nothing here needs to
|
|
11
|
+
* know the font, its advance widths, or its kerning, all of which a text layer
|
|
12
|
+
* leaves out and every face does differently.
|
|
13
|
+
*
|
|
14
|
+
* When the groups do not come to the number of characters expected - glyphs
|
|
15
|
+
* that touch, a comma that merges with the digit beside it - the run is left
|
|
16
|
+
* alone rather than guessed at: the caller can only verify what it can place.
|
|
17
|
+
* That is all-or-nothing over a whole run, which costs more the longer the run
|
|
18
|
+
* is: one pair of touching letters in a 52-character sentence loses the other
|
|
19
|
+
* fifty. `glyphWords` splits a run at its spaces first - the widest gaps in the
|
|
20
|
+
* same profile - so a sentence is verified word by word and only the word that
|
|
21
|
+
* will not segment is given up.
|
|
22
|
+
*
|
|
23
|
+
* **Direction.** A run's characters advance along the run, which is not always
|
|
24
|
+
* left to right: a form's margin instruction is often printed at a right angle
|
|
25
|
+
* to the page. The profile is taken along whichever axis the run's `angle`
|
|
26
|
+
* says, and quarter turns are the only ones handled - anything between would
|
|
27
|
+
* need the crop resampled, and is left unverifiable instead of guessed at.
|
|
28
|
+
*/
|
|
29
|
+
/** A rectangle in PDF points from the page's top-left corner. */
|
|
30
|
+
export interface Box {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
/** Degrees the run is turned by, if it is; only quarter turns are handled. */
|
|
36
|
+
angle?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface CellOptions {
|
|
39
|
+
/** The least a pixel may stand out from the run's own paper and count as print. Default `0.19`. */
|
|
40
|
+
contrast?: number;
|
|
41
|
+
/** Empty columns that still join two groups into one character. Default `0`. */
|
|
42
|
+
join?: number;
|
|
43
|
+
/** The run is printed light on a dark bar, so its paper is the dark part. */
|
|
44
|
+
lightOnDark?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The box of each printed character of a run, left to right.
|
|
48
|
+
*
|
|
49
|
+
* @param page - The original page, greyscale, `0` black to `1` white.
|
|
50
|
+
* @param dpi - What that page was rendered at.
|
|
51
|
+
* @param run - The run's box, in points.
|
|
52
|
+
* @param count - How many characters the run prints, spaces excluded.
|
|
53
|
+
* @param options - Contrast against the run's paper, column joining, and polarity.
|
|
54
|
+
* @returns One box per character, or `null` when they cannot be told apart.
|
|
55
|
+
*/
|
|
56
|
+
export declare function glyphCells(page: GrayImage, dpi: number, run: Box, count: number, options?: CellOptions): Box[] | null;
|
|
57
|
+
/**
|
|
58
|
+
* The box of each word of a run, in order, split at the run's own spaces.
|
|
59
|
+
*
|
|
60
|
+
* @param page - The original page, greyscale.
|
|
61
|
+
* @param dpi - What that page was rendered at.
|
|
62
|
+
* @param run - The run's box, in points, with its angle.
|
|
63
|
+
* @param counts - Characters in each word, in order, spaces excluded.
|
|
64
|
+
* @param options - Contrast against the run's paper, column joining, and polarity.
|
|
65
|
+
* @returns One box per word, or `null` when the words cannot be told apart.
|
|
66
|
+
*/
|
|
67
|
+
export declare function glyphWords(page: GrayImage, dpi: number, run: Box, counts: readonly number[], options?: CellOptions): Box[] | null;
|
|
68
|
+
/**
|
|
69
|
+
* Where every character of a run sits, or `null` where it could not be placed.
|
|
70
|
+
*
|
|
71
|
+
* The whole run is tried first, which is what a figure wants: short, its glyphs
|
|
72
|
+
* separate, and nothing gained by taking it apart. When that fails the run is
|
|
73
|
+
* split at its spaces and each word placed on its own, so one pair of touching
|
|
74
|
+
* letters costs its own word rather than the sentence around it - on the W-9's
|
|
75
|
+
* certification line, two characters rather than fifty-two.
|
|
76
|
+
*
|
|
77
|
+
* @param page - The original page, greyscale.
|
|
78
|
+
* @param dpi - What that page was rendered at.
|
|
79
|
+
* @param run - The run's box, in points, with its angle.
|
|
80
|
+
* @param text - What the run prints; spaces divide the words.
|
|
81
|
+
* @param options - Contrast against the run's paper, column joining, and polarity.
|
|
82
|
+
* @returns One entry per printed character, spaces excluded, or `null` when not
|
|
83
|
+
* even the words could be told apart.
|
|
84
|
+
*/
|
|
85
|
+
export declare function placeGlyphs(page: GrayImage, dpi: number, run: Box, text: string, options?: CellOptions): Array<Box | null> | null;
|
|
86
|
+
//# sourceMappingURL=glyph-cells.use-case.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { GrayImage } from '@scanmate/ink';
|
|
2
|
+
import type { Box } from './glyph-cells.use-case.js';
|
|
3
|
+
/**
|
|
4
|
+
* What each character looks like in print, taken from the original itself.
|
|
5
|
+
*
|
|
6
|
+
* The original is the only place a rival glyph can honestly come from: it is
|
|
7
|
+
* the document's own face, at the document's own size, rendered by whatever
|
|
8
|
+
* rendered the page. Every run whose characters can be told apart contributes
|
|
9
|
+
* its glyphs, filed by face and size, and a run is then verified against the
|
|
10
|
+
* ones that share its face and size. A run printed light on a dark bar is
|
|
11
|
+
* turned dark on light first, so a figure on a shaded total line is matched
|
|
12
|
+
* against the same digits as one on paper. Nothing is rendered, no font is embedded,
|
|
13
|
+
* and a face the page never prints has no templates - so nothing is claimed
|
|
14
|
+
* about it.
|
|
15
|
+
*/
|
|
16
|
+
/** A run of the original's text layer, where it sits and how it is set. */
|
|
17
|
+
export interface PrintedRun extends Box {
|
|
18
|
+
text: string;
|
|
19
|
+
fontName?: string;
|
|
20
|
+
fontSize?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Glyph images by face, size and character. */
|
|
23
|
+
export type Templates = ReadonlyMap<string, GrayImage[]>;
|
|
24
|
+
/** The key a run's glyphs are filed under. */
|
|
25
|
+
export declare function templateKey(run: PrintedRun, character: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Collects a glyph image for every character the page prints and can place.
|
|
28
|
+
*
|
|
29
|
+
* @param page - The original page, greyscale.
|
|
30
|
+
* @param dpi - What it was rendered at.
|
|
31
|
+
* @param runs - Its text layer.
|
|
32
|
+
* @returns Glyph images, by {@link templateKey}.
|
|
33
|
+
*/
|
|
34
|
+
export declare function collectTemplates(page: GrayImage, dpi: number, runs: readonly PrintedRun[]): Templates;
|
|
35
|
+
/**
|
|
36
|
+
* The greyscale of one box of the page, or `null` when it lies outside it;
|
|
37
|
+
* inverted when the run it belongs to is printed light on dark.
|
|
38
|
+
*/
|
|
39
|
+
export declare function cut(page: GrayImage, dpi: number, box: Box, invert?: boolean): GrayImage | null;
|
|
40
|
+
//# sourceMappingURL=glyph-templates.use-case.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { glyphCells, glyphWords, placeGlyphs } from './glyph-cells.use-case.js';
|
|
2
|
+
export type { Box, CellOptions } from './glyph-cells.use-case.js';
|
|
3
|
+
export { collectTemplates, templateKey } from './glyph-templates.use-case.js';
|
|
4
|
+
export type { PrintedRun, Templates } from './glyph-templates.use-case.js';
|
|
5
|
+
export { mergeVerifiedFigures } from './merge-reading.mapper.js';
|
|
6
|
+
export { printPolarity } from './print-polarity.policy.js';
|
|
7
|
+
export type { PrintPolarity } from './print-polarity.policy.js';
|
|
8
|
+
export { FIGURE_CHARACTERS, TEXT_CHARACTERS, verifyPrintedRun } from './verify-print.use-case.js';
|
|
9
|
+
export type { PrintVerification, VerifyOptions } from './verify-print.use-case.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PrintedRun } from './glyph-templates.use-case.js';
|
|
2
|
+
import type { PrintVerification } from './verify-print.use-case.js';
|
|
3
|
+
/**
|
|
4
|
+
* Puts what the ink settled into what the scan was read as.
|
|
5
|
+
*
|
|
6
|
+
* The check speaks for the figures it decided, and for nothing else. It looks at
|
|
7
|
+
* digits, cell by cell, and leaves a cell it cannot call. So its answer is merged
|
|
8
|
+
* into the reading one character at a time rather than replacing it: replacing it
|
|
9
|
+
* would erase a changed letter, or a digit too soft to judge, along with the
|
|
10
|
+
* misreadings this is here to clear - and erasing a real change is the one thing
|
|
11
|
+
* the check must never do.
|
|
12
|
+
*
|
|
13
|
+
* @param read - What the scan was read as; empty when nothing was read there.
|
|
14
|
+
* @param run - The run as the original prints it.
|
|
15
|
+
* @param verification - What `verifyPrintedRun` decided about it.
|
|
16
|
+
* @returns The reading with the decided figures settled.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mergeVerifiedFigures(read: string, run: PrintedRun, verification: PrintVerification): string;
|
|
19
|
+
//# sourceMappingURL=merge-reading.mapper.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GrayImage } from '@scanmate/ink';
|
|
2
|
+
import type { Box } from './glyph-cells.use-case.js';
|
|
3
|
+
/** Which way the original prints a run: dark text on light, or light text on a dark bar. */
|
|
4
|
+
export type PrintPolarity = 'dark-on-light' | 'light-on-dark';
|
|
5
|
+
/**
|
|
6
|
+
* Which way the original prints a run, read off its own crisp rendering: the
|
|
7
|
+
* glyphs are the pixels far from the background, and the background is most of
|
|
8
|
+
* the box.
|
|
9
|
+
*/
|
|
10
|
+
export declare function printPolarity(page: GrayImage, dpi: number, run: Box): PrintPolarity;
|
|
11
|
+
//# sourceMappingURL=print-polarity.policy.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { GrayImage } from '@scanmate/ink';
|
|
2
|
+
import type { PrintedRun, Templates } from './glyph-templates.use-case.js';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a printed figure is still the figure that was printed, decided by
|
|
5
|
+
* matching its ink rather than by reading it.
|
|
6
|
+
*
|
|
7
|
+
* OCR asks an open question - what does this say? - and at the resolution of a
|
|
8
|
+
* returned scan it answers badly: a 0 on a shaded bar at 120 dpi comes back as
|
|
9
|
+
* a 9, and a digit written over another comes back as whatever the language
|
|
10
|
+
* model prefers. The closed question is far easier: is this still the glyph the
|
|
11
|
+
* original printed here, or does it look more like a different one?
|
|
12
|
+
*
|
|
13
|
+
* The print is crisp and the scan is not, so the print is first softened to the
|
|
14
|
+
* scan's own sharpness - the amount that makes the scan's glyphs sit best on
|
|
15
|
+
* the original's, measured on the run itself - and every glyph is compared at
|
|
16
|
+
* that sharpness. Without it a blurred 0 matches a crisp 8 as well as it
|
|
17
|
+
* matches a crisp 0.
|
|
18
|
+
*
|
|
19
|
+
* Each character of a figure is matched twice over. Once against the original's
|
|
20
|
+
* own ink at that very place - same face, same size, same position, so a scan
|
|
21
|
+
* of it correlates highly however grey or grainy it is - and once against every
|
|
22
|
+
* other character the page prints in that face and size. The printed glyph has
|
|
23
|
+
* to win by a margin to pass, and a rival has to win by a margin to count as a
|
|
24
|
+
* change; anything in between is left undecided rather than guessed at.
|
|
25
|
+
*
|
|
26
|
+
* A run whose glyphs cannot be told apart is left to the reading, and so is a
|
|
27
|
+
* run on a page that prints too few characters in that face for the rivals to
|
|
28
|
+
* be represented - a glyph whose own template is missing could otherwise be
|
|
29
|
+
* confirmed as the one it replaced, simply for lacking anything better to
|
|
30
|
+
* match.
|
|
31
|
+
*
|
|
32
|
+
* **Two scopes.** `'figures'` checks the digits of a figure against the ten
|
|
33
|
+
* digits, and nothing else: a full stop read as a comma is how a figure is
|
|
34
|
+
* written, not what it says. It is cheap enough to run over every printed run
|
|
35
|
+
* of a page, which is how a changed amount is caught even when the reading
|
|
36
|
+
* never noticed.
|
|
37
|
+
*
|
|
38
|
+
* `'text'` checks every character against letters and digits alike. The rival
|
|
39
|
+
* set is six times larger and the work grows with it, so this is not for a
|
|
40
|
+
* whole page - it is for one run somebody is already arguing about, where the
|
|
41
|
+
* reading disagrees and the ink at that run says nothing moved. There it
|
|
42
|
+
* answers the question the reading could not: are these the same glyphs?
|
|
43
|
+
*
|
|
44
|
+
* Its two answers are not worth the same. Swept over 327 runs of four real
|
|
45
|
+
* documents, `'text'` called four unchanged runs changed - a `t` read as a `k`,
|
|
46
|
+
* a `g` as a `t` - where `'figures'` has never made a false call on any scan
|
|
47
|
+
* measured here. Letters at 8 pt through a scanner are simply more confusable
|
|
48
|
+
* than digits, and the margin that separates the ten does not separate the
|
|
49
|
+
* sixty-two. So `agrees === true` is good evidence that a run is untouched, and
|
|
50
|
+
* `agrees === false` is a reason to look closer rather than a verdict; callers
|
|
51
|
+
* are expected to use it to clear a dispute, not to open one.
|
|
52
|
+
*/
|
|
53
|
+
/** What a figure's characters are checked against: the ten digits, and only those. */
|
|
54
|
+
export declare const FIGURE_CHARACTERS = "0123456789";
|
|
55
|
+
/**
|
|
56
|
+
* What a run of text is checked against, under `scope: 'text'`: letters and
|
|
57
|
+
* digits. Punctuation is left out deliberately - a comma and a full stop differ
|
|
58
|
+
* by a few pixels at these sizes, and telling them apart is neither reliable
|
|
59
|
+
* nor worth reporting.
|
|
60
|
+
*/
|
|
61
|
+
export declare const TEXT_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
62
|
+
export interface VerifyOptions {
|
|
63
|
+
/** How far the winner must correlate ahead of the loser, `0` to `1`. Default `0.12`. */
|
|
64
|
+
minMargin?: number;
|
|
65
|
+
/** How well a rival must match before a character counts as changed. Default `0.5`. */
|
|
66
|
+
minScore?: number;
|
|
67
|
+
/** How well the printed glyph must match before the ink counts as unchanged. Default `0.7`. */
|
|
68
|
+
minPrinted?: number;
|
|
69
|
+
/** Characters the page must print in this face and size for the run to be checked. Default `8`, or `24` for text. */
|
|
70
|
+
minRivals?: number;
|
|
71
|
+
/**
|
|
72
|
+
* What to check: the digits of a figure (`'figures'`, the default, cheap
|
|
73
|
+
* enough for a whole page), or every letter and digit of the run (`'text'`,
|
|
74
|
+
* for a single run under dispute).
|
|
75
|
+
*/
|
|
76
|
+
scope?: 'figures' | 'text';
|
|
77
|
+
/** How badly the printed glyph must match for a character to count as changed. Default `0.85`. */
|
|
78
|
+
maxPrinted?: number;
|
|
79
|
+
/** Digits in a row before a group is treated as a figure. Default `2`. */
|
|
80
|
+
minDigits?: number;
|
|
81
|
+
/** The characters a figure's glyphs are matched against. Default {@link FIGURE_CHARACTERS}. */
|
|
82
|
+
characters?: string;
|
|
83
|
+
}
|
|
84
|
+
/** One character of a figure, and how its ink matched. */
|
|
85
|
+
export interface CellVerification {
|
|
86
|
+
/** Position in the run's text. */
|
|
87
|
+
at: number;
|
|
88
|
+
/** What the original prints there. */
|
|
89
|
+
printed: string;
|
|
90
|
+
/** How well the scan's ink matches the original's own ink there. */
|
|
91
|
+
printedScore: number;
|
|
92
|
+
/** The best any other character managed. */
|
|
93
|
+
rival: string;
|
|
94
|
+
rivalScore: number;
|
|
95
|
+
/** What this cell was taken to be, or `null` when it was too close to call. */
|
|
96
|
+
read: string | null;
|
|
97
|
+
}
|
|
98
|
+
export interface PrintVerification {
|
|
99
|
+
/** The run as its ink reads, printed characters kept where nothing was checked. */
|
|
100
|
+
reading: string;
|
|
101
|
+
/** The ink says what the original printed. */
|
|
102
|
+
agrees: boolean;
|
|
103
|
+
/** Cells decided, one way or the other. */
|
|
104
|
+
checked: number;
|
|
105
|
+
/** The smallest margin any decided cell was decided by. */
|
|
106
|
+
confidence: number;
|
|
107
|
+
/** Every character checked, with the scores behind the verdict. */
|
|
108
|
+
cells: CellVerification[];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Checks the figures of one run against the original's print.
|
|
112
|
+
*
|
|
113
|
+
* @param original - The original page, greyscale.
|
|
114
|
+
* @param scan - The scan, aligned onto the original's canvas, greyscale.
|
|
115
|
+
* @param dpi - What the original was rendered at.
|
|
116
|
+
* @param run - The run to check, from the original's text layer.
|
|
117
|
+
* @param templates - Glyphs collected from the original by `collectTemplates`.
|
|
118
|
+
* @param options - Margin, figure length and the characters figures use.
|
|
119
|
+
* @returns What the ink says, or `null` when the run could not be checked.
|
|
120
|
+
*/
|
|
121
|
+
export declare function verifyPrintedRun(original: GrayImage, scan: GrayImage, dpi: number, run: PrintedRun, templates: Templates, options?: VerifyOptions): PrintVerification | null;
|
|
122
|
+
//# sourceMappingURL=verify-print.use-case.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scanmate/ocr",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "How closely a scan's text matches the original's: OCR matched run by run on the page, figures held exact, every difference located.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eduardo Russo",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"!**/*.js.map"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@scanmate/ink": "^0.0
|
|
44
|
+
"@scanmate/ink": "^0.1.0",
|
|
45
45
|
"@tesseract.js-data/eng": "^1.0.0",
|
|
46
46
|
"fastest-levenshtein": "^1.0.16",
|
|
47
47
|
"tesseract.js": "^7.0.0"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@scanmate/align": "^0.0
|
|
54
|
-
"@scanmate/extract": "^0.0
|
|
53
|
+
"@scanmate/align": "^0.1.0",
|
|
54
|
+
"@scanmate/extract": "^0.1.0"
|
|
55
55
|
}
|
|
56
56
|
}
|