AI disclosure: Codex wrote this article and performed the test operations for a LensUp-related workflow. The source is an intentionally fictional printed note. The article does not claim that the human account owner personally ran the experiment.
A note can be ready to read before it is ready to publish. An OCR check on October 23, 2026 produced a small example: the browser interface showed 96% confidence, the downloaded text kept the meeting details, and the title still needed a structural decision.
This was a clean English PNG, with English explicitly selected. It was not a photograph, a handwritten page or a multilingual accuracy trial. The retained download contained this text:
WORKSHOP NOTE Fictional OCR sample
Start time: 09:30
Room: Cedar 12
Bring 3 notebooks.
Return 2 markers.
Keep this image for comparison.
The image had a title and subtitle on separate lines. The export made them the first paragraph together. Calling the whole result simply “good” or “bad” hides the useful information: the practical details survived, while one possible publishing template was not satisfied.
Decide what “ready” means for the destination
For a searchable personal note, that paragraph may be acceptable. For a collection whose navigation depends on Markdown headings, it may require editing. Those are different acceptance decisions, even though the underlying download is identical.
The chosen publishing profile for this experiment was modest: preserve the source text after whitespace normalization; keep four critical statements as separate paragraphs; make the title an H2; keep the subtitle as its own paragraph. The H2 is a template decision, not a universal definition of a successful OCR conversion.
The actual export passed the content and critical-paragraph checks. It failed the two title/subtitle rules. An authored comparison with the same words and an H2 passed all four. That comparison was created for testing; it is not another recognition result, a corrected download or an implemented product fix.
| Artifact | Known text | Critical paragraphs | H2 title | Separate subtitle |
|---|---|---|---|---|
| Retained OCR export | Pass | Pass | Fail | Fail |
| Authored layout example | Pass | Pass | Pass | Pass |
Authored example with 09:3O typo |
Fail | Fail | Pass | Pass |
The typo row is a constructed counterexample. It shows why a tidy document structure cannot approve the meeting time. It does not claim the OCR engine made that error.
Make the distinction visible in the parser
Marked's lexer gives a quick way to inspect the structural part. Install marked@17.0.5 in a fresh Node project, save the retained text above as actual-note.md, then save this probe as inspect-note.mjs:
import { readFile } from 'node:fs/promises';
import { marked } from 'marked';
const markdown = await readFile(process.argv[2], 'utf8');
const tokens = marked.lexer(markdown).filter((token) => token.type !== 'space');
console.log(JSON.stringify({
firstBlockType: tokens[0]?.type,
firstBlockText: tokens[0]?.text,
headingCount: tokens.filter((token) => token.type === 'heading').length,
paragraphCount: tokens.filter((token) => token.type === 'paragraph').length,
}, null, 2));
npm init -y
npm install --save-exact marked@17.0.5
node inspect-note.mjs actual-note.md
For the retained text, the result is:
{
"firstBlockType": "paragraph",
"firstBlockText": "WORKSHOP NOTE Fictional OCR sample",
"headingCount": 0,
"paragraphCount": 6
}
For comparison, make a separate file called authored-profile-example.md. Replace the combined first paragraph with the following, leaving the remaining text unchanged:
## WORKSHOP NOTE
Fictional OCR sample
Running the same probe reports a first block of type heading, one heading and six paragraphs. The probe is an inspection aid, not the complete acceptance validator: it does not compare source text or verify the time and quantities. A complete local check for this fixture inspected those separately and rejected unsupported constructs rather than silently accepting text found inside a code fence.
The important boundary is the parser's result. Uppercase letters or a visually bold first line do not by themselves establish the Markdown heading required by the template. A supported Setext H2 can satisfy the same structural requirement, so checking only for a literal ## prefix would also be too narrow.
Keep the confidence number in context
The 96% figure is the recorded UI badge. There is no labeled dataset here from which to derive an accuracy percentage, and no saved raw engine block tree from which to reproduce its aggregation. Current application code derives the displayed free-mode badge from the formatter's aggregate recognition confidence; that remains distinct from the downstream rules above.
Tesseract.js's versioned API types expose confidence, text and blocks as separate data. The final note template adds another layer. The observed merge alone cannot identify which recognition or formatting step caused it.
A reviewable OCR workflow therefore keeps the original image, the untouched export and a destination-specific checklist. When the content passes but structure does not, the next action may be a small, visible edit. When a critical time or quantity fails, the next action is comparison with the source. Recording those decisions separately makes the note easier to trust and the test result easier to explain.
Comments
Join the discussionNo comments yet. Be the first to comment.