# Python OCR 识别游戏数字：用 Tesseract 读取计数器

> 在 MAS 中用 read_text 给游戏脚本加上 Python OCR 识别数字的能力：在 Asset Lab 中选区域，调整 psm 和颜色转换，解析计数器和计时器。

Source: https://automationmacro.com/zh-CN/docs/guides/ocr-text-reading (教程, updated 2026-09-05)

模板匹配告诉游戏脚本某个按钮是否在屏幕上。Python OCR 识别数字则告诉它计数器写的是什么：体力 8/50、计时器 00:42、一个等级数字。这篇指南把 `mas.read_text` 从 Asset Lab 中画出的区域，带到一个在体力低于限值时停止的宏。适合任何在模拟器、云设备或手机上用 Python 写 Macro Automation Studio（MAS）宏的人。

## 开始之前

- Code Editor 中打开了一个代码型项目，并选择了一台设备。参见[快速开始](/docs/getting-started)。
- 应用或游戏正显示你要读的文字，且分辨率是你会一直保持的那个。
- 你已读过[图像识别指南](/docs/guides/image-recognition-macros)，或者知道 `Region` 是什么。

## Tesseract OCR 在游戏脚本中如何工作

`read_text` 把一张截图，或者截图中某个区域内的部分，送给应用内的 Tesseract OCR。它返回引擎读到的内容。

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

结果是一个 `TextRecognitionResult`，有三个字段：`text`（字符串）、`confidence`（0.0 到 1.0）和 `region`（它看的位置）。读整屏也能用，但紧凑的区域更快、准确得多，所以第一步永远是区域。

## 在 Asset Lab 中选一个 OCR 区域

1. 把设备切到有计数器的屏幕。
2. 在 Code Editor 中打开**资源**面板，点击**打开资源助手**。Asset Lab 会带着实时画面打开。
3. 围绕文字画一个框。留一点边距，但不要包含图标、边框或旁边的数字。
4. 运行实时 OCR 测试。调整框直到文字能干净地读出来；测试用的引擎和你的脚本相同。
5. 把四个坐标复制到 `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)
```

调试时用 `repr` 打印：它会显示 `print` 隐藏的多余空格和换行。区域是截取分辨率下的像素坐标，所以让每台设备都保持那个分辨率。参见 [Asset Lab](/docs/asset-lab)。

## 选择模型

`model` 选择 Tesseract 数据。`"eng_best"` 是默认值，也最准确；`"eng_fast"` 在干净文字上用准确度换速度；`"eng"` 是标准英文集。从默认开始，只在循环每分钟读几十次时才切换。

## 选择页面分割模式

`psm` 告诉 Tesseract 预期什么形状的文字。模式选错是结果为空或乱码最常见的原因。

| psm | 预期 | 适用于 |
|---|---|---|
| 7 | 单行文字（默认） | 计数器、计时器、单行标签 |
| 8 | 单个词 | 孤立的数字或短角标 |
| 6 | 一个统一的文字块 | 对话框中的一段文字 |
| 11 | 任意位置的零散文字，无顺序 | 整屏、结果列表 |

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

取值范围是 0 到 13，但这四个几乎覆盖所有宏。单行区域什么都读不到时，先试 8，再试 6。

## 修复深色背景上的浅色文字

Tesseract 预期浅色背景上的深色文字。游戏计数器通常相反：深色条上的白色数字，常常还带彩色描边。`color_conversion` 在 OCR 之前预处理裁剪区域。

```python
from mas import ColorConversion

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

- `ColorConversion.NONE`（默认）按原样发送像素。
- `ColorConversion.BLACK_WHITE` 转为灰度并应用自动阈值，得到纯黑白。对嘈杂或彩色背景上的高对比度文字，这是最好的第一选择。
- `ColorConversion.BGR_TO_GRAY` 或 `RGB_TO_GRAY` 给出普通灰度，在 `BLACK_WHITE` 的阈值吃掉细笔画时有帮助。

在 Asset Lab 中对真实屏幕测试每个选项。没有 `GRAYSCALE` 成员；请用上面的名字之一。

## 设置超时

`timeout_ms` 默认 30000。`psm=7` 的紧凑区域远不到一秒就能返回；繁忙屏幕上的整屏 `psm=11` 读取要更久。在必须保持响应的循环中调低超时，并把调用包起来，让慢速读取不会结束运行。

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

## 从文字中解析数字

`result.text` 是一个字符串，有时带空格、逗号、斜杠或多余的字母。用正则表达式解析它，并把"没有数字"当作一种真实结果。

```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")` 得到 `1250`；`current_and_max("8/50")` 得到 `(8, 50)`。`O` 和 `l` 的替换修复两种最常见的数字误读；只对纯数字的区域使用。

## 使用置信度

`confidence` 的范围是 0.0 到 1.0，覆盖 Tesseract 找到的所有词。干净的计数器读出来在 0.9 以上。它下降时，在采用这个值之前再读一次，并把两者都记录下来，方便之后看出规律。

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

## 复用一张截图

每次 `read_text` 都会截一帧新图，除非你传入一张。当循环一次要读几个区域时，截一张图传给每个调用；这样各个值就属于同一个瞬间。

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

## 用 Python 在安卓模拟器上识别文字：完整示例

这个脚本点击一个按钮，直到体力计数器低于限值或时间预算用完。把区域、图片 ID 和限值换成你的。

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

每次点击前都读一次，宏就不会作弊：它从不花掉它看不见的体力。`unreadable` 计数器把损坏的区域变成一次失败的运行，订阅 `macro.failed` 的 Webhook 可以报告它。参见[循环与定时](/docs/guides/loops-and-scheduling)。如果你更愿意描述任务，[MAS Agent](/agent) 会替你测量区域并写出这段代码。

## 故障排查

### OCR 返回空字符串

区域没框住文字、模式不对，或者文字是深色背景上的浅色。在 Asset Lab 中检查区域，单个数字把 `psm` 切到 8，文字块切到 6，并加上 `color_conversion=ColorConversion.BLACK_WHITE`。非常小的文字也会读成空。如果数字只有几个像素高，在模拟器显示设置中提高分辨率，并重新截取每个区域。

### 数字读错

零读成字母 O，一读成 l，八读成 B，或者逗号消失。首先收紧区域，让图标不碰到数字。然后试 `BLACK_WHITE`，笔画断裂时改用 `BGR_TO_GRAY`。最后在代码中归一化：在纯数字区域把 `O` 换成 `0`、`l` 换成 `1`，去掉逗号，并拒绝在两次读取之间剧烈跳变的值。

### OCR 很慢

用 `psm=11` 和 `eng_best` 读整屏是最慢的情况。读区域，不读整屏；用 `psm=7` 或 `8`；把一张 `screenshot` 传给多个调用。`model="eng_fast"` 在干净文字上有帮助。如果一次读取仍然要几秒，区域可能太大了；一个计数器最多只需要几百像素宽的框。

没有任何自动化工具是 100% 无风险的，请负责任地使用自动化，并自行斟酌决定。
