@scanmate/ocr 0.5.0 → 0.6.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/dist/index.esm.js
CHANGED
|
@@ -975,13 +975,21 @@ function verifyPrintedRun(original, scan, dpi, run, templates, options = {}) {
|
|
|
975
975
|
minPrinted = MIN_PRINTED_SCORE,
|
|
976
976
|
minRivals = scope === 'text' ? MIN_TEXT_RIVALS : MIN_RIVALS,
|
|
977
977
|
minDigits = 2,
|
|
978
|
-
characters = scope === '
|
|
978
|
+
characters = scope === 'figures' ? FIGURE_CHARACTERS$1 : TEXT_CHARACTERS,
|
|
979
|
+
claimed
|
|
979
980
|
} = options;
|
|
980
981
|
const printed = [...run.text].filter(character => character.trim() !== '');
|
|
981
|
-
|
|
982
|
+
// What the reading says it is, lined up with what the original printed. The
|
|
983
|
+
// two must correspond character for character or the comparison is meaningless.
|
|
984
|
+
const proposed = scope === 'confirm' ? [...(claimed ?? '')].filter(character => character.trim() !== '') : null;
|
|
985
|
+
if (proposed !== null && (proposed.length === 0 || proposed.length !== printed.length)) return {
|
|
986
|
+
verified: false,
|
|
987
|
+
because: 'no-claim'
|
|
988
|
+
};
|
|
989
|
+
const wanted = proposed === null ? scope === 'text' ? textCells(printed, characters) : figureCells(printed, minDigits) : disputedCells(printed, proposed, characters);
|
|
982
990
|
if (wanted.size === 0) return {
|
|
983
991
|
verified: false,
|
|
984
|
-
because: 'no-figure'
|
|
992
|
+
because: proposed === null ? 'no-figure' : 'no-claim'
|
|
985
993
|
};
|
|
986
994
|
// A total on a shaded bar is printed light on dark; turned round, it is a figure like any other.
|
|
987
995
|
const lightOnDark = printPolarity(original, dpi, run) === 'light-on-dark';
|
|
@@ -992,9 +1000,25 @@ function verifyPrintedRun(original, scan, dpi, run, templates, options = {}) {
|
|
|
992
1000
|
verified: false,
|
|
993
1001
|
because: 'unplaceable'
|
|
994
1002
|
};
|
|
995
|
-
|
|
1003
|
+
/**
|
|
1004
|
+
* Who each cell is up against.
|
|
1005
|
+
*
|
|
1006
|
+
* Identifying an unknown glyph needs most of the alphabet: to assert "this is
|
|
1007
|
+
* a 7" you must be able to rule the others out, and the bar of eight digits
|
|
1008
|
+
* is what the false-call rate justified. A settlement is not asking that. The
|
|
1009
|
+
* reading has already named its answer, so the question is which of two named
|
|
1010
|
+
* characters this ink is - and two templates answer it. A wrong verdict then
|
|
1011
|
+
* requires the ink to match the *other specific character* better, not merely
|
|
1012
|
+
* to be hard to read.
|
|
1013
|
+
*/
|
|
996
1014
|
const rivals = [...characters].filter(character => templates.has(templateKey(run, character)));
|
|
997
|
-
|
|
1015
|
+
const rivalsAt = index => {
|
|
1016
|
+
if (proposed === null) return rivals;
|
|
1017
|
+
const against = proposed[index];
|
|
1018
|
+
return against !== undefined && templates.has(templateKey(run, against)) ? [against] : [];
|
|
1019
|
+
};
|
|
1020
|
+
const enough = proposed === null ? rivals.length >= minRivals : [...wanted].some(index => rivalsAt(index).length > 0);
|
|
1021
|
+
if (!enough) return {
|
|
998
1022
|
verified: false,
|
|
999
1023
|
because: 'few-rivals'
|
|
1000
1024
|
};
|
|
@@ -1042,7 +1066,7 @@ function verifyPrintedRun(original, scan, dpi, run, templates, options = {}) {
|
|
|
1042
1066
|
let confidence = 1;
|
|
1043
1067
|
let checked = 0;
|
|
1044
1068
|
for (const [index, crop] of crops) {
|
|
1045
|
-
const passes = recoveries.map(recovery => judgeCell(crop, printed[index],
|
|
1069
|
+
const passes = recoveries.map(recovery => judgeCell(crop, printed[index], rivalsAt(index), run, templates, recovery, limits));
|
|
1046
1070
|
const first = passes[0];
|
|
1047
1071
|
if (first === undefined) continue;
|
|
1048
1072
|
// Undecided unless every pass reads it the same way: a verdict that changes
|
|
@@ -1116,6 +1140,15 @@ function textCells(printed, characters) {
|
|
|
1116
1140
|
for (const [index, character] of printed.entries()) if (characters.includes(character)) wanted.add(index);
|
|
1117
1141
|
return wanted;
|
|
1118
1142
|
}
|
|
1143
|
+
/** Where the print and the reading disagree, which is the whole of what a settlement asks about. */
|
|
1144
|
+
function disputedCells(printed, proposed, characters) {
|
|
1145
|
+
const wanted = new Set();
|
|
1146
|
+
for (const [index, character] of printed.entries()) {
|
|
1147
|
+
const against = proposed[index];
|
|
1148
|
+
if (against !== undefined && against !== character && characters.includes(character) && characters.includes(against)) wanted.add(index);
|
|
1149
|
+
}
|
|
1150
|
+
return wanted;
|
|
1151
|
+
}
|
|
1119
1152
|
function figureCells(printed, minDigits) {
|
|
1120
1153
|
const wanted = new Set();
|
|
1121
1154
|
let group = [];
|
|
@@ -1740,7 +1773,8 @@ async function readPage(page, engine, options, templates) {
|
|
|
1740
1773
|
'unplaceable': 0,
|
|
1741
1774
|
'few-rivals': 0,
|
|
1742
1775
|
'too-coarse': 0,
|
|
1743
|
-
'undecided': 0
|
|
1776
|
+
'undecided': 0,
|
|
1777
|
+
'no-claim': 0
|
|
1744
1778
|
}
|
|
1745
1779
|
};
|
|
1746
1780
|
const seenChanged = new Set();
|
|
@@ -70,10 +70,22 @@ export interface VerifyOptions {
|
|
|
70
70
|
minRivals?: number;
|
|
71
71
|
/**
|
|
72
72
|
* What to check: the digits of a figure (`'figures'`, the default, cheap
|
|
73
|
-
* enough for a whole page),
|
|
74
|
-
*
|
|
73
|
+
* enough for a whole page), every letter and digit of the run (`'text'`, for
|
|
74
|
+
* a single run under dispute), or the one question a settlement actually
|
|
75
|
+
* asks (`'confirm'`).
|
|
76
|
+
*
|
|
77
|
+
* `'confirm'` needs {@link claimed} and answers "is this ink the character
|
|
78
|
+
* the original printed, or the one the reading says it is?" - which takes
|
|
79
|
+
* two templates rather than a near-complete alphabet, because the reading
|
|
80
|
+
* has already named the alternative. See {@link VerifyOptions.claimed}.
|
|
75
81
|
*/
|
|
76
|
-
scope?: 'figures' | 'text';
|
|
82
|
+
scope?: 'figures' | 'text' | 'confirm';
|
|
83
|
+
/**
|
|
84
|
+
* What the reading claims the run says, for `scope: 'confirm'`. Compared
|
|
85
|
+
* character by character against the original's own text, so it has to hold
|
|
86
|
+
* the same number of printed characters; anything else abstains.
|
|
87
|
+
*/
|
|
88
|
+
claimed?: string;
|
|
77
89
|
/** How badly the printed glyph must match for a character to count as changed. Default `0.85`. */
|
|
78
90
|
maxPrinted?: number;
|
|
79
91
|
/** Digits in a row before a group is treated as a figure. Default `2`. */
|
|
@@ -109,8 +121,10 @@ export interface CellVerification {
|
|
|
109
121
|
* - `too-coarse` - the scan resolves the cell below the height it is matched
|
|
110
122
|
* at, so a verdict would rest on detail it never captured.
|
|
111
123
|
* - `undecided` - measured, and nothing won by enough to say.
|
|
124
|
+
* - `no-claim` - `scope: 'confirm'` without a reading to test, or one that
|
|
125
|
+
* does not line up with the print character for character.
|
|
112
126
|
*/
|
|
113
|
-
export type PrintAbstention = 'no-figure' | 'unplaceable' | 'few-rivals' | 'too-coarse' | 'undecided';
|
|
127
|
+
export type PrintAbstention = 'no-figure' | 'unplaceable' | 'few-rivals' | 'too-coarse' | 'undecided' | 'no-claim';
|
|
114
128
|
/** What the ink said, or why it would not say. */
|
|
115
129
|
export type PrintCheck = ({
|
|
116
130
|
verified: true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scanmate/ocr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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.
|
|
44
|
+
"@scanmate/ink": "^0.6.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.
|
|
54
|
-
"@scanmate/extract": "^0.
|
|
53
|
+
"@scanmate/align": "^0.6.0",
|
|
54
|
+
"@scanmate/extract": "^0.6.0"
|
|
55
55
|
}
|
|
56
56
|
}
|