nexround170 downloadsOCR images in the current note using on-device PaddleOCR via ONNX Runtime — no server, no API key. Recognized text is inserted inline below each image.
An Obsidian plugin that scans the active note for image references and inserts the recognized text directly below each image, using a fully on-device OCR engine (PaddleOCR v6 via ONNX Runtime). No server, no API key, no internet connection required after the one-time runtime setup.
TextLens: OCR Current Note processes every image in the active file![[image.png]]) and standard Markdown ()Cmd+Z entrymain.js, manifest.json, and styles.css from the latest release<your-vault>/.obsidian/plugins/text-lens/git clone https://github.com/Nexround/obsidian-text-lens.git
cd obsidian-text-lens
npm install
npm run build
Copy main.js, manifest.json, and styles.css into your vault's plugin directory.
Open any note that contains images, then open the command palette (Cmd/Ctrl+P) and choose:
TextLens: OCR Current Note
The plugin reads all image buffers in parallel, then sends them to the OCR model in a single batch call, and inserts the recognised text immediately below each image.
![[screenshot.png]]
> [!note]+ OCR: screenshot.png
> Hello World
> This is recognized text
![[screenshot.png]]
```ocr
Hello World
This is recognized text
```
| Setting | Default | Description |
|---|---|---|
| Setup | — | Download ONNX Runtime native binaries (~40 MB) for your platform |
| Model tier | Small | tiny / small / medium — trades speed for accuracy. Models are downloaded once and cached at ~/.cache/ppu-paddle-ocr/ |
| Unload / Load engine | — | Manually control the ~200 MB ONNX inference session in memory |
| Delete runtime files | — | Remove all downloaded native binaries (~40 MB) from the plugin directory |
| Clear model cache | — | Delete cached model weights from ~/.cache/ppu-paddle-ocr/ |
| Setting | Default | Description |
|---|---|---|
| Output format | Callout | > [!note]+ callout or ```ocr fenced block |
| Skip already-processed | On | Don't re-OCR images that already have a block below them |
| Merge wrapped lines | On | Join soft-wrapped OCR lines into natural prose; preserves paragraph gaps and list items |
| Max concurrency | 3 | Maximum number of images the OCR model processes in parallel during batch inference (1–20) |
| Setting | Default | Description |
|---|---|---|
| Developer mode | Off | Log each image's raw OCR result to the console (Ctrl+Shift+I) |
npm run dev # watch mode
npm run build # production build → main.js
src/
main.ts Plugin entry, commands, settings UI, OCR dispatch
local-ocr.ts LocalOcrEngine — wraps ppu-paddle-ocr via CJS bundle
native-manager.ts Runtime installer — downloads onnxruntime-node & @napi-rs/canvas
scripts/
build-bundle.mjs Bundles ppu-paddle-ocr + ppu-ocv + opencv-js → ppu-bundle.cjs
deploy.mjs Builds and copies all artifacts to a local vault for testing
ppu-bundle.cjs?Obsidian's Electron renderer loads pages from the app:// protocol. Chromium treats a dynamic import("file://…") as cross-origin and blocks it. build-bundle.mjs pre-bundles ppu-paddle-ocr into a single CJS file so local-ocr.ts can load it with require(), which uses Node.js's CJS resolver and has no protocol restriction.
ppu-bundle.cjs is not committed to the repository. It is built in CI and attached to each GitHub Release as a release asset, then downloaded on-demand by the plugin's Setup flow.
MIT