Extrae texto de imágenes automáticamente con GCP Vision y Python
Convierte una página escaneada o una captura de pantalla en texto con un pequeño programa de línea de comandos en Python. Este ejemplo envía un JPEG o PNG local a Google Cloud Vision e imprime el texto reconocido, o un resumen JSON para que otro programa lo procese. No necesita un bucket de Cloud Storage.
Requisitos
- Python 3.14.7 o una versión de mantenimiento posterior de Python 3.14, instalado desde las descargas de Python.
- La CLI de Google Cloud y un proyecto de Google Cloud con la facturación y la API de Cloud Vision habilitadas.
- Un JPEG o PNG local que tengas permiso para enviar a Google Cloud.
Los siguientes comandos de shell usan Bash en macOS. El tutorial se probó con Python 3.14.7 y
google-cloud-vision 3.16.0. Abre una terminal en un directorio exclusivo para el ejemplo;
guarda allí el programa y tu imagen. Comprueba que los requisitos previos funcionen antes de crear
el entorno virtual:
python3.14 --version && gcloud --version
Configura el proyecto en la nube
En Google Cloud Console, selecciona un proyecto, confirma que la facturación esté habilitada y habilita la API de Cloud Vision. Anota el ID del proyecto. Si otra persona administra el proyecto, pídele que complete esos pasos y autorice tu cuenta; consulta la guía de configuración de Vision de Google.
Configuración para desarrollo
Para el desarrollo local, usa Application Default Credentials (ADC). Sustituye
PROJECT_ID a continuación por el ID de tu proyecto y luego inicia sesión con una
cuenta autorizada para usar ese proyecto:
gcloud auth application-default login &&
gcloud auth application-default set-quota-project PROJECT_ID
Las credenciales ADC son independientes del inicio de sesión habitual de la CLI. El comando de
proyecto de cuotas necesita serviceusage.services.use, que proporciona el rol Service Usage Consumer.
Una configuración existente de GOOGLE_APPLICATION_CREDENTIALS tiene prioridad sobre el archivo ADC local;
comprueba que apunte a las credenciales que quieres usar o elimina esa configuración de esta terminal
antes de usar el inicio de sesión local. La guía de autenticación
de Google explica los requisitos tanto del inicio de sesión como del proyecto de cuotas. No incluyas
credenciales en el programa.
Autenticación en producción
Para un futuro despliegue, da preferencia a una cuenta de servicio vinculada en Google Cloud o a Workload Identity Federation en otros entornos. Ambas opciones funcionan con ADC sin descargar una clave de cuenta de servicio. Este tutorial usa credenciales de usuario locales y no configura un despliegue en producción.
Instala el cliente de Python
Crea un entorno virtual nuevo e instala la versión fijada del cliente. Llamar directamente a su intérprete evita la activación en el shell y mantiene la instalación dentro de este directorio:
test ! -e .venv && test ! -L .venv &&
python3.14 -m venv .venv &&
./.venv/bin/python -m pip install 'google-cloud-vision==3.16.0'
La primera comprobación impide reutilizar un .venv existente. Si la instalación
falla después de crear el entorno, conserva tus archivos y vuelve a intentar solo el comando de
instalación:
./.venv/bin/python -m pip install 'google-cloud-vision==3.16.0'
Si falló la creación del entorno virtual, elige un directorio nuevo para el ejemplo antes de repetir
la configuración. Usa el intérprete .venv para los comandos posteriores, aunque
otro entorno esté activo en tu terminal.
Guarda el programa de OCR
Guarda el bloque completo como extract_text.py. Usa document_text_detection para
texto denso de páginas y lee full_text_annotation.text, como se describe en el
tutorial de detección de texto en documentos de Google.
El transporte REST sigue usando el cliente oficial de Python y ADC.
import argparse
import json
from pathlib import Path
import sys
import time
from google.api_core.exceptions import ServiceUnavailable
from google.cloud import vision
class OcrError(RuntimeError):
"""A safe diagnostic generated by this program."""
def process_ocr_text(raw_text: str) -> dict:
lines = raw_text.splitlines()
return {
'full_text': raw_text,
'lines': lines,
'line_count': len(lines),
'word_count': len(raw_text.split()),
'cleaned_text': ' '.join(raw_text.split()),
}
def safe_ocr_call(client, image):
for attempt in range(3):
try:
response = client.document_text_detection(
image=image, retry=None, timeout=30.0
)
except ServiceUnavailable:
if attempt == 2:
raise
time.sleep(2 ** attempt)
continue
if response.error.code:
raise OcrError(f'Vision OCR failed (code {response.error.code}).')
return response
def extract_text(image_path: str) -> str:
path = Path(image_path)
if path.suffix.lower() not in ('.jpg', '.jpeg', '.png'):
raise OcrError('Use one JPEG or PNG image, not a PDF or TIFF.')
content = path.read_bytes()
if not content.startswith((b'\x89PNG\r\n\x1a\n', b'\xff\xd8\xff')):
raise OcrError('Input does not have a JPEG or PNG signature.')
with vision.ImageAnnotatorClient(transport='rest') as client:
response = safe_ocr_call(client, vision.Image(content=content))
return response.full_text_annotation.text
def main() -> int:
parser = argparse.ArgumentParser(description='Extract text from one JPEG or PNG.')
parser.add_argument('image_path')
parser.add_argument('--json', action='store_true', help='Print a JSON summary.')
args = parser.parse_args()
try:
text = extract_text(args.image_path)
except OcrError as error:
print(error, file=sys.stderr)
return 1
except Exception as error:
print(f'OCR failed ({type(error).__name__}).', file=sys.stderr)
return 1
if args.json:
print(json.dumps(process_ocr_text(text), ensure_ascii=False))
else:
sys.stdout.write(text)
if not text:
print('No text detected.', file=sys.stderr)
return 0
if __name__ == '__main__':
sys.exit(main())
La comprobación de la firma detecta archivos vacíos y errores de formato evidentes antes de realizar una solicitud. No decodifica ni valida por completo una imagen; si hay daños más allá del encabezado, Vision puede rechazarla o recuperarla. Inspecciona la imagen original y compara el texto reconocido con ella antes de usar el resultado.
Ejecútalo con tu imagen
Guarda un escaneo o una captura de pantalla nítidos como invoice.png junto al programa
y luego ejecuta:
./.venv/bin/python extract_text.py invoice.png
El texto reconocido se envía a la salida estándar con sus saltos de línea originales. Una solicitud
exitosa sin texto reconocido termina con el código de salida cero, deja vacía la salida estándar e
imprime No text detected. en la salida de error estándar. Este es un resultado del
reconocimiento, no una prueba de que la imagen no contenga texto escrito. Los errores terminan con
el código de salida uno y sin texto extraído; los argumentos de línea de comandos incorrectos
terminan con el código de salida dos.
Para obtener el ejemplo de normalización integrado, ejecuta el mismo programa con
--json:
./.venv/bin/python extract_text.py --json invoice.png
full_text conserva la respuesta del OCR. lines,
line_count y word_count describen ese texto;
cleaned_text reduce las secuencias de espacios en blanco para facilitar las búsquedas.
Estos campos no identifican totales de facturas, tablas ni párrafos del documento. Un resultado en
blanco produce cadenas vacías, un array lines vacío y conteos en cero.
Diagnostica una extracción fallida
El programa desactiva los reintentos automáticos del cliente y realiza como máximo tres llamadas al
método de OCR. Solo reintenta ServiceUnavailable, con esperas de uno y dos segundos. Cada
llamada tiene un tiempo de espera máximo de 30 segundos para la solicitud; ese no es un límite de
tiempo para todo el comando, que incluye la autenticación y las esperas entre reintentos. No se
reintentan los fallos de permisos, el agotamiento de cuotas ni los errores por imagen incluidos
en la respuesta.
FileNotFoundError: comprueba el nombre del archivo de imagen y el directorio actual de tu terminal.DefaultCredentialsError: completa el inicio de sesión de ADC o corrige una configuración de credenciales que no querías usar.Forbidden,PermissionDeniedo el código 7 incluido en la respuesta: comprueba el acceso al proyecto, la habilitación de la API y el proyecto de cuotas de ADC. Repetir la misma solicitud no puede otorgar permisos.TooManyRequests,ResourceExhaustedo el código 8 incluido en la respuesta: revisa las cuotas del proyecto antes de volver a intentarlo manualmente.- El código 3 incluido en la respuesta o
InvalidArgument: comprueba que la entrada sea un JPEG o PNG legible e intacto.
No se imprimen los mensajes de error de terceros porque pueden contener detalles de la solicitud. Los códigos numéricos incluidos en la respuesta y los nombres de las excepciones indican qué comprobar a continuación sin exponer esos detalles.
Mantén el flujo de trabajo de imágenes dentro de sus límites
Este programa lee una imagen completa en memoria y la envía a un servicio en la nube. Usa primero una imagen de prueba pequeña, comprueba los formatos compatibles y los límites de tamaño y revisa los precios de Vision antes de ejecutar una carga de trabajo mayor. El procesamiento por lotes puede reducir la sobrecarga de las solicitudes, pero no elimina los cargos por función aplicada a cada imagen.
Para archivos PDF o TIFF de varias páginas, usa las API de anotación de archivos en lugar de cambiar el nombre del archivo o pasarlo a esta CLI de imágenes. La detección automática de idiomas y el OCR aún pueden interpretar mal el texto; evalúa tus propios escaneos, sistemas de escritura y diseños de página. Un ejemplo sintético sin imperfecciones no demuestra la precisión en recibos, escritura a mano ni documentos de producción.
