diff --git a/resource/tasks.json b/resource/tasks.json index d0a197a8c0..a1b04e4918 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -1925,21 +1925,25 @@ ] }, "InfrastOperNameHash": { - "template": "empty.png", - "templThreshold_Doc": "作为哈希距离的阈值使用", - "templThreshold": 65, + "algorithm": "hash", + "hash": [], + "threshold": 65, "rectMove_Doc": "基于笑脸的位置移动", "rectMove": [ 0, 20, 113, 22 + ], + "maskRange": [ + 100, + 255 ] }, "InfrastOperFaceHash": { - "template": "empty.png", - "templThreshold_Doc": "作为哈希距离的阈值使用", - "templThreshold": 30, + "algorithm": "hash", + "hash": [], + "threshold": 30, "rectMove_Doc": "基于笑脸的位置移动", "rectMove": [ 0, diff --git a/src/MeoAssistant/AbstractImageAnalyzer.cpp b/src/MeoAssistant/AbstractImageAnalyzer.cpp index 432dbcd7d1..8ce18a24f8 100644 --- a/src/MeoAssistant/AbstractImageAnalyzer.cpp +++ b/src/MeoAssistant/AbstractImageAnalyzer.cpp @@ -50,48 +50,3 @@ asst::Rect asst::AbstractImageAnalyzer::empty_rect_to_full(const Rect& rect, con { return rect.empty() ? Rect(0, 0, image.cols, image.rows) : rect; } - -std::string asst::AbstractImageAnalyzer::calc_text_hash() const -{ - return calc_text_hash(m_roi); -} - -std::string asst::AbstractImageAnalyzer::calc_text_hash(const Rect& roi) const -{ - // 从左往右找到第一个白色点 - Rect white_roi = roi; - constexpr static int HashKernelSize = 16; - int threshold = 200; - bool find_point = false; - for (int i = 0; i != white_roi.width && !find_point; ++i) { - for (int j = 0; j != white_roi.height && !find_point; ++j) { - cv::Point point(white_roi.x + i, white_roi.y + j); - auto value = m_image.at(point); - if (value[0] > threshold && value[1] > threshold && value[2] > threshold) { - white_roi.x += i; - white_roi.width -= i; - find_point = true; - break; - } - } - } - cv::Mat image_roi = m_image(utils::make_rect(white_roi)); - cv::Mat bin; - cv::cvtColor(image_roi, image_roi, cv::COLOR_BGR2GRAY); - cv::threshold(image_roi, bin, threshold, 255, cv::THRESH_BINARY); - cv::resize(bin, bin, cv::Size(HashKernelSize, HashKernelSize)); - std::stringstream hash_value; - uchar* pix = bin.data; - int tmp_dec = 0; - for (int ro = 0; ro < 256; ro++) { - tmp_dec = tmp_dec << 1; - if ((bool)*pix) - tmp_dec++; - if (ro % 4 == 3) { - hash_value << std::hex << tmp_dec; - tmp_dec = 0; - } - pix++; - } - return hash_value.str(); -} diff --git a/src/MeoAssistant/AbstractImageAnalyzer.h b/src/MeoAssistant/AbstractImageAnalyzer.h index e8ca93b713..86fb653bc2 100644 --- a/src/MeoAssistant/AbstractImageAnalyzer.h +++ b/src/MeoAssistant/AbstractImageAnalyzer.h @@ -21,9 +21,6 @@ namespace asst virtual bool analyze() = 0; void correct_roi() noexcept; - std::string calc_text_hash() const; // 使用m_roi - std::string calc_text_hash(const Rect& roi) const; // 使用参数roi - AbstractImageAnalyzer& operator=(const AbstractImageAnalyzer&) = delete; AbstractImageAnalyzer& operator=(AbstractImageAnalyzer&&) = delete; diff --git a/src/MeoAssistant/AsstDef.h b/src/MeoAssistant/AsstDef.h index 374dae9653..3ab576bbb4 100644 --- a/src/MeoAssistant/AsstDef.h +++ b/src/MeoAssistant/AsstDef.h @@ -230,10 +230,10 @@ namespace asst struct HashTaskInfo : public TaskInfo { virtual ~HashTaskInfo() = default; - std::vector hashs; // 多个哈希值 - int hamming_threshold = 0; // 汉明距离阈值 - std::pair mask_range; // 掩码的二值化范围 - bool bound = false; // 是否裁剪周围黑边 + std::vector hashs; // 需要多个哈希值 + int dist_threshold = 0; // 汉明距离阈值 + std::pair mask_range; // 掩码的二值化范围 + bool bound = false; // 是否裁剪周围黑边 }; struct HandleInfo diff --git a/src/MeoAssistant/AsstUtils.hpp b/src/MeoAssistant/AsstUtils.hpp index 458705d081..540606dff4 100644 --- a/src/MeoAssistant/AsstUtils.hpp +++ b/src/MeoAssistant/AsstUtils.hpp @@ -150,23 +150,6 @@ namespace asst return str; } - inline int 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; - } - inline std::string callcmd(const std::string& cmdline) { constexpr int PipeBuffSize = 4096; diff --git a/src/MeoAssistant/BattleImageAnalyzer.cpp b/src/MeoAssistant/BattleImageAnalyzer.cpp index a89568e282..0f80f093b1 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.cpp +++ b/src/MeoAssistant/BattleImageAnalyzer.cpp @@ -132,7 +132,7 @@ int asst::BattleImageAnalyzer::oper_cost_analyze(const Rect& roi) hash_analyzer.analyze(); int cost = 0; - for (const std::string& num_name : hash_analyzer.get_result()) { + for (const std::string& num_name : hash_analyzer.get_min_dist_name()) { if (num_name.empty()) { Log.error("hash result is empty"); return 0; diff --git a/src/MeoAssistant/HashImageAnalyzer.cpp b/src/MeoAssistant/HashImageAnalyzer.cpp index 5344cf3166..085ae518d2 100644 --- a/src/MeoAssistant/HashImageAnalyzer.cpp +++ b/src/MeoAssistant/HashImageAnalyzer.cpp @@ -5,13 +5,15 @@ bool asst::HashImageAnalyzer::analyze() { m_hash_result.clear(); - m_hamming_result.clear(); - m_result.clear(); + m_min_dist_name.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; + if (roi.channels() == 3) { + cv::cvtColor(roi, roi, cv::COLOR_BGR2GRAY); + } cv::inRange(roi, m_mask_range.first, m_mask_range.second, bin); roi = bin; } @@ -30,21 +32,16 @@ bool asst::HashImageAnalyzer::analyze() } 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; + int min_dist = INT_MAX; + std::string cur_min_dist_name; 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; + if (hm < min_dist) { + cur_min_dist_name = name; + min_dist = hm; } } - m_result.emplace_back(min_hm_name); - m_hamming_result.emplace_back(std::move(cur_hamming)); - + m_min_dist_name.emplace_back(std::move(cur_min_dist_name)); m_hash_result.emplace_back(std::move(hash_result)); } @@ -76,16 +73,26 @@ void asst::HashImageAnalyzer::set_need_bound(bool need_bound) noexcept m_need_bound = need_bound; } -const std::vector& asst::HashImageAnalyzer::get_result() const noexcept +const std::vector& asst::HashImageAnalyzer::get_min_dist_name() const noexcept { - return m_result; + return m_min_dist_name; } -std::string asst::HashImageAnalyzer::shash(const cv::Mat& gray) +const std::vector& asst::HashImageAnalyzer::get_hash() const noexcept +{ + return m_hash_result; +} + +std::string asst::HashImageAnalyzer::shash(const cv::Mat& img) { constexpr static int HashKernelSize = 16; cv::Mat resized; - cv::resize(gray, resized, cv::Size(HashKernelSize, HashKernelSize)); + cv::resize(img, resized, cv::Size(HashKernelSize, HashKernelSize)); + if (img.channels() == 3) { + cv::Mat temp; + cv::cvtColor(resized, temp, cv::COLOR_BGR2GRAY); + resized = temp; + } std::stringstream hash_value; uchar* pix = resized.data; int tmp_dec = 0; diff --git a/src/MeoAssistant/HashImageAnalyzer.h b/src/MeoAssistant/HashImageAnalyzer.h index 30b17fc7b8..be75f5e02c 100644 --- a/src/MeoAssistant/HashImageAnalyzer.h +++ b/src/MeoAssistant/HashImageAnalyzer.h @@ -19,21 +19,22 @@ namespace asst void set_need_split(bool need_split) noexcept; void set_need_bound(bool need_bound) noexcept; - const std::vector& get_result() const noexcept; + const std::vector& get_min_dist_name() const noexcept; + const std::vector& get_hash() const noexcept; - protected: - static std::string shash(const cv::Mat& gray); + static std::string shash(const cv::Mat& img); 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); + protected: + 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; + std::vector m_min_dist_name; }; } diff --git a/src/MeoAssistant/InfrastAbstractTask.cpp b/src/MeoAssistant/InfrastAbstractTask.cpp index d8ff50a4b3..21ce467a3e 100644 --- a/src/MeoAssistant/InfrastAbstractTask.cpp +++ b/src/MeoAssistant/InfrastAbstractTask.cpp @@ -11,15 +11,6 @@ #include "Resource.h" #include "ProcessTask.h" -asst::InfrastAbstractTask::InfrastAbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain) - : AbstractTask(callback, callback_arg, std::move(task_chain)) -{ - m_face_hash_thres = static_cast(std::dynamic_pointer_cast( - Task.get("InfrastOperFaceHash"))->templ_threshold); - m_name_hash_thres = static_cast(std::dynamic_pointer_cast( - Task.get("InfrastOperNameHash"))->templ_threshold); -} - asst::InfrastAbstractTask& asst::InfrastAbstractTask::set_work_mode(infrast::WorkMode work_mode) noexcept { m_work_mode = work_mode; diff --git a/src/MeoAssistant/InfrastAbstractTask.h b/src/MeoAssistant/InfrastAbstractTask.h index bec82ec8f5..dcb06fb130 100644 --- a/src/MeoAssistant/InfrastAbstractTask.h +++ b/src/MeoAssistant/InfrastAbstractTask.h @@ -9,7 +9,6 @@ namespace asst { public: using AbstractTask::AbstractTask; - InfrastAbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain); virtual ~InfrastAbstractTask() = default; InfrastAbstractTask& set_work_mode(infrast::WorkMode work_mode) noexcept; @@ -44,8 +43,5 @@ namespace asst double m_mood_threshold = 0; mutable std::string m_facility_name_cache; int m_cur_facility_index = 0; - - int m_face_hash_thres = 0; - int m_name_hash_thres = 0; }; } diff --git a/src/MeoAssistant/InfrastOperImageAnalyzer.cpp b/src/MeoAssistant/InfrastOperImageAnalyzer.cpp index fa53df693e..b3525991d6 100644 --- a/src/MeoAssistant/InfrastOperImageAnalyzer.cpp +++ b/src/MeoAssistant/InfrastOperImageAnalyzer.cpp @@ -2,6 +2,7 @@ #include "InfrastSmileyImageAnalyzer.h" #include "MatchImageAnalyzer.h" +#include "HashImageAnalyzer.h" #include "Logger.hpp" #include "Resource.h" @@ -208,13 +209,12 @@ void asst::InfrastOperImageAnalyzer::face_hash_analyze() const Rect hash_rect_move = Task.get("InfrastOperFaceHash")->rect_move; + HashImageAnalyzer hash_analyzer(m_image); for (auto&& oper : m_result) { - Rect roi = hash_rect_move; - roi.x += oper.smiley.rect.x; - roi.y += oper.smiley.rect.y; - - cv::Mat image_roi = m_image(utils::make_rect(roi)); - oper.face_hash = hash_calc(image_roi); + Rect roi = oper.smiley.rect.move(hash_rect_move); + hash_analyzer.set_roi(roi); + hash_analyzer.analyze(); + oper.face_hash = hash_analyzer.get_hash().front(); } } @@ -222,43 +222,17 @@ void asst::InfrastOperImageAnalyzer::name_hash_analyze() { LogTraceFunction; - const Rect hash_rect_move = Task.get("InfrastOperNameHash")->rect_move; - - cv::Mat gray; - cv::cvtColor(m_image, gray, cv::COLOR_BGR2GRAY); + const auto task_ptr = std::dynamic_pointer_cast( + Task.get("InfrastOperNameHash")); + HashImageAnalyzer hash_analyzer(m_image); + hash_analyzer.set_mask_range(task_ptr->mask_range); + hash_analyzer.set_need_bound(true); for (auto&& oper : m_result) { - Rect roi = hash_rect_move; - roi.x += oper.smiley.rect.x; - roi.y += oper.smiley.rect.y; - - constexpr static int threshold = 100; - auto check_point = [&](cv::Point point) -> bool { - auto value = gray.at(point); - return value > threshold; - }; - // 找到四个方向上最靠外的白色点,把ROI缩小裁出来 - int left = -1, right = -1, top = INT_MAX, bottom = -1; - for (int i = 0; i != roi.width; ++i) { - for (int j = 0; j != roi.height; ++j) { - cv::Point point(roi.x + i, roi.y + j); - if (check_point(point)) { - if (left < 0) { - left = i; - } - right = i; - top = (std::min)(top, j); - bottom = (std::max)(bottom, j); - } - } - } - roi.x += left; - roi.width = right - left + 1; - roi.y += top; - roi.height = bottom - top + 1; - - cv::Mat hash_roi = m_image(utils::make_rect(roi)); - oper.name_hash = hash_calc(hash_roi); + Rect roi = oper.smiley.rect.move(task_ptr->rect_move); + hash_analyzer.set_roi(roi); + hash_analyzer.analyze(); + oper.name_hash = hash_analyzer.get_hash().front(); } } @@ -448,28 +422,3 @@ void asst::InfrastOperImageAnalyzer::doing_analyze() // TODO: infrast::Doing::Resting的识别 } } - -std::string asst::InfrastOperImageAnalyzer::hash_calc(const cv::Mat image) -{ - //constexpr static int HashKernelSize = 16; - const static cv::Size HashKernel(16, 16); - - cv::Mat hash_img; - cv::resize(image, hash_img, HashKernel); - cv::cvtColor(hash_img, hash_img, cv::COLOR_BGR2GRAY); - - std::stringstream hash_value; - cv::uint8_t* pix = hash_img.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(); -} diff --git a/src/MeoAssistant/InfrastOperImageAnalyzer.h b/src/MeoAssistant/InfrastOperImageAnalyzer.h index 0a0c5f1261..a1c5bb1677 100644 --- a/src/MeoAssistant/InfrastOperImageAnalyzer.h +++ b/src/MeoAssistant/InfrastOperImageAnalyzer.h @@ -68,8 +68,6 @@ namespace asst void selected_analyze(); void doing_analyze(); - static std::string hash_calc(const cv::Mat image); - std::string m_facility; std::vector m_result; int m_to_be_calced = All; diff --git a/src/MeoAssistant/InfrastProductionTask.cpp b/src/MeoAssistant/InfrastProductionTask.cpp index 8a6a8ec241..70bdb29582 100644 --- a/src/MeoAssistant/InfrastProductionTask.cpp +++ b/src/MeoAssistant/InfrastProductionTask.cpp @@ -4,9 +4,9 @@ #include -#include "AsstUtils.hpp" #include "Controller.h" #include "InfrastOperImageAnalyzer.h" +#include "HashImageAnalyzer.h" #include "Logger.hpp" #include "MatchImageAnalyzer.h" #include "MultiMatchImageAnalyzer.h" @@ -189,6 +189,8 @@ size_t asst::InfrastProductionTask::opers_detect() const auto& cur_all_opers = oper_analyzer.get_result(); max_num_of_opers_per_page = (std::max)(max_num_of_opers_per_page, cur_all_opers.size()); + const int face_hash_thres = std::dynamic_pointer_cast( + Task.get("InfrastOperFaceHash"))->dist_threshold; int cur_available_num = static_cast(cur_all_opers.size()); for (const auto& cur_oper : cur_all_opers) { if (cur_oper.skills.empty()) { @@ -204,9 +206,9 @@ size_t asst::InfrastProductionTask::opers_detect() m_all_available_opers.cbegin(), m_all_available_opers.cend(), [&](const infrast::Oper& oper) -> bool { // 有可能是同一个干员,比一下hash - int dist = utils::hamming(cur_oper.face_hash, oper.face_hash); + int dist = HashImageAnalyzer::hamming(cur_oper.face_hash, oper.face_hash); Log.debug("opers_detect hash dist |", dist); - return dist < m_face_hash_thres; + return dist < face_hash_thres; }); // 如果两个的hash距离过小,则认为是同一个干员,不进行插入 if (find_iter != m_all_available_opers.cend()) { @@ -291,6 +293,9 @@ bool asst::InfrastProductionTask::optimal_calc() return true; } + const int name_hash_thres = std::dynamic_pointer_cast( + Task.get("InfrastOperNameHash"))->dist_threshold; + // 遍历所有组合,找到效率最高的 auto& all_group = Resrc.infrast().get_skills_group(facility_name()); for (const infrast::SkillsGroup& group : all_group) { @@ -370,9 +375,9 @@ bool asst::InfrastProductionTask::optimal_calc() bool hash_matched = false; if (!opt.possible_hashs.empty()) { for (const auto& [key, hash] : opt.possible_hashs) { - int dist = utils::hamming(find_iter->name_hash, hash); + int dist = HashImageAnalyzer::hamming(find_iter->name_hash, hash); Log.debug("optimal_calc | name hash dist", dist, hash, find_iter->name_hash); - if (dist < m_name_hash_thres) { + if (dist < name_hash_thres) { hash_matched = true; break; } @@ -448,6 +453,10 @@ bool asst::InfrastProductionTask::opers_choose() auto& facility_info = Resrc.infrast().get_facility_info(facility_name()); int cur_max_num_of_opers = facility_info.max_num_of_opers - m_cur_num_of_lokced_opers; + const int name_hash_thres = std::dynamic_pointer_cast( + Task.get("InfrastOperNameHash"))->dist_threshold; + const int face_hash_thres = std::dynamic_pointer_cast( + Task.get("InfrastOperFaceHash"))->dist_threshold; while (true) { if (need_exit()) { return false; @@ -496,9 +505,9 @@ bool asst::InfrastProductionTask::opers_choose() else { // 既要技能相同,也要hash相同,双重校验 for (const auto& [_, hash] : opt_iter->possible_hashs) { - int dist = utils::hamming(lhs.name_hash, hash); + int dist = HashImageAnalyzer::hamming(lhs.name_hash, hash); Log.debug("opers_choose | name hash dist", dist); - if (dist < m_name_hash_thres) { + if (dist < name_hash_thres) { return true; } } @@ -525,9 +534,9 @@ bool asst::InfrastProductionTask::opers_choose() auto avlb_iter = std::find_if( m_all_available_opers.cbegin(), m_all_available_opers.cend(), [&](const infrast::Oper& lhs) -> bool { - int dist = utils::hamming(lhs.face_hash, find_iter->face_hash); + int dist = HashImageAnalyzer::hamming(lhs.face_hash, find_iter->face_hash); Log.debug("opers_choose | face hash dist", dist); - if (dist < m_face_hash_thres) { + if (dist < face_hash_thres) { return true; } return false; diff --git a/src/MeoAssistant/TaskData.cpp b/src/MeoAssistant/TaskData.cpp index 9522803201..1cfe036afd 100644 --- a/src/MeoAssistant/TaskData.cpp +++ b/src/MeoAssistant/TaskData.cpp @@ -103,7 +103,7 @@ bool asst::TaskData::parse(const json::value& json) 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); + hash_task_info_ptr->dist_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(), @@ -217,4 +217,4 @@ bool asst::TaskData::parse(const json::value& json) } #endif return true; -} +} \ No newline at end of file