refactor.重构哈希计算的调用逻辑

This commit is contained in:
MistEO
2022-01-17 23:55:55 +08:00
parent 51f511aec9
commit 8b593d4b9d
14 changed files with 80 additions and 190 deletions

View File

@@ -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,

View File

@@ -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<cv::Vec3b>(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<cv::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();
}

View File

@@ -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;

View File

@@ -230,10 +230,10 @@ namespace asst
struct HashTaskInfo : public TaskInfo
{
virtual ~HashTaskInfo() = default;
std::vector<std::string> hashs; // 多个哈希值
int hamming_threshold = 0; // 汉明距离阈值
std::pair<int, int> mask_range; // 掩码的二值化范围
bool bound = false; // 是否裁剪周围黑边
std::vector<std::string> hashs; // 需要多个哈希值
int dist_threshold = 0; // 汉明距离阈值
std::pair<int, int> mask_range; // 掩码的二值化范围
bool bound = false; // 是否裁剪周围黑边
};
struct HandleInfo

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<cv::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<std::string>& asst::HashImageAnalyzer::get_result() const noexcept
const std::vector<std::string>& 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<std::string>& 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;

View File

@@ -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<std::string>& get_result() const noexcept;
const std::vector<std::string>& get_min_dist_name() const noexcept;
const std::vector<std::string>& 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<cv::Mat> split_bin(const cv::Mat& bin);
static cv::Mat bound_bin(const cv::Mat& bin);
protected:
std::pair<int, int> m_mask_range;
std::unordered_map<std::string, std::string> m_hash_templates;
bool m_need_split = false;
bool m_need_bound = false;
std::vector<std::string> m_hash_result;
std::vector<std::unordered_map<std::string, int>> m_hamming_result;
std::vector<std::string> m_result;
std::vector<std::string> m_min_dist_name;
};
}

View File

@@ -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<int>(std::dynamic_pointer_cast<MatchTaskInfo>(
Task.get("InfrastOperFaceHash"))->templ_threshold);
m_name_hash_thres = static_cast<int>(std::dynamic_pointer_cast<MatchTaskInfo>(
Task.get("InfrastOperNameHash"))->templ_threshold);
}
asst::InfrastAbstractTask& asst::InfrastAbstractTask::set_work_mode(infrast::WorkMode work_mode) noexcept
{
m_work_mode = work_mode;

View File

@@ -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;
};
}

View File

@@ -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<cv::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<HashTaskInfo>(
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<uchar>(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<cv::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();
}

View File

@@ -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<infrast::Oper> m_result;
int m_to_be_calced = All;

View File

@@ -4,9 +4,9 @@
#include <calculator/calculator.hpp>
#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<HashTaskInfo>(
Task.get("InfrastOperFaceHash"))->dist_threshold;
int cur_available_num = static_cast<int>(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<HashTaskInfo>(
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<HashTaskInfo>(
Task.get("InfrastOperNameHash"))->dist_threshold;
const int face_hash_thres = std::dynamic_pointer_cast<HashTaskInfo>(
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;

View File

@@ -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;
}
}