diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj
index 9b4ae492f1..b73052e7f1 100644
--- a/src/MaaCore/MaaCore.vcxproj
+++ b/src/MaaCore/MaaCore.vcxproj
@@ -35,6 +35,7 @@
+
@@ -120,7 +121,6 @@
-
@@ -150,6 +150,7 @@
+
diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters
index 9862636263..40f5d8e07a 100644
--- a/src/MaaCore/MaaCore.vcxproj.filters
+++ b/src/MaaCore/MaaCore.vcxproj.filters
@@ -270,9 +270,6 @@
源文件\Utils
-
- 源文件\Utils
-
源文件\Utils
@@ -429,6 +426,9 @@
源文件\Vision\Miscellaneous
+
+ 源文件\Vision\Miscellaneous
+
@@ -710,5 +710,8 @@
源文件\Vision\Miscellaneous
+
+ 源文件\Vision\Miscellaneous
+
\ No newline at end of file
diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp
index 85a9b79e07..64420167ac 100644
--- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp
+++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp
@@ -7,17 +7,16 @@
#include "Utils/NoWarningCV.h"
-#include "Controller.h"
#include "Config/Miscellaneous/CopilotConfig.h"
#include "Config/Miscellaneous/TilePack.h"
#include "Config/TaskData.h"
-#include "Config/TemplResource.h"
-#include "Vision/MatchImageAnalyzer.h"
-#include "Vision/Miscellaneous/BattleImageAnalyzer.h"
-#include "Vision/OcrWithPreprocessImageAnalyzer.h"
+#include "Controller.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
-#include "Utils/MoreCV.hpp"
+#include "Vision/MatchImageAnalyzer.h"
+#include "Vision/Miscellaneous/BattleImageAnalyzer.h"
+#include "Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.h"
+#include "Vision/OcrWithPreprocessImageAnalyzer.h"
#include "Utils/ImageIo.hpp"
@@ -653,21 +652,16 @@ bool asst::BattleProcessTask::wait_to_end(const BattleAction& action)
bool asst::BattleProcessTask::try_possible_skill(const cv::Mat& image)
{
- auto task_ptr = Task.get("BattleAutoSkillFlag");
-
- const Rect& skill_roi_move = task_ptr->rect_move;
- cv::Mat temp = TemplResource::get_instance().get_templ(task_ptr->templ_name);
-
+ BattleSkillReadyImageAnalyzer skill_analyzer(image);
bool used = false;
for (auto& info : m_used_opers | views::values) {
if (info.info.skill_usage != BattleSkillUsage::Possibly && info.info.skill_usage != BattleSkillUsage::Once) {
continue;
}
- auto roi = make_rect(Rect { info.pos.x, info.pos.y, 0, 0 }.move(skill_roi_move));
- roi &= cv::Rect(0, 0, image.cols, image.rows);
-
- const auto result = find_skill_ready(image(roi), temp);
- if (result.empty()) continue;
+ skill_analyzer.set_base_point(info.pos);
+ if (!skill_analyzer.analyze()) {
+ continue;
+ }
ctrler()->click(info.pos);
sleep(Task.get("BattleUseOper")->pre_delay);
diff --git a/src/MaaCore/Task/Roguelike/RoguelikeBattleTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/RoguelikeBattleTaskPlugin.cpp
index d17187b198..2e332d1b5c 100644
--- a/src/MaaCore/Task/Roguelike/RoguelikeBattleTaskPlugin.cpp
+++ b/src/MaaCore/Task/Roguelike/RoguelikeBattleTaskPlugin.cpp
@@ -10,15 +10,14 @@
#include "Config/Miscellaneous/TilePack.h"
#include "Config/Roguelike/RoguelikeCopilotConfig.h"
#include "Config/TaskData.h"
-#include "Config/TemplResource.h"
#include "Controller.h"
#include "Status.h"
#include "Task/ProcessTask.h"
#include "Utils/ImageIo.hpp"
#include "Utils/Logger.hpp"
-#include "Utils/MoreCV.hpp"
#include "Vision/MatchImageAnalyzer.h"
#include "Vision/Miscellaneous/BattleImageAnalyzer.h"
+#include "Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.h"
#include "Vision/OcrWithPreprocessImageAnalyzer.h"
bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& details) const
@@ -902,11 +901,7 @@ bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
return false;
}
- auto task_ptr = Task.get("BattleAutoSkillFlag");
-
- const Rect& skill_roi_move = task_ptr->rect_move;
- cv::Mat temp = TemplResource::get_instance().get_templ(task_ptr->templ_name);
-
+ BattleSkillReadyImageAnalyzer skill_analyzer(image);
bool used = false;
for (auto& [loc, oper_name] : m_used_tiles) {
std::string status_key = Status::RoguelikeSkillUsagePrefix + oper_name;
@@ -920,16 +915,11 @@ bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
continue;
}
const Point pos = m_normal_tile_info.at(loc).pos;
- const Rect pos_rect(pos.x, pos.y, 1, 1);
- auto roi = make_rect(pos_rect.move(skill_roi_move));
- roi &= cv::Rect(0, 0, image.cols, image.rows);
-
- auto result = find_skill_ready(image(roi), temp);
- if (result.empty()) continue;
-
- Log.trace(result.front().second);
-
- ctrler()->click(pos_rect);
+ skill_analyzer.set_base_point(pos);
+ if (!skill_analyzer.analyze()) {
+ continue;
+ }
+ ctrler()->click(pos);
sleep(Task.get("BattleUseOper")->pre_delay);
bool ret = ProcessTask(*this, { "BattleSkillReadyOnClick" }).set_retry_times(2).run();
if (!ret) {
diff --git a/src/MaaCore/Utils/MoreCV.hpp b/src/MaaCore/Utils/MoreCV.hpp
deleted file mode 100644
index 998fa154d0..0000000000
--- a/src/MaaCore/Utils/MoreCV.hpp
+++ /dev/null
@@ -1,79 +0,0 @@
-#pragma once
-
-#include "Utils/NoWarningCV.h"
-
-#include
-#include
-#include
-
-namespace asst
-{
- inline void key_color(cv::InputArray src, cv::OutputArray dst, const cv::Scalar& color,
- const cv::Scalar& tolerance = {})
- {
- return cv::inRange(src, color - tolerance, color + tolerance, dst);
- }
-
- template >
- inline auto match_template_helper(
- const cv::Mat1f& image, const cv::Size& repel_box = { 0, 0 }, Pred&& filter = [](auto&&) { return true; },
- Compare&& cmp = Compare {})
- {
- using value_type = std::pair;
- std::vector result {};
- for (auto i = 0; i < image.cols; ++i)
- for (auto j = 0; j < image.rows; ++j) {
- const cv::Point pt = { i, j };
- const auto value = image.at(pt);
- if (!filter(value)) continue;
- auto iter = std::find_if(result.rbegin(), result.rend(), [&](const value_type& pair) {
- const auto diff = pair.first - pt;
- return std::abs(diff.x) < repel_box.width && std::abs(diff.y) < repel_box.height;
- });
- if (iter != result.rend()) {
- if (cmp(iter->second, value)) {
- iter->first = pt;
- iter->second = value;
- }
- }
- else {
- result.emplace_back(pt, value);
- }
- }
- return result;
- }
-
- inline auto find_skill_ready(const cv::Mat& image, const cv::Mat& temp)
- {
- auto preprocess = [](cv::Mat &img) {
- cv::Mat mask_y2;
- key_color(img, mask_y2, { 40, 94, 103 }, { 10, 10, 10 }); // BGR
- img.setTo(cv::Scalar { 2, 216, 255 }, mask_y2); // BGR
- cv::Mat mask_y1;
- key_color(img, mask_y1, { 2, 216, 255 }, { 20, 20, 20 });
- cv::dilate(mask_y1, mask_y1, cv::getStructuringElement(cv::MORPH_RECT, { 4, 4 }));
- cv::bitwise_not(mask_y1, mask_y1);
- img.setTo(cv::Scalar { 0, 0, 0 }, mask_y1);
- };
-
- cv::Mat tmp = temp.clone();
- cv::Mat img = image.clone();
-
- cv::Mat template_mask;
- key_color(tmp, template_mask, { 0, 255, 0 }, { 0, 0, 0 });
- cv::bitwise_not(template_mask, template_mask);
-
- preprocess(img);
- preprocess(tmp);
-
- cv::Mat match;
- cv::matchTemplate(img, tmp, match, cv::TM_SQDIFF, template_mask);
- match /= template_mask.cols * template_mask.rows;
- cv::sqrt(match, match);
-
- auto result = match_template_helper(
- match, { tmp.cols, tmp.rows }, [](float v) { return v < 130.; }, std::greater<> {});
- return result;
- }
-
-} // namespace asst
diff --git a/src/MaaCore/Vision/AbstractImageAnalyzer.h b/src/MaaCore/Vision/AbstractImageAnalyzer.h
index e7d2047750..63efee8f3e 100644
--- a/src/MaaCore/Vision/AbstractImageAnalyzer.h
+++ b/src/MaaCore/Vision/AbstractImageAnalyzer.h
@@ -51,4 +51,4 @@ namespace asst
using InstHelper::ctrler;
using InstHelper::need_exit;
};
-}
+} // namespace asst
diff --git a/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.cpp b/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.cpp
index 7c7b1339eb..f4e86664ff 100644
--- a/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.cpp
+++ b/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.cpp
@@ -12,10 +12,8 @@
#include "Vision/MultiMatchImageAnalyzer.h"
#include "Vision/OcrWithFlagTemplImageAnalyzer.h"
#include "Utils/Logger.hpp"
-#include "Utils/MoreCV.hpp"
- bool
- asst::BattleImageAnalyzer::set_target(int target)
+bool asst::BattleImageAnalyzer::set_target(int target)
{
m_target = target;
return true;
@@ -43,18 +41,7 @@ bool asst::BattleImageAnalyzer::analyze()
// 可能没有干员(全上场了),所以干员识别结果不影响返回值
if (m_target & Target::Oper) {
- bool oper_ret = opers_analyze();
- if (m_target == Target::Skill) {
- ret = oper_ret;
- }
- }
-
- // 可能没有可使用的技能,所以技能识别结果不影响返回值
- if (m_target & Target::Skill) {
- bool skill_ret = skill_analyze();
- if (m_target == Target::Skill) {
- ret = skill_ret;
- }
+ opers_analyze();
}
if (m_target & Target::Kills) {
@@ -82,11 +69,6 @@ const std::vector& asst::BattleImageAnalyzer::get_homes() const noex
return m_homes;
}
-const std::vector& asst::BattleImageAnalyzer::get_ready_skills() const noexcept
-{
- return m_ready_skills;
-}
-
int asst::BattleImageAnalyzer::get_hp() const noexcept
{
return m_hp;
@@ -358,30 +340,6 @@ bool asst::BattleImageAnalyzer::home_analyze()
return true;
}
-bool asst::BattleImageAnalyzer::skill_analyze()
-{
- LogTraceFunction;
-
- const auto skill_task_ptr = Task.get("BattleSkillReady");
- const Rect& rect_move = skill_task_ptr->rect_move;
-
- cv::Mat temp = TemplResource::get_instance().get_templ(skill_task_ptr->templ_name);
-
- const auto result = find_skill_ready(m_image(make_rect(skill_task_ptr->roi)), temp);
-
- if (result.empty()) return false;
-
- ranges::transform(result, std::back_inserter(m_ready_skills), [&](auto&& pair) {
- const cv::Point& pos = pair.first;
- return Rect { pos.x + skill_task_ptr->roi.x, pos.y + skill_task_ptr->roi.y, temp.cols, temp.rows }.move(
- rect_move);
- });
-
- Log.trace(m_ready_skills);
-
- return true;
-}
-
bool asst::BattleImageAnalyzer::hp_analyze()
{
// 识别 HP 的那个蓝白色图标
diff --git a/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.h b/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.h
index b0c337d611..caf43a87d5 100644
--- a/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.h
+++ b/src/MaaCore/Vision/Miscellaneous/BattleImageAnalyzer.h
@@ -14,7 +14,7 @@ namespace asst
HP = 1, // 剩余生命值
Home = 2, // 蓝色的家门
Oper = 4, // 下方的干员信息
- Skill = 8, // cd 转好了可以使用的技能
+ //Skill = 8, // cd 转好了可以使用的技能
Kills = 16, // 击杀数
Cost = 32, // 费用
Vacancies = 64, // 剩余可部署干员数
@@ -33,7 +33,6 @@ namespace asst
virtual const std::vector& get_opers() const noexcept;
virtual const std::vector& get_homes() const noexcept;
- const std::vector& get_ready_skills() const noexcept;
int get_hp() const noexcept;
int get_kills() const noexcept;
int get_total_kills() const noexcept;
@@ -50,7 +49,7 @@ namespace asst
bool oper_available_analyze(const Rect& roi);
bool home_analyze(); // 识别蓝色的家门
- bool skill_analyze(); // 识别技能是否可用
+ //bool skill_analyze(); // 识别技能是否可用
bool hp_analyze(); // 识别剩余生命值
bool kills_analyze(); // 识别击杀数
bool cost_analyze(); // 识别费用
diff --git a/src/MaaCore/Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.cpp b/src/MaaCore/Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.cpp
new file mode 100644
index 0000000000..de650268ae
--- /dev/null
+++ b/src/MaaCore/Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.cpp
@@ -0,0 +1,66 @@
+#include "BattleSkillReadyImageAnalyzer.h"
+
+#include "Utils/NoWarningCV.h"
+
+#include
+#include
+#include
+
+#include "Config/TaskData.h"
+#include "Config/TemplResource.h"
+#include "Utils/Logger.hpp"
+
+bool asst::BattleSkillReadyImageAnalyzer::analyze()
+{
+ auto task_ptr = Task.get("BattleAutoSkillFlag");
+ const cv::Mat& templ = TemplResource::get_instance().get_templ(task_ptr->templ_name);
+
+ auto key_color = [](cv::InputArray src, cv::OutputArray dst, const cv::Scalar& color,
+ const cv::Scalar& tolerance = {}) {
+ cv::inRange(src, color - tolerance, color + tolerance, dst);
+ };
+ auto preprocess = [&](cv::Mat& img) {
+ cv::Mat mask_y2;
+ key_color(img, mask_y2, { 40, 94, 103 }, { 10, 10, 10 }); // BGR
+ img.setTo(cv::Scalar { 2, 216, 255 }, mask_y2); // BGR
+ cv::Mat mask_y1;
+ key_color(img, mask_y1, { 2, 216, 255 }, { 20, 20, 20 });
+ cv::dilate(mask_y1, mask_y1, cv::getStructuringElement(cv::MORPH_RECT, { 4, 4 }));
+ cv::bitwise_not(mask_y1, mask_y1);
+ img.setTo(cv::Scalar { 0, 0, 0 }, mask_y1);
+ };
+
+ cv::Mat tmp = templ.clone();
+ cv::Mat img = m_image.clone()(make_rect(m_roi));
+
+ cv::Mat template_mask;
+ key_color(tmp, template_mask, { 0, 255, 0 }, { 0, 0, 0 });
+ cv::bitwise_not(template_mask, template_mask);
+
+ preprocess(img);
+ preprocess(tmp);
+
+ cv::Mat match;
+ cv::matchTemplate(img, tmp, match, cv::TM_SQDIFF, template_mask);
+ match /= template_mask.cols * template_mask.rows;
+ cv::sqrt(match, match);
+
+ // TODO: 这块我没太看懂,等 horror 改改
+ //auto all_matched = match_template_helper(
+ // match, { tmp.cols, tmp.rows }, [](float v) { return v < 130.; }, std::greater<> {});
+
+ //if (all_matched.empty()) {
+ // return false;
+ //}
+
+ //m_result = all_matched.front();
+ return true;
+}
+
+void asst::BattleSkillReadyImageAnalyzer::set_base_point(const Point& pt)
+{
+ auto task_ptr = Task.get("BattleAutoSkillFlag");
+ const Rect& skill_roi_move = task_ptr->rect_move;
+
+ set_roi(Rect(pt.x, pt.y, 0, 0).move(skill_roi_move));
+}
diff --git a/src/MaaCore/Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.h b/src/MaaCore/Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.h
new file mode 100644
index 0000000000..4411943910
--- /dev/null
+++ b/src/MaaCore/Vision/Miscellaneous/BattleSkillReadyImageAnalyzer.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Vision/AbstractImageAnalyzer.h"
+
+namespace asst
+{
+ class BattleSkillReadyImageAnalyzer final : public AbstractImageAnalyzer
+ {
+ public:
+ using AbstractImageAnalyzer::AbstractImageAnalyzer;
+ virtual ~BattleSkillReadyImageAnalyzer() override = default;
+
+ virtual bool analyze() override;
+
+ void set_base_point(const Point& pt);
+
+ private:
+ MatchRect m_result;
+ };
+}
diff --git a/src/MaaCore/Vision/MultiMatchImageAnalyzer.cpp b/src/MaaCore/Vision/MultiMatchImageAnalyzer.cpp
index 51cadfb761..a2e1bcf0c2 100644
--- a/src/MaaCore/Vision/MultiMatchImageAnalyzer.cpp
+++ b/src/MaaCore/Vision/MultiMatchImageAnalyzer.cpp
@@ -3,7 +3,6 @@
#include "Utils/Ranges.hpp"
#include
-#include "Utils/MoreCV.hpp"
#include "Utils/NoWarningCV.h"
#include "Config/TaskData.h"
@@ -122,14 +121,32 @@ bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat templ)
cv::matchTemplate(image_roi, templ, matched, cv::TM_CCOEFF_NORMED, mask);
}
- const auto result =
- match_template_helper(matched, { static_cast(templ.cols * 0.6), static_cast(templ.rows * 0.6) },
- [this](float value) -> bool { return m_templ_thres < value && value < 2.0; });
-
- ranges::transform(result, std::back_inserter(m_result), [&, this](const auto& pair) -> MatchRect {
- const cv::Point roi_pt = pair.first;
- return { pair.second, { roi_pt.x + m_roi.x, roi_pt.y + m_roi.y, templ.cols, templ.rows } };
- });
+ int mini_distance = (std::min)(templ.cols, templ.rows) / 2;
+ for (int i = 0; i != matched.rows; ++i) {
+ for (int j = 0; j != matched.cols; ++j) {
+ auto value = matched.at(i, j);
+ if (m_templ_thres <= value && value < 2.0) {
+ Rect rect(j + m_roi.x, i + m_roi.y, templ.cols, templ.rows);
+ bool need_push = true;
+ // 如果有两个点离得太近,只取里面得分高的那个
+ // 一般相邻的都是刚刚push进去的,这里倒序快一点
+ for (auto& iter : ranges::reverse_view(m_result)) {
+ if (std::abs(j + m_roi.x - iter.rect.x) < mini_distance &&
+ std::abs(i + m_roi.y - iter.rect.y) < mini_distance) {
+ if (iter.score < value) {
+ iter.rect = rect;
+ iter.score = value;
+ } // else 这个点就放弃了
+ need_push = false;
+ break;
+ }
+ }
+ if (need_push) {
+ m_result.emplace_back(value, rect);
+ }
+ }
+ }
+ }
#ifdef ASST_DEBUG
for (const auto& rect : m_result) {