diff --git a/Storage/customs/gitlihang/output-ocr-result-log/README.md b/Storage/customs/gitlihang/output-ocr-result-log/README.md new file mode 100644 index 0000000..f5453f1 --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/README.md @@ -0,0 +1,89 @@ +# PR Send Data wps + +在UI日志界面上显示OCR结果 + +## 功能 + + - 通过logger日志模块,将ocr结果输出到UI,方便用户查看关键OCR节点结果。 + + +## 文件说明 + + - `logger.py`:日志输出模块。 + - `returnOCR.py`:Python脚本。 + + +## 使用方式 +### 输出及click动作(暂时只写了click动作,其他动作暂未写)。 + """ +"接受进入的副本名称": { + "recognition": "OCR", + "expected": [ + "" + ], + "roi": [ + 441, + 25, + 411, + 70 + ] + }, + +"活动-三界奇缘-结束": { + "action": "Custom", + "custom_action": "returnOCR", + "custom_action_param": { + "recognition_name": "接受进入的副本名称", + "action_key": "Click", + "return_text": "同意进入副本名称:", + "click_target": [ + 727, + 621, + 190, + 64 + ] + } + } + 自定义返回ocr识别结果,根据action_key执行不同的动作, + action_key: 动作名称,用于判断动作类型,如Click、Move等 + recognition_name: task任务名称,用于指定识别任务名称,返回该节点的结果。 + return_text: 输出的描述,用于指定返回的描述 + click_target: 点击坐标,格式为[x1, y1, x2, y2],仅在action_key为Click时使用。如果不提供click_target,则默认点击识别结果的中心位置。 + + """ +### 只输出,无动作 +``` +"活动-三界奇缘-正确率": { + "recognition": "OCR", + "expected": [], + "roi": [ + 320, + 69, + 116, + 51 + ] + }, + "活动-三界奇缘-结束": { + + "action": "Custom", + "custom_action": "returnOCR", + "custom_action_param": { + "recognition_name": "活动-三界奇缘-正确率", + "action_key": "", + "return_text": "三界奇缘结果正确率:" + } + }, +``` +## 依赖 + + - json + - loguru + + + +## 注意事项 + + - 注意的值"custom_action_param" + + +feat(customs): 新增在UI日志界面上显示OCR结果。 \ No newline at end of file diff --git a/Storage/customs/gitlihang/output-ocr-result-log/action/__init__.py b/Storage/customs/gitlihang/output-ocr-result-log/action/__init__.py new file mode 100644 index 0000000..fb575e8 --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/action/__init__.py @@ -0,0 +1 @@ +from .returnOCR import * diff --git a/Storage/customs/gitlihang/output-ocr-result-log/action/returnOCR.py b/Storage/customs/gitlihang/output-ocr-result-log/action/returnOCR.py new file mode 100644 index 0000000..88b786c --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/action/returnOCR.py @@ -0,0 +1,74 @@ +from maa.agent.agent_server import AgentServer +from maa.custom_action import CustomAction +from maa.context import Context +import json + +from utils import logger + +@AgentServer.custom_action("returnOCR") +class ReturnOCR(CustomAction): + """ + 自定义返回ocr识别结果,根据action_key执行不同的动作 + "custom_action_param": { + "action_key": "Click", + "recognition_name": "识别输出测试", + "return_text": "输出的描述" + "click_target": [] # 点击坐标,格式为[x1, y1, x2, y2],仅在action_key为Click时使用 + } + action_key: 动作名称,用于判断动作类型,如Click、Move等 + recognition_name: task任务名称,用于指定识别任务名称,返回该节点的结果。 + return_text: 输出的描述,用于指定返回的描述 + click_target: 点击坐标,格式为[x1, y1, x2, y2],仅在action_key为Click时使用。如果不提供click_target,则默认点击识别结果的中心位置。 + + """ + def run( + self, + context: Context, + argv: CustomAction.RunArg, + ) -> CustomAction.RunResult: + # logger.info("进入returnOCR") + # 解析自定义参数,并判断是否为空 + argv_dict: dict = json.loads(argv.custom_action_param) + if not argv_dict: + return CustomAction.RunResult(success=True) + # 获取自定义参数 + action_key = argv_dict.get("action_key", "") + recognition_name = argv_dict.get("recognition_name", "") + return_text = argv_dict.get("return_text", "") + click_target = argv_dict.get("click_target", []) + + # 获取ocr识别结果数据 + image = context.tasker.controller.post_screencap().wait().get() + reco_result = context.run_recognition( + recognition_name, + image + ) + # 打印OCR识别结果 + if reco_result and reco_result.hit: + best_result = reco_result.best_result + # 输出到ui界面 + logger.info(f"{return_text}: {best_result.text}") + # 根据action_key执行不同的动作 + if action_key == "Click": + # 点击传入参数中的坐标位置 + if click_target: + box = click_target + center_x = box[0] + box[2] // 2 + center_y = box[1] + box[3] // 2 + logger.debug(f"点击位置: ({center_x}, {center_y})") + context.tasker.controller.post_click(center_x, center_y).wait() + # 点击最佳识别结果的中心位置 + elif best_result: + box = best_result.box + center_x = box[0] + box[2] // 2 + center_y = box[1] + box[3] // 2 + logger.debug(f"点击位置: ({center_x}, {center_y})") + context.tasker.controller.post_click(center_x, center_y).wait() + else: + logger.warning("没有识别到结果,无法执行点击") + elif action_key == "": + logger.debug(f"仅返回OCR数据,不执行动作: {action_key}") + else: + logger.warning(f"OCR识别失败 - 任务名称: {recognition_name}") + + return CustomAction.RunResult(success=True) \ No newline at end of file diff --git a/Storage/customs/gitlihang/output-ocr-result-log/maahub_meta.json b/Storage/customs/gitlihang/output-ocr-result-log/maahub_meta.json new file mode 100644 index 0000000..b8b300a --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/maahub_meta.json @@ -0,0 +1,20 @@ +{ + "id": "gitlihang/output-ocr-result-log", + "title": "在UI日志界面上显示OCR结果", + "description": "在UI界面上显示OCR识别结果,方便用户查看任务进度和结果。", + "author": "gitlihang", + "source": "Maa_MHXY_MG", + "sourceGithub": "https://github.com/gitlihang/Maa_MHXY_MG", + "tags": ["ocr-result", "log", "ui"], + "createdAt": "2026-06-04", + "updatedAt": "2026-06-04", + "version": "1.0.0", + "mfwVersion": "5.X", + "entry": "main.py", + "readme": "./README.md", + "status": "stable", + "type": "custom", + "language": "python", + "runtime": "python 3.12.10", + "dependencies": ["loguru","json"] +} diff --git a/Storage/customs/gitlihang/output-ocr-result-log/main.py b/Storage/customs/gitlihang/output-ocr-result-log/main.py new file mode 100644 index 0000000..e69de29 diff --git a/Storage/customs/gitlihang/output-ocr-result-log/pipeline.json b/Storage/customs/gitlihang/output-ocr-result-log/pipeline.json new file mode 100644 index 0000000..7505e2e --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/pipeline.json @@ -0,0 +1,20 @@ +{ + "OCR识别接口": { + "recognition": "OCR", + "expected": [], + "roi": [ + 320, + 69, + 116, + 51 + ] + },"活动-三界奇缘-结束": { + "action": "Custom", + "custom_action": "returnOCR", + "custom_action_param": { + "recognition_name": "OCR识别接口", + "action_key": "", + "return_text": "三界奇缘结果正确率:" + } + } +} diff --git a/Storage/customs/gitlihang/output-ocr-result-log/utils/__init__.py b/Storage/customs/gitlihang/output-ocr-result-log/utils/__init__.py new file mode 100644 index 0000000..7823c3c --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/utils/__init__.py @@ -0,0 +1 @@ +from .logger import logger \ No newline at end of file diff --git a/Storage/customs/gitlihang/output-ocr-result-log/utils/logger.py b/Storage/customs/gitlihang/output-ocr-result-log/utils/logger.py new file mode 100644 index 0000000..8ac9d3f --- /dev/null +++ b/Storage/customs/gitlihang/output-ocr-result-log/utils/logger.py @@ -0,0 +1,84 @@ +import os +import sys + +try: + from loguru import logger as _logger + + def setup_logger(log_dir="debug/custom", console_level="INFO"): + """设置 loguru logger + + Args: + log_dir: 日志文件目录 + console_level: 控制台输出等级 (DEBUG, INFO, WARNING, ERROR) + """ + os.makedirs(log_dir, exist_ok=True) + _logger.remove() + + # 定义日志级别的简短格式 + def format_level(record): + level_map = { + "INFO": "info", + "ERROR": "err", + "WARNING": "warn", + "DEBUG": "debug", + "CRITICAL": "critical", + "SUCCESS": "success", + "TRACE": "trace", + } + record["extra"]["level_short"] = level_map.get( + record["level"].name, record["level"].name.lower() + ) + return True + + _logger.add( + sys.stderr, + format="{extra[level_short]}:{message}", + colorize=True, + level=console_level, + filter=format_level, + ) + _logger.add( + f"{log_dir}/{{time:YYYY-MM-DD}}.log", + rotation="00:00", # midnight + retention="2 weeks", + compression="zip", + level="DEBUG", + format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} | {message}", + encoding="utf-8", + enqueue=True, + backtrace=True, # 包含完整的异常回溯信息 + diagnose=True, # 包含变量值信息 + ) + return _logger + + def change_console_level(level="DEBUG"): + """动态修改控制台日志等级""" + setup_logger(console_level=level) + _logger.info(f"控制台日志等级已更改为: {level}") + + logger = setup_logger() +except ImportError: + import logging + + class ShortLevelFormatter(logging.Formatter): + """自定义 Formatter,使用简短的日志级别名称""" + + level_map = { + "INFO": "info", + "ERROR": "err", + "WARNING": "warn", + "DEBUG": "debug", + "CRITICAL": "critical", + } + + def format(self, record): + record.level_short = self.level_map.get( + record.levelname, record.levelname.lower() + ) + return super().format(record) + + handler = logging.StreamHandler(sys.stderr) + handler.setFormatter(ShortLevelFormatter("%(level_short)s:%(message)s")) + logging.root.addHandler(handler) + logging.root.setLevel(logging.INFO) + logger = logging \ No newline at end of file