# OCR Python com Tesseract para bot de jogo Android

> Dê a um bot de jogo Android o OCR do Tesseract com read_text: escolha uma região no Asset Lab, ajuste psm e conversão de cor, e leia contadores e timers.

Source: https://automationmacro.com/pt-BR/docs/guides/ocr-text-reading (Guias, updated 2026-09-05)

O template matching diz a um bot de jogo Android se um botão está na tela. O OCR do Tesseract, chamado do Python, diz o que um contador mostra: energia 8 de 50, um timer em 00:42, um número de nível. Este guia leva `mas.read_text` de uma região desenhada no Asset Lab até uma macro que para quando a energia cai abaixo de um limite. Ele é para quem escreve macros do Macro Automation Studio (MAS) em Python em um emulador, em um dispositivo na nuvem ou em um celular.

## Antes de começar

- Um projeto Baseado em código está aberto no Code Editor e um dispositivo está selecionado. Veja [Primeiros passos](/docs/getting-started).
- O app ou jogo mostra o texto que você quer ler, na resolução que você vai manter.
- Você leu o [guia de reconhecimento de imagem](/docs/guides/image-recognition-macros) ou sabe o que é uma `Region`.

## Como o OCR Python com Tesseract funciona em um bot de jogo Android

`read_text` envia uma captura de tela, ou a parte dela dentro de uma região, ao Tesseract OCR dentro do app. Ele retorna o que o motor leu.

```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
```

O resultado é um `TextRecognitionResult` com três campos: `text` (a string), `confidence` (0.0 a 1.0) e `region` (onde ele olhou). Ler a tela inteira funciona, mas uma região justa é mais rápida e muito mais precisa, então o primeiro passo é sempre a região.

## Escolha uma região de OCR no Asset Lab

1. Leve o dispositivo até a tela com o contador.
2. No Code Editor, abra o painel **Assets** e clique em **Abrir o Asset Helper**. O Asset Lab abre com a tela ao vivo.
3. Desenhe uma caixa em volta do texto. Inclua uma pequena margem, mas nenhum ícone, borda ou número vizinho.
4. Rode o teste de OCR ao vivo. Ajuste a caixa até o texto voltar limpo; o teste usa o mesmo motor que o seu script.
5. Copie as quatro coordenadas para uma `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)
```

Imprima com `repr` enquanto ajusta: ele mostra espaços e quebras de linha perdidos que o `print` esconde. As regiões são coordenadas em pixels na resolução da captura, então mantenha todo dispositivo nessa resolução. Veja [Asset Lab](/docs/asset-lab).

## Escolha o modelo

`model` seleciona os dados do Tesseract. `"eng_best"` é o padrão e o mais preciso; `"eng_fast"` troca precisão por velocidade em texto limpo; `"eng"` é o conjunto padrão de inglês. Comece pelo padrão e só troque quando um loop ler dezenas de vezes por minuto.

## Escolha um modo de segmentação de página

`psm` diz ao Tesseract que forma de texto esperar. O modo errado é a causa mais comum de resultados vazios ou embaralhados.

| psm | Espera | Use para |
|---|---|---|
| 7 | Uma única linha de texto (padrão) | Contadores, timers, rótulos de uma linha |
| 8 | Uma única palavra | Um número sozinho ou um selo curto |
| 6 | Um bloco uniforme de texto | Um parágrafo em um diálogo |
| 11 | Texto esparso em qualquer lugar, sem ordem | Uma tela inteira, uma 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)
```

Os valores vão de 0 a 13, mas esses quatro cobrem quase toda macro. Quando uma região de uma linha não retorna nada, tente 8 primeiro, depois 6.

## Corrija texto claro sobre fundo escuro

O Tesseract espera texto escuro sobre fundo claro. Os contadores de jogos costumam ser o oposto, dígitos brancos sobre uma barra escura, muitas vezes com um contorno colorido. `color_conversion` pré-processa o recorte antes do OCR.

```python
from mas import ColorConversion

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

- `ColorConversion.NONE` (padrão) envia os pixels como estão.
- `ColorConversion.BLACK_WHITE` converte para escala de cinza e aplica um limiar automático, dando preto e branco puros. É a melhor primeira tentativa para texto de alto contraste sobre fundos ruidosos ou coloridos.
- `ColorConversion.BGR_TO_GRAY` ou `RGB_TO_GRAY` dá escala de cinza simples, o que ajuda quando o limiar de `BLACK_WHITE` come traços finos.

Teste cada opção no Asset Lab na tela real. Não existe um membro `GRAYSCALE`; use um dos nomes acima.

## Defina um tempo limite

`timeout_ms` tem 30000 como padrão. Uma região justa com `psm=7` retorna bem abaixo de um segundo; uma leitura de tela inteira com `psm=11` em uma tela cheia demora mais. Reduza o tempo limite em um loop que precisa continuar responsivo, e envolva a chamada para que uma leitura lenta não encerre a execução.

```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
```

## Extraia números do texto

`result.text` é uma string, às vezes com espaços, vírgulas, uma barra ou uma letra perdida. Interprete-a com uma expressão regular e trate "nenhum número" como um 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")` dá `1250`; `current_and_max("8/50")` dá `(8, 50)`. As trocas de `O` e `l` corrigem as duas leituras erradas de dígito mais comuns; aplique-as só a regiões que contêm apenas dígitos.

## Use a confiança

`confidence` vai de 0.0 a 1.0 sobre as palavras que o Tesseract encontrou. Um contador limpo lê a 0.9 ou mais. Quando ela cair, leia de novo antes de agir sobre o valor, e registre os dois para ver o padrão depois.

```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")
```

## Reaproveite uma captura de tela

Todo `read_text` captura um quadro novo, a menos que você passe um. Quando um loop lê várias regiões de uma vez, tire uma captura e passe-a a cada chamada; os valores então pertencem ao mesmo instante.

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

## Ler texto em um emulador Android com Python: um exemplo completo

O script toca em um botão até o contador de energia cair abaixo de um limite ou o limite de tempo passar. Troque a região, o ID da imagem e o limite pelos seus.

```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()
```

Ler antes de cada toque mantém a macro honesta: ela nunca gasta energia que não consegue ver. O contador `unreadable` transforma uma região quebrada em uma execução com falha, que um webhook em `macro.failed` pode informar. Veja [Loops e agendamento](/docs/guides/loops-and-scheduling). Se preferir descrever a tarefa, o [MAS Agent](/agent) mede a região e escreve esse código para você.

## Solução de problemas

### O OCR retorna uma string vazia

A região não pega o texto, o modo está errado, ou o texto é claro sobre escuro. Confira a região no Asset Lab, troque `psm` para 8 para um único número ou 6 para um bloco, e adicione `color_conversion=ColorConversion.BLACK_WHITE`. Texto muito pequeno também é lido como nada. Se os dígitos têm só alguns pixels de altura, aumente a resolução do emulador nas configurações de tela e capture todas as regiões de novo.

### Dígitos lidos errado

Zero lido como a letra O, um como l, oito como B, ou uma vírgula que some. Primeiro aperte a região para nenhum ícone tocar nos dígitos. Depois tente `BLACK_WHITE` e, se os traços quebrarem, `BGR_TO_GRAY` em vez disso. Por fim, normalize no código: troque `O` por `0` e `l` por `1` em regiões só de dígitos, remova as vírgulas e rejeite valores que saltam demais entre leituras.

### OCR lento

Leituras de tela inteira com `psm=11` e `eng_best` são o caso lento. Leia uma região, não a tela; use `psm=7` ou `8`; e passe uma única `screenshot` a várias chamadas. `model="eng_fast"` ajuda em texto limpo. Se uma leitura ainda leva segundos, a região provavelmente é grande; um contador precisa de uma caixa de no máximo algumas centenas de pixels de largura.

Nenhuma ferramenta de automação é 100% livre de riscos, por isso automatize com responsabilidade e a seu próprio critério.
