textsnap: OCR sin conexión desde imágenes, capturas de pantalla y páginas web en CPU
textsnap es una herramienta Python de un solo comando que extrae texto plano de imágenes, capturas de pantalla y páginas web utilizando un modelo ONNX cuantizado, sin necesidad de GPU ni nube.
¿Qué es textsnap?
textsnap es una herramienta OCR ligera y sin conexión que convierte imágenes, capturas de pantalla y páginas web en texto plano, todo en tu CPU, sin dependencia de la nube. Utiliza un modelo de lenguaje-visión PaddleOCR-VL-1.5 cuantizado de 0.9B (q4 ONNX) para analizar páginas completas en un portátil estándar. Sin CUDA, sin trucos exclusivos de la serie M, solo núcleos normales.
¿Por qué textsnap?
La mayoría de las herramientas OCR requieren una GPU, envían tus datos a la nube o son complicadas de configurar. textsnap resuelve los tres problemas:
- Funciona en CPU — El modelo está cuantizado a q4 ONNX, lo que lo hace eficiente en cualquier portátil moderno.
- Completamente sin conexión — Después de la primera ejecución (descarga de ~890 MB), todo permanece local. Sin claves API, sin cuotas, sin que tus datos salgan de tu máquina.
- Un solo comando —
pip install textsnapy estás listo. La herramienta es un único módulo Python. - Múltiples tipos de entrada — Portapapeles, archivos de imagen locales, URLs de imagen directas o URLs de páginas web completas.
- Salida en Markdown o texto plano — Conserva tablas, encabezados y estructura por defecto, o aplana a texto plano con
--plaintext.
Inicio rápido
# Instalar
pip install textsnap
# Capturar algo
textsnap screenshot.png
textsnap https://example.com/article --plaintext
textsnap photo.jpg -o ~/notes/receipt.txt
La primera ejecución descarga el modelo (~890 MB). Cada ejecución posterior es sin conexión.
Lo que maneja
| Fuente | Ejemplo |
|---|---|
| Portapapeles | textsnap (sin argumento) |
| Archivo de imagen local | textsnap ruta/a/img.png |
| URL de imagen directa | textsnap https://example.com/x.png |
| URL de página web | textsnap https://example.com/article |
Los archivos locales cubren todo lo que Pillow puede decodificar: .png, .jpg, .jpeg, .webp, .bmp, .gif, .tiff y más. Para URLs de páginas web, textsnap usa readability para aislar el contenido principal, luego toma la imagen más prominente de la página y aplica OCR.
Instalación
pip install textsnap
Esto instala dos comandos equivalentes en tu PATH: textsnap (canónico) y ocr (alias).
Para instalar desde una copia local del código fuente:
pip install .
Para una instalación reproducible con versiones exactas de dependencias fijadas:
pip install -r requirements-lock.txt
pip install .
Nota sobre el portapapeles: Leer imágenes del portapapeles depende de ImageGrab de Pillow. En Linux es posible que necesites tener instalado xclip o wl-clipboard. macOS y Windows funcionan sin configuración adicional.
Uso
# Portapapeles (sin argumento)
textsnap
# Archivo de imagen local
textsnap ruta/a/captura.png
# URL de imagen directa
textsnap "https://example.com/diagram.png"
# Página web — aplica OCR a la imagen más prominente de la página
textsnap "https://example.com/article"
# Aplana el markdown del modelo a texto plano
textsnap input.png --plaintext
# Ruta de salida personalizada
textsnap input.png -o ./out/extracted.txt
# Aumenta el límite de tokens para páginas muy densas
textsnap pagina-densa.png --max-tokens 4096
# Usa un directorio de modelo local en lugar de descargar
textsnap input.png --model-dir ~/models/paddleocr-vl
Salida
La salida es texto plano, UTF-8. La ubicación predeterminada es ./textsnaps/ (se crea si no existe) en el directorio de trabajo actual; se puede sobrescribir con -o. El nombre del archivo se deriva del nombre base del archivo de imagen (por ejemplo, recibo_ocr.txt), o del slug de la página web para entradas URL.
textsnap es silencioso por defecto, al estilo Unix: lo único que imprime en stdout es la ruta al archivo que escribió, por lo que se integra limpiamente:
OUT=$(textsnap recibo.png) # captura la ruta
textsnap recibo.png | xargs cat # imprime el texto reconocido
Usa -v para enviar diagnósticos de progreso (tipo de entrada, tamaño de imagen, velocidad de decodificación, conteo de tokens) a stderr; stdout sigue siendo solo la ruta en cualquier caso.
La salida de archivo predeterminada es el markdown nativo del modelo: conserva tablas, encabezados y estructura del documento:
# Informe Trimestral
| Región | Ingresos |
| ------ | ------- |
| EMEA | $1.2M |
| APAC | $0.9M |
Con --plaintext, el markdown se aplana a texto simple:
Informe Trimestral
Región Ingresos
EMEA $1.2M
APAC $0.9M
Banderas
| Bandera | Descripción |
|---|---|
-o, --output |
Ruta del archivo .txt de salida. Predeterminado: ./textsnaps/<nombre>_ocr.txt. |
-v, --verbose |
Imprime diagnósticos de progreso en stderr. Desactivado por defecto. |
--plaintext |
Aplana el markdown nativo del modelo a texto plano. |
--model-dir |
Usa archivos ONNX/config de este directorio en lugar de descargarlos. |
--max-tokens |
Límite de tokens generados. Predeterminado 2048. Auméntalo para páginas muy densas. |
--no-verify |
Omite la verificación SHA-256 de los archivos del modelo descargados (no recomendado). |
--generate-checksums |
Descarga los archivos del modelo fijados, escribe un manifiesto nuevo y sale. |
Seguridad
textsnap descarga automáticamente ~890 MB de pesos del modelo desde Hugging Face Hub en la primera ejecución, por lo que trata esos archivos como no confiables hasta que se demuestre lo contrario:
- Revisión del modelo fijada. Las descargas están fijadas a una revisión específica del repositorio, por lo que un
mainmovido o reetiquetado no puede intercambiar los pesos silenciosamente. - Verificación SHA-256. Cada archivo descargado se hashea y se verifica contra resúmenes conocidos antes de cargarlo. Una discrepancia aborta la ejecución con un error claro en lugar de ejecutar pesos no verificados. Los resúmenes viven en
model_checksums.sha256y también están incrustados en el script como respaldo, por lo que la verificación funciona tanto si instalas desde el código fuente como desde una rueda. - Dependencias fijadas.
requirements-lock.txtfija versiones exactas de dependencias para instalaciones reproducibles; el archivo documenta cómo agregar entradas--hashpor rueda conpip-compile --generate-hashespara un fijado completo de la cadena de suministro.
Regenera el manifiesto de resúmenes después de un aumento deliberado de la revisión del modelo:
textsnap --generate-checksums
Para omitir la verificación (para experimentación local con un modelo modificado), usa --no-verify.
Cómo funciona
- Carga. Desde el portapapeles, un archivo local, una URL de imagen directa o — para una URL de página web — la imagen más prominente dentro del contenido principal de la página (legibilidad + una heurística de prominencia).
- Preprocesamiento. La imagen se limita a 640px en su lado más largo, luego se procesa con el redimensionamiento inteligente y parcheado al estilo Qwen2-VL de PaddleOCR-VL, produciendo el tensor de valores de píxel y la cuadrícula que espera el codificador visual.
- Reconocimiento. Tres componentes ONNX se ejecutan en CPU: un codificador visual (q4), un modelo de incrustación de tokens (fp32) y un decodificador autorregresivo (q4) con un caché KV integrado. Decodificación voraz, con un guardia de repetición que detiene los bucles descontrolados temprano.
- Formato. Markdown nativo por defecto;
--plaintextlo reduce a texto simple.
Ninguna imagen se envía a ningún lado. No se mantiene estado entre ejecuciones excepto el modelo en caché.
Modelo y caché
Los componentes ONNX de PaddleOCR-VL-1.5 se descargan en la primera ejecución en ~/.cache/textsnap/:
onnx/vision_encoder_q4.onnx— codificador visual + proyector de fusión espacialonnx/decoder_q4.onnx— decodificador autorregresivoonnx/embedding.onnx— incrustaciones de tokens (fp32; no existe variante q4)tokenizer.json,config.json
En total ~890 MB. Para usar tu propia copia, apunta --model-dir a un directorio que contenga los mismos archivos onnx/ más tokenizer.json y config.json.
Notas y limitaciones
- La primera ejecución es la lenta — descarga ~890 MB. Después de eso, textsnap está completamente sin conexión.
- La decodificación en CPU es secuencial. Los documentos densos de página completa tardan más que una captura de pantalla corta. textsnap fija los conteos de hilos a tus núcleos físicos e imprime una lectura en vivo de tokens/segundo para que una ejecución lenta se vea visiblemente activa, no colgada.
--max-tokenslimita la salida. Las páginas muy densas pueden alcanzar el límite predeterminado de 2048 tokens y truncarse; auméntalo si falta la parte final de una página.- Las entradas de página web aplican OCR a una imagen — la más prominente en el contenido principal, no toda la página renderizada.
- La decodificación voraz puede ocasionalmente entrar en bucle en diseños repetitivos; un guardia incorporado detecta y recorta estos casos.
Licencia
MIT para este proyecto. El modelo es PaddleOCR-VL-1.5, distribuido bajo Apache-2.0 por PaddlePaddle; textsnap obtiene la exportación ONNX de onnx-community/PaddleOCR-VL-1.5-ONNX. Impulsado por onnxruntime y huggingface_hub.