diff --git a/resource/tasks.json b/resource/tasks.json index 53cd403a72..39bdb29291 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -6598,8 +6598,8 @@ 22 ], "maskRange": [ - 150, - 255 + 0, + 110 ] }, "NumberOcrReplace": { diff --git a/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp b/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp index a95ca36045..939d544b10 100644 --- a/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp +++ b/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp @@ -7,6 +7,7 @@ #include "Config/Miscellaneous/ItemConfig.h" #include "Config/Miscellaneous/StageDropsConfig.h" #include "Config/TaskData.h" +#include "Config/TemplResource.h" #include "Utils/ImageIo.hpp" #include "Utils/Logger.hpp" #include "Vision/MatchImageAnalyzer.h" @@ -192,9 +193,9 @@ bool asst::StageDropsImageAnalyzer::analyze_drops() std::string item = match_item(item_roi, drop_type, size - i, size); bool use_word_model = item == LMD_ID; - int quantity = match_quantity(item_roi, use_word_model); + int quantity = match_quantity(item_roi, item, use_word_model); if (use_word_model && quantity == 0) { - quantity = match_quantity(item_roi, false); + quantity = match_quantity(item_roi, item, false); } Log.info("Item id:", item, ", quantity:", quantity); #ifdef ASST_DEBUG @@ -462,7 +463,8 @@ std::string asst::StageDropsImageAnalyzer::match_item(const Rect& roi, StageDrop return result; } -int asst::StageDropsImageAnalyzer::match_quantity(const Rect& roi, bool use_word_model) +std::optional asst::StageDropsImageAnalyzer::match_quantity_string(const asst::Rect& roi, + bool use_word_model) { auto task_ptr = Task.get("StageDrops-Quantity"); @@ -508,7 +510,7 @@ int asst::StageDropsImageAnalyzer::match_quantity(const Rect& roi, bool use_word } if (contours.empty()) { - return 0; + return std::nullopt; } // 前面的 split 算法经过了大量的测试集验证,分割效果一切正常 @@ -525,10 +527,94 @@ int asst::StageDropsImageAnalyzer::match_quantity(const Rect& roi, bool use_word analyzer.set_use_char_model(!use_word_model); if (!analyzer.analyze()) { - return 0; + return std::nullopt; } - const auto& result = analyzer.get_result().front(); + return analyzer.get_result().front(); +} + +std::optional asst::StageDropsImageAnalyzer::match_quantity_string(const asst::Rect& roi, + const std::string& item, + bool use_word_model) +{ + auto task_ptr = Task.get("StageDrops-Quantity"); + auto templ = TemplResource::get_instance().get_templ(item).clone(); + if (templ.empty()) { + Log.error("templ is empty: ", item); + return std::nullopt; + } + cv::resize(templ, templ, cv::Size {}, 0.975, 0.975); // make it more accurate + + MatchImageAnalyzer analyzer(m_image); + analyzer.set_templ(templ); + analyzer.set_mask_range(1, 255); + analyzer.set_mask_with_close(true); + analyzer.set_roi(roi); + if (!analyzer.analyze()) { + return std::nullopt; + } + Rect new_roi = analyzer.get_result().rect; + cv::Mat item_img = m_image(make_rect(new_roi)); + + // ref: DepotImageAnalyzer::match_quantity + cv::Mat quotient; + cv::divide(item_img + cv::Scalar { 1, 1, 1 }, templ + cv::Scalar { 1, 1, 1 }, quotient, 255); + + cv::Mat mask_r; + cv::Mat mask_g; + cv::Mat mask_b; + static constexpr int lb = 60; + static constexpr int ub = 140; + cv::inRange(quotient, cv::Scalar { lb, 0, 0 }, cv::Scalar { ub, 255, 255 }, mask_r); + cv::inRange(quotient, cv::Scalar { 0, lb, 0 }, cv::Scalar { 255, ub, 255 }, mask_g); + cv::inRange(quotient, cv::Scalar { 0, 0, lb }, cv::Scalar { 255, 255, ub }, mask_b); + cv::Mat mask; + cv::bitwise_or(mask_r, mask_g, mask); + cv::bitwise_or(mask, mask_b, mask); + + cv::Mat templ_mask; + cv::inRange(templ, cv::Scalar { 0, 0, 0 }, cv::Scalar { 0, 0, 0 }, templ_mask); + cv::bitwise_not(templ_mask, templ_mask); + cv::bitwise_and(mask, templ_mask, mask); + mask(cv::Rect { 0, 0, mask.cols / 4, mask.rows }) = cv::Scalar { 0, 0, 0 }; + cv::morphologyEx(mask, mask, cv::MORPH_OPEN, cv::getStructuringElement(cv::MORPH_RECT, { 4, 4 })); + + auto mask_rect = cv::boundingRect(mask); + if (mask_rect.height < 20) mask_rect.height = 20; + + cv::Mat ocr_img = m_image.clone(); + cv::subtract(ocr_img(make_rect(new_roi)), templ * 0.41, ocr_img(make_rect(new_roi))); + + OcrWithPreprocessImageAnalyzer ocr(ocr_img); + ocr.set_task_info("NumberOcrReplace"); + Rect ocr_roi { new_roi.x + mask_rect.x, new_roi.y + mask_rect.y, mask_rect.width, mask_rect.height }; + ocr.set_roi(ocr_roi); + ocr.set_expansion(0); + ocr.set_use_char_model(!use_word_model); + + ocr.set_threshold(task_ptr->mask_range.first, task_ptr->mask_range.second); + + if (!ocr.analyze()) { + return std::nullopt; + } + + return ocr.get_result().front(); +} + +int asst::StageDropsImageAnalyzer::match_quantity(const asst::Rect& roi, const std::string& item, bool use_word_model) +{ + TextRect result; + // is furniture? + if (item.empty() || item == "furni") { + auto opt = match_quantity_string(roi, use_word_model); + if (!opt) return 0; + result = opt.value(); + } + else { + auto opt = match_quantity_string(roi, item, use_word_model); + if (!opt) return 0; + result = opt.value(); + } #ifdef ASST_DEBUG cv::rectangle(m_image_draw, make_rect(result.rect), cv::Scalar(0, 0, 255)); @@ -548,7 +634,7 @@ int asst::StageDropsImageAnalyzer::match_quantity(const Rect& roi, bool use_word multiple = 10000; digit_str.erase(w_pos, digit_str.size()); } - else if (size_t k_pos = digit_str.find("k"); k_pos != std::string::npos) { + else if (size_t k_pos = digit_str.find('k'); k_pos != std::string::npos) { multiple = 1000; digit_str.erase(k_pos, digit_str.size()); } diff --git a/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.h b/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.h index 92c840470f..4c8640c467 100644 --- a/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.h +++ b/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.h @@ -2,6 +2,8 @@ #include "Vision/AbstractImageAnalyzer.h" #include "Config/Miscellaneous/StageDropsConfig.h" +#include + namespace asst { class StageDropsImageAnalyzer final : public AbstractImageAnalyzer @@ -27,7 +29,10 @@ namespace asst bool analyze_baseline(); bool analyze_drops(); - int match_quantity(const Rect& roi, bool use_word_model = false); + int match_quantity(const Rect& roi, const std::string& item, bool use_word_model = false); + std::optional match_quantity_string(const Rect& roi, bool use_word_model = false); + std::optional match_quantity_string(const Rect& roi, const std::string& item, bool use_word_model = false); + StageDropType match_droptype(const Rect& roi); std::string match_item(const Rect& roi, StageDropType type, int index, int size);