Download src/ocr.js from NagaYu/dasumae: direct link, hf CLI and curl.
- Browser
- Download file 2.54 kB
-
https://huggingface.co/spaces/NagaYu/dasumae/resolve/main/src/ocr.js
- Command line
-
hf download hf://spaces/NagaYu/dasumae/src/ocr.js
-
curl -L -o ocr.js https://huggingface.co/spaces/NagaYu/dasumae/resolve/main/src/ocr.js
2.54 kB
| // OCR via Tesseract.js, self-hosted, using the EXACT Phase 0 configuration: | |
| // languages 'jpn+eng', OEM 1 (LSTM_ONLY), PSM 3 (AUTO). | |
| // Tesseract.js runs the wasm in its own Web Worker, so the heavy OCR work stays | |
| // off the main thread and the UI never freezes. All paths point at the bundled | |
| // vendor/ copies — no third-party request is made. | |
| // The vendored ESM build exposes a single default export (the Tesseract object); | |
| // createWorker is a property of it. | |
| import Tesseract from '../vendor/tesseract/tesseract.esm.min.js'; | |
| const { createWorker } = Tesseract; | |
| const WORKER_PATH = new URL('../vendor/tesseract/worker.min.js', import.meta.url).href; | |
| const CORE_PATH = new URL('../vendor/tesseract/tesseract-core-simd-lstm.js', import.meta.url).href; | |
| const LANG_PATH = new URL('../vendor/tessdata/', import.meta.url).href; | |
| // Extract word-level boxes exactly as the Phase 0 spike did, so detect.js sees | |
| // the same shape of input it was measured against. | |
| function wordsFromData(data) { | |
| const words = []; | |
| for (const b of data.blocks || []) { | |
| for (const p of b.paragraphs || []) { | |
| for (const l of p.lines || []) { | |
| for (const w of l.words || []) { | |
| if (!w.text || !w.text.trim()) continue; | |
| const { x0, y0, x1, y1 } = w.bbox; | |
| words.push({ text: w.text, confidence: w.confidence, bbox: { x: x0, y: y0, w: x1 - x0, h: y1 - y0 } }); | |
| } | |
| } | |
| } | |
| } | |
| return words; | |
| } | |
| let workerPromise = null; | |
| export function initOcr(onProgress) { | |
| if (workerPromise) return workerPromise; | |
| workerPromise = (async () => { | |
| const worker = await createWorker('jpn+eng', 1, { | |
| workerPath: WORKER_PATH, | |
| corePath: CORE_PATH, | |
| langPath: LANG_PATH, | |
| // Run the worker from its real file URL (not a blob:) so the emscripten | |
| // core resolves its sibling .wasm relative to vendor/tesseract/. | |
| workerBlobURL: false, | |
| cacheMethod: 'none', // always load from the bundled vendor copy (offline-safe) | |
| gzip: true, | |
| logger: (m) => { if (onProgress) onProgress(m); }, | |
| }); | |
| await worker.setParameters({ tessedit_pageseg_mode: '3' }); // PSM 3 AUTO | |
| return worker; | |
| })(); | |
| return workerPromise; | |
| } | |
| // Run OCR on a drawable source (canvas at natural size) -> { words, ms }. | |
| export async function runOcr(source, onProgress) { | |
| const worker = await initOcr(onProgress); | |
| const t0 = performance.now(); | |
| const { data } = await worker.recognize(source, {}, { blocks: true }); | |
| return { words: wordsFromData(data), ms: performance.now() - t0 }; | |
| } | |