Release v6.3.7 (#15872)

## Summary by Sourcery

更新战场击杀数识别逻辑,改为使用基于区域的 OCR,并缓存击杀标记位置,同时改进相关模板匹配;另外刷新安装和新手文档,并调整 Markdown
链接检查的路径。

Bug Fixes(错误修复):
- 通过将击杀标记检测与数字识别分离,并更健壮地处理缺失或空的 OCR 结果,提高击杀数 OCR 的识别效果。
- 修复击杀数和费用识别的缓存命中检查,以正确比较前一帧和当前帧的尺寸。

Enhancements(功能增强):
- 从缓存命中检查中返回击杀标记位置,以便在后续 OCR 中复用已检测的区域。
- 在检测击杀标记时应用高斯模糊和自定义模板匹配配置,以提高匹配的可靠性。
- 调整文档页面的排序和图标,以改善导航体验。

CI:
- 配置 Markdown 链接检查工具,将文档的公开目录作为内部链接校验的根目录。

Documentation(文档):
- 拆分并扩展 GUI 安装说明,提供专门的下载与安装指南,并相应更新新手引导步骤。
- 明确用户的日志与反馈指引,包括如何生成可共享的日志归档。
- 更新 CLI 安装页面图标,并调整文档导航顺序。

Chores(杂务):
- 为 TXWY AT 关卡配置新增一个 resource/tasks JSON 文件。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Update battlefield kill count recognition to use region-based OCR with
cached kill-flag location and add related template matching
improvements, while refreshing installation and newbie documentation and
adjusting markdown link checking paths.

Bug Fixes:
- Improve kill count OCR by separating kill-flag detection from number
recognition and handling missing or empty OCR results more robustly.
- Fix cache hit checks for kill count and cost recognition to properly
compare previous and current frame sizes.

Enhancements:
- Return kill-flag position from cache hit checks to reuse detected
regions for subsequent OCR.
- Apply Gaussian blur and custom template matching configuration when
detecting the kill-flag to improve match reliability.
- Adjust ordering and icons of documentation pages for better
navigation.

CI:
- Configure markdown link checker to use the docs public directory as
its root for internal link validation.

Documentation:
- Split and expand GUI installation instructions into a dedicated
download and install guide, updating newbie onboarding steps
accordingly.
- Clarify logging and feedback guidance for users, including generating
shareable log archives.
- Update CLI install page icon and documentation navigation order.

Chores:
- Add a new resource/tasks JSON file for TXWY AT stage configuration.

</details>
This commit is contained in:
Status102
2026-02-28 16:09:33 +08:00
committed by GitHub
15 changed files with 255 additions and 55 deletions

View File

@@ -47,6 +47,7 @@ jobs:
with:
# 仅检查内部链接,排除所有外部链接
args: >
--root-dir ./docs/.vuepress/public
--verbose --no-progress
--cache --max-cache-age 1d --cache-exclude-status '429, 500..599'
--exclude 'https?://.*'

View File

@@ -1,4 +1,4 @@
## v6.3.6
## v6.3.7
### 拉电线不知天地为何物 | Highlight
@@ -65,6 +65,27 @@ We warmly welcome community developers to assist with adaptation and submit impr
以下是详细内容:
## v6.3.7
### 改进 | Improved
* 重构战斗中击杀识别, 击杀图标匹配使用高斯模糊预处理 (#15868) @status102
### 修复 | Fix
* 自动战斗最右侧单位无法识别 @status102
* 自动战斗击杀数识别便宜 @status102
* correct_rect在x y负方向超限不完全的情况下, 校正后width height错误 @status102
### 文档 | Docs
* 修族谱 (#15856) @JasonHuang79
* Add dedicated "MAA GUI download and installation guide" (#15138) @JasonHuang79 @Rbqwow
### 其他 | Other
* 繁中服「墟」活動導航 (#15849) @momomochi987
## v6.3.6
### 改进 | Improved

View File

@@ -1,6 +1,6 @@
---
order: 1
icon: material-symbols:download
icon: material-symbols:download-2-rounded
---
# 安装及编译

View File

@@ -1,5 +1,5 @@
---
order: 3
order: 4
icon: mdi:plug
---

View File

@@ -1,5 +1,5 @@
---
order: 2
order: 3
icon: ph:question-fill
---

View File

@@ -0,0 +1,93 @@
---
order: 2
icon: material-symbols:download-2-rounded
---
<!-- markdownlint-disable MD024 -->
# 下载与安装
::: tip
你正在查阅 MAA GUI 的下载与安装说明文档。若你需要查阅 maa-cli 的下载与安装说明,请访问 maa-cli 的 [安装及编译](./cli/install.md) 文档。目前安卓牛牛MAA 安卓版)已开放测试,请前往 [MAA-Meow](https://github.com/Aliothmoon/MAA-Meow) 了解更多情况。
:::
## 下载 MAA
MAA 提供多种下载方式,包括官网下载,从包管理器安装,群文件下载等方式。请选择适合你的方式进行下载。
### 通过 [官网](https://maa.plus) 下载最新的 MAA 软件包
官网一般会自动选择正确的版本架构,对于大多数阅读本文的用户来说,应为 Windows x64。对于阅读本文的 macOS 用户来说,应下载 macOS 通用版。
### 通过 [Mirror酱](https://mirrorchyan.com/zh/projects?rid=MAA&source=maadocs-install) 下载最新的 MAA 软件包
请确认系统架构并下载对应的软件包。对于大多数阅读本文的 Windows 用户来说,应为 Windows x64。对于阅读本文的 Mac 用户Mirror酱 不提供通用软件包请确认您的芯片架构arm/x86后下载对应的软件包。
::: tip
[Mirror酱](https://mirrorchyan.com/zh/projects?rid=MAA&source=maadocs-install) 是独立的第三方下载加速服务,需要付费使用,而非 MAA 收费。其运营成本由订阅收入支撑,部分收益将回馈项目开发者。欢迎订阅 CDK 享受高速下载,同时支持项目持续开发。
:::
### 使用 Windows 包管理器Winget安装
::: tip
本方法仅适用于使用 Windows 的用户
:::
请在终端中运行以下命令:
```bash
winget install maa
```
通过此方式安装的默认安装路径为 `C:\Users\用户名\AppData\Local\Microsoft\WinGet\Packages`
### 通过 QQ 群文件下载最新的 MAA 软件包
1. 加入 [MAA 官方 QQ 群](https://api.maa.plus/MaaAssistantArknights/api/qqgroup/index.html)
2. 在群文件中找到最新的 MAA 软件包进行下载。
### 通过 [GitHub Releases](https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases) 下载最新的 MAA 软件包
请确认系统架构并下载对应的软件包。对于大多数阅读本文的 Windows 用户来说,应为 `MAA-<版本号>-win-x64.zip`。对于阅读本文的 macOS 用户来说,应选择 `MAA-<版本号>-macos-universal.dmg`
## Linux 和其他操作系统
MAA GUI **暂未支持** Linux 和其他操作系统。你可以使用 **maa-cli** 来在这些系统上使用 MAA 的功能。请访问 maa-cli 的 [安装及编译](./cli/install.md) 文档了解更多信息。
## 安装 MAA
### Windows
下载完成后,你会得到一个 `.zip` 文件。用解压缩软件将其完全解压后,会得到一个包含 MAA 所有文件的文件夹。
::: warning
1.请不要将 MAA 解压到如 `C:\``C:\Program Files\` 等需要 UAC 权限的路径。
2.MAA 需要 Visual C++ Redistributable x64VCRedist x64和 .NET 10 。请在解压后的 MAA 目录中以管理员身份运行 `DependencySetup_依赖库安装.bat` 来安装这些依赖。安装完成后再运行 `maa.exe`
更多信息请参考[常见问题](./faq.md)置顶。
:::
双击 `MAA.exe` 即可启动 MAA。
::: tip
通过 Windows 包管理器Winget安装的用户可直接在命令行中输入 `maa` 来启动 MAA 而无需解压缩、安装运行库等额外操作。若 PATH 中有 `maa-cli`,可能需要额外步骤以区分二者。
:::
### macOS
下载完成后,你会得到一个 `.dmg` 文件。双击打开该 `.dmg`,将 `MAA.app` 拖入 `/Applications` 以完成安装。
## 后续步骤
安装完成后,请返回 [新手上路](./newbie.md) 继续配置,或前往 [功能介绍](./introduction/) 查看 MAA 支持的各项功能吧!若您在安装中遇到问题,可以尝试查阅 [常见问题](./faq.md) 来解决问题。

View File

@@ -17,28 +17,19 @@ icon: ri:guide-fill
非 Windows 用户请参阅[模拟器及设备支持](./device/)。
2. 下载正确的版本
[MAA 官网](https://maa.plus/)一般会自动选择正确的版本架构,对于大多数阅读本文的用户来说,应为 Windows x64。
3. 正确解压
确认解压完整,并确保将 MAA 解压到一个独立的文件夹中。请勿将 MAA 解压到如 `C:\``C:\Program Files\` 等需要 UAC 权限的路径。
4. 安装运行库
MAA 需要 VCRedist x64 和 .NET 10请运行 MAA 目录下的 `DependencySetup_依赖库安装.bat` 以安装。
更多信息请参考[常见问题](./faq.md)置顶。
5. 确认模拟器支持
2. 确认模拟器支持
查阅[模拟器和设备支持](./device/),确认正在使用的模拟器支持情况。
6. 正确设置模拟器分辨率
3. 下载与安装
查阅[下载与安装](./install.md)。
4. 正确设置模拟器分辨率
模拟器分辨率应为横屏的 `1280x720``1920x1080`对于美服YosterEN玩家必须为 `1920x1080`
::::
::::
## 初始配置
@@ -64,12 +55,10 @@ icon: ri:guide-fill
**遇到问题多看文档多看文档多看文档** &emsp;&emsp; **遇到问题多看文档多看文档多看文档** &emsp;&emsp; **遇到问题多看文档多看文档多看文档**
:::
1. 运行日志位于 MAA 文件夹下的 `debug` 文件夹内。日志很重要,非常重要。向他人寻求帮助时,请务必带着 `asst.log``gui.log` 一同前去。
1. 使用 `设置` - `问题反馈` - `生成日志压缩包` 可生成便于共享的日志压缩包。日志很重要,非常重要。向他人寻求帮助时,请务必带着日志压缩包一同前去。
2. MAA 为了尽可能满足所有用户的各种需求,提供了大量的自定义选项。在认为 MAA 缺少了某项功能时,有可能只是没找到而不是 MAA 没有,如 `手动输入关卡名``不将已进驻的干员放入宿舍`
3. 将鼠标指针放在某些选项上会给出详细说明,如 `不将已进驻的干员放入宿舍`
3. 对于部分复选框,在其上右键单击可以将其变为半选状态,这种状态下的复选框会在 MAA 下一次启动时自动清除,可以理解为 `仅一次`
4. 对于部分复选框来说,在其上右键单击可以将其变为半选状态,这种状态下的复选框会在 MAA 下一次启动时自动清除,可以理解为 `仅一次`
5. ~~如果不知道自己需要什么,不方便解决有可能会出现的问题,请勿使用内测版。~~
4. ~~如果不知道自己需要什么,不方便解决有可能会出现的问题,请勿使用内测版。~~

View File

@@ -0,0 +1,8 @@
{
"AT-OpenOcr": {
"text": ["墟", "鍛冶", "舊跡", "絕緣", "石階"]
},
"ATChapterToAT": {
"text": ["不夜街區"]
}
}

View File

@@ -3771,7 +3771,7 @@
"algorithm": "OcrDetect",
"isAscii": true,
"text": [],
"roi": [50, -20, 100, 40],
"roi": [50, 0, 100, 40],
"useRaw": false,
"specialParams_doc": "Kill区域变化阈值, 模板匹配, 100=1.0; 正常在 0.997-1 之间波动, 少有0.995; _5->_6 的分数最高, 可达0.94",
"specialParams": [98]
@@ -3816,7 +3816,7 @@
},
"BattleOpersFlag": {
"templThreshold": 0.65,
"roi": [35, 588, 1210, 18],
"roi": [35, 588, 1245, 18],
"maskRange": [1, 255]
},
"BattleAvatarReMatch": {
@@ -3934,6 +3934,12 @@
"text": [],
"roi": [73, 378, 221, 31]
},
"StageDrops-NoDropCheck": {
"template": "empty.png",
"roi": [60, 510, 150, 160],
"specialParams_Doc": "灰度过滤阈值",
"specialParams": [120]
},
"StageDrops-BaseLine": {
"template": "empty.png",
"roi": [0, 636, 1280, 2],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,6 +1,9 @@
#include "DebugTask.h"
#include "MaaUtils/Encoding.h"
#include <boost/regex.hpp>
#include <filesystem>
#include <shared_mutex>
#include "Common/AsstTypes.h"
#include "Config/TaskData.h"
@@ -9,7 +12,10 @@
#include "Utils/Logger.hpp"
#include "Vision/Battle/BattlefieldClassifier.h"
#include "Vision/Battle/BattlefieldMatcher.h"
#include "Vision/BestMatcher.h"
#include "Vision/FeatureMatcher.h"
#include "Vision/Matcher.h"
#include "Config/TemplResource.h"
#include "Vision/Miscellaneous/DepotImageAnalyzer.h"
#include "Vision/Miscellaneous/StageDropsImageAnalyzer.h"
#include "Vision/MultiMatcher.h"
@@ -23,6 +29,42 @@ asst::DebugTask::DebugTask(const AsstCallback& callback, Assistant* inst) :
bool asst::DebugTask::run()
{
auto image_dep = MaaNS::imread(utils::path("C:\\Users\\status102\\Desktop\\deploy.png"));
static const std::unordered_map<std::string, battle::Role> RoleMap = {
{ "Caster", battle::Role::Caster }, { "Medic", battle::Role::Medic }, { "Pioneer", battle::Role::Pioneer },
{ "Sniper", battle::Role::Sniper }, { "Special", battle::Role::Special }, { "Support", battle::Role::Support },
{ "Tank", battle::Role::Tank }, { "Warrior", battle::Role::Warrior }, { "Drone", battle::Role::Drone },
};
auto image = MaaNS::imread(utils::path("C:\\Users\\status102\\Desktop\\kill_1080p_Vec.png"));
cv::Mat template_image = TemplResource::get_instance().get_templ("BattleKillsFlag.png");
auto task = Task.get("BattleKillsFlag");
// 对模板进行高斯模糊预处理(解决小尺寸模板的细线条缩放问题)
cv::Mat template_blurred;
cv::GaussianBlur(template_image, template_blurred, cv::Size(3, 3), 0);
// 同时对输入图像应用相同的模糊处理
cv::Mat image_blurred;
cv::GaussianBlur(make_roi(image, task->roi), image_blurred, cv::Size(3, 3), 0);
// 使用模糊后的图像进行匹配
Matcher analyzer(image_blurred);
analyzer.set_task_info("BattleKillsFlag");
analyzer.set_roi({ 0, 0, task->roi.width, task->roi.height });
if (!analyzer.analyze()) {
Log.info("flag not found");
}
// 使用模糊后的图像进行匹配
Matcher analyzer2(image_blurred);
analyzer2.set_task_info("BattleKillsFlag");
analyzer2.set_templ(template_blurred);
analyzer2.set_roi({ 0, 0, task->roi.width, task->roi.height });
if (!analyzer2.analyze()) {
Log.info("flag not found");
}
return true;
}

View File

@@ -273,17 +273,48 @@ bool BattlefieldMatcher::kills_flag_analyze() const
BattlefieldMatcher::MatchResult<std::pair<int, int>> BattlefieldMatcher::kills_analyze() const
{
if (hit_kills_cache()) {
const auto& flag_task = Task.get<MatchTaskInfo>("BattleKillsFlag");
cv::Mat template_image = TemplResource::get_instance().get_templ("BattleKillsFlag.png");
cv::Mat match_image = make_roi(m_image, flag_task->roi);
// 对模板图和截图都进行一次模糊减少yj缩放对文字及细节的影响提升命中率
cv::Mat template_blur, image_blur;
cv::GaussianBlur(template_image, template_blur, cv::Size(3, 3), 0);
cv::GaussianBlur(match_image, image_blur, cv::Size(3, 3), 0);
Matcher flag_match(image_blur);
flag_match.set_mask_ranges(flag_task->mask_ranges);
flag_match.set_roi({ 0, 0, flag_task->roi.width, flag_task->roi.height });
flag_match.set_templ(template_blur);
flag_match.set_method(MatchMethod::Ccoeff);
flag_match.set_threshold(flag_task->templ_thresholds);
if (!flag_match.analyze()) {
LogWarn << __FUNCTION__ << "kill flag not found";
return {};
}
auto flag_rect = flag_match.get_result().rect;
flag_rect.x += flag_task->roi.x;
flag_rect.y += flag_task->roi.y;
if (hit_kills_cache(flag_rect)) {
return { .status = MatchStatus::HitCache };
}
TemplDetOCRer kills_analyzer(m_image);
kills_analyzer.set_task_info("BattleKillsFlag", "BattleKills");
kills_analyzer.set_replace(Task.get<OcrTaskInfo>("NumberOcrReplace")->replace_map);
auto kills_opt = kills_analyzer.analyze();
const auto& ocr_task = Task.get<OcrTaskInfo>("BattleKills");
RegionOCRer flag_analyzer(m_image);
flag_analyzer.set_roi(flag_rect.move(ocr_task->roi));
flag_analyzer.set_use_char_model(ocr_task->is_ascii);
flag_analyzer.set_use_raw(ocr_task->use_raw);
flag_analyzer.set_replace(Task.get<OcrTaskInfo>("NumberOcrReplace")->replace_map);
auto kills_opt = flag_analyzer.analyze();
if (!kills_opt) {
return {};
}
const std::string& kills_text = kills_opt->front().text;
const std::string& kills_text = kills_opt->text;
if (kills_text.empty()) {
return {};
}
size_t pos = kills_text.find('/');
if (pos == std::string::npos) {
@@ -338,22 +369,17 @@ BattlefieldMatcher::MatchResult<std::pair<int, int>> BattlefieldMatcher::kills_a
return { .value = std::make_pair(kills, total_kills), .status = MatchStatus::Success };
}
bool asst::BattlefieldMatcher::hit_kills_cache() const
bool asst::BattlefieldMatcher::hit_kills_cache(Rect flag_rect) const
{
if (m_image_prev.empty() || m_image.cols != m_image_prev.cols || m_image.rows != m_image_prev.rows) {
if (m_image_prev.empty() || m_image.size != m_image_prev.size) {
return false;
}
Matcher flag_match(m_image);
flag_match.set_task_info("BattleKillsFlag");
if (!flag_match.analyze()) {
return false;
}
const auto& flag_rect = flag_match.get_result().rect;
const auto& task = Task.get("BattleKills");
const auto& roi = flag_rect.move(task->roi);
cv::Mat kills_image_cache = make_roi(m_image_prev, roi);
cv::Mat kills_image = make_roi(m_image, roi);
const auto& ocr_task = Task.get<OcrTaskInfo>("BattleKills");
const auto& roi = flag_rect.move(ocr_task->roi);
cv::Mat kills_image_cache = make_roi(m_image_prev, correct_rect(roi, m_image_prev));
cv::Mat kills_image = make_roi(m_image, correct_rect(roi, m_image));
cv::cvtColor(kills_image_cache, kills_image_cache, cv::COLOR_BGR2GRAY);
cv::cvtColor(kills_image, kills_image, cv::COLOR_BGR2GRAY);
cv::Mat match;
@@ -362,7 +388,7 @@ bool asst::BattlefieldMatcher::hit_kills_cache() const
cv::minMaxLoc(match, nullptr, &mark);
// 正常在 0.997-1 之间波动, 少有0.995
// _5->_6 的分数最高, 可达0.94
const double threshold = static_cast<double>(task->special_params[0]) / 100;
const double threshold = static_cast<double>(ocr_task->special_params[0]) / 100;
return mark > threshold;
}
@@ -394,7 +420,7 @@ BattlefieldMatcher::MatchResult<int> BattlefieldMatcher::costs_analyze() const
bool asst::BattlefieldMatcher::hit_costs_cache() const
{
if (m_image_prev.empty() || m_image.cols != m_image_prev.cols || m_image.rows != m_image_prev.rows) {
if (m_image_prev.empty() || m_image.size != m_image_prev.size) {
return false;
}
const auto& task = Task.get("BattleCostData");

View File

@@ -68,11 +68,11 @@ protected:
bool oper_available_analyze(const Rect& roi) const;
MatchResult<std::pair<int, int>> kills_analyze() const; // 识别击杀数
// 识别是否持有费用是否命中缓存
bool hit_kills_cache() const;
// 识别击杀数是否命中缓存
bool hit_kills_cache(Rect flag_rect) const;
bool cost_symbol_analyze() const; // 识别费用左侧图标
MatchResult<int> costs_analyze() const; // 识别费用
// 识别是否持有费用是否命中缓存
// 识别持有费用是否命中缓存
bool hit_costs_cache() const;
bool in_detail_analyze() const; // 识别是否在详情页
bool speed_button_analyze() const; // 识别是否有加速按钮(在详情页就没有)

View File

@@ -14,12 +14,18 @@ using namespace asst;
VisionHelper::VisionHelper(const cv::Mat& image, const Rect& roi, Assistant* inst) :
InstHelper(inst),
m_image(image),
m_image(image)
#ifdef ASST_DEBUG
m_image_draw(image.clone()),
,
m_image_draw(image.clone())
#endif
m_roi(correct_rect(roi, image))
{
if (roi.empty()) {
m_roi = { 0, 0, image.cols, image.rows };
}
else {
m_roi = correct_rect(roi, image);
}
}
void VisionHelper::set_image(const cv::Mat& image)
@@ -90,6 +96,14 @@ Rect VisionHelper::correct_rect(const Rect& rect, const cv::Mat& image)
}
Rect res = rect;
if (res.x < 0) {
res.x = 0;
res.width += rect.x;
}
if (res.y < 0) {
res.y = 0;
res.height += rect.y;
}
res.x = std::clamp(res.x, 0, image.cols - 1);
res.y = std::clamp(res.y, 0, image.rows - 1);
res.width = std::clamp(res.width, 1, image.cols - res.x);

View File

@@ -2653,7 +2653,7 @@
"resource/template/Battle/BattleFlag/EndOfAction": "3a4e90ae88d0ff83304c793bd05695335888d67d0b3224066a22b465c5f53f0b",
"resource/template/Battle/BattleFlag/BattleSpeedUp": "f81a6ea54970ec3fdfbea14c2887e067e60ca3ba7d3c7168da7b19c5a62c91ca",
"resource/template/Battle/BattleFlag/BattleSkillReadyOnClick-TopView2": "9de4d366eab581c30dfde35346a94197727a6397e7c8f99da07a828f70b89607",
"resource/template/Battle/BattleFlag/BattleKillsFlag": "e167dd5a8ef789c7626eb463110d1899d1987a84003ff9d15fa9a4ddf34103f5",
"resource/template/Battle/BattleFlag/BattleKillsFlag": "7b5db70e0fd69ea0f09db643d6e3c31999fe521a6439fe3ab47aa85a014f3b59",
"resource/template/Battle/BattleFlag/AbandonAction": "052a60fec1eb98ff3ab12814e40426ff682377b89c9ee844d3cff210c662559e",
"resource/template/Battle/BattleFlag/BattleSkillReady": "6dcb381619811d91419ffda02fa4764908e3d61107abe2ecd0c8e92b631a6746",
"resource/template/Battle/BattleFlag/PrtsErrorConfirm": "57ce7a88d0a231a8b77ea24c7f69e4cce65038095dda68d6782afddbcd37566e",