@scanmate/ocr 0.2.0 → 0.3.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
@@ -727,8 +727,20 @@ function templateKey(run, character) {
727
727
  * @param runs - Its text layer.
728
728
  * @returns Glyph images, by {@link templateKey}.
729
729
  */
730
- function collectTemplates(page, dpi, runs) {
731
- const templates = new Map();
730
+ /**
731
+ * Folds one page's glyphs into a store that already holds others.
732
+ *
733
+ * Templates are filed by face, size and turn, and a PDF renders those
734
+ * identically wherever they appear - so a glyph from page 4 is as good a
735
+ * template as one from page 1, and on many documents it is the only one there
736
+ * is. Measured on a real order confirmation: the face its page-1 total is set
737
+ * in carries six distinct digits on that page and nine across the document, and
738
+ * six is below the bar for checking anything at all.
739
+ *
740
+ * The per-character cap applies to the store as a whole, so a long document
741
+ * costs no more memory than a short one.
742
+ */
743
+ function collectInto(templates, page, dpi, runs) {
732
744
  for (const run of runs) {
733
745
  const characters = [...run.text].filter(character => character.trim() !== '');
734
746
  const lightOnDark = printPolarity(page, dpi, run) === 'light-on-dark';
@@ -743,6 +755,9 @@ function collectTemplates(page, dpi, runs) {
743
755
  }
744
756
  return templates;
745
757
  }
758
+ function collectTemplates(page, dpi, runs) {
759
+ return collectInto(new Map(), page, dpi, runs);
760
+ }
746
761
  /** Files one glyph under its key, up to the few that are worth keeping. */
747
762
  function keep(templates, key, glyph) {
748
763
  if (glyph === null) return;
@@ -1457,6 +1472,24 @@ function luminance(data, i) {
1457
1472
  async function ocrPages(pages, options = {}) {
1458
1473
  const engine = options.engine ?? (await createTesseractEngine(options.tesseract));
1459
1474
  try {
1475
+ // The glyphs of the whole document, gathered before any page is read.
1476
+ //
1477
+ // A page's own figures are often set in a face it uses for little else, and
1478
+ // a run cannot be checked against rivals that are not there - on a real
1479
+ // order confirmation the face of the page-1 total carries six distinct
1480
+ // digits on that page and nine across the document, and six is below the
1481
+ // bar for checking anything at all. The same face at the same size renders
1482
+ // identically on every page, so a glyph from page 4 is as good a template
1483
+ // as one from page 1.
1484
+ //
1485
+ // Each page's greyscale is dropped as soon as its glyphs are taken, so this
1486
+ // holds one page of pixels at a time, not the document.
1487
+ const templates = new Map();
1488
+ if (options.printCheck !== false) for (const page of pages) {
1489
+ const items = page.metadata?.original?.textItems;
1490
+ if (items === undefined || items === null || items.length === 0) continue;
1491
+ collectInto(templates, toGrayscale(page.original.raster), page.original.dpi ?? options.assumeDpi ?? 150, items);
1492
+ }
1460
1493
  const results = [];
1461
1494
  for (const [position, page] of pages.entries()) {
1462
1495
  const index = position + 1;
@@ -1468,7 +1501,7 @@ async function ocrPages(pages, options = {}) {
1468
1501
  index,
1469
1502
  total: pages.length
1470
1503
  });
1471
- const result = await readPage(page, engine, options);
1504
+ const result = await readPage(page, engine, options, templates);
1472
1505
  results.push({
1473
1506
  ...page,
1474
1507
  text: result
@@ -1506,7 +1539,7 @@ async function ocrPages(pages, options = {}) {
1506
1539
  if (options.engine === undefined) await engine.terminate();
1507
1540
  }
1508
1541
  }
1509
- async function readPage(page, engine, options) {
1542
+ async function readPage(page, engine, options, templates) {
1510
1543
  const {
1511
1544
  original: originalSource = 'auto',
1512
1545
  targetDpi = 300,
@@ -1600,7 +1633,6 @@ async function readPage(page, engine, options) {
1600
1633
  const printed = items.map(item => ({
1601
1634
  ...item
1602
1635
  }));
1603
- const templates = collectTemplates(originalGray, originalDpi, printed);
1604
1636
  for (const [r, run] of printed.entries()) {
1605
1637
  const verified = verifyPrintedRun(originalGray, scanGray, originalDpi, run, templates, printCheck);
1606
1638
  if (verified === null) continue;
@@ -1714,5 +1746,5 @@ function mean(values) {
1714
1746
  return values.length === 0 ? 0 : values.reduce((a, b) => a + b, 0) / values.length;
1715
1747
  }
1716
1748
 
1717
- export { DEFAULT_NORMALISE, DEFAULT_RECHECK_PASSES, DEFAULT_TESSERACT_OPTIONS, FIGURE_CHARACTERS$1 as FIGURE_CHARACTERS, TEXT_CHARACTERS, claimWords, collectTemplates, compareTexts, cosine, createTesseractEngine, diacriticsMap, dice, foldConfusables, foldDiacritics, glyphCells, glyphWords, jaccard, jaroWinkler, judgeRun, judgeRuns, levenshtein, levenshteinSimilarity, matchWords, normaliseText, ocrPages, placeGlyphs, printPolarity, readRun, recheckRun, templateKey, tokenise, verifyPrintedRun, wordDistance, wordRecall };
1749
+ export { DEFAULT_NORMALISE, DEFAULT_RECHECK_PASSES, DEFAULT_TESSERACT_OPTIONS, FIGURE_CHARACTERS$1 as FIGURE_CHARACTERS, TEXT_CHARACTERS, claimWords, collectInto, collectTemplates, compareTexts, cosine, createTesseractEngine, diacriticsMap, dice, foldConfusables, foldDiacritics, glyphCells, glyphWords, jaccard, jaroWinkler, judgeRun, judgeRuns, levenshtein, levenshteinSimilarity, matchWords, normaliseText, ocrPages, placeGlyphs, printPolarity, readRun, recheckRun, templateKey, tokenise, verifyPrintedRun, wordDistance, wordRecall };
1718
1750
  //# sourceMappingURL=index.esm.js.map
@@ -30,8 +30,8 @@ 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 { collectTemplates, FIGURE_CHARACTERS, glyphCells, glyphWords, placeGlyphs, printPolarity, templateKey, TEXT_CHARACTERS, verifyPrintedRun } from './print-verification/index.js';
34
- export type { CellOptions, PrintPolarity, PrintVerification, Templates, VerifyOptions } from './print-verification/index.js';
33
+ export { collectInto, collectTemplates, FIGURE_CHARACTERS, glyphCells, glyphWords, placeGlyphs, printPolarity, templateKey, TEXT_CHARACTERS, verifyPrintedRun } from './print-verification/index.js';
34
+ export type { CellOptions, PrintPolarity, PrintVerification, TemplateStore, Templates, VerifyOptions } from './print-verification/index.js';
35
35
  export { claimWords, DEFAULT_RECHECK_PASSES, judgeRun, judgeRuns, matchWords, readRun, recheckRun } from './page-reading/index.js';
36
36
  export type { Claims, MatchOptions, Recheck, RecheckOptions, RecheckPass, Reference, Verdict, WordMatch } from './page-reading/index.js';
37
37
  export { cosine, dice, jaccard, jaroWinkler, levenshtein, levenshteinSimilarity, wordDistance, wordRecall } from './text-similarity/index.js';
@@ -15,6 +15,8 @@ import type { GrayImage, ScanmateOrientedRect, TextRun } from '@scanmate/ink';
15
15
  /** A run of the original's text layer, where it sits and how it is set. */
16
16
  /** Glyph images by face, size and character. */
17
17
  export type Templates = ReadonlyMap<string, GrayImage[]>;
18
+ /** A store still being filled. Readers take {@link Templates}; only the collector writes. */
19
+ export type TemplateStore = Map<string, GrayImage[]>;
18
20
  /** The key a run's glyphs are filed under. */
19
21
  export declare function templateKey(run: TextRun, character: string): string;
20
22
  /**
@@ -25,6 +27,20 @@ export declare function templateKey(run: TextRun, character: string): string;
25
27
  * @param runs - Its text layer.
26
28
  * @returns Glyph images, by {@link templateKey}.
27
29
  */
30
+ /**
31
+ * Folds one page's glyphs into a store that already holds others.
32
+ *
33
+ * Templates are filed by face, size and turn, and a PDF renders those
34
+ * identically wherever they appear - so a glyph from page 4 is as good a
35
+ * template as one from page 1, and on many documents it is the only one there
36
+ * is. Measured on a real order confirmation: the face its page-1 total is set
37
+ * in carries six distinct digits on that page and nine across the document, and
38
+ * six is below the bar for checking anything at all.
39
+ *
40
+ * The per-character cap applies to the store as a whole, so a long document
41
+ * costs no more memory than a short one.
42
+ */
43
+ export declare function collectInto(templates: TemplateStore, page: GrayImage, dpi: number, runs: readonly TextRun[]): Templates;
28
44
  export declare function collectTemplates(page: GrayImage, dpi: number, runs: readonly TextRun[]): Templates;
29
45
  /**
30
46
  * The greyscale of one box of the page, or `null` when it lies outside it;
@@ -1,7 +1,7 @@
1
1
  export { glyphCells, glyphWords, placeGlyphs } from './glyph-cells.use-case.js';
2
2
  export type { CellOptions } from './glyph-cells.use-case.js';
3
- export { collectTemplates, templateKey } from './glyph-templates.use-case.js';
4
- export type { Templates } from './glyph-templates.use-case.js';
3
+ export { collectInto, collectTemplates, templateKey } from './glyph-templates.use-case.js';
4
+ export type { TemplateStore, Templates } from './glyph-templates.use-case.js';
5
5
  export { mergeVerifiedFigures } from './merge-reading.mapper.js';
6
6
  export { printPolarity } from './print-polarity.policy.js';
7
7
  export type { PrintPolarity } from './print-polarity.policy.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scanmate/ocr",
3
- "version": "0.2.0",
3
+ "version": "0.3.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.2.0",
44
+ "@scanmate/ink": "^0.3.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.2.0",
54
- "@scanmate/extract": "^0.2.0"
53
+ "@scanmate/align": "^0.3.0",
54
+ "@scanmate/extract": "^0.3.0"
55
55
  }
56
56
  }