diff --git a/resource/tasks.json b/resource/tasks.json index 38edf3af43..d0a197a8c0 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -3007,5 +3007,155 @@ "ClueGiveToNextPage", "ClueGiveTo1stConfirm" ] + }, + "BattleOpersFlag": { + "templThreshold": 0.85, + "roi": [ + 0, + 588, + 1280, + 18 + ], + "maskRange": [ + 1, + 255 + ] + }, + "BattleOperClickRange": { + "algorithm": "JustReturn", + "rectMove": [ + -59, + 6, + 98, + 120 + ] + }, + "BattleOperAvailable": { + "template": "empty.png", + "specialThreshold": 100, + "specialThreshold_Doc": "用于做V通道平均值阈值" + }, + "BattleOperRoleRange": { + "algorithm": "JustReturn", + "rectMove": [ + -41, + 6, + 31, + 25 + ] + }, + "BattleOperRoleCaster": { + }, + "BattleOperRoleMedic": { + }, + "BattleOperRolePioneer": { + }, + "BattleOperRoleSniper": { + }, + "BattleOperRoleSpecial": { + }, + "BattleOperRoleSupport": { + }, + "BattleOperRoleTank": { + }, + "BattleOperRoleWarrior": { + }, + "BattleOperCostRange": { + "algorithm": "hash", + "hash": [ + ], + "rectMove": [ + -10, + 12, + 30, + 17 + ] + }, + "BattleOperCostChannelH": { + "algorithm": "hash", + "hash": [ + ], + "maskRange": [ + 0, + 255 + ] + }, + "BattleOperCostChannelS": { + "algorithm": "hash", + "hash": [ + ], + "maskRange": [ + 0, + 50 + ] + }, + "BattleOperCostChannelV": { + "algorithm": "hash", + "hash": [ + ], + "maskRange": [ + 80, + 255 + ] + }, + "BattleOperCost0": { + "algorithm": "hash", + "hash": [ + "1ff83ffc3c3e780e700ee007e007e007e007e007e007f00f780e7c1e3ffc1ff8" + ] + }, + "BattleOperCost1": { + "algorithm": "hash", + "hash": [ + "00ff03ff1fff7fffffffff3f003f003f003f003f003f003f003f003f003f003f" + ] + }, + "BattleOperCost2": { + "algorithm": "hash", + "hash": [ + "3ffe7ffefc3ff81fe01f001f001f003e007e01fc07f81fe03fc07e00ffffffff" + ] + }, + "BattleOperCost3": { + "algorithm": "hash", + "hash": [ + "3ffe7ffffc3ff81fe01f001f03fe07fc07fe003f001fe01fe01ff83fffff7ffe" + ] + }, + "BattleOperCost4": { + "algorithm": "hash", + "hash": [ + "007c00fc01fc03fc03fc0fbc1f3c3f3c7e3cfc3cffffffffffff003c003c003c" + ] + }, + "BattleOperCost5": { + "algorithm": "hash", + "hash": [ + "3ffe7c007c0078007ff87ffe7ffe7c3f781f0007e007f81ffc1fffff7ffe3ffc" + ] + }, + "BattleOperCost6": { + "algorithm": "hash", + "hash": [ + "03c003800f001f003e003ff87ffc7c3ef00ee00fe007e00ff00e7c3e3ffc1ff8" + ] + }, + "BattleOperCost7": { + "algorithm": "hash", + "hash": [ + "ffffffff003e007e007c00f800f801f001f003c003c00f801f001f003e003e00" + ] + }, + "BattleOperCost8": { + "algorithm": "hash", + "hash": [ + "3ffc7ffe7c3e700e700e7c7e7ffc3ffc7ffefefef00fe00ff00ff03ffffe7ffe" + ] + }, + "BattleOperCost9": { + "algorithm": "hash", + "hash": [ + "3ffc7ffe7c3ff00ff007f007f00ffc0f7fff3ffe1ffe00fc01f803f00ff00fc0" + ] } } diff --git a/resource/template/BattleOperRoleCaster.png b/resource/template/BattleOperRoleCaster.png new file mode 100644 index 0000000000..46156e08f8 Binary files /dev/null and b/resource/template/BattleOperRoleCaster.png differ diff --git a/resource/template/BattleOperRoleMedic.png b/resource/template/BattleOperRoleMedic.png new file mode 100644 index 0000000000..60b7057b27 Binary files /dev/null and b/resource/template/BattleOperRoleMedic.png differ diff --git a/resource/template/BattleOperRolePioneer.png b/resource/template/BattleOperRolePioneer.png new file mode 100644 index 0000000000..2478f7c692 Binary files /dev/null and b/resource/template/BattleOperRolePioneer.png differ diff --git a/resource/template/BattleOperRoleSniper.png b/resource/template/BattleOperRoleSniper.png new file mode 100644 index 0000000000..2903cd93e7 Binary files /dev/null and b/resource/template/BattleOperRoleSniper.png differ diff --git a/resource/template/BattleOperRoleSpecial.png b/resource/template/BattleOperRoleSpecial.png new file mode 100644 index 0000000000..599b02b20b Binary files /dev/null and b/resource/template/BattleOperRoleSpecial.png differ diff --git a/resource/template/BattleOperRoleSupport.png b/resource/template/BattleOperRoleSupport.png new file mode 100644 index 0000000000..1e9f87ad3c Binary files /dev/null and b/resource/template/BattleOperRoleSupport.png differ diff --git a/resource/template/BattleOperRoleTank.png b/resource/template/BattleOperRoleTank.png new file mode 100644 index 0000000000..ee37e76a2a Binary files /dev/null and b/resource/template/BattleOperRoleTank.png differ diff --git a/resource/template/BattleOperRoleWarrior.png b/resource/template/BattleOperRoleWarrior.png new file mode 100644 index 0000000000..4cb666a225 Binary files /dev/null and b/resource/template/BattleOperRoleWarrior.png differ diff --git a/resource/template/BattleOpersFlag.png b/resource/template/BattleOpersFlag.png new file mode 100644 index 0000000000..11739081c7 Binary files /dev/null and b/resource/template/BattleOpersFlag.png differ diff --git a/src/MeoAssistant/AbstractImageAnalyzer.cpp b/src/MeoAssistant/AbstractImageAnalyzer.cpp index 4d03377695..432dbcd7d1 100644 --- a/src/MeoAssistant/AbstractImageAnalyzer.cpp +++ b/src/MeoAssistant/AbstractImageAnalyzer.cpp @@ -9,8 +9,7 @@ asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image) , m_image_draw(image.clone()) #endif -{ -} +{} asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image, const Rect& roi) : m_image(image), @@ -23,7 +22,7 @@ asst::AbstractImageAnalyzer::AbstractImageAnalyzer(const cv::Mat image, const Re ; } -void asst::AbstractImageAnalyzer::set_image(const cv::Mat image) +void asst::AbstractImageAnalyzer::_set_image(const cv::Mat image) { m_image = image; #ifdef ASST_DEBUG @@ -33,7 +32,7 @@ void asst::AbstractImageAnalyzer::set_image(const cv::Mat image) void asst::AbstractImageAnalyzer::set_image(const cv::Mat image, const Rect& roi) { - set_image(image); + _set_image(image); m_roi = empty_rect_to_full(roi, image); } diff --git a/src/MeoAssistant/AbstractImageAnalyzer.h b/src/MeoAssistant/AbstractImageAnalyzer.h index 65f3cb5295..e8ca93b713 100644 --- a/src/MeoAssistant/AbstractImageAnalyzer.h +++ b/src/MeoAssistant/AbstractImageAnalyzer.h @@ -16,8 +16,7 @@ namespace asst AbstractImageAnalyzer(AbstractImageAnalyzer&&) = delete; virtual ~AbstractImageAnalyzer() = default; - virtual void set_image(const cv::Mat image); - virtual void set_image(const cv::Mat image, const Rect& roi); + virtual void set_image(const cv::Mat image, const Rect& roi = Rect()); virtual void set_roi(const Rect& roi) noexcept; virtual bool analyze() = 0; void correct_roi() noexcept; @@ -29,6 +28,7 @@ namespace asst AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete; protected: + virtual void _set_image(const cv::Mat image); static Rect empty_rect_to_full(const Rect& rect, const cv::Mat image) noexcept; cv::Mat m_image; diff --git a/src/MeoAssistant/Assistant.cpp b/src/MeoAssistant/Assistant.cpp index 944a2861c3..650792f41d 100644 --- a/src/MeoAssistant/Assistant.cpp +++ b/src/MeoAssistant/Assistant.cpp @@ -12,6 +12,7 @@ #include "Resource.h" #include "CreditShoppingTask.h" +#include "BattleTask.h" #include "InfrastDormTask.h" #include "InfrastInfoTask.h" #include "InfrastMfgTask.h" @@ -335,15 +336,13 @@ bool Assistant::append_debug() return false; } - //std::unique_lock lock(m_mutex); + std::unique_lock lock(m_mutex); - //{ - // constexpr static const char* DebugTaskChain = "Debug"; - // auto shift_task_ptr = std::make_shared(task_callback, (void*)this, DebugTaskChain); - // shift_task_ptr->set_work_mode(infrast::WorkMode::Aggressive); - // shift_task_ptr->set_product("MoodAddition"); - // m_tasks_queue.emplace(shift_task_ptr); - //} + { + constexpr static const char* DebugTaskChain = "Debug"; + auto debug_task_ptr = std::make_shared(task_callback, (void*)this, DebugTaskChain); + m_tasks_queue.emplace(debug_task_ptr); + } return true; } diff --git a/src/MeoAssistant/AsstDef.h b/src/MeoAssistant/AsstDef.h index 602ff7ee13..374dae9653 100644 --- a/src/MeoAssistant/AsstDef.h +++ b/src/MeoAssistant/AsstDef.h @@ -85,6 +85,10 @@ namespace asst { return "[ " + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(width) + ", " + std::to_string(height) + " ]"; } + Rect move(Rect move) const + { + return Rect(x + move.x, y + move.y, move.width, move.height); + } int x = 0; int y = 0; @@ -122,8 +126,8 @@ namespace asst Invaild = -1, JustReturn, MatchTemplate, - CompareHist, - OcrDetect + OcrDetect, + Hash }; struct MatchRect @@ -222,6 +226,16 @@ namespace asst std::pair mask_range; // 掩码的二值化范围 }; + // hash 计算任务的信息 + struct HashTaskInfo : public TaskInfo + { + virtual ~HashTaskInfo() = default; + std::vector hashs; // 多个哈希值 + int hamming_threshold = 0; // 汉明距离阈值 + std::pair mask_range; // 掩码的二值化范围 + bool bound = false; // 是否裁剪周围黑边 + }; + struct HandleInfo { std::string class_name; diff --git a/src/MeoAssistant/BattleImageAnalyzer.cpp b/src/MeoAssistant/BattleImageAnalyzer.cpp new file mode 100644 index 0000000000..a89568e282 --- /dev/null +++ b/src/MeoAssistant/BattleImageAnalyzer.cpp @@ -0,0 +1,173 @@ +#include "BattleImageAnalyzer.h" + +#include "MultiMatchImageAnalyzer.h" +#include "MatchImageAnalyzer.h" +#include "HashImageAnalyzer.h" +#include "Logger.hpp" +#include "TaskData.h" + +bool asst::BattleImageAnalyzer::analyze() +{ + return opers_analyze(); +} + +const std::vector& asst::BattleImageAnalyzer::get_opers() const noexcept +{ + return m_opers; +} + +bool asst::BattleImageAnalyzer::opers_analyze() +{ + MultiMatchImageAnalyzer flags_analyzer(m_image); + flags_analyzer.set_task_info("BattleOpersFlag"); + if (!flags_analyzer.analyze()) { + return false; + } + flags_analyzer.sort_result(); + + const auto click_move = Task.get("BattleOperClickRange")->rect_move; + const auto role_move = Task.get("BattleOperRoleRange")->rect_move; + const auto cost_move = Task.get("BattleOperCostRange")->rect_move; + + for (const MatchRect& flag_mrect : flags_analyzer.get_result()) { + Oper oper; + oper.rect = flag_mrect.rect.move(click_move); + oper.available = oper_available_analyze(oper.rect); + +#ifdef ASST_DEBUG + if (oper.available) { + cv::rectangle(m_image_draw, utils::make_rect(oper.rect), cv::Scalar(0, 255, 0), 2); + } + else { + cv::rectangle(m_image_draw, utils::make_rect(oper.rect), cv::Scalar(0, 0, 255), 2); + } +#endif + + Rect role_rect = flag_mrect.rect.move(role_move); + oper.role = oper_role_analyze(role_rect); + Rect cost_rect = flag_mrect.rect.move(cost_move); + + oper.cost = oper_cost_analyze(cost_rect); + + m_opers.emplace_back(std::move(oper)); + } + + return true; +} + +asst::BattleImageAnalyzer::Role asst::BattleImageAnalyzer::oper_role_analyze(const Rect& roi) +{ + static const std::unordered_map RolesName = { + { Role::Caster, "Caster" }, + { Role::Medic, "Medic" }, + { Role::Pioneer, "Pioneer" }, + { Role::Sniper, "Sniper" }, + { Role::Special, "Special" }, + { Role::Support, "Support" }, + { Role::Tank, "Tank" }, + { Role::Warrior, "Warrior" } + }; + + MatchImageAnalyzer role_analyzer(m_image); + + Role result = Role::Invaild; + double max_score = 0; + for (auto&& [role, role_name] : RolesName) { + role_analyzer.set_task_info("BattleOperRole" + role_name); + role_analyzer.set_roi(roi); + if (!role_analyzer.analyze()) { + continue; + } + if (double cur_socre = role_analyzer.get_result().score; + max_score < cur_socre) { + result = role; + max_score = cur_socre; + } + } + +#ifdef ASST_DEBUG + cv::putText(m_image_draw, RolesName.at(result), cv::Point(roi.x, roi.y - 5), 1, 1, cv::Scalar(0, 255, 255)); +#endif + + return result; +} + +int asst::BattleImageAnalyzer::oper_cost_analyze(const Rect& roi) +{ + static const std::array NumName = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" + }; + static bool inited = false; + static cv::Scalar range_lower, range_upper; + + static HashImageAnalyzer hash_analyzer; + if (!inited) { + auto [h_l, h_u] = std::dynamic_pointer_cast( + Task.get("BattleOperCostChannelH"))->mask_range; + auto [s_l, s_u] = std::dynamic_pointer_cast( + Task.get("BattleOperCostChannelS"))->mask_range; + auto [v_l, v_u] = std::dynamic_pointer_cast( + Task.get("BattleOperCostChannelV"))->mask_range; + range_lower = cv::Scalar(h_l, s_l, v_l); + range_upper = cv::Scalar(h_u, s_u, v_u); + std::unordered_map num_hashs; + for (auto&& num : NumName) { + auto hashs_vec = std::dynamic_pointer_cast( + Task.get("BattleOperCost" + num))->hashs; + for (size_t i = 0; i != hashs_vec.size(); ++i) { + num_hashs.emplace(num + "_" + std::to_string(i), hashs_vec.at(i)); + } + } + hash_analyzer.set_hash_templates(std::move(num_hashs)); + hash_analyzer.set_need_bound(true); + hash_analyzer.set_need_split(true); + inited = true; + } + + cv::Mat hsv; + cv::cvtColor(m_image(utils::make_rect(roi)), hsv, cv::COLOR_BGR2HSV); + cv::Mat bin; + cv::inRange(hsv, range_lower, range_upper, bin); + hash_analyzer.set_image(bin); + hash_analyzer.analyze(); + + int cost = 0; + for (const std::string& num_name : hash_analyzer.get_result()) { + if (num_name.empty()) { + Log.error("hash result is empty"); + return 0; + } + cost *= 10; + cost += num_name.at(0) - '0'; + } + +#ifdef ASST_DEBUG + cv::putText(m_image_draw, std::to_string(cost), cv::Point(roi.x, roi.y - 20), 1, 1, cv::Scalar(0, 0, 255)); +#endif + + return cost; +} + +bool asst::BattleImageAnalyzer::oper_available_analyze(const Rect& roi) +{ + cv::Mat hsv; + cv::cvtColor(m_image(utils::make_rect(roi)), hsv, cv::COLOR_BGR2HSV); + cv::Scalar avg = cv::mean(hsv); + Log.trace("oper available, mean", avg[2]); + + static int thres = std::dynamic_pointer_cast(Task.get("BattleOperAvailable"))->special_threshold; + if (avg[2] < thres) { + return false; + } + return true; +} + +bool asst::BattleImageAnalyzer::home_analyze() +{ + return false; +} + +bool asst::BattleImageAnalyzer::placed_analyze() +{ + return false; +} diff --git a/src/MeoAssistant/BattleImageAnalyzer.h b/src/MeoAssistant/BattleImageAnalyzer.h new file mode 100644 index 0000000000..922627c316 --- /dev/null +++ b/src/MeoAssistant/BattleImageAnalyzer.h @@ -0,0 +1,57 @@ +#pragma once +#include "AbstractImageAnalyzer.h" + +namespace asst +{ + class BattleImageAnalyzer : public AbstractImageAnalyzer + { + public: + enum class Role + { + Invaild, + Caster, + Medic, + Pioneer, + Sniper, + Special, + Support, + Tank, + Warrior + }; + struct Oper + { + int cost = 0; + Role role = Role::Invaild; + bool available = false; + Rect rect; + }; + public: + using AbstractImageAnalyzer::AbstractImageAnalyzer; + virtual ~BattleImageAnalyzer() = default; + + virtual bool analyze() override; + + const std::vector& get_opers() const noexcept; + protected: + + bool opers_analyze(); // 识别干员 + Role oper_role_analyze(const Rect& roi); + int oper_cost_analyze(const Rect& roi); + bool oper_available_analyze(const Rect& roi); + bool home_analyze(); // 识别蓝色的家门 + bool placed_analyze(); // 识别可放置干员的位置 + + std::vector m_opers; + + private: + // 该分析器不支持外部设置ROI + virtual void set_roi(const Rect& roi) noexcept override + { + AbstractImageAnalyzer::set_roi(roi); + } + virtual void set_image(const cv::Mat image, const Rect& roi) + { + AbstractImageAnalyzer::set_image(image, roi); + } + }; +} diff --git a/src/MeoAssistant/BattleTask.cpp b/src/MeoAssistant/BattleTask.cpp new file mode 100644 index 0000000000..6cf61bfa92 --- /dev/null +++ b/src/MeoAssistant/BattleTask.cpp @@ -0,0 +1,11 @@ +#include "BattleTask.h" + +#include "BattleImageAnalyzer.h" +#include "Controller.h" + +bool asst::BattleTask::_run() +{ + BattleImageAnalyzer oper_analyzer(Ctrler.get_image()); + oper_analyzer.analyze(); + return false; +} diff --git a/src/MeoAssistant/BattleTask.h b/src/MeoAssistant/BattleTask.h new file mode 100644 index 0000000000..1e7f6c4869 --- /dev/null +++ b/src/MeoAssistant/BattleTask.h @@ -0,0 +1,16 @@ +#pragma once + +#include "AbstractTask.h" + +namespace asst +{ + class BattleTask : public AbstractTask + { + public: + using AbstractTask::AbstractTask; + virtual ~BattleTask() = default; + + protected: + virtual bool _run() override; + }; +} diff --git a/src/MeoAssistant/HashImageAnalyzer.cpp b/src/MeoAssistant/HashImageAnalyzer.cpp new file mode 100644 index 0000000000..5344cf3166 --- /dev/null +++ b/src/MeoAssistant/HashImageAnalyzer.cpp @@ -0,0 +1,161 @@ +#include "HashImageAnalyzer.h" + +#include "AsstUtils.hpp" + +bool asst::HashImageAnalyzer::analyze() +{ + m_hash_result.clear(); + m_hamming_result.clear(); + m_result.clear(); + + cv::Mat roi = m_image(utils::make_rect(m_roi)); + + if (m_mask_range.first != 0 || m_mask_range.second != 0) { + cv::Mat bin; + cv::inRange(roi, m_mask_range.first, m_mask_range.second, bin); + roi = bin; + } + + std::vector to_hash_vector; + if (m_need_split) { + to_hash_vector = split_bin(roi); + } + else { + to_hash_vector.emplace_back(roi); + } + + for (auto&& to_hash : to_hash_vector) { + if (m_need_bound) { + to_hash = bound_bin(to_hash); + } + std::string hash_result = shash(to_hash); + + decltype(m_hamming_result)::value_type cur_hamming; + std::string min_hm_name; + + int min_hamming = INT_MAX; + for (auto&& [name, templ] : m_hash_templates) { + int hm = hamming(hash_result, templ); + cur_hamming.emplace(name, hm); + if (hm < min_hamming) { + min_hm_name = name; + min_hamming = hm; + } + } + m_result.emplace_back(min_hm_name); + m_hamming_result.emplace_back(std::move(cur_hamming)); + + m_hash_result.emplace_back(std::move(hash_result)); + } + + return true; +} + +void asst::HashImageAnalyzer::set_mask_range(int lower, int upper) noexcept +{ + m_mask_range = std::make_pair(lower, upper); +} + +void asst::HashImageAnalyzer::set_mask_range(std::pair mask_range) noexcept +{ + m_mask_range = std::move(mask_range); +} + +void asst::HashImageAnalyzer::set_hash_templates(std::unordered_map hash_templates) noexcept +{ + m_hash_templates = std::move(hash_templates); +} + +void asst::HashImageAnalyzer::set_need_split(bool need_split) noexcept +{ + m_need_split = need_split; +} + +void asst::HashImageAnalyzer::set_need_bound(bool need_bound) noexcept +{ + m_need_bound = need_bound; +} + +const std::vector& asst::HashImageAnalyzer::get_result() const noexcept +{ + return m_result; +} + +std::string asst::HashImageAnalyzer::shash(const cv::Mat& gray) +{ + constexpr static int HashKernelSize = 16; + cv::Mat resized; + cv::resize(gray, resized, cv::Size(HashKernelSize, HashKernelSize)); + std::stringstream hash_value; + uchar* pix = resized.data; + int tmp_dec = 0; + for (int ro = 0; ro < 256; ro++) { + tmp_dec = tmp_dec << 1; + if (*pix > 127) + tmp_dec++; + if (ro % 4 == 3) { + hash_value << std::hex << tmp_dec; + tmp_dec = 0; + } + pix++; + } + return hash_value.str(); +} + +std::vector asst::HashImageAnalyzer::split_bin(const cv::Mat& bin) +{ + std::vector result; + + int range_start = 0; + bool started = false; + for (int i = 0; i != bin.cols; ++i) { + bool line_without_true = true; + for (int j = 0; j != bin.rows; ++j) { + cv::uint8_t value = bin.at(j, i); + + if (value) { + line_without_true = false; + if (!started) { + started = true; + range_start = i; + } + break; + } + } + if (started && line_without_true) { + started = false; + cv::Mat range_img = bin(cv::Range::all(), cv::Range(range_start, i)); + result.emplace_back(range_img); + } + } + if (started) { + started = false; + cv::Mat range_img = bin(cv::Range::all(), cv::Range(range_start, bin.cols)); + result.emplace_back(range_img); + } + + return result; +} + +cv::Mat asst::HashImageAnalyzer::bound_bin(const cv::Mat& bin) +{ + return bin(cv::boundingRect(bin)); +} + +int asst::HashImageAnalyzer::hamming(std::string hash1, std::string hash2) +{ + constexpr static int HammingFlags = 64; + + hash1.insert(hash1.begin(), HammingFlags - hash1.size(), '0'); + hash2.insert(hash2.begin(), HammingFlags - hash2.size(), '0'); + int dist = 0; + for (int i = 0; i < HammingFlags; i = i + 16) { + unsigned long long x = strtoull(hash1.substr(i, 16).c_str(), nullptr, 16) + ^ strtoull(hash2.substr(i, 16).c_str(), nullptr, 16); + while (x) { + dist++; + x = x & (x - 1); + } + } + return dist; +} diff --git a/src/MeoAssistant/HashImageAnalyzer.h b/src/MeoAssistant/HashImageAnalyzer.h new file mode 100644 index 0000000000..30b17fc7b8 --- /dev/null +++ b/src/MeoAssistant/HashImageAnalyzer.h @@ -0,0 +1,39 @@ +#pragma once +#include "AbstractImageAnalyzer.h" + +#include + +namespace asst +{ + class HashImageAnalyzer : public AbstractImageAnalyzer + { + public: + using AbstractImageAnalyzer::AbstractImageAnalyzer; + virtual ~HashImageAnalyzer() = default; + + virtual bool analyze() override; + + void set_mask_range(int lower, int upper) noexcept; + void set_mask_range(std::pair mask_range) noexcept; + void set_hash_templates(std::unordered_map hash_templates) noexcept; + void set_need_split(bool need_split) noexcept; + void set_need_bound(bool need_bound) noexcept; + + const std::vector& get_result() const noexcept; + + protected: + static std::string shash(const cv::Mat& gray); + static int hamming(std::string hash1, std::string hash2); + static std::vector split_bin(const cv::Mat& bin); + static cv::Mat bound_bin(const cv::Mat& bin); + + std::pair m_mask_range; + std::unordered_map m_hash_templates; + bool m_need_split = false; + bool m_need_bound = false; + + std::vector m_hash_result; + std::vector> m_hamming_result; + std::vector m_result; + }; +} diff --git a/src/MeoAssistant/MatchImageAnalyzer.cpp b/src/MeoAssistant/MatchImageAnalyzer.cpp index 34c88152df..8217a5004a 100644 --- a/src/MeoAssistant/MatchImageAnalyzer.cpp +++ b/src/MeoAssistant/MatchImageAnalyzer.cpp @@ -87,7 +87,7 @@ bool asst::MatchImageAnalyzer::match_templ(const cv::Mat templ) "templ size:", templ.cols, templ.rows); return false; } - if (m_mask_range.first == m_mask_range.second) { + if (m_mask_range.first == 0 && m_mask_range.second == 0) { cv::matchTemplate(image_roi, templ, matched, cv::TM_CCOEFF_NORMED); } else { diff --git a/src/MeoAssistant/MeoAssistant.vcxproj b/src/MeoAssistant/MeoAssistant.vcxproj index fc407b934b..462c30c982 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj +++ b/src/MeoAssistant/MeoAssistant.vcxproj @@ -24,10 +24,13 @@ + + + @@ -73,9 +76,12 @@ + + + diff --git a/src/MeoAssistant/MeoAssistant.vcxproj.filters b/src/MeoAssistant/MeoAssistant.vcxproj.filters index c1a5ac8653..df94deef5f 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj.filters +++ b/src/MeoAssistant/MeoAssistant.vcxproj.filters @@ -234,6 +234,15 @@ 头文件\TaskPlugin + + 头文件\Task + + + 头文件\ImageAnalyzer + + + 头文件\ImageAnalyzer\General + @@ -377,6 +386,15 @@ 源文件\TaskPlugin + + 源文件\Task + + + 源文件\ImageAnalyzer + + + 源文件\ImageAnalyzer\General + diff --git a/src/MeoAssistant/MultiMatchImageAnalyzer.cpp b/src/MeoAssistant/MultiMatchImageAnalyzer.cpp index d0f504c4fc..18dd2e9161 100644 --- a/src/MeoAssistant/MultiMatchImageAnalyzer.cpp +++ b/src/MeoAssistant/MultiMatchImageAnalyzer.cpp @@ -95,7 +95,7 @@ bool asst::MultiMatchImageAnalyzer::multi_match_templ(const cv::Mat templ) return false; } - if (m_mask_range.first == m_mask_range.second) { + if (m_mask_range.first == 0 && m_mask_range.second == 0) { cv::matchTemplate(image_roi, templ, matched, cv::TM_CCOEFF_NORMED); } else { diff --git a/src/MeoAssistant/ProcessTaskImageAnalyzer.h b/src/MeoAssistant/ProcessTaskImageAnalyzer.h index 8e8ff7826f..48a244cb35 100644 --- a/src/MeoAssistant/ProcessTaskImageAnalyzer.h +++ b/src/MeoAssistant/ProcessTaskImageAnalyzer.h @@ -21,7 +21,7 @@ namespace asst virtual ~ProcessTaskImageAnalyzer(); virtual bool analyze() override; - virtual void set_image(const cv::Mat image) override; + void set_image(const cv::Mat image); void set_tasks(std::vector tasks_name) { diff --git a/src/MeoAssistant/TaskData.cpp b/src/MeoAssistant/TaskData.cpp index 88f9e855b9..9522803201 100644 --- a/src/MeoAssistant/TaskData.cpp +++ b/src/MeoAssistant/TaskData.cpp @@ -54,8 +54,9 @@ bool asst::TaskData::parse(const json::value& json) else if (algorithm_str == "ocrdetect") { algorithm = AlgorithmType::OcrDetect; } - // CompareHist是MatchTemplate的衍生算法,不应作为单独的配置参数出现 - // else if (algorithm_str == "comparehist") {} + else if (algorithm_str == "hash") { + algorithm = AlgorithmType::Hash; + } else { m_last_error = "algorithm error " + algorithm_str; return false; @@ -96,6 +97,22 @@ bool asst::TaskData::parse(const json::value& json) } task_info_ptr = ocr_task_info_ptr; } break; + case AlgorithmType::Hash: + { + auto hash_task_info_ptr = std::make_shared(); + for (const json::value& hash : task_json.at("hash").as_array()) { + hash_task_info_ptr->hashs.emplace_back(hash.as_string()); + } + hash_task_info_ptr->hamming_threshold = task_json.get("threshold", 0.0); + if (task_json.exist("maskRange")) { + hash_task_info_ptr->mask_range = std::make_pair( + task_json.at("maskRange")[0].as_integer(), + task_json.at("maskRange")[1].as_integer()); + } + hash_task_info_ptr->bound = task_json.get("bound", true); + + task_info_ptr = hash_task_info_ptr; + } break; } task_info_ptr->cache = task_json.get("cache", true); task_info_ptr->algorithm = algorithm;