diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj
index e2ea60b4ec..1c19fc39e1 100644
--- a/src/MaaCore/MaaCore.vcxproj
+++ b/src/MaaCore/MaaCore.vcxproj
@@ -83,11 +83,10 @@
-
-
+
-
-
+
+
@@ -236,11 +235,10 @@
-
-
+
-
-
+
+
diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters
index f95d5fe6ad..d1bcc174e7 100644
--- a/src/MaaCore/MaaCore.vcxproj.filters
+++ b/src/MaaCore/MaaCore.vcxproj.filters
@@ -567,19 +567,16 @@
源文件\Task\Interface
-
+
源文件\Vision\Battle
源文件\Vision\Battle
-
+
源文件\Vision\Battle
-
- 源文件\Vision\Battle
-
-
+
源文件\Vision\Battle
@@ -968,19 +965,16 @@
源文件\Task\Interface
-
+
源文件\Vision\Battle
源文件\Vision\Battle
-
+
源文件\Vision\Battle
-
- 源文件\Vision\Battle
-
-
+
源文件\Vision\Battle
diff --git a/src/MaaCore/Task/BattleHelper.cpp b/src/MaaCore/Task/BattleHelper.cpp
index 2bb2932c1d..535204b955 100644
--- a/src/MaaCore/Task/BattleHelper.cpp
+++ b/src/MaaCore/Task/BattleHelper.cpp
@@ -11,7 +11,7 @@
#include "Utils/ImageIo.hpp"
#include "Utils/Logger.hpp"
#include "Utils/NoWarningCV.h"
-#include "Vision/Battle/BattleSkillReadyImageAnalyzer.h"
+#include "Vision/Battle/BattlefieldClassifier.h"
#include "Vision/Battle/BattlefieldMatcher.h"
#include "Vision/BestMatcher.h"
#include "Vision/Matcher.h"
@@ -476,7 +476,8 @@ bool asst::BattleHelper::check_and_use_skill(const std::string& name, bool& has_
bool asst::BattleHelper::check_and_use_skill(const Point& loc, bool& has_error, const cv::Mat& reusable)
{
cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable;
- BattleSkillReadyImageAnalyzer skill_analyzer(image);
+ BattlefieldClassifier skill_analyzer(image);
+ skill_analyzer.set_object_of_interest({ .skill_ready = true });
auto target_iter = m_normal_tile_info.find(loc);
if (target_iter == m_normal_tile_info.end()) {
@@ -485,7 +486,7 @@ bool asst::BattleHelper::check_and_use_skill(const Point& loc, bool& has_error,
}
const Point& battlefield_point = target_iter->second.pos;
skill_analyzer.set_base_point(battlefield_point);
- if (!skill_analyzer.analyze()) {
+ if (!skill_analyzer.analyze()->skill_ready.ready) {
return false;
}
diff --git a/src/MaaCore/Task/Experiment/CombatRecordRecognitionTask.cpp b/src/MaaCore/Task/Experiment/CombatRecordRecognitionTask.cpp
index e41f8416e1..3a46b381c6 100644
--- a/src/MaaCore/Task/Experiment/CombatRecordRecognitionTask.cpp
+++ b/src/MaaCore/Task/Experiment/CombatRecordRecognitionTask.cpp
@@ -7,10 +7,9 @@
#include "Utils/Logger.hpp"
#include "Utils/NoWarningCV.h"
#include "Utils/Ranges.hpp"
-#include "Vision/Battle/BattleDeployDirectionImageAnalyzer.h"
-#include "Vision/Battle/BattleFormationImageAnalyzer.h"
-#include "Vision/Battle/BattleOperatorsImageAnalyzer.h"
-#include "Vision/Battle/BattleSkillReadyImageAnalyzer.h"
+#include "Vision/Battle/BattleFormationAnalyzer.h"
+#include "Vision/Battle/BattlefieldClassifier.h"
+#include "Vision/Battle/BattlefieldDetector.h"
#include "Vision/Battle/BattlefieldMatcher.h"
#include "Vision/BestMatcher.h"
#include "Vision/RegionOCRer.h"
@@ -117,7 +116,7 @@ bool asst::CombatRecordRecognitionTask::analyze_formation()
const int skip_count = m_video_fps > m_formation_fps ? static_cast(m_video_fps / m_formation_fps) - 1 : 0;
- BattleFormationImageAnalyzer formation_ananlyzer;
+ BattleFormationAnalyzer formation_ananlyzer;
int no_changes_count = 0;
for (size_t i = 0; i < m_video_frame_count; i += skip_frames(skip_count) + 1) {
cv::Mat frame;
@@ -131,13 +130,14 @@ bool asst::CombatRecordRecognitionTask::analyze_formation()
cv::resize(frame, frame, cv::Size(), m_scale, m_scale, cv::INTER_AREA);
formation_ananlyzer.set_image(frame);
- bool analyzed = formation_ananlyzer.analyze();
+ auto formation_opt = formation_ananlyzer.analyze();
show_img(formation_ananlyzer);
// 有些视频会有个过渡或者动画啥的,只取一帧识别的可能不全。多识别几帧
- if (analyzed) {
- const auto& cur = formation_ananlyzer.get_result();
- if (cur.size() > m_formation.size()) {
- m_formation = cur;
+ if (formation_opt) {
+ if (formation_opt->size() > m_formation.size()) {
+ for (const auto& [name, avatar] : *formation_opt) {
+ m_formation.insert_or_assign(name, avatar);
+ }
}
else if (++no_changes_count > 5) {
m_formation_end_frame = i;
@@ -509,9 +509,10 @@ bool asst::CombatRecordRecognitionTask::compare_skill(ClipInfo& clip, ClipInfo&
const std::string oper_name = pre_clip.ends_oper_name;
const Point target_location = m_operator_locations[oper_name];
const Point target_position = m_normal_tile_info[target_location].pos;
- BattleSkillReadyImageAnalyzer analyzer(pre_clip.end_frame);
+ BattlefieldClassifier analyzer(pre_clip.end_frame);
+ analyzer.set_object_of_interest({ .skill_ready = true });
analyzer.set_base_point(target_position);
- bool pre_ready = analyzer.analyze();
+ bool pre_ready = analyzer.analyze()->skill_ready.ready;
show_img(analyzer);
if (!pre_ready) {
@@ -539,7 +540,7 @@ bool asst::CombatRecordRecognitionTask::compare_skill(ClipInfo& clip, ClipInfo&
}
cv::resize(frame, frame, cv::Size(), m_scale, m_scale, cv::INTER_AREA);
analyzer.set_image(frame);
- bool cur_ready = analyzer.analyze();
+ bool cur_ready = analyzer.analyze()->skill_ready.ready;
if (pre_ready && !cur_ready) {
json::object condition = analyze_action_condition(clip, &pre_clip);
@@ -590,13 +591,14 @@ bool asst::CombatRecordRecognitionTask::detect_operators(ClipInfo& clip, [[maybe
}
cv::resize(frame, frame, cv::Size(), m_scale, m_scale, cv::INTER_AREA);
- BattleOperatorsImageAnalyzer analyzer(frame);
- analyzer.analyze();
+ BattlefieldDetector analyzer(frame);
+ analyzer.set_object_of_interest({ .operators = true });
+ auto result_opt = analyzer.analyze();
show_img(analyzer);
DetectionResult cur_locations;
auto tiles = m_normal_tile_info | views::values;
- for (const auto& box : analyzer.get_results()) {
+ for (const auto& box : result_opt->operators) {
Rect rect = box.rect.move(det_box_move);
auto iter = ranges::find_if(tiles, [&](const TilePack::TileInfo& t) { return rect.include(t.pos); });
if (iter == tiles.end()) {
@@ -650,18 +652,19 @@ bool asst::CombatRecordRecognitionTask::classify_direction(ClipInfo& clip, ClipI
callback(AsstMsg::SubTaskStart, basic_info_with_what("ClassifyDirection"));
/* classify direction */
- using Losses = BattleDeployDirectionImageAnalyzer::RawResults;
- constexpr size_t ClsSize = BattleDeployDirectionImageAnalyzer::ClassificationSize;
- std::unordered_map dir_cls_sampling;
+ using Raw = BattlefieldClassifier::DeployDirectionResult::Raw;
+ constexpr size_t ClsSize = BattlefieldClassifier::DeployDirectionResult::ClsSize;
+ std::unordered_map dir_cls_sampling;
for (const cv::Mat& frame : clip.random_frames) {
- BattleDeployDirectionImageAnalyzer analyzer(frame);
+ BattlefieldClassifier analyzer(frame);
+ analyzer.set_object_of_interest({ .skill_ready = false, .deploy_direction = true });
for (const auto& loc : newcomer) {
analyzer.set_base_point(m_normal_tile_info.at(loc).pos);
- analyzer.analyze();
+ auto result_opt = analyzer.analyze();
show_img(analyzer);
for (size_t i = 0; i < ClsSize; ++i) {
- dir_cls_sampling[loc][i] += analyzer.get_raw_results()[i];
+ dir_cls_sampling[loc][i] += result_opt->deploy_direction.raw[i];
}
}
}
diff --git a/src/MaaCore/Task/Interface/DebugTask.cpp b/src/MaaCore/Task/Interface/DebugTask.cpp
index 46990e6a1c..241423bcd6 100644
--- a/src/MaaCore/Task/Interface/DebugTask.cpp
+++ b/src/MaaCore/Task/Interface/DebugTask.cpp
@@ -6,7 +6,7 @@
#include "Utils/ImageIo.hpp"
#include "Utils/Logger.hpp"
-#include "Vision/Battle/BattleSkillReadyImageAnalyzer.h"
+#include "Vision/Battle/BattlefieldClassifier.h"
#include "Vision/Battle/BattlefieldMatcher.h"
#include "Vision/Miscellaneous/DepotImageAnalyzer.h"
#include "Vision/Miscellaneous/StageDropsImageAnalyzer.h"
@@ -42,17 +42,19 @@ void asst::DebugTask::test_skill_ready()
int correct = 0;
for (const auto& entry : std::filesystem::directory_iterator(R"(../../test/skill_ready/y)")) {
cv::Mat image = imread(entry.path().string());
- BattleSkillReadyImageAnalyzer analyzer(image);
+ BattlefieldClassifier analyzer(image);
+ analyzer.set_object_of_interest({ .skill_ready = true });
total++;
- if (analyzer.analyze()) {
+ if (analyzer.analyze()->skill_ready.ready) {
correct++;
}
}
for (const auto& entry : std::filesystem::directory_iterator(R"(../../test/skill_ready/n)")) {
cv::Mat image = imread(entry.path().string());
- BattleSkillReadyImageAnalyzer analyzer(image);
+ BattlefieldClassifier analyzer(image);
+ analyzer.set_object_of_interest({ .skill_ready = true });
total++;
- if (!analyzer.analyze()) {
+ if (!analyzer.analyze()->skill_ready.ready) {
correct++;
}
}
diff --git a/src/MaaCore/Task/Reclamation/ReclamationBattlePlugin.cpp b/src/MaaCore/Task/Reclamation/ReclamationBattlePlugin.cpp
index b38b237541..2af92a9ca3 100644
--- a/src/MaaCore/Task/Reclamation/ReclamationBattlePlugin.cpp
+++ b/src/MaaCore/Task/Reclamation/ReclamationBattlePlugin.cpp
@@ -8,7 +8,6 @@
#include "Status.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
-#include "Vision/Battle/BattleSkillReadyImageAnalyzer.h"
#include "Vision/Matcher.h"
#include "Vision/OCRer.h"
@@ -126,7 +125,8 @@ bool asst::ReclamationBattlePlugin::communicate_with_aux(
std::ignore = npcName;
std::ignore = orderComp;
// auto image = ctrler()->get_image();
- // BattleSkillReadyImageAnalyzer skillReadyAnalyzer(image);
+ // BattlefieldClassifier skillReadyAnalyzer(image);
+ // analyzer.set_object_of_interest({ .skill_ready = true });
// if (!skillReadyAnalyzer.analyze()) {
// Log.info(__FUNCTION__, " | ", "no ready skills");
// return false;
diff --git a/src/MaaCore/Vision/Battle/BattleDeployDirectionImageAnalyzer.cpp b/src/MaaCore/Vision/Battle/BattleDeployDirectionImageAnalyzer.cpp
deleted file mode 100644
index 41718abfd4..0000000000
--- a/src/MaaCore/Vision/Battle/BattleDeployDirectionImageAnalyzer.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#include "BattleDeployDirectionImageAnalyzer.h"
-
-#include "Utils/NoWarningCV.h"
-
-#include
-#include
-#include
-
-#include
-
-#include "Config/OnnxSessions.h"
-#include "Config/TaskData.h"
-#include "Utils/Logger.hpp"
-
-bool asst::BattleDeployDirectionImageAnalyzer::analyze()
-{
- cv::Mat image = m_image(make_rect(m_roi));
- std::vector input = image_to_tensor(image);
-
- auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
- constexpr int64_t batch_size = 1;
- std::array input_shape { batch_size, image.channels(), image.cols, image.rows };
-
- Ort::Value input_tensor = Ort::Value::CreateTensor(memory_info, input.data(), input.size(),
- input_shape.data(), input_shape.size());
-
- RawResults results;
- std::array output_shape { batch_size, ClassificationSize };
- Ort::Value output_tensor = Ort::Value::CreateTensor(memory_info, results.data(), results.size(),
- output_shape.data(), output_shape.size());
-
- auto& session = OnnxSessions::get_instance().get("deploy_direction_cls");
- // 这俩是hardcode在模型里的
- constexpr const char* input_names[] = { "input" }; // session.GetInputName()
- constexpr const char* output_names[] = { "output" }; // session.GetOutputName()
-
- Ort::RunOptions run_options;
- session.Run(run_options, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
- m_raw_results = results;
- Log.info(__FUNCTION__, "raw result:", m_raw_results);
-
- softmax(results);
- Log.info(__FUNCTION__, "after softmax:", results);
-
- m_class_id = std::max_element(results.begin(), results.end()) - results.begin();
-
-#ifdef ASST_DEBUG
- static const std::unordered_map ClassNames = {
- { 0, "Right" },
- { 1, "Down" },
- { 2, "Left" },
- { 3, "Up" },
- };
- cv::putText(m_image_draw, ClassNames.at(m_class_id), cv::Point(m_roi.x, m_roi.y + m_roi.height),
- cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 255, 0), 2);
- cv::putText(m_image_draw, std::to_string(results[m_class_id]), cv::Point(m_roi.x, m_roi.y + m_roi.height + 20),
- cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 255, 0), 2);
-#endif
-
- return true;
-}
-
-void asst::BattleDeployDirectionImageAnalyzer::set_base_point(const Point& pt)
-{
- auto task_ptr = Task.get("BattleDeployDirectionRectMove");
- const Rect& roi_move = task_ptr->rect_move;
-
- set_roi(Rect(pt.x, pt.y, 0, 0).move(roi_move));
-}
diff --git a/src/MaaCore/Vision/Battle/BattleDeployDirectionImageAnalyzer.h b/src/MaaCore/Vision/Battle/BattleDeployDirectionImageAnalyzer.h
deleted file mode 100644
index a3fe1563cf..0000000000
--- a/src/MaaCore/Vision/Battle/BattleDeployDirectionImageAnalyzer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "Vision/OnnxHelper.h"
-
-namespace asst
-{
- class BattleDeployDirectionImageAnalyzer final : public OnnxHelper
- {
- public:
- static constexpr size_t ClassificationSize = 4;
- using RawResults = std::array;
-
- public:
- using OnnxHelper::OnnxHelper;
- virtual ~BattleDeployDirectionImageAnalyzer() override = default;
-
- void set_base_point(const Point& pt);
- bool analyze();
-
- size_t get_class_id() const { return m_class_id; }
- RawResults get_raw_results() const { return m_raw_results; }
-
- private:
- std::array m_raw_results;
- size_t m_class_id = 0;
- };
-}
diff --git a/src/MaaCore/Vision/Battle/BattleFormationAnalyzer.cpp b/src/MaaCore/Vision/Battle/BattleFormationAnalyzer.cpp
new file mode 100644
index 0000000000..08090c47ad
--- /dev/null
+++ b/src/MaaCore/Vision/Battle/BattleFormationAnalyzer.cpp
@@ -0,0 +1,76 @@
+#include "BattleFormationAnalyzer.h"
+
+#include "Config/Miscellaneous/BattleDataConfig.h"
+#include "Config/TaskData.h"
+#include "Utils/Logger.hpp"
+#include "Utils/NoWarningCV.h"
+#include "Vision/Miscellaneous/PipelineAnalyzer.h"
+#include "Vision/TemplDetOCRer.h"
+
+using namespace asst;
+
+BattleFormationAnalyzer::ResultsVecOpt BattleFormationAnalyzer::analyze() const
+{
+ LogTraceFunction;
+
+ PipelineAnalyzer start_button_analyzer(m_image);
+ start_button_analyzer.set_tasks(Task.get("BattleStartAll")->next);
+ if (!start_button_analyzer.analyze()) {
+ return std::nullopt;
+ }
+
+ TemplDetOCRer::ResultsVec oper_names;
+
+ TemplDetOCRer oper_names_analyzer(m_image);
+ oper_names_analyzer.set_task_info("BattleFormationOCRNameFlag", "BattleFormationOperNames");
+ oper_names_analyzer.set_bin_expansion(3);
+ auto oper_names_opt = oper_names_analyzer.analyze();
+ if (!oper_names_opt) {
+ return std::nullopt;
+ }
+ ResultsVec results = proc_ocr_result(*oper_names_opt);
+
+ // 识别到了结果,但是确实没有可用的干员名。说明游戏UI可能是老版本的,布局不一样
+ if (results.empty()) {
+ oper_names_analyzer.set_task_info("BattleFormationOCRNameFlag", "BattleFormationOperNamesOldVersion");
+ oper_names_analyzer.set_bin_expansion(2);
+ auto old_layout_oper_names_opt = oper_names_analyzer.analyze();
+ if (!old_layout_oper_names_opt) {
+ return std::nullopt;
+ }
+ results = proc_ocr_result(*old_layout_oper_names_opt);
+ }
+
+ if (results.empty()) {
+ return std::nullopt;
+ }
+
+ return results;
+}
+
+BattleFormationAnalyzer::ResultsVec BattleFormationAnalyzer::proc_ocr_result(
+ const TemplDetOCRer::ResultsVec& ocr_result) const
+{
+ ResultsVec results;
+
+ for (const auto& name_res : ocr_result) {
+ if (BattleData.is_name_invalid(name_res.text)) {
+ continue;
+ }
+
+ const std::string& name = name_res.text;
+ Rect base_point { name_res.rect.x + name_res.rect.width, name_res.rect.y, 0, 0 };
+ Rect avatar_rect = base_point.move(Task.get("BattleFormationOperAvatarMove")->rect_move);
+ cv::Mat avatar = m_image(make_rect(avatar_rect));
+ results.emplace_back(Result { .name = name, .avatar = avatar });
+
+#ifdef ASST_DEBUG
+ cv::rectangle(m_image_draw, make_rect(name_res.rect), cv::Scalar(0, 255, 0), 2);
+ cv::rectangle(m_image_draw, make_rect(avatar_rect), cv::Scalar(0, 0, 255), 2);
+ cv::putText(m_image_draw, BattleData.get_id(name), cv::Point(avatar_rect.x, avatar_rect.y - 20), 1, 1.2,
+ cv::Scalar(0, 0, 255), 2);
+#endif
+ }
+
+ return results;
+}
diff --git a/src/MaaCore/Vision/Battle/BattleFormationAnalyzer.h b/src/MaaCore/Vision/Battle/BattleFormationAnalyzer.h
new file mode 100644
index 0000000000..21a72fb267
--- /dev/null
+++ b/src/MaaCore/Vision/Battle/BattleFormationAnalyzer.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Vision/TemplDetOCRer.h"
+#include "Vision/VisionHelper.h"
+
+namespace asst
+{
+ class BattleFormationAnalyzer : public VisionHelper
+ {
+ public:
+ struct Result
+ {
+ std::string to_string() const { return name; }
+ explicit operator std::string() const { return to_string(); }
+
+ std::string name;
+ cv::Mat avatar;
+ };
+ using ResultsVec = std::vector;
+ using ResultsVecOpt = std::optional;
+
+ public:
+ using VisionHelper::VisionHelper;
+ virtual ~BattleFormationAnalyzer() override = default;
+
+ ResultsVecOpt analyze() const;
+
+ protected:
+ ResultsVec proc_ocr_result(const TemplDetOCRer::ResultsVec& ocr_result) const;
+ };
+}
diff --git a/src/MaaCore/Vision/Battle/BattleFormationImageAnalyzer.cpp b/src/MaaCore/Vision/Battle/BattleFormationImageAnalyzer.cpp
deleted file mode 100644
index d01f8b8a74..0000000000
--- a/src/MaaCore/Vision/Battle/BattleFormationImageAnalyzer.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "BattleFormationImageAnalyzer.h"
-
-#include "Config/Miscellaneous/BattleDataConfig.h"
-#include "Config/TaskData.h"
-#include "Utils/Logger.hpp"
-#include "Utils/NoWarningCV.h"
-#include "Vision/Miscellaneous/PipelineAnalyzer.h"
-#include "Vision/TemplDetOCRer.h"
-
-bool asst::BattleFormationImageAnalyzer::analyze()
-{
- LogTraceFunction;
- m_result.clear();
-
- PipelineAnalyzer start_button_analyzer(m_image);
- start_button_analyzer.set_tasks(Task.get("BattleStartAll")->next);
- if (!start_button_analyzer.analyze()) {
- return false;
- }
-
- TemplDetOCRer oper_names_analyzer(m_image);
- oper_names_analyzer.set_task_info("BattleFormationOCRNameFlag", "BattleFormationOperNames");
- oper_names_analyzer.set_bin_expansion(3);
- if (!oper_names_analyzer.analyze()) {
- return false;
- }
-
- auto proc_ocr_result = [&]() {
- const auto& oper_names = oper_names_analyzer.get_result();
- for (const auto& name_res : oper_names) {
- if (BattleData.is_name_invalid(name_res.text)) {
- continue;
- }
- const std::string& name = name_res.text;
- Rect base_point { name_res.rect.x + name_res.rect.width, name_res.rect.y, 0, 0 };
- Rect avatar_rect = base_point.move(Task.get("BattleFormationOperAvatarMove")->rect_move);
- cv::Mat avatar = m_image(make_rect(avatar_rect));
- m_result.emplace(name, avatar);
-#ifdef ASST_DEBUG
- cv::rectangle(m_image_draw, make_rect(name_res.rect), cv::Scalar(0, 255, 0), 2);
- cv::rectangle(m_image_draw, make_rect(avatar_rect), cv::Scalar(0, 0, 255), 2);
- cv::putText(m_image_draw, utils::utf8_to_ansi(name), cv::Point(avatar_rect.x, avatar_rect.y - 20), 1, 1.2,
- cv::Scalar(0, 0, 255), 2);
-#endif
- }
- };
-
- proc_ocr_result();
-
- // 识别到了结果,但是确实没有可用的干员名。说明游戏UI可能是老版本的,布局不一样
- if (m_result.empty()) {
- oper_names_analyzer.set_task_info("BattleFormationOCRNameFlag", "BattleFormationOperNamesOldVersion");
- oper_names_analyzer.set_bin_expansion(2);
- if (!oper_names_analyzer.analyze()) {
- return false;
- }
- proc_ocr_result();
- }
-
- return !m_result.empty();
-}
diff --git a/src/MaaCore/Vision/Battle/BattleFormationImageAnalyzer.h b/src/MaaCore/Vision/Battle/BattleFormationImageAnalyzer.h
deleted file mode 100644
index 6020785dda..0000000000
--- a/src/MaaCore/Vision/Battle/BattleFormationImageAnalyzer.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include "Vision/VisionHelper.h"
-
-namespace asst
-{
- class BattleFormationImageAnalyzer : public VisionHelper
- {
- public:
- using VisionHelper::VisionHelper;
- virtual ~BattleFormationImageAnalyzer() override = default;
-
- bool analyze();
-
- const auto& get_result() const { return m_result; }
-
- private:
- std::unordered_map m_result;
- };
-}
diff --git a/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.cpp b/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.cpp
index b24642460d..9a3a310d03 100644
--- a/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.cpp
+++ b/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.cpp
@@ -16,10 +16,9 @@ bool asst::BattleOperatorsImageAnalyzer::analyze()
{
LogTraceFunction;
- m_results.clear();
- double x_scale = 640.0 / m_image.cols;
- double y_scale = 640.0 / m_image.rows;
+ const double x_scale = 640.0 / m_image.cols;
+ const double y_scale = 640.0 / m_image.rows;
cv::Mat image;
cv::resize(m_image, image, cv::Size(), x_scale, y_scale, cv::INTER_AREA);
diff --git a/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.h b/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.h
deleted file mode 100644
index bb7f25860e..0000000000
--- a/src/MaaCore/Vision/Battle/BattleOperatorsImageAnalyzer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include "Vision/OnnxHelper.h"
-
-namespace asst
-{
- // 战斗中干员(血条)检测,yolov8 模型
- class BattleOperatorsImageAnalyzer final : public OnnxHelper
- {
- public:
- enum class Cls
- {
- Operator = 0,
- };
- struct Box
- {
- Cls cls = Cls::Operator;
- Rect rect;
- float score = .0f;
- };
-
- public:
- using OnnxHelper::OnnxHelper;
- virtual ~BattleOperatorsImageAnalyzer() override = default;
-
- bool analyze();
-
- const std::vector& get_results() const { return m_results; }
-#ifdef ASST_DEBUG
- const std::vector& get_draw_rect() const { return m_draw_rect; }
-#endif
-
- private:
- std::vector m_results;
-
-#ifdef ASST_DEBUG
- std::vector m_draw_rect;
-#endif
- };
-}
diff --git a/src/MaaCore/Vision/Battle/BattleSkillReadyImageAnalyzer.cpp b/src/MaaCore/Vision/Battle/BattleSkillReadyImageAnalyzer.cpp
deleted file mode 100644
index 1a8466a587..0000000000
--- a/src/MaaCore/Vision/Battle/BattleSkillReadyImageAnalyzer.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "BattleSkillReadyImageAnalyzer.h"
-
-#include "Utils/NoWarningCV.h"
-
-#include
-#include
-#include
-
-#include
-
-#include "Config/OnnxSessions.h"
-#include "Config/TaskData.h"
-#include "Utils/Logger.hpp"
-
-bool asst::BattleSkillReadyImageAnalyzer::analyze()
-{
- cv::Mat image = m_image(make_rect(m_roi));
- std::vector input = image_to_tensor(image);
-
- auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
- constexpr int64_t batch_size = 1;
- std::array input_shape { batch_size, image.channels(), image.cols, image.rows };
-
- Ort::Value input_tensor = Ort::Value::CreateTensor(memory_info, input.data(), input.size(),
- input_shape.data(), input_shape.size());
-
- constexpr size_t classification_size = 2;
- std::array results;
- std::array output_shape { batch_size, classification_size };
- Ort::Value output_tensor = Ort::Value::CreateTensor(memory_info, results.data(), results.size(),
- output_shape.data(), output_shape.size());
-
- auto& session = OnnxSessions::get_instance().get("skill_ready_cls");
- // 这俩是hardcode在模型里的
- constexpr const char* input_names[] = { "input" }; // session.GetInputName()
- constexpr const char* output_names[] = { "output" }; // session.GetOutputName()
-
- Ort::RunOptions run_options;
- session.Run(run_options, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
- Log.info(__FUNCTION__, "raw result: ", results);
-
- softmax(results);
- Log.info(__FUNCTION__, "after softmax: ", results);
- bool ready = results[1] > results[0];
-
-#ifdef ASST_DEBUG
- if (ready) {
- cv::rectangle(m_image_draw, make_rect(m_roi), cv::Scalar(0, 165, 255), 2);
- cv::putText(m_image_draw, std::to_string(results[1]), cv::Point(m_roi.x, m_roi.y - 10), 1, 1.2,
- cv::Scalar(0, 165, 255), 2);
- }
-#endif
-
- return ready;
-}
-
-void asst::BattleSkillReadyImageAnalyzer::set_base_point(const Point& pt)
-{
- auto task_ptr = Task.get("BattleSkillReady");
- 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/Battle/BattleSkillReadyImageAnalyzer.h b/src/MaaCore/Vision/Battle/BattleSkillReadyImageAnalyzer.h
deleted file mode 100644
index 5d3143fdf7..0000000000
--- a/src/MaaCore/Vision/Battle/BattleSkillReadyImageAnalyzer.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include "Vision/OnnxHelper.h"
-
-namespace asst
-{
- class BattleSkillReadyImageAnalyzer final : public OnnxHelper
- {
- public:
- using OnnxHelper::OnnxHelper;
- virtual ~BattleSkillReadyImageAnalyzer() override = default;
-
- bool analyze();
-
- void set_base_point(const Point& pt);
- };
-}
diff --git a/src/MaaCore/Vision/Battle/BattlefieldClassifier.cpp b/src/MaaCore/Vision/Battle/BattlefieldClassifier.cpp
new file mode 100644
index 0000000000..b1188885d5
--- /dev/null
+++ b/src/MaaCore/Vision/Battle/BattlefieldClassifier.cpp
@@ -0,0 +1,151 @@
+#include "BattlefieldClassifier.h"
+
+#include "Utils/NoWarningCV.h"
+
+#include
+#include
+#include
+
+#include
+
+#include "Config/OnnxSessions.h"
+#include "Config/TaskData.h"
+#include "Utils/Logger.hpp"
+
+using namespace asst;
+
+BattlefieldClassifier::ResultOpt BattlefieldClassifier::analyze() const
+{
+ Result result { .object_of_interest = m_object_of_interest };
+ bool analyzed = false;
+
+ if (m_object_of_interest.skill_ready) {
+ result.skill_ready = skill_ready_analyze();
+ analyzed = true;
+ }
+
+ if (m_object_of_interest.deploy_direction) {
+ result.deploy_direction = deploy_direction_analyze();
+ analyzed = true;
+ }
+
+ if (!analyzed) {
+ return std::nullopt;
+ }
+
+ return result;
+}
+
+BattlefieldClassifier::SkillReadyResult BattlefieldClassifier::skill_ready_analyze() const
+{
+ auto task_ptr = Task.get("BattleSkillReady");
+ const Rect& skill_roi_move = task_ptr->rect_move;
+ Rect roi = Rect(m_base_point.x, m_base_point.y, 0, 0).move(skill_roi_move);
+
+ cv::Mat image = make_roi(m_image, correct_rect(roi, m_image));
+ std::vector input = image_to_tensor(image);
+
+ auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
+ constexpr int64_t batch_size = 1;
+ std::array input_shape { batch_size, image.channels(), image.cols, image.rows };
+
+ Ort::Value input_tensor = Ort::Value::CreateTensor(memory_info, input.data(), input.size(),
+ input_shape.data(), input_shape.size());
+ SkillReadyResult::Raw raw_results;
+ std::array output_shape { batch_size, SkillReadyResult::ClsSize };
+ Ort::Value output_tensor = Ort::Value::CreateTensor(memory_info, raw_results.data(), raw_results.size(),
+ output_shape.data(), output_shape.size());
+
+ auto& session = OnnxSessions::get_instance().get("skill_ready_cls");
+ // 这俩是hardcode在模型里的
+ constexpr const char* input_names[] = { "input" }; // session.GetInputName()
+ constexpr const char* output_names[] = { "output" }; // session.GetOutputName()
+
+ Ort::RunOptions run_options;
+ session.Run(run_options, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
+ Log.info(__FUNCTION__, "raw results:", raw_results);
+
+ SkillReadyResult::Prob prob = softmax(raw_results);
+ Log.info(__FUNCTION__, "prob:", prob);
+ bool ready = prob[1] > prob[0];
+ float score = std::max(prob[0], prob[1]);
+
+#ifdef ASST_DEBUG
+ if (ready) {
+ cv::rectangle(m_image_draw, make_rect(roi), cv::Scalar(0, 165, 255), 2);
+ cv::putText(m_image_draw, std::to_string(score), cv::Point(roi.x, roi.y - 10), 1, 1.2, cv::Scalar(0, 165, 255),
+ 2);
+ }
+#endif
+
+ return SkillReadyResult {
+ .ready = ready,
+ .rect = roi,
+ .score = score,
+ .raw = raw_results,
+ .prob = prob,
+ .base_point = m_base_point,
+ };
+}
+
+BattlefieldClassifier::DeployDirectionResult BattlefieldClassifier::deploy_direction_analyze() const
+{
+ const auto& task_ptr = Task.get("BattleDeployDirectionRectMove");
+ const Rect& roi_move = task_ptr->rect_move;
+ Rect roi = Rect(m_base_point.x, m_base_point.y, 0, 0).move(roi_move);
+
+ cv::Mat image = make_roi(m_image, correct_rect(roi, m_image));
+ std::vector input = image_to_tensor(image);
+
+ auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
+ constexpr int64_t batch_size = 1;
+ std::array input_shape { batch_size, image.channels(), image.cols, image.rows };
+
+ Ort::Value input_tensor = Ort::Value::CreateTensor(memory_info, input.data(), input.size(),
+ input_shape.data(), input_shape.size());
+
+ DeployDirectionResult::Raw raw_results;
+ std::array output_shape { batch_size, DeployDirectionResult::ClsSize };
+ Ort::Value output_tensor = Ort::Value::CreateTensor(memory_info, raw_results.data(), raw_results.size(),
+ output_shape.data(), output_shape.size());
+
+ auto& session = OnnxSessions::get_instance().get("deploy_direction_cls");
+ // 这俩是hardcode在模型里的
+ constexpr const char* input_names[] = { "input" }; // session.GetInputName()
+ constexpr const char* output_names[] = { "output" }; // session.GetOutputName()
+
+ Ort::RunOptions run_options;
+ session.Run(run_options, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
+ Log.info(__FUNCTION__, "raw result:", raw_results);
+
+ DeployDirectionResult::Prob prob = softmax(raw_results);
+ Log.info(__FUNCTION__, "after softmax:", prob);
+
+ size_t class_id = std::max_element(prob.begin(), prob.end()) - prob.begin();
+
+#ifdef ASST_DEBUG
+ static const std::unordered_map ClassNames = {
+ { 0, "Right" },
+ { 1, "Down" },
+ { 2, "Left" },
+ { 3, "Up" },
+ };
+ if (ClassNames.size() != prob.size()) {
+ Log.error("ClassNames.size() != prob.size()", ClassNames.size(), prob.size());
+ throw std::runtime_error("ClassNames.size() != prob.size()");
+ }
+ cv::putText(m_image_draw, ClassNames.at(class_id), cv::Point(roi.x, roi.y + roi.height), cv::FONT_HERSHEY_PLAIN,
+ 1.2, cv::Scalar(0, 255, 0), 2);
+ cv::putText(m_image_draw, std::to_string(prob[class_id]), cv::Point(roi.x, roi.y + roi.height + 20),
+ cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 255, 0), 2);
+#endif
+
+ return DeployDirectionResult {
+ .direction = static_cast(class_id),
+ .rect = roi,
+ .score = prob[class_id],
+ .raw = raw_results,
+ .prob = prob,
+ .base_point = m_base_point,
+ };
+}
diff --git a/src/MaaCore/Vision/Battle/BattlefieldClassifier.h b/src/MaaCore/Vision/Battle/BattlefieldClassifier.h
new file mode 100644
index 0000000000..e722d2c797
--- /dev/null
+++ b/src/MaaCore/Vision/Battle/BattlefieldClassifier.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "Common/AsstBattleDef.h"
+#include "Vision/OnnxHelper.h"
+#include "Vision/VisionHelper.h"
+
+namespace asst
+{
+ class BattlefieldClassifier final : public VisionHelper, public OnnxHelper
+ {
+ public:
+ struct ObjectOfInterest
+ {
+ bool skill_ready = false;
+ bool deploy_direction = false;
+ };
+
+ public:
+ struct SkillReadyResult
+ {
+ static constexpr size_t ClsSize = 2;
+ using Raw = std::array;
+ using Prob = Raw;
+
+ bool ready = false;
+ Rect rect;
+ float score = .0f;
+ Raw raw;
+ Prob prob; // after softmax
+
+ Point base_point;
+ };
+
+ struct DeployDirectionResult
+ {
+ static constexpr size_t ClsSize = 4;
+ using Raw = std::array;
+ using Prob = Raw;
+
+ battle::DeployDirection direction = battle::DeployDirection::None;
+ Rect rect;
+ float score = .0f;
+ Raw raw;
+ Prob prob; // after softmax
+
+ Point base_point;
+ };
+
+ struct Result
+ {
+ ObjectOfInterest object_of_interest;
+
+ SkillReadyResult skill_ready;
+ DeployDirectionResult deploy_direction;
+ };
+
+ using ResultOpt = std::optional;
+
+ public:
+ using VisionHelper::VisionHelper;
+ virtual ~BattlefieldClassifier() override = default;
+
+ void set_object_of_interest(ObjectOfInterest obj) { m_object_of_interest = obj; }
+ void set_base_point(const Point& pt) { m_base_point = pt; }
+
+ ResultOpt analyze() const;
+
+ protected:
+ SkillReadyResult skill_ready_analyze() const;
+ DeployDirectionResult deploy_direction_analyze() const;
+
+ ObjectOfInterest m_object_of_interest; // 待识别的目标
+ Point m_base_point;
+ };
+} // namespace asst
diff --git a/src/MaaCore/Vision/Battle/BattlefieldDetector.cpp b/src/MaaCore/Vision/Battle/BattlefieldDetector.cpp
new file mode 100644
index 0000000000..3facf67770
--- /dev/null
+++ b/src/MaaCore/Vision/Battle/BattlefieldDetector.cpp
@@ -0,0 +1,146 @@
+#include "BattlefieldDetector.h"
+
+#include
+#include
+#include
+
+#include "Utils/NoWarningCV.h"
+#include
+
+#include "Config/Miscellaneous/TilePack.h"
+#include "Config/OnnxSessions.h"
+#include "Config/TaskData.h"
+#include "Utils/Logger.hpp"
+
+using namespace asst;
+
+BattlefieldDetector::ResultOpt BattlefieldDetector::analyze() const
+{
+ LogTraceFunction;
+
+ Result result { .object_of_interest = m_object_of_interest };
+ bool analyzed = false;
+
+ if (m_object_of_interest.operators) {
+ result.operators = operator_analyze();
+ analyzed = true;
+ }
+
+ if (!analyzed) {
+ return std::nullopt;
+ }
+
+ return result;
+}
+
+std::vector BattlefieldDetector::operator_analyze() const
+{
+ const double x_scale = 640.0 / m_image.cols;
+ const double y_scale = 640.0 / m_image.rows;
+
+ cv::Mat image;
+ cv::resize(m_image, image, cv::Size(), x_scale, y_scale, cv::INTER_AREA);
+ std::vector input = image_to_tensor(image);
+
+ auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
+ constexpr int64_t batch_size = 1;
+ std::array input_shape { batch_size, image.channels(), image.cols, image.rows };
+
+ Ort::Value input_tensor = Ort::Value::CreateTensor(memory_info, input.data(), input.size(),
+ input_shape.data(), input_shape.size());
+
+ auto& session = OnnxSessions::get_instance().get("operators_det");
+
+ Ort::AllocatorWithDefaultOptions allocator;
+ std::string input_name = session.GetInputNameAllocated(0, allocator).get();
+ std::string output_name = session.GetOutputNameAllocated(0, allocator).get();
+ std::vector input_names = { input_name.c_str() };
+ std::vector output_names = { output_name.c_str() };
+
+ Ort::RunOptions run_options;
+ auto output_tensors = session.Run(run_options, input_names.data(), &input_tensor, input_names.size(),
+ output_names.data(), output_names.size());
+
+ const float* raw_output = output_tensors[0].GetTensorData();
+ // output_shape is { 1, 5, 8400 }
+ std::vector output_shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();
+
+ // yolov8 的 onnx 输出和前面的 v5, v7 等似乎不太一样,目前网上 yolov8 的 demo 较少,文档也没找到
+ // 这里的输出解析是我跟着数据推测的:
+ // center_x0, center_x1, ..... center_x8399
+ // center_y0, center_y1, ..... center_y8399
+ // w0, w1, ..... w8399
+ // h0, h1, ..... h8399
+ // conf0, conf1, ..... conf8399
+ // 如果后面要做多分类,可能得再看下怎么改(我也不知道shape会变成啥样)
+ std::vector> output(output_shape[1]);
+ for (int64_t i = 0; i < output_shape[1]; i++) {
+ output[i] = std::vector(raw_output + i * output_shape[2], raw_output + (i + 1) * output_shape[2]);
+ }
+
+#ifdef ASST_DEBUG
+
+ const int draw_offset_y = static_cast(m_image.rows * -0.15);
+ const int draw_offset_h = static_cast(m_image.rows * 0.13);
+
+#endif
+
+ std::vector all_results;
+ const auto& conf_vec = output.back();
+ for (size_t i = 0; i < conf_vec.size(); ++i) {
+ float score = conf_vec[i];
+ constexpr float Threshold = 0.3f;
+ if (score < Threshold) {
+ continue;
+ }
+
+ int center_x = static_cast(output[0][i] / x_scale);
+ int center_y = static_cast(output[1][i] / y_scale);
+ int w = static_cast(output[2][i] / x_scale);
+ int h = static_cast(output[3][i] / y_scale);
+
+ int x = center_x - w / 2;
+ int y = center_y - h / 2;
+ Rect rect { x, y, w, h };
+
+ OperatorResult box { OperatorResult::Cls::Operator, rect, score };
+
+#ifdef ASST_DEBUG
+
+ Rect draw_rect = box.rect;
+ draw_rect.y += draw_offset_y;
+ draw_rect.height += draw_offset_h;
+ cv::rectangle(m_image_draw, make_rect(draw_rect), cv::Scalar(0, 0, 255), 5);
+ cv::putText(m_image_draw, std::to_string(box.score), cv::Point(draw_rect.x, draw_rect.y - 10),
+ cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0, 0, 255), 2);
+#endif
+
+ all_results.emplace_back(std::move(box));
+ }
+
+ // NMS
+ constexpr double NmsThreshold = 0.7f;
+ std::sort(all_results.begin(), all_results.end(),
+ [](const OperatorResult& a, const OperatorResult& b) { return a.score > b.score; });
+
+ std::vector nms_results;
+ for (size_t i = 0; i < all_results.size(); ++i) {
+ const auto& box = all_results[i];
+ if (box.score < 0.1f) {
+ continue;
+ }
+ nms_results.emplace_back(box);
+ for (size_t j = i + 1; j < all_results.size(); ++j) {
+ auto& box2 = all_results[j];
+ if (box2.score < 0.1f) {
+ continue;
+ }
+ int iou_area = (make_rect(box.rect) & make_rect(box2.rect)).area();
+ if (iou_area > NmsThreshold * box2.rect.area()) {
+ box2.score = 0;
+ }
+ }
+ }
+
+ return nms_results;
+}
diff --git a/src/MaaCore/Vision/Battle/BattlefieldDetector.h b/src/MaaCore/Vision/Battle/BattlefieldDetector.h
new file mode 100644
index 0000000000..b4effee310
--- /dev/null
+++ b/src/MaaCore/Vision/Battle/BattlefieldDetector.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "Vision/OnnxHelper.h"
+
+namespace asst
+{
+ class BattlefieldDetector final : public VisionHelper, public OnnxHelper
+ {
+ public:
+ struct ObjectOfInterest
+ {
+ bool operators = false;
+ };
+
+ public:
+ // 战斗中干员(血条)检测,yolov8 模型
+ struct OperatorResult
+ {
+ enum class Cls
+ {
+ Operator = 0,
+ };
+
+ Cls cls = Cls::Operator;
+ Rect rect;
+ float score = .0f;
+ };
+
+ struct Result
+ {
+ ObjectOfInterest object_of_interest;
+
+ std::vector operators;
+ };
+
+ using ResultOpt = std::optional;
+
+ public:
+ using VisionHelper::VisionHelper;
+ virtual ~BattlefieldDetector() override = default;
+
+ void set_object_of_interest(ObjectOfInterest obj) { m_object_of_interest = obj; }
+
+ ResultOpt analyze() const;
+
+ protected:
+ std::vector operator_analyze() const;
+
+ ObjectOfInterest m_object_of_interest;
+ };
+}
diff --git a/src/MaaCore/Vision/OnnxHelper.cpp b/src/MaaCore/Vision/OnnxHelper.cpp
index ce67697bf2..1e9c61dc05 100644
--- a/src/MaaCore/Vision/OnnxHelper.cpp
+++ b/src/MaaCore/Vision/OnnxHelper.cpp
@@ -2,7 +2,9 @@
#include "Utils/NoWarningCV.h"
-std::vector asst::OnnxHelper::image_to_tensor(const cv::Mat& image)
+using namespace asst;
+
+std::vector OnnxHelper::image_to_tensor(const cv::Mat& image)
{
cv::Mat src = image.clone();
cv::cvtColor(src, src, cv::COLOR_BGR2RGB);
@@ -17,7 +19,7 @@ std::vector asst::OnnxHelper::image_to_tensor(const cv::Mat& image)
return tensor;
}
-cv::Mat asst::OnnxHelper::hwc_to_chw(const cv::Mat& src)
+cv::Mat OnnxHelper::hwc_to_chw(const cv::Mat& src)
{
std::vector rgb_images;
cv::split(src, rgb_images);
diff --git a/src/MaaCore/Vision/OnnxHelper.h b/src/MaaCore/Vision/OnnxHelper.h
index 50e3829c2d..165e52166d 100644
--- a/src/MaaCore/Vision/OnnxHelper.h
+++ b/src/MaaCore/Vision/OnnxHelper.h
@@ -4,25 +4,26 @@
namespace asst
{
- class OnnxHelper : public VisionHelper
+ class OnnxHelper
{
public:
- using VisionHelper::VisionHelper;
- virtual ~OnnxHelper() override = default;
+ virtual ~OnnxHelper() = default;
protected:
template
- inline static void softmax(T& input)
+ inline static T softmax(const T& input)
{
- float rowmax = *std::max_element(input.begin(), input.end());
- std::vector y(input.size());
+ T output = input;
+ float rowmax = *std::max_element(output.begin(), output.end());
+ std::vector y(output.size());
float sum = 0.0f;
- for (size_t i = 0; i != input.size(); ++i) {
- sum += y[i] = std::exp(input[i] - rowmax);
+ for (size_t i = 0; i != output.size(); ++i) {
+ sum += y[i] = std::exp(output[i] - rowmax);
}
- for (size_t i = 0; i != input.size(); ++i) {
- input[i] = y[i] / sum;
+ for (size_t i = 0; i != output.size(); ++i) {
+ output[i] = y[i] / sum;
}
+ return output;
}
static std::vector image_to_tensor(const cv::Mat& image);