Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions autowsgr/combat/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ def click_image(device: AndroidController, template_key: TemplateKey, timeout: f
return False


def get_ship_drop(device: AndroidController) -> str | None:
def get_ship_drop(device: AndroidController, ocr: OCREngine) -> str | None:
"""获取掉落舰船名称。

Parameters
Expand All @@ -317,10 +317,8 @@ def get_ship_drop(device: AndroidController) -> str | None:
str | None
掉落的舰船名称,或 ``None`` 如果未获取到。
"""
from autowsgr.vision import EasyOCREngine

screen = device.screenshot()
ocr = EasyOCREngine.create(gpu=False)
result = recognize_ship_drop(screen, ocr)
if result.ship_name:
_log.info('[Combat] 掉落识别: {} ({})', result.ship_name, result.ship_type or '未知')
Expand Down
2 changes: 1 addition & 1 deletion autowsgr/combat/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ def _handle_result(self) -> ConditionFlag:

def _handle_get_ship(self) -> ConditionFlag:
"""处理获取舰船。"""
ship_name = get_ship_drop(self._device)
ship_name = get_ship_drop(self._device, self._ocr)
if ship_name:
_log.info('[Combat] 获得舰船: {}', ship_name)

Expand Down
3 changes: 3 additions & 0 deletions autowsgr/infra/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
GameAPP,
MapEntrance,
OcrBackend,
OcrMirror,
OSType,
RepairMode,
ShipType,
Expand Down Expand Up @@ -80,6 +81,8 @@ class OCRConfig(BaseModel):
"""OCR 后端"""
gpu: bool = False
"""是否使用 GPU 加速"""
mirror: OcrMirror = OcrMirror.tencent
"""EasyOCR 模型下载镜像源"""


class LogConfig(BaseModel):
Expand Down
2 changes: 1 addition & 1 deletion autowsgr/scheduler/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def create_ocr(self) -> OCREngine:
cfg = self.config
_log.info('[Launcher] 创建 OCR 引擎 (backend={})', cfg.ocr.backend.value)
# 目前仅支持 EasyOCR,后续可按 cfg.ocr.backend 分发
self._ocr = EasyOCREngine.create(gpu=cfg.ocr.gpu)
self._ocr = EasyOCREngine.create(gpu=cfg.ocr.gpu, mirror=cfg.ocr.mirror)
return self._ocr

# ── 构造 GameContext ──
Expand Down
13 changes: 13 additions & 0 deletions autowsgr/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ class OcrBackend(StrEnum):
paddleocr = 'paddleocr'


class OcrMirror(StrEnum):
"""EasyOCR 模型下载镜像源。"""

origin = 'origin'
"""EasyOCR 原生下载"""
github = 'github'
"""GitHub releases"""
tencent = 'tencent'
"""EdgeOne CDN (腾讯云)"""
modelscope = 'modelscope'
"""ModelScope 镜像"""


# ── 游戏概念 ──


Expand Down
6 changes: 3 additions & 3 deletions autowsgr/ui/map/panels/sortie.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
if TYPE_CHECKING:
import numpy as np

from autowsgr.vision import EasyOCREngine
from autowsgr.vision import OCREngine


_log = get_logger('ui')
Expand Down Expand Up @@ -103,7 +103,7 @@ def _parse_numerator(text: str, max_val: int) -> int:
return None


def recognize_loot_count(screen: np.ndarray, ocr: EasyOCREngine) -> int | None:
def recognize_loot_count(screen: np.ndarray, ocr: OCREngine) -> int | None:
"""识别出征面板战利品 (胖次) 已获取数量。

OCR ``X/50`` 区域并提取 ``/`` 前的数字, 上限固定为 50。
Expand All @@ -123,7 +123,7 @@ def recognize_loot_count(screen: np.ndarray, ocr: EasyOCREngine) -> int | None:
return count


def recognize_ship_count(screen: np.ndarray, ocr: EasyOCREngine) -> int | None:
def recognize_ship_count(screen: np.ndarray, ocr: OCREngine) -> int | None:
"""识别出征面板舰船已获取数量。

OCR ``X/500`` 区域并提取 ``/`` 前的数字, 上限固定为 500。
Expand Down
75 changes: 75 additions & 0 deletions autowsgr/vision/easyocr_models_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""EasyOCR 模型管理 — 检查、校验、下载。

在运行时初始化 EasyOCR 前调用 :func:`ensure_models`,自动完成:
1. 检查模型目录中是否存在所需模型文件
2. 校验 MD5
3. 若缺失或损坏,从用户配置的镜像源下载
"""

from __future__ import annotations

import os
import shutil

from autowsgr.infra.logger import get_logger

# 从 CLI 模块导入共享常量和下载函数(单一维护点)
from .easyocr_models_cli import (
EXPECTED_MD5,
MODEL_FILES,
check_model_file,
download_model_file,
ensure_model_dir,
ensure_modelscope,
)


_log = get_logger('vision.model_download')


def ensure_models(mirror: str = 'tencent') -> None:
"""确保所有 EasyOCR 模型就绪。

Parameters
----------
mirror:
镜像源名称: ``'origin'`` / ``'github'`` / ``'tencent'`` / ``'modelscope'``。
为 ``'origin'`` 时不执行任何检查或下载,由 EasyOCR 自行处理。
"""
model_dir = ensure_model_dir()

if mirror == 'origin':
return

if mirror == 'modelscope':
ensure_modelscope()

for fname in MODEL_FILES:
path = os.path.join(model_dir, fname)
if check_model_file(path, fname):
_log.info('[Model] {} 已就绪', fname)
continue

if os.path.isfile(path):
_log.warning('[Model] {} MD5 不匹配,将重新下载', fname)

_log.info('[Model] 从镜像源 {} 下载EasyOCR模型 {}', mirror, path)
_download_and_verify(fname, mirror, model_dir, path)


def _download_and_verify(fname: str, mirror: str, model_dir: str, dest: str) -> None:
"""下载单个模型文件到临时位置,校验后移入模型目录。"""
tmp = os.path.join(model_dir, fname + '.tmp')
# 清理可能残留的临时文件
if os.path.exists(tmp):
os.remove(tmp)
try:
download_model_file(fname, mirror, tmp, expected_md5=EXPECTED_MD5[fname])
if os.path.exists(dest):
os.remove(dest)
shutil.move(tmp, dest)
_log.info('[Model] {} 下载完成并校验通过', fname)
except Exception:
if os.path.exists(tmp):
os.remove(tmp)
raise
Loading
Loading