# OCR Tesseract en Python para un bot de juego Android

> OCR Tesseract en Python para tu bot de juego Android con read_text en MAS: elige una región en Asset Lab, ajusta psm y el color, y lee contadores.

Source: https://automationmacro.com/es/docs/guides/ocr-text-reading (Guías, updated 2026-09-05)

La coincidencia de plantillas le dice a un bot de juego Android si un botón está en pantalla. El OCR Tesseract en Python le dice qué marca un contador: energía 8 de 50, un temporizador en 00:42, un número de nivel. Esta guía lleva `mas.read_text` desde una región dibujada en Asset Lab hasta una macro que se detiene cuando la energía baja de un límite. Está pensada para quien escribe macros de Macro Automation Studio (MAS) en Python en un emulador, un dispositivo en la nube o un móvil.

## Antes de empezar

- Un proyecto basado en código está abierto en el Code Editor y hay un dispositivo seleccionado. Consulta [Primeros pasos](/docs/getting-started).
- La app o el juego muestra el texto que quieres leer, a la resolución que vas a mantener.
- Has leído la [guía de reconocimiento de imagen](/docs/guides/image-recognition-macros) o sabes qué es una `Region`.

## Cómo funciona el OCR Tesseract en Python dentro de un bot de juego

`read_text` envía una captura, o la parte de ella dentro de una región, al Tesseract OCR que va dentro de la app. Devuelve lo que leyó el motor.

```python
def read_text(
    region: Region | None = None,
    screenshot: Screenshot | None = None,
    model: str = "eng_best",
    psm: int = 7,
    color_conversion: ColorConversion = ColorConversion.NONE,
    timeout_ms: int = 30000,
) -> TextRecognitionResult
```

El resultado es un `TextRecognitionResult` con tres campos: `text` (la cadena), `confidence` (de 0.0 a 1.0) y `region` (dónde miró). Leer toda la pantalla funciona, pero una región ajustada es más rápida y mucho más precisa, así que el primer paso es siempre la región.

## Elegir una región OCR en Asset Lab

1. Lleva el dispositivo a la pantalla con el contador.
2. En el Code Editor, abre el panel **Recursos** y haz clic en **Abrir Asset Helper**. Asset Lab se abre con la pantalla en directo.
3. Dibuja un recuadro alrededor del texto. Incluye un poco de margen, pero ningún icono, borde ni número vecino.
4. Ejecuta la prueba de OCR en directo. Ajusta el recuadro hasta que el texto salga limpio; la prueba usa el mismo motor que tu script.
5. Copia las cuatro coordenadas a una `Region(x1, y1, x2, y2)`.

```python
import mas
from mas import Region

ENERGY = Region(x1=380, y1=20, x2=520, y2=60)

result = mas.read_text(region=ENERGY)
print(repr(result.text), result.confidence)
```

Imprime con `repr` mientras ajustas: muestra los espacios y saltos de línea sueltos que `print` esconde. Las regiones son coordenadas en píxeles a la resolución de captura, así que mantén todos los dispositivos en esa resolución. Consulta [Asset Lab](/docs/asset-lab).

## Elegir el modelo

`model` selecciona los datos de Tesseract. `"eng_best"` es el predeterminado y el más preciso; `"eng_fast"` cambia precisión por velocidad en texto limpio; `"eng"` es el conjunto estándar en inglés. Empieza con el predeterminado y cambia solo cuando un bucle lee decenas de veces por minuto.

## Elegir un modo de segmentación de página

`psm` le dice a Tesseract qué forma de texto esperar. El modo equivocado es la causa más común de resultados vacíos o ilegibles.

| psm | Espera | Úsalo para |
|---|---|---|
| 7 | Una sola línea de texto (predeterminado) | Contadores, temporizadores, etiquetas de una línea |
| 8 | Una sola palabra | Un número suelto o una insignia corta |
| 6 | Un bloque uniforme de texto | Un párrafo en un diálogo |
| 11 | Texto disperso en cualquier sitio, sin orden | Una pantalla entera, una lista de resultados |

```python
level = mas.read_text(region=Region(x1=20, y1=20, x2=90, y2=60), psm=8)
dialog = mas.read_text(region=Region(x1=60, y1=300, x2=480, y2=600), psm=6)
```

Los valores van de 0 a 13, pero estos cuatro cubren casi todas las macros. Cuando una región de una sola línea no devuelve nada, prueba primero 8 y luego 6.

## Arreglar texto claro sobre fondo oscuro

Tesseract espera texto oscuro sobre fondo claro. Los contadores de los juegos suelen ser lo contrario, dígitos blancos sobre una barra oscura, muchas veces con un contorno de color. `color_conversion` preprocesa el recorte antes del OCR.

```python
from mas import ColorConversion

energy = mas.read_text(
    region=ENERGY,
    psm=7,
    color_conversion=ColorConversion.BLACK_WHITE,
)
```

- `ColorConversion.NONE` (predeterminado) envía los píxeles tal cual.
- `ColorConversion.BLACK_WHITE` convierte a escala de grises y aplica un umbral automático, dando blanco y negro puros. Es la mejor primera opción para texto de alto contraste sobre fondos con ruido o de color.
- `ColorConversion.BGR_TO_GRAY` o `RGB_TO_GRAY` dan escala de grises simple, que ayuda cuando el umbral de `BLACK_WHITE` se come los trazos finos.

Prueba cada opción en Asset Lab sobre la pantalla real. No existe un miembro `GRAYSCALE`; usa uno de los nombres de arriba.

## Fijar un tiempo límite

`timeout_ms` es 30000 por defecto. Una región ajustada con `psm=7` responde en mucho menos de un segundo; una lectura de pantalla completa con `psm=11` sobre una pantalla cargada tarda más. Baja el tiempo límite en un bucle que deba seguir respondiendo y envuelve la llamada para que una lectura lenta no termine la ejecución.

```python
try:
    result = mas.read_text(region=ENERGY, timeout_ms=5000)
except mas.RPCError as e:
    mas.log(f"OCR failed: {e}", level="warning")
    result = None
```

## Extraer números del texto

`result.text` es una cadena, a veces con espacios, comas, una barra o una letra suelta. Analízala con una expresión regular y trata "sin número" como un resultado real.

```python
import re

def first_int(text: str) -> int | None:
    cleaned = text.replace(",", "").replace("O", "0").replace("l", "1")
    m = re.search(r"\d+", cleaned)
    return int(m.group()) if m else None

def current_and_max(text: str) -> tuple[int | None, int | None]:
    m = re.search(r"(\d+)\s*/\s*(\d+)", text.replace(",", ""))
    return (int(m.group(1)), int(m.group(2))) if m else (None, None)
```

`first_int("Energy 1,250")` da `1250`; `current_and_max("8/50")` da `(8, 50)`. Los cambios de `O` y `l` arreglan las dos confusiones de dígitos más comunes; aplícalos solo a regiones que no contengan más que dígitos.

## Usar la confianza

`confidence` va de 0.0 a 1.0 sobre las palabras que Tesseract encontró. Un contador limpio se lee a 0.9 o más. Cuando baja, vuelve a leer antes de actuar sobre el valor, y registra ambos para ver el patrón más tarde.

```python
for attempt in range(3):
    result = mas.read_text(region=ENERGY, psm=7)
    value = first_int(result.text)
    if value is not None and result.confidence >= 0.8:
        break
    mas.log(f"Low confidence {result.confidence:.2f} for {result.text!r}", level="warning")
```

## Reutilizar una captura

Cada `read_text` captura un fotograma nuevo salvo que le pases uno. Cuando un bucle lee varias regiones a la vez, haz una captura y pásala a cada llamada; los valores pertenecen entonces al mismo instante.

```python
shot = mas.take_screenshot()
energy = mas.read_text(region=ENERGY, screenshot=shot)
gold = mas.read_text(region=GOLD, screenshot=shot)
```

## Leer texto en un emulador Android con Python: ejemplo completo

El script toca un botón hasta que el contador de energía baja de un límite o se agota un presupuesto de tiempo. Sustituye la región, el ID de imagen y el límite por los tuyos.

```python
import random
import re
import sys
import time

import mas
from mas import ColorConversion, Region

images = mas.images({"attack_button": 101})

ENERGY = Region(x1=380, y1=20, x2=520, y2=60)
MIN_ENERGY = 10
TIME_BUDGET_S = 15 * 60


def read_energy() -> int | None:
    for _ in range(3):
        result = mas.read_text(
            region=ENERGY,
            psm=7,
            color_conversion=ColorConversion.BLACK_WHITE,
            timeout_ms=5000,
        )
        m = re.search(r"\d+", result.text.replace(",", "").replace("O", "0"))
        if m and result.confidence >= 0.7:
            return int(m.group())
        mas.log(f"Unclear energy {result.text!r} ({result.confidence:.2f})", level="warning")
        time.sleep(1)
    return None


def main():
    started = time.monotonic()
    unreadable = 0

    while time.monotonic() - started < TIME_BUDGET_S:
        energy = read_energy()
        if energy is None:
            unreadable += 1
            if unreadable >= 5:
                mas.log("Energy unreadable five times, stopping", level="error")
                sys.exit(2)
            continue
        unreadable = 0
        if energy < MIN_ENERGY:
            mas.log(f"Energy {energy} is below {MIN_ENERGY}, done")
            break

        button = mas.find_object_retry(images.attack_button, total_tries=3, time_sleep=2.0)
        if button is None:
            mas.log("Attack button not found", level="warning")
            continue
        mas.click(button.x, button.y, delay_ms=1000)
        time.sleep(random.uniform(0.8, 2.0))

    mas.log("Finished")


if __name__ == "__main__":
    main()
```

Leer antes de cada toque mantiene la macro honesta: nunca gasta energía que no puede ver. El contador `unreadable` convierte una región rota en una ejecución fallida, que un webhook en `macro.failed` puede notificar. Consulta [Bucles y programación](/docs/guides/loops-and-scheduling). Si prefieres describir la tarea, [MAS Agent](/agent) mide la región y escribe este código por ti.

## Solución de problemas

### El OCR devuelve una cadena vacía

La región no abarca el texto, el modo es incorrecto o el texto es claro sobre oscuro. Revisa la región en Asset Lab, cambia `psm` a 8 para un solo número o a 6 para un bloque, y añade `color_conversion=ColorConversion.BLACK_WHITE`. El texto muy pequeño también se lee como nada. Si los dígitos miden solo unos píxeles de alto, sube la resolución del emulador en sus ajustes de pantalla y vuelve a capturar todas las regiones.

### Dígitos mal leídos

El cero se lee como la letra O, el uno como l, el ocho como B, o desaparece una coma. Primero ajusta la región para que ningún icono toque los dígitos. Luego prueba `BLACK_WHITE` y, si los trazos se rompen, `BGR_TO_GRAY` en su lugar. Por último, normaliza en el código: sustituye `O` por `0` y `l` por `1` en regiones de solo dígitos, quita las comas y rechaza valores que salten de forma disparatada entre lecturas.

### OCR lento

Las lecturas de pantalla completa con `psm=11` y `eng_best` son el caso lento. Lee una región, no la pantalla; usa `psm=7` u `8`; y pasa una sola `screenshot` a varias llamadas. `model="eng_fast"` ayuda con texto limpio. Si una lectura sigue tardando segundos, la región probablemente es grande; un contador necesita un recuadro de como mucho unos cientos de píxeles de ancho.

Ninguna herramienta de automatización está libre de riesgos al 100 %, así que automatiza con responsabilidad y bajo tu propio criterio.
