#include "HashImageAnalyzer.h" #include "AsstUtils.hpp" #include "Logger.hpp" bool asst::HashImageAnalyzer::analyze() { m_hash_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; } 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); //Log.debug(hash_result); int min_dist = INT_MAX; std::string cur_min_dist_name; for (auto&& [name, templ] : m_hash_templates) { int hm = hamming(hash_result, templ); //Log.debug(name, "dist:", hm); if (hm < min_dist) { cur_min_dist_name = name; min_dist = hm; } } m_min_dist_name.emplace_back(std::move(cur_min_dist_name)); 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_min_dist_name() const noexcept { return m_min_dist_name; } 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(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; 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; }