From 98cde630a479257f75131ffeb35f7751b9cae754 Mon Sep 17 00:00:00 2001 From: zzyyyl Date: Sun, 21 Jul 2024 13:58:25 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20mask=5Frange=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=BD=A9=E8=89=B2=E6=8E=A9=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/tasks.json | 20 +++-- src/MaaCore/Common/AsstTypes.h | 3 +- src/MaaCore/Config/TaskData.cpp | 65 ++++++++++++++++- src/MaaCore/Task/BattleHelper.cpp | 3 +- .../Vision/Battle/BattlefieldMatcher.cpp | 9 +-- src/MaaCore/Vision/Config/MatcherConfig.cpp | 12 ++- src/MaaCore/Vision/Config/MatcherConfig.h | 6 +- .../Infrast/InfrastOperImageAnalyzer.cpp | 6 +- src/MaaCore/Vision/Matcher.cpp | 73 ++++++++++++------- .../Miscellaneous/DepotImageAnalyzer.cpp | 2 +- .../Miscellaneous/StageDropsImageAnalyzer.cpp | 14 ++-- 11 files changed, 160 insertions(+), 53 deletions(-) diff --git a/resource/tasks.json b/resource/tasks.json index e373299640..90deb40bd8 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -3967,7 +3967,12 @@ "template": "empty.png", "templThreshold": 800, "templThreshold_Doc": "这个任务中作为符合range的像素点数量阈值", - "maskRange": [95, 105], + "maskRange": [ + [ + [95, 0, 0], + [105, 255, 255] + ] + ], "rectMove": [-18, -220, 10, 160] }, "InfrastOperOnShift": { @@ -4840,10 +4845,13 @@ "specialParams": [300], "specialParams_Doc": "这个任务中作为符合range的像素点数量阈值", "rectMove": [-68, 124, 114, 4], - "maskRange_Doc": "拿来做H通道的范围", - "maskRange": [0, 20], - "specificRect_Doc": "拿来做S、V通道的范围", - "specificRect": [100, 255, 0, 150] + "maskRange_Doc": "HSV的范围", + "maskRange": [ + [ + [0, 100, 0], + [20, 255, 150] + ] + ] }, "BattleOperRoleRange": { "template": "empty.png", @@ -5069,7 +5077,7 @@ }, "DepotQuantity": { "template": "empty.png", - "maskRange": [110, 255] + "specialParams": [110, 255] }, "DepotBegin": { "algorithm": "JustReturn", diff --git a/src/MaaCore/Common/AsstTypes.h b/src/MaaCore/Common/AsstTypes.h index 4918795de1..dcf3f2be05 100644 --- a/src/MaaCore/Common/AsstTypes.h +++ b/src/MaaCore/Common/AsstTypes.h @@ -488,9 +488,10 @@ namespace asst constexpr MatchTaskInfo(MatchTaskInfo&&) noexcept = default; constexpr MatchTaskInfo& operator=(const MatchTaskInfo&) = default; constexpr MatchTaskInfo& operator=(MatchTaskInfo&&) noexcept = default; + using Range = std::pair, std::vector>; std::vector templ_names; // 匹配模板图片文件名 std::vector templ_thresholds; // 模板匹配阈值 - std::pair mask_range; // 掩码的二值化范围 + std::vector mask_range; // 掩码的二值化范围 std::vector methods; // 匹配方法 }; using MatchTaskPtr = std::shared_ptr; diff --git a/src/MaaCore/Config/TaskData.cpp b/src/MaaCore/Config/TaskData.cpp index 58e6d84f01..5e44e2b715 100644 --- a/src/MaaCore/Config/TaskData.cpp +++ b/src/MaaCore/Config/TaskData.cpp @@ -516,7 +516,70 @@ asst::TaskPtr asst::TaskData::generate_match_task_info(std::string_view name, co return nullptr; } - utils::get_and_check_value_or(name, task_json, "maskRange", match_task_info_ptr->mask_range, default_ptr->mask_range); + auto mask_opt = task_json.find("maskRange"); + if (!mask_opt) { + match_task_info_ptr->mask_range = default_ptr->mask_range; + } + else if (!mask_opt->is_array()) { + Log.error("Invalid mask type in task", name); + return nullptr; + } + else if (auto mask_opt_array = mask_opt->as_array(); mask_opt_array.size() == 2 + && mask_opt_array[0].is_number() + && mask_opt_array[1].is_number()) { + match_task_info_ptr->mask_range.emplace_back( + std::vector { mask_opt_array[0].as_integer() }, + std::vector { mask_opt_array[1].as_integer() }); + } + else { + /* [ + [[0, 0, 0], [0, 0, 255]], + [[0, 0, 0], [0, 255, 0]], + [[0, 0, 0], [255, 0, 0]] + ] + */ + match_task_info_ptr->mask_range.clear(); + for (const auto& mask : mask_opt_array) { + if (!mask.is_array()) { + Log.error("Invalid mask in task", name); + return nullptr; + } + const auto& mask_range = mask.as_array(); + if (mask_range.size() != 2) { + Log.error("Invalid mask in task", name); + return nullptr; + } + if (!mask_range[0].is_array() || !mask_range[1].is_array()) { + Log.error("Invalid mask in task", name); + return nullptr; + } + const auto& lower = mask_range[0].as_array(); + const auto& upper = mask_range[1].as_array(); + if (!ranges::all_of(lower, [](const json::value& v) { return v.is_number(); }) || + !ranges::all_of(upper, [](const json::value& v) { return v.is_number(); })) { + Log.error("Invalid mask in task", name); + return nullptr; + } + if (lower.size() == 1 && upper.size() == 1) { + match_task_info_ptr->mask_range.emplace_back( + std::vector { lower[0].as_integer() }, + std::vector { upper[0].as_integer() }); + continue; + } + if (lower.size() == 3 && upper.size() == 3) { + match_task_info_ptr->mask_range.emplace_back( + std::vector { lower[0].as_integer(), + lower[1].as_integer(), + lower[2].as_integer() }, + std::vector { upper[0].as_integer(), + upper[1].as_integer(), + upper[2].as_integer() }); + continue; + } + Log.error("Invalid mask in task", name); + return nullptr; + } + } return match_task_info_ptr; } diff --git a/src/MaaCore/Task/BattleHelper.cpp b/src/MaaCore/Task/BattleHelper.cpp index c91d258a97..759bf2a76d 100644 --- a/src/MaaCore/Task/BattleHelper.cpp +++ b/src/MaaCore/Task/BattleHelper.cpp @@ -160,8 +160,7 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable, b static const auto cooling_mask_range = Task.get("BattleAvatarCoolingData")->mask_range; avatar_analyzer.set_threshold(cooling_threshold); - avatar_analyzer - .set_mask_range(cooling_mask_range.first, cooling_mask_range.second, true, true); + avatar_analyzer.set_mask_range(cooling_mask_range, true, true); } else { static const auto threshold = diff --git a/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp b/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp index a97ea7ad65..82d5803670 100644 --- a/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp +++ b/src/MaaCore/Vision/Battle/BattlefieldMatcher.cpp @@ -193,15 +193,10 @@ bool BattlefieldMatcher::oper_cooling_analyze(const Rect& roi) const auto img_roi = m_image(make_rect(roi)); cv::Mat hsv; cv::cvtColor(img_roi, hsv, cv::COLOR_BGR2HSV); - int h_low = cooling_task_ptr->mask_range.first; - int h_up = cooling_task_ptr->mask_range.second; - int s_low = cooling_task_ptr->specific_rect.x; - int s_up = cooling_task_ptr->specific_rect.y; - int v_low = cooling_task_ptr->specific_rect.width; - int v_up = cooling_task_ptr->specific_rect.height; + const auto& mask_range = cooling_task_ptr->mask_range[0]; cv::Mat bin; - cv::inRange(hsv, cv::Scalar(h_low, s_low, v_low), cv::Scalar(h_up, s_up, v_up), bin); + cv::inRange(hsv, mask_range.first, mask_range.second, bin); int count = 0; for (int i = 0; i != bin.rows; ++i) { diff --git a/src/MaaCore/Vision/Config/MatcherConfig.cpp b/src/MaaCore/Vision/Config/MatcherConfig.cpp index 270ed63676..aa7c11828b 100644 --- a/src/MaaCore/Vision/Config/MatcherConfig.cpp +++ b/src/MaaCore/Vision/Config/MatcherConfig.cpp @@ -41,7 +41,17 @@ void asst::MatcherConfig::set_threshold(std::vector templ_thres) noexcep void MatcherConfig::set_mask_range(int lower, int upper, bool mask_with_src, bool mask_with_close) { - m_params.mask_range = std::make_pair(lower, upper); + m_params.mask_range = { { { lower }, { upper } } }; + m_params.mask_with_src = mask_with_src; + m_params.mask_with_close = mask_with_close; +} + +void MatcherConfig::set_mask_range( + std::vector mask_range, + bool mask_with_src, + bool mask_with_close) +{ + m_params.mask_range = std::move(mask_range); m_params.mask_with_src = mask_with_src; m_params.mask_with_close = mask_with_close; } diff --git a/src/MaaCore/Vision/Config/MatcherConfig.h b/src/MaaCore/Vision/Config/MatcherConfig.h index 2bfb3d7508..55cde50673 100644 --- a/src/MaaCore/Vision/Config/MatcherConfig.h +++ b/src/MaaCore/Vision/Config/MatcherConfig.h @@ -14,7 +14,7 @@ namespace asst { std::vector> templs; std::vector templ_thres; - std::pair mask_range; + std::vector mask_range; std::vector methods; bool mask_with_src = false; bool mask_with_close = false; @@ -34,6 +34,10 @@ namespace asst void set_threshold(double templ_thres) noexcept; void set_threshold(std::vector templ_thres) noexcept; void set_mask_range(int lower, int upper, bool mask_with_src = false, bool mask_with_close = false); + void set_mask_range( + std::vector mask_range, + bool mask_with_src = false, + bool mask_with_close = false); void set_method(MatchMethod method) noexcept; protected: diff --git a/src/MaaCore/Vision/Infrast/InfrastOperImageAnalyzer.cpp b/src/MaaCore/Vision/Infrast/InfrastOperImageAnalyzer.cpp index 712a7bf414..2f6b1440c0 100644 --- a/src/MaaCore/Vision/Infrast/InfrastOperImageAnalyzer.cpp +++ b/src/MaaCore/Vision/Infrast/InfrastOperImageAnalyzer.cpp @@ -228,7 +228,7 @@ void asst::InfrastOperImageAnalyzer::skill_analyze() Matcher skill_analyzer(m_image); - skill_analyzer.set_mask_range(task_ptr->mask_range.first, task_ptr->mask_range.second); + skill_analyzer.set_mask_range(task_ptr->mask_range); skill_analyzer.set_threshold(task_ptr->templ_thresholds.front()); skill_analyzer.set_method(task_ptr->methods.front()); @@ -358,8 +358,8 @@ void asst::InfrastOperImageAnalyzer::selected_analyze() cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV); std::vector channels; cv::split(hsv, channels); - int mask_lowb = selected_task_ptr->mask_range.first; - int mask_uppb = selected_task_ptr->mask_range.second; + int mask_lowb = selected_task_ptr->mask_range[0].first[0]; + int mask_uppb = selected_task_ptr->mask_range[0].second[0]; int count = 0; auto& h_channel = channels.at(0); diff --git a/src/MaaCore/Vision/Matcher.cpp b/src/MaaCore/Vision/Matcher.cpp index ad43570b22..b5b67a24d4 100644 --- a/src/MaaCore/Vision/Matcher.cpp +++ b/src/MaaCore/Vision/Matcher.cpp @@ -94,41 +94,64 @@ std::vector Matcher::preproc_and_match(const cv::Mat& image, } cv::Mat matched; - cv::Mat hsv_image; - cv::Mat hsv_templ; - bool use_hsv = (method == MatchMethod::CcoeffHSV); - if (use_hsv) { - cv::cvtColor(image, hsv_image, cv::COLOR_BGR2HSV); - cv::cvtColor(templ, hsv_templ, cv::COLOR_BGR2HSV); + cv::Mat image_for_match; + cv::Mat templ_for_match; + if (method == MatchMethod::CcoeffHSV) { + cv::cvtColor(image, image_for_match, cv::COLOR_BGR2HSV); + cv::cvtColor(templ, templ_for_match, cv::COLOR_BGR2HSV); } + else { + cv::cvtColor(image, image_for_match, cv::COLOR_BGR2RGB); + cv::cvtColor(templ, templ_for_match, cv::COLOR_BGR2RGB); + } + int match_algorithm = cv::TM_CCOEFF_NORMED; if (method == MatchMethod::Ccoeff || method == MatchMethod::CcoeffHSV) { match_algorithm = cv::TM_CCOEFF_NORMED; } - if (params.mask_range.first == 0 && params.mask_range.second == 0) { - cv::matchTemplate( - use_hsv ? hsv_image : image, - use_hsv ? hsv_templ : templ, - matched, - match_algorithm); - } - else { - cv::Mat mask; - cv::cvtColor(params.mask_with_src ? image : templ, mask, cv::COLOR_BGR2GRAY); - cv::inRange(mask, params.mask_range.first, params.mask_range.second, mask); - if (params.mask_with_close) { + auto calc_mask = [&](const cv::Mat& templ_for_mask, bool with_close)->std::optional { + cv::Mat templ_for_gray_mask; + cv::cvtColor(templ_for_mask, templ_for_gray_mask, cv::COLOR_BGR2GRAY); + + // Union all masks, not intersection + cv::Mat mask = cv::Mat::zeros(templ_for_gray_mask.size(), CV_8UC1); + for (const auto& range : params.mask_range) { + cv::Mat current_mask; + if (range.first.size() == 1 && range.second.size() == 1) { + cv::inRange(templ_for_gray_mask, range.first[0], range.second[0], current_mask); + } + else if (range.first.size() == 3 && range.second.size() == 3) { + cv::inRange(templ_for_mask, range.first, range.second, current_mask); + } + else { + Log.error("Invalid mask range"); + return std::nullopt; + } + cv::bitwise_or(mask, current_mask, mask); + } + + if (with_close) { cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); cv::morphologyEx(mask, mask, cv::MORPH_CLOSE, kernel); } - cv::matchTemplate( - use_hsv ? hsv_image : image, - use_hsv ? hsv_templ : templ, - matched, - match_algorithm, - mask); - } + return mask; + }; + if (method == MatchMethod::Ccoeff || method == MatchMethod::CcoeffHSV) { + if (params.mask_range.empty()) { + cv::matchTemplate(image_for_match, templ_for_match, matched, match_algorithm); + } + else { + auto mask_opt = calc_mask( + params.mask_with_src ? image_for_match : templ_for_match, + params.mask_with_close); + if (!mask_opt) { + return {}; + } + cv::matchTemplate(image_for_match, templ_for_match, matched, match_algorithm, mask_opt.value()); + } + } results.emplace_back(RawResult { .matched = matched, .templ = templ, .templ_name = templ_name }); } return results; diff --git a/src/MaaCore/Vision/Miscellaneous/DepotImageAnalyzer.cpp b/src/MaaCore/Vision/Miscellaneous/DepotImageAnalyzer.cpp index 1314a14745..7fb4cf1921 100644 --- a/src/MaaCore/Vision/Miscellaneous/DepotImageAnalyzer.cpp +++ b/src/MaaCore/Vision/Miscellaneous/DepotImageAnalyzer.cpp @@ -256,7 +256,7 @@ int asst::DepotImageAnalyzer::match_quantity(const ItemInfo& item) RegionOCRer analyzer(m_image_resized); analyzer.set_task_info("NumberOcrReplace"); analyzer.set_roi(ocr_roi); - analyzer.set_bin_threshold(task_ptr->mask_range.first, task_ptr->mask_range.second); + analyzer.set_bin_threshold(task_ptr->special_params[0], task_ptr->special_params[1]); if (!analyzer.analyze()) { return 0; diff --git a/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp b/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp index d7e5cca77d..9583fdc102 100644 --- a/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp +++ b/src/MaaCore/Vision/Miscellaneous/StageDropsImageAnalyzer.cpp @@ -426,7 +426,11 @@ bool asst::StageDropsImageAnalyzer::analyze_baseline() } cv::Mat preprocessed_bin; - cv::inRange(preprocessed_roi, task_ptr->mask_range.first, task_ptr->mask_range.second, preprocessed_bin); + cv::inRange( + preprocessed_roi, + task_ptr->mask_range[0].first[0], + task_ptr->mask_range[0].second[0], + preprocessed_bin); cv::Rect bounding_rect = cv::boundingRect(preprocessed_bin); cv::Mat bounding = preprocessed_bin(bounding_rect); @@ -590,7 +594,7 @@ std::string asst::StageDropsImageAnalyzer::match_item(const Rect& roi, StageDrop auto match_item_with_templs = [&](const std::vector& templs_list) -> std::string { Matcher analyzer(m_image); - analyzer.set_mask_range(0, 0, false, true); + analyzer.set_mask_range(std::vector {}, false, true); analyzer.set_task_info("StageDrops-Item"); analyzer.set_roi(roi); @@ -641,7 +645,7 @@ std::optional asst::StageDropsImageAnalyzer::match_quantity_stri cv::Mat gray; cv::cvtColor(quantity_img, gray, cv::COLOR_BGR2GRAY); cv::Mat bin; - cv::inRange(gray, task_ptr->mask_range.first, task_ptr->mask_range.second, bin); + cv::inRange(gray, task_ptr->mask_range[0].first[0], task_ptr->mask_range[0].second[0], bin); // split const int max_spacing = static_cast(task_ptr->templ_thresholds.front()); @@ -689,7 +693,7 @@ std::optional asst::StageDropsImageAnalyzer::match_quantity_stri RegionOCRer analyzer(m_image); analyzer.set_task_info("NumberOcrReplace"); analyzer.set_roi(Rect(quantity_roi.x + far_left, quantity_roi.y, far_right - far_left, quantity_roi.height)); - analyzer.set_bin_threshold(task_ptr->mask_range.first, task_ptr->mask_range.second); + analyzer.set_bin_threshold(task_ptr->mask_range[0].first[0], task_ptr->mask_range[0].second[0]); analyzer.set_use_char_model(!use_word_model); if (!analyzer.analyze()) { @@ -759,7 +763,7 @@ std::optional asst::StageDropsImageAnalyzer::match_quantity_stri 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_use_char_model(!use_word_model); - ocr.set_bin_threshold(task_ptr->mask_range.first, task_ptr->mask_range.second); + ocr.set_bin_threshold(task_ptr->mask_range[0].first[0], task_ptr->mask_range[0].second[0]); if (!ocr.analyze()) { return std::nullopt; From a66c24842cac48dec719535b5a52b88213a5da3e Mon Sep 17 00:00:00 2001 From: zzyyyl Date: Sun, 21 Jul 2024 15:44:27 +0800 Subject: [PATCH 2/3] =?UTF-8?q?chore:=20=E6=96=B0=E5=A2=9E=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=B7=A5=E5=85=B7=E6=A0=B9=E6=8D=AE=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=87=8D=E8=A6=81=E9=A2=9C=E8=89=B2=E7=94=9F?= =?UTF-8?q?=E6=88=90=20mask=5Frange?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GenerateMaskRange/generate_mask_range.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tools/GenerateMaskRange/generate_mask_range.py diff --git a/tools/GenerateMaskRange/generate_mask_range.py b/tools/GenerateMaskRange/generate_mask_range.py new file mode 100644 index 0000000000..4cf0fb3569 --- /dev/null +++ b/tools/GenerateMaskRange/generate_mask_range.py @@ -0,0 +1,94 @@ +import cv2 +import numpy as np +import matplotlib.pyplot as plt +from pathlib import Path + +cur_dir = Path(__file__).parent.resolve() +maa_dir = cur_dir.parent.parent + +def generate_mask_ranges(image, base_mask, thresholds): + mask_ranges = [] + mask = cv2.inRange(hsv_image, (0, 0, 0), (0, 0, 0)) + for i, threshold in enumerate(thresholds): + current_mask = cv2.bitwise_and(base_mask, cv2.bitwise_not(mask)) + + hist_0 = cv2.calcHist([image], [0], current_mask, [256], [0, 256]) + hist_1 = cv2.calcHist([image], [1], current_mask, [256], [0, 256]) + hist_2 = cv2.calcHist([image], [2], current_mask, [256], [0, 256]) + + hue_threshold = threshold * hist_0.max() + sat_threshold = threshold * hist_1.max() + val_threshold = threshold * hist_2.max() + + try: + lh = int(np.where(hist_0 > hue_threshold)[0][0]) + uh = int(np.where(hist_0 > hue_threshold)[0][-1]) + ls = int(np.where(hist_1 > sat_threshold)[0][0]) + us = int(np.where(hist_1 > sat_threshold)[0][-1]) + lv = int(np.where(hist_2 > val_threshold)[0][0]) + uv = int(np.where(hist_2 > val_threshold)[0][-1]) + except: + print(f"Failed at #{i}") + break + + mask_ranges.append([[lh, ls, lv], [uh, us, uv]]) + + mask = cv2.bitwise_or(mask, cv2.inRange(image, (lh, ls, lv), (uh, us, uv))) + return mask_ranges, mask + +def show_image_mask(image, mask, image_for_hist): + image_with_mask = cv2.bitwise_and(image, image, mask=mask) + + fig, axs = plt.subplots(2, 3, figsize=(15, 8)) + + hist_0 = cv2.calcHist([image_for_hist], [0], None, [256], [0, 256]) + hist_1 = cv2.calcHist([image_for_hist], [1], None, [256], [0, 256]) + hist_2 = cv2.calcHist([image_for_hist], [2], None, [256], [0, 256]) + + axs[0, 0].plot(hist_0, color='r') + axs[0, 0].set_title('Channel 0 Histogram') + axs[0, 0].set_xlim([0, 256]) + + axs[0, 1].plot(hist_1, color='g') + axs[0, 1].set_title('Channel 1 Histogram') + axs[0, 1].set_xlim([0, 256]) + + axs[0, 2].plot(hist_2, color='b') + axs[0, 2].set_title('Channel 2 Histogram') + axs[0, 2].set_xlim([0, 256]) + + # 原图 + axs[1, 0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) + axs[1, 0].set_title('Original Image') + axs[1, 0].axis('off') + + # 掩码后的图像 + axs[1, 1].imshow(cv2.cvtColor(image_with_mask, cv2.COLOR_BGR2RGB)) + axs[1, 1].set_title('Image with Recommand Mask Range') + axs[1, 1].axis('off') + + # 显示掩码 + axs[1, 2].imshow(mask, cmap='gray') + axs[1, 2].set_title('Recommand Impmortant Mask Range') + axs[1, 2].axis('off') + + plt.tight_layout() + plt.show() + +if __name__ == '__main__': + image = cv2.imread(maa_dir / "resource" / "template" / "Sarkaz@Roguelike@StageCombatDpsEnter.png") + hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 自定义参数 + hsv_base_mask = cv2.inRange(hsv_image, (0, 1, 40), (180, 255, 255)) # 忽略暗色背景 + hsv_thresholds = [0.6] * 3 + rgb_base_mask = cv2.inRange(rgb_image, (20, 20, 20), (180, 255, 255)) # 忽略暗色背景 + rgb_thresholds = [0.6] * 3 + + hsv_mask_ranges, hsv_mask = generate_mask_ranges(hsv_image, hsv_base_mask, hsv_thresholds) + print(f'Recommand HSV Mask Range: {hsv_mask_ranges}') + show_image_mask(image, hsv_mask, hsv_image) + rgb_mask_ranges, rgb_mask = generate_mask_ranges(rgb_image, rgb_base_mask, rgb_thresholds) + print(f'Recommand RGB Mask Range: {rgb_mask_ranges}') + show_image_mask(image, rgb_mask, rgb_image) From b74809ada63e807e82e0cd6b9c14d6dabae42730 Mon Sep 17 00:00:00 2001 From: zzyyyl Date: Sun, 21 Jul 2024 15:47:32 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20task-schema=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=B8=AD=E5=AF=B9=20maskRange=20=E7=9A=84?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh-cn/protocol/task-schema.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/zh-cn/protocol/task-schema.md b/docs/zh-cn/protocol/task-schema.md index d4f54d6446..a4b63651ac 100644 --- a/docs/zh-cn/protocol/task-schema.md +++ b/docs/zh-cn/protocol/task-schema.md @@ -98,8 +98,14 @@ icon: material-symbols:task "templThreshold": 0.8, // 可选项,图片模板匹配得分的阈值,超过阈值才认为识别到了,可以是数字或数字列表 // 默认 0.8, 可根据日志查看实际得分是多少 - "maskRange": [ 1, 255 ], // 可选项,灰度掩码范围。例如将图片不需要识别的部分涂成黑色(灰度值为 0) - // 然后设置"maskRange"的范围为 [ 1, 255 ], 匹配的时候即刻忽略涂黑的部分 + "maskRange": [ 1, 255 ], // 可选项,掩码范围。 array | list, 2>> + // 当为 array 时 是灰度掩码范围 + // 例如将图片不需要识别的部分涂成黑色(灰度值为 0) + // 然后设置"maskRange"的范围为 [ 1, 255 ], 匹配的时候即忽略涂黑的部分 + // 当为 list, 2>> 时 + // 最内层代表一个颜色,由 method 决定它是 RGB 或 HSV; + // 中间一层是颜色范围的下限和上限; + // 最外层代表允许多个范围,最后取并集作为待识别区域。 "method": "Ccoeff", // 可选项,模板匹配算法,可以是列表 // 不填写时默认为 Ccoeff