# Tesseract OCR をゲーム bot で使う: 数値の読み取り

> MAS の read_text で Android ゲーム bot に Tesseract OCR を組み込む: Asset Lab で領域を選び、psm と色変換を調整し、カウンターやタイマーの数値を取り出します。

Source: https://automationmacro.com/ja/docs/guides/ocr-text-reading (ガイド, updated 2026-09-05)

テンプレートマッチングは、Android ゲーム bot にボタンが画面にあるかどうかを教えます。Tesseract OCR はカウンターが何を示しているかを教えます: エネルギー 8 / 50、00:42 のタイマー、レベルの数字。このガイドでは、Asset Lab で描いた領域から、エネルギーが上限を下回ったら止まるマクロまで、`mas.read_text` を Python で使います。エミュレータ、クラウドデバイス、スマホで Macro Automation Studio (MAS) のマクロを Python で書くすべての方向けです。

## 始める前に

- コードベースのプロジェクトが Code Editor で開いていて、デバイスが選ばれていること。[はじめに](/docs/getting-started) を参照してください。
- アプリかゲームが、今後も変えない解像度で、読みたい文字を表示していること。
- [画像認識ガイド](/docs/guides/image-recognition-macros) を読んだか、`Region` が何かを知っていること。

## Android ゲーム bot での 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
```

結果は 3 つのフィールドを持つ `TextRecognitionResult` です: `text` (文字列)、`confidence` (0.0 から 1.0)、`region` (見た場所)。画面全体を読むこともできますが、狭い領域の方が速く、はるかに正確なので、最初の一歩は常に領域です。

## Asset Lab で OCR 領域を選ぶ

1. デバイスをカウンターのある画面にします。
2. Code Editor で **アセット** パネルを開き、**Asset Helper を開く** をクリックします。Asset Lab がライブ画面で開きます。
3. 文字の周りに枠を描きます。少し余白を含めますが、アイコン、枠線、隣の数字は含めません。
4. ライブの OCR テストを実行します。文字がきれいに読めるまで枠を調整します。テストはスクリプトと同じエンジンを使います。
5. 4 つの座標を `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"` は標準の英語セットです。既定から始め、ループが 1 分間に何十回も読むときだけ切り替えてください。

## ページセグメンテーションモードを選ぶ

`psm` は Tesseract にどんな形の文字を期待するかを伝えます。間違ったモードは、空や文字化けした結果の最も多い原因です。

| psm | 期待するもの | 用途 |
|---|---|---|
| 7 | 1 行の文字 (既定) | カウンター、タイマー、1 行のラベル |
| 8 | 1 語 | 単独の数字や短いバッジ |
| 6 | 均一な 1 ブロックの文字 | ダイアログ内の段落 |
| 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 までありますが、この 4 つでほぼすべてのマクロをカバーします。1 行の領域が何も返さないときは、まず 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` の狭い領域は 1 秒よりずっと速く返ります。混み合った画面での全画面 `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` の置き換えは、最もよくある 2 つの数字の誤読を直します。数字しか含まない領域にだけ適用してください。

## 信頼度を使う

`confidence` は Tesseract が見つけた単語全体で 0.0 から 1.0 です。きれいなカウンターは 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")
```

## 1 枚のスクリーンショットを再利用する

`read_text` は渡さない限り、毎回新しいフレームを取得します。ループが複数の領域を同時に読むときは、スクリーンショットを 1 枚撮って各呼び出しに渡します。値は同じ瞬間のものになります。

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

## Python で Android エミュレータの文字を読む: 完全な例

このスクリプトは、エネルギーのカウンターが上限を下回るか、時間の予算を過ぎるまでボタンをタップします。領域、画像 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、1 が l、8 が B と読まれる、またはカンマが消えます。まず、アイコンが数字に触れないよう領域を詰めます。次に `BLACK_WHITE` を試し、線が途切れるなら代わりに `BGR_TO_GRAY` を試します。最後にコードで正規化します: 数字だけの領域では `O` を `0` に、`l` を `1` に置き換え、カンマを取り除き、読み取りの間で大きく跳ぶ値を捨てます。

### OCR が遅い

`psm=11` と `eng_best` での全画面の読み取りが遅いケースです。画面ではなく領域を読み、`psm=7` か `8` を使い、1 枚の `screenshot` を複数の呼び出しに渡してください。きれいな文字では `model="eng_fast"` が役立ちます。それでも読み取りに数秒かかるなら、領域がおそらく大きすぎます。カウンターに必要な枠は、幅がせいぜい数百ピクセルです。

100%リスクのない自動化ツールは存在しないため、自動化は責任を持って、ご自身の判断で行ってください。
