From 53c2deaf0772b214bd6ed49776cd66776a96e99e Mon Sep 17 00:00:00 2001 From: status102 <102887808+status102@users.noreply.github.com> Date: Fri, 16 May 2025 13:13:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ProcessTask=E6=94=AF=E6=8C=81=E7=89=B9?= =?UTF-8?q?=E5=BE=81=E5=8C=B9=E9=85=8D=20(#12701)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: ProcessTask支持特征匹配 * chore: test * fix: test * docs: task文档 --- docs/en-us/protocol/task-schema.md | 18 +++++- docs/ja-jp/protocol/task-schema.md | 18 +++++- docs/ko-kr/protocol/task-schema.md | 18 +++++- docs/zh-cn/protocol/task-schema.md | 18 +++++- docs/zh-tw/protocol/task-schema.md | 18 +++++- src/MaaCore/Common/AsstTypes.h | 45 ++++++++++++++ src/MaaCore/Config/TaskData.cpp | 62 +++++++++++++++++++ src/MaaCore/Config/TaskData.h | 8 +++ src/MaaCore/Task/ProcessTask.cpp | 7 +++ .../Vision/Config/FeatureMatcherConfig.cpp | 22 +++++++ .../Vision/Config/FeatureMatcherConfig.h | 17 ++--- src/MaaCore/Vision/FeatureMatcher.cpp | 17 ++--- .../Vision/Miscellaneous/PipelineAnalyzer.cpp | 32 ++++++++++ .../Vision/Miscellaneous/PipelineAnalyzer.h | 4 +- 14 files changed, 278 insertions(+), 26 deletions(-) diff --git a/docs/en-us/protocol/task-schema.md b/docs/en-us/protocol/task-schema.md index fc3811385c..95e18ff6e9 100644 --- a/docs/en-us/protocol/task-schema.md +++ b/docs/en-us/protocol/task-schema.md @@ -136,12 +136,28 @@ Usage of `resource/tasks` and description of each field "isAscii": false, // optional, whether the text content to be recognized is ASCII characters // default false if not filled - "withoutDet": false // Optional, whether to not use the detection model + "withoutDet": false, // Optional, whether to not use the detection model // default false if not filled /* The following fields are only valid when the algorithm is Hash */ // The algorithm is not mature, and is only used in some special cases, so it is not recommended for now // Todo + + /* The following fields are valid only when algorithm is FeatureMatch */ + + "template": "xxx.png", // Optional, the image file name to match, can be a string or a string list + // Default "taskname.png" + + "count": 4, // The number of feature points to match (threshold), default value = 4 + + "ratio": 0.6, // The distance ratio of the KNN matching algorithm, [0 - 1.0], the larger the ratio, the looser the match and the easier it is to connect. Default 0.6 + + "detector": "SIFT", // Feature point detector type, optional values ​​are SIFT, ORB, AKAZE, SURF; default value = SIFT + // SIFT: High computational complexity, scale invariance, rotation invariance. Best effect. + // ORB: Very fast computational speed, rotation invariance. But not scale invariance. + // BRISK: Very fast computational speed, scale invariance, rotation invariance. + // KAZE: Applicable to 2D and 3D images, with scale invariance and rotation invariance. + // AKAZE: Fast calculation speed, with scale invariance and rotation invariance. } } ``` diff --git a/docs/ja-jp/protocol/task-schema.md b/docs/ja-jp/protocol/task-schema.md index 88575b3aaa..1e55af6e54 100644 --- a/docs/ja-jp/protocol/task-schema.md +++ b/docs/ja-jp/protocol/task-schema.md @@ -140,12 +140,28 @@ JSONファイルはコメントをサポートしていません。テキスト "isAscii": false, // オプション、認識されるテキストコンテンツが ASCII 文字であるかどうか // 指定されていない場合、デフォルトはfalse - "withoutDet": false // オプション、検出モデルを使用しない場合 + "withoutDet": false, // オプション、検出モデルを使用しない場合 // 指定されていない場合、デフォルトは false /* 以下のフィールドは、algorithmがHashの場合にのみ有効です */ // アルゴリズムが未熟であり、特殊な場合にのみ使用されるため、現時点では推奨されていません // Todo + + /* 以下のフィールドは、アルゴリズムが FeatureMatch の場合のみ有効です */ + + "template": "xxx.png", // オプション。マッチングする画像ファイル名。文字列または文字列リストを指定できます。 + // デフォルトは "taskname.png" + + "count": 4, // マッチングする特徴点の数(閾値)。デフォルト値は 4 + + "ratio": 0.6, // KNN マッチングアルゴリズムの距離比。[0 - 1.0]。比率が大きいほどマッチングが緩くなり、接続されやすくなります。デフォルトは 0.6 + + "detector": "SIFT", // 特徴点検出の種類。オプションの値は SIFT、ORB、AKAZE、SURF。デフォルト値は SIFT + // SIFT: 計算量が多い、スケール不変性、回転不変性。最も効果的。 + // ORB: 計算速度が非常に速い、回転不変性。ただし、スケール不変性は保証されません。 + // BRISK: 非常に高速な計算速度、スケール不変性、回転不変性を備えています。 + // KAZE: 2Dおよび3D画像に適用可能、スケール不変性、回転不変性を備えています。 + // AKAZE: 高速な計算速度、スケール不変性、回転不変性を備えています。 } } ``` diff --git a/docs/ko-kr/protocol/task-schema.md b/docs/ko-kr/protocol/task-schema.md index 26b73d0d58..167286523d 100644 --- a/docs/ko-kr/protocol/task-schema.md +++ b/docs/ko-kr/protocol/task-schema.md @@ -143,12 +143,28 @@ icon: material-symbols:task "isAscii": false, // 선택 사항, 인식할 텍스트 내용이 ASCII 문자인지 여부를 나타냅니다. // 기본값은 false입니다. - "withoutDet": false // 선택 사항, 탐지 모델을 사용하지 않을지 여부를 나타냅니다. + "withoutDet": false, // 선택 사항, 탐지 모델을 사용하지 않을지 여부를 나타냅니다. // 기본값은 false입니다. /* 다음 필드들은 algorithm이 Hash인 경우에만 유효합니다. */ // 이 알고리즘은 아직 미성숙하며, 특수한 경우에만 사용되므로 현재는 권장되지 않습니다. // Todo + + /* 다음 필드는 알고리즘이 FeatureMatch인 경우에만 유효합니다. */ + + "template": "xxx.png", // 선택 사항. 일치시킬 이미지 파일 이름. 문자열 또는 문자열 목록일 수 있습니다. + // 기본값: "taskname.png" + + "count": 4, // 일치시킬 특징점 수(임계값), 기본값: 4 + + "ratio": 0.6, // KNN 일치 알고리즘의 거리 비율, [0 - 1.0]. 비율이 클수록 일치가 느슨해지고 연결하기가 더 쉽습니다. 기본값: 0.6 + + "detector": "SIFT", // 특징점 검출기 유형, 선택 사항: SIFT, ORB, AKAZE, SURF; 기본값: SIFT + // SIFT: 높은 계산 복잡도, 스케일 불변성, 회전 불변성. 최상의 효과. + // ORB: 매우 빠른 계산 속도, 회전 불변성. 하지만 스케일 불변성은 없습니다. + // BRISK: 매우 빠른 계산 속도, 스케일 불변성, 회전 불변성을 가집니다. + // KAZE: 2D 및 3D 이미지에 적용 가능하며, 스케일 불변성과 회전 불변성을 가집니다. + // AKAZE: 빠른 계산 속도, 스케일 불변성과 회전 불변성을 가집니다. } } ``` diff --git a/docs/zh-cn/protocol/task-schema.md b/docs/zh-cn/protocol/task-schema.md index 2252062d82..7b6b57ed3f 100644 --- a/docs/zh-cn/protocol/task-schema.md +++ b/docs/zh-cn/protocol/task-schema.md @@ -24,6 +24,7 @@ icon: material-symbols:task // - JustReturn: 不进行识别,直接执行 action // - MatchTemplate: 匹配图片 // - OcrDetect: 文字识别 + // - FeatureMatch: 特征匹配 "action": "ClickSelf", // 可选项,表示识别到后的动作 // 不填写时默认为 DoNothing @@ -144,8 +145,23 @@ icon: material-symbols:task /* 以下字段仅当 algorithm 为 JustReturn,action 为 Input 时有效 */ - "inputText": "A string text." // 必选项,要输入的文字内容,以字符串的形式 + "inputText": "A string text.", // 必选项,要输入的文字内容,以字符串的形式 + + /* 以下字段仅当 algorithm 为 FeatureMatch 时有效 */ + "template": "xxx.png", // 可选项,要匹配的图片文件名,可以是字符串或字符串列表 + // 默认 "任务名.png" + + "count": 4, // 匹配的特征点的数量要求 (阈值), 默认值 = 4 + + "ratio": 0.6, // KNN 匹配算法的距离比值, [0 - 1.0], 越大则匹配越宽松, 更容易连线. 默认0.6 + + "detector": "SIFT", // 特征点检测器类型, 可选值为 SIFT, ORB, AKAZE, SURF; 默认值 = SIFT + // SIFT: 计算复杂度高,具有尺度不变性、旋转不变性。效果最好。 + // ORB: 计算速度非常快,具有旋转不变性。但不具有尺度不变性。 + // BRISK: 计算速度非常快,具有尺度不变性、旋转不变性。 + // KAZE: 适用于2D和3D图像,具有尺度不变性、旋转不变性。 + // AKAZE: 计算速度较快,具有尺度不变性、旋转不变性。 } } ``` diff --git a/docs/zh-tw/protocol/task-schema.md b/docs/zh-tw/protocol/task-schema.md index 532264ff06..325cd5de04 100644 --- a/docs/zh-tw/protocol/task-schema.md +++ b/docs/zh-tw/protocol/task-schema.md @@ -140,12 +140,28 @@ icon: material-symbols:task "isAscii": false, // 可選項,要辨識的文字內容是否為 ASCII 碼字元 // 不填寫預設 false - "withoutDet": false // 可選項,是否不使用檢測模型 + "withoutDet": false, // 可選項,是否不使用檢測模型 // 不填寫預設 false /* 以下欄位僅當 algorithm 為 Hash 時有效 */ // 演算法不成熟,僅部分特例情況中用到了,暫不推薦使用 // Todo + + /* 以下欄位僅當 algorithm 為 FeatureMatch 時有效 */ + + "template": "xxx.png", // 可選項,要符合的圖片檔案名,可以是字串或字串列表 + // 預設 "任務名稱.png" + + "count": 4, // 匹配的特徵點的數量要求 (閾值), 預設值 = 4 + + "ratio": 0.6, // KNN 匹配演算法的距離比值, [0 - 1.0], 越大則匹配越寬鬆, 更容易連線. 預設0.6 + + "detector": "SIFT", // 特徵點偵測器類型, 可選值為 SIFT, ORB, AKAZE, SURF; 預設值 = SIFT + // SIFT: 計算複雜度高,具有尺度不變性、旋轉不變性。效果最好。 + // ORB: 計算速度非常快,具有旋轉不變性。但不具有尺度不變性。 + // BRISK: 計算速度非常快,具有尺度不變性、旋轉不變性。 + // KAZE: 適用於2D和3D影像,具有尺度不變性、旋轉不變性。 + // AKAZE: 計算速度較快,具有尺度不變性、旋轉不變性。 } } ``` diff --git a/src/MaaCore/Common/AsstTypes.h b/src/MaaCore/Common/AsstTypes.h index 2faa7079c4..bad4bc49b6 100644 --- a/src/MaaCore/Common/AsstTypes.h +++ b/src/MaaCore/Common/AsstTypes.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,7 @@ enum class AlgorithmType JustReturn, MatchTemplate, OcrDetect, + FeatureMatch, }; inline AlgorithmType get_algorithm_type(std::string algorithm_str) @@ -336,6 +338,7 @@ inline AlgorithmType get_algorithm_type(std::string algorithm_str) { "matchtemplate", AlgorithmType::MatchTemplate }, { "justreturn", AlgorithmType::JustReturn }, { "ocrdetect", AlgorithmType::OcrDetect }, + { "featurematch", AlgorithmType::FeatureMatch }, }; if (algorithm_map.contains(algorithm_str)) { return algorithm_map.at(algorithm_str); @@ -350,6 +353,7 @@ inline std::string enum_to_string(AlgorithmType algo) { AlgorithmType::JustReturn, "JustReturn" }, { AlgorithmType::MatchTemplate, "MatchTemplate" }, { AlgorithmType::OcrDetect, "OcrDetect" }, + { AlgorithmType::FeatureMatch, "FeatureMatch" }, }; if (auto it = algorithm_map.find(algo); it != algorithm_map.end()) { return it->second; @@ -457,6 +461,30 @@ inline std::string enum_to_string(MatchMethod method) } return "Invalid"; } + +enum class FeatureDetector +{ + SIFT, // 计算复杂度高,具有尺度不变性、旋转不变性。效果最好。 + SURF, + ORB, // 计算速度非常快,具有旋转不变性。但不具有尺度不变性。 + BRISK, // 计算速度非常快,具有尺度不变性、旋转不变性。 + KAZE, // 适用于2D和3D图像,具有尺度不变性、旋转不变性。 + AKAZE, // 计算速度较快,具有尺度不变性、旋转不变性。 +}; + +inline std::optional get_feature_detector(std::string method_str) +{ + utils::touppers(method_str); + static const std::unordered_map method_map = { + { "SIFT", FeatureDetector::SIFT }, { "SURF", FeatureDetector::SURF }, { "ORB", FeatureDetector::ORB }, + { "BRISK", FeatureDetector::BRISK }, { "KAZE", FeatureDetector::KAZE }, { "AKAZE", FeatureDetector::AKAZE }, + }; + if (auto it = method_map.find(method_str); it != method_map.end()) { + return it->second; + } + return std::nullopt; +} + } // namespace asst namespace asst @@ -574,5 +602,22 @@ struct MatchTaskInfo : public TaskInfo using MatchTaskPtr = std::shared_ptr; using MatchTaskConstPtr = std::shared_ptr; +struct FeatureMatchTaskInfo : public TaskInfo +{ + constexpr FeatureMatchTaskInfo() = default; + constexpr virtual ~FeatureMatchTaskInfo() override = default; + constexpr FeatureMatchTaskInfo(const FeatureMatchTaskInfo&) = default; + constexpr FeatureMatchTaskInfo(FeatureMatchTaskInfo&&) noexcept = default; + constexpr FeatureMatchTaskInfo& operator=(const FeatureMatchTaskInfo&) = default; + constexpr FeatureMatchTaskInfo& operator=(FeatureMatchTaskInfo&&) noexcept = default; + std::string templ_names; // 匹配模板图片文件名 + FeatureDetector detector = FeatureDetector::SIFT; // 特征检测器 + int count = 4; // 匹配特征点的阈值 + double ratio = 0.6; // KNN 匹配算法的距离比值 +}; + +using FeatureMatchTaskPtr = std::shared_ptr; +using FeatureMatchTaskConstPtr = std::shared_ptr; + inline static const std::string UploadDataSource = "MaaAssistantArknights"; } // namespace asst diff --git a/src/MaaCore/Config/TaskData.cpp b/src/MaaCore/Config/TaskData.cpp index 411ba56169..927a70555f 100644 --- a/src/MaaCore/Config/TaskData.cpp +++ b/src/MaaCore/Config/TaskData.cpp @@ -477,6 +477,13 @@ asst::TaskPtr asst::TaskData::generate_task_info(std::string_view name) case AlgorithmType::OcrDetect: task = generate_ocr_task_info(name, json, std::dynamic_pointer_cast(base)); break; + case AlgorithmType::FeatureMatch: + task = generate_feature_match_task_info( + name, + json, + std::dynamic_pointer_cast(base), + raw->type); + break; case AlgorithmType::JustReturn: task = std::make_shared(); break; @@ -784,6 +791,41 @@ asst::TaskPtr asst::TaskData::generate_ocr_task_info( return ocr_task_info_ptr; } +asst::TaskPtr asst::TaskData::generate_feature_match_task_info( + std::string_view name, + const json::value& task_json, + FeatureMatchTaskConstPtr default_ptr, + TaskDerivedType derived_type) +{ + if (default_ptr == nullptr) { + default_ptr = default_feature_match_task_info_ptr; + } + auto task_info_ptr = std::make_shared(); + if (!utils::get_and_check_value_or(name, task_json, "template", task_info_ptr->templ_names, [&]() { + return derived_type == TaskDerivedType::Implicit ? default_ptr->templ_names : std::string(name) + ".png"; + })) { // 隐式 Template Task 时继承,其它时默认值使用任务名 + return nullptr; + } + m_templ_required.insert(task_info_ptr->templ_names); + utils::get_and_check_value_or(name, task_json, "count", task_info_ptr->count, default_ptr->count); + auto detector_opt = task_json.find("detector"); + if (!detector_opt) { + task_info_ptr->detector = default_ptr->detector; + } + else if (detector_opt->is_string()) { + if (auto detector = get_feature_detector(detector_opt->as_string())) { + task_info_ptr->detector = *detector; + } + } + else { + Log.error("Invalid detector type in task", name); + return nullptr; + } + utils::get_and_check_value_or(name, task_json, "ratio", task_info_ptr->ratio, default_ptr->ratio); + + return task_info_ptr; +} + asst::ResultOrError asst::TaskData::compile_raw_tasklist( const TaskList& raw_tasks, std::string_view self_name, @@ -957,6 +999,17 @@ asst::OcrTaskConstPtr asst::TaskData::_default_ocr_task_info() return ocr_task_info_ptr; } +asst::FeatureMatchTaskConstPtr asst::TaskData::_default_feature_match_task_info() +{ + // btw, 为啥还要默认值再设一遍? + auto task_info_ptr = std::make_shared(); + // task_info_ptr->count = 4; + // task_info_ptr->ratio = 0.6; + // task_info_ptr->detector = FeatureDetector::SIFT; + + return task_info_ptr; +} + asst::TaskConstPtr asst::TaskData::_default_task_info() { auto task_info_ptr = std::make_shared(); @@ -1005,6 +1058,15 @@ bool asst::TaskData::syntax_check(std::string_view task_name, const json::value& "cache", "fullMatch", "isAscii", "ocrReplace", "rectMove", "replaceFull", "roi", "text", "withoutDet", } }, + { AlgorithmType::FeatureMatch, + { + // common + "action", "algorithm", "baseTask", "exceededNext", "maxTimes", + "next", "onErrorNext", "postDelay", "preDelay", "reduceOtherTimes", + "specialParams", "sub", "subErrorIgnored", + // specific + "template", "count", "ratio", "detector", + } }, { AlgorithmType::JustReturn, { // common diff --git a/src/MaaCore/Config/TaskData.h b/src/MaaCore/Config/TaskData.h index 83a503cf59..f2f5e1c32f 100644 --- a/src/MaaCore/Config/TaskData.h +++ b/src/MaaCore/Config/TaskData.h @@ -15,11 +15,14 @@ class TaskData final : public SingletonHolder, public AbstractConfigWi private: static MatchTaskConstPtr _default_match_task_info(); static OcrTaskConstPtr _default_ocr_task_info(); + static FeatureMatchTaskConstPtr _default_feature_match_task_info(); static TaskConstPtr _default_task_info(); // 从模板任务生成 static inline const MatchTaskConstPtr default_match_task_info_ptr = _default_match_task_info(); static inline const OcrTaskConstPtr default_ocr_task_info_ptr = _default_ocr_task_info(); + static inline const FeatureMatchTaskConstPtr default_feature_match_task_info_ptr = + _default_feature_match_task_info(); static inline const TaskConstPtr default_task_info_ptr = _default_task_info(); static std::string append_prefix(std::string_view task_name, std::string_view task_prefix); @@ -63,6 +66,11 @@ private: MatchTaskConstPtr default_ptr, TaskDerivedType derived_type); TaskPtr generate_ocr_task_info(std::string_view name, const json::value&, OcrTaskConstPtr default_ptr); + TaskPtr generate_feature_match_task_info( + std::string_view name, + const json::value& task_json, + FeatureMatchTaskConstPtr default_ptr, + TaskDerivedType derived_type); decltype(auto) insert_or_assign_raw_task(std::string_view task_name, TaskDerivedPtr task_info_ptr) { diff --git a/src/MaaCore/Task/ProcessTask.cpp b/src/MaaCore/Task/ProcessTask.cpp index cd2dff2515..606d5b92fa 100644 --- a/src/MaaCore/Task/ProcessTask.cpp +++ b/src/MaaCore/Task/ProcessTask.cpp @@ -176,6 +176,13 @@ ProcessTask::HitDetail ProcessTask::find_first(const TaskList& list) /* const, e .task_ptr = task_ptr }; } + if (task_ptr->algorithm == AlgorithmType::FeatureMatch) { + auto& raw_result = std::get(res_opt->result); + return { .rect = res_opt->rect, + .reco_detail = json::object { { "count", raw_result.count } }, + .task_ptr = task_ptr }; + } + return { .rect = res_opt->rect, .task_ptr = task_ptr }; } diff --git a/src/MaaCore/Vision/Config/FeatureMatcherConfig.cpp b/src/MaaCore/Vision/Config/FeatureMatcherConfig.cpp index 90e3b3ca66..4a9d90893b 100644 --- a/src/MaaCore/Vision/Config/FeatureMatcherConfig.cpp +++ b/src/MaaCore/Vision/Config/FeatureMatcherConfig.cpp @@ -1 +1,23 @@ #include "FeatureMatcherConfig.h" + +#include "Config/TaskData.h" + +void asst::FeatureMatcherConfig::set_task_info(const std::shared_ptr& task_ptr) +{ + _set_task_info(*std::dynamic_pointer_cast(task_ptr)); +} + +void asst::FeatureMatcherConfig::set_task_info(const std::string& task_name) +{ + set_task_info(Task.get(task_name)); +} + +void asst::FeatureMatcherConfig::_set_task_info(FeatureMatchTaskInfo task_info) +{ + m_params.count = task_info.count; + m_params.distance_ratio = task_info.ratio; + m_params.detector = task_info.detector; + m_params.templs = task_info.templ_names; + _set_roi(task_info.roi); + // m_params.green_mask = task_info.green_mask; +} diff --git a/src/MaaCore/Vision/Config/FeatureMatcherConfig.h b/src/MaaCore/Vision/Config/FeatureMatcherConfig.h index e3e85ff540..27c501b3b5 100644 --- a/src/MaaCore/Vision/Config/FeatureMatcherConfig.h +++ b/src/MaaCore/Vision/Config/FeatureMatcherConfig.h @@ -8,15 +8,7 @@ namespace asst class FeatureMatcherConfig { public: - enum class Detector - { - SIFT, // 计算复杂度高,具有尺度不变性、旋转不变性。效果最好。 - SURF, - ORB, // 计算速度非常快,具有旋转不变性。但不具有尺度不变性。 - BRISK, // 计算速度非常快,具有尺度不变性、旋转不变性。 - KAZE, // 适用于2D和3D图像,具有尺度不变性、旋转不变性。 - AKAZE, // 计算速度较快,具有尺度不变性、旋转不变性。 - }; + using Detector = FeatureDetector; // enum class Matcher //{ @@ -40,10 +32,10 @@ public: FeatureMatcherConfig() = default; virtual ~FeatureMatcherConfig() = default; - void set_params(Params params) { m_params = std::move(params); } + void set_task_info(const std::shared_ptr& task_ptr); + void set_task_info(const std::string& task_name); - // void set_task_info(const std::shared_ptr& task_ptr); - // void set_task_info(const std::string& task_name); + void set_params(Params params) { m_params = std::move(params); } void set_templ(std::variant templ) { m_params.templs = { std::move(templ) }; } @@ -55,6 +47,7 @@ public: protected: virtual void _set_roi(const Rect& roi) = 0; + virtual void _set_task_info(FeatureMatchTaskInfo task_info); // void _set_task_info(MatchTaskInfo task_info); diff --git a/src/MaaCore/Vision/FeatureMatcher.cpp b/src/MaaCore/Vision/FeatureMatcher.cpp index 04bcf1c8ea..f1516f469f 100644 --- a/src/MaaCore/Vision/FeatureMatcher.cpp +++ b/src/MaaCore/Vision/FeatureMatcher.cpp @@ -15,8 +15,6 @@ asst::FeatureMatcher::ResultsVecOpt asst::FeatureMatcher::analyze() const { auto start_time = std::chrono::steady_clock::now(); - const auto& image = m_image; - const auto& templ_ptr = m_params.templs; cv::Mat templ; std::string templ_name; @@ -41,8 +39,8 @@ asst::FeatureMatcher::ResultsVecOpt asst::FeatureMatcher::analyze() const #endif } - if (templ.cols > image.cols || templ.rows > image.rows) { - LogError << "templ size is too large" << templ_name << "image size:" << image.cols << image.rows + if (templ.cols > m_image.cols || templ.rows > m_image.rows) { + LogError << "templ size is too large" << templ_name << "image size:" << m_image.cols << m_image.rows << "templ size:" << templ.cols << templ.rows; return std::nullopt; } @@ -51,16 +49,19 @@ asst::FeatureMatcher::ResultsVecOpt asst::FeatureMatcher::analyze() const auto results = feature_match(templ, keypoints_1, descriptors_1); std::erase_if(results, [&](const auto& res) { return res.count < m_params.count; }); - m_result = std::move(results); auto cost = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time); + if (results.empty()) { + return std::nullopt; + } - for (const auto& r : m_result) { - Log.debug("feature_match |", templ_name, "count:", r.count, "rect:", r.rect, "roi:", m_roi); + for (const auto& r : results) { + Log.trace("feature_match |", templ_name, "count:", r.count, "rect:", r.rect, "roi:", m_roi); #ifdef ASST_DEBUG cv::rectangle(m_image_draw, make_rect(r.rect), cv::Scalar(0, 0, 255), 2); #endif } - Log.trace("count:", m_result.size(), ", cost:", cost.count()); + Log.trace("count:", results.size(), ", cost:", cost.count(), "ms"); + m_result = std::move(results); return m_result; } diff --git a/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.cpp b/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.cpp index c2e02c01d2..6d9c1e2c74 100644 --- a/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.cpp +++ b/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.cpp @@ -43,6 +43,12 @@ PipelineAnalyzer::ResultOpt PipelineAnalyzer::analyze() const return Result { .task_ptr = task_ptr, .result = ocr_opt->front(), .rect = ocr_opt->front().rect }; } break; + case AlgorithmType::FeatureMatch: + if (auto match_opt = feature_match(task_ptr)) { + Log.trace(__FUNCTION__, "| FeatureMatch", task_ptr->name); + return Result { .task_ptr = task_ptr, .result = match_opt->front(), .rect = match_opt->front().rect }; + } + break; default: break; } @@ -127,3 +133,29 @@ OCRer::ResultsVecOpt PipelineAnalyzer::ocr(const std::shared_ptr& task return result_vec; } + +FeatureMatcher::ResultsVecOpt asst::PipelineAnalyzer::feature_match(const std::shared_ptr& task_ptr) const +{ + FeatureMatcher match_analyzer(m_image, m_roi); + + const auto match_task_ptr = std::dynamic_pointer_cast(task_ptr); + match_analyzer.set_task_info(match_task_ptr); + + bool use_cache = m_inst && match_task_ptr->cache; + if (use_cache) { + auto cache_opt = status()->get_rect(match_task_ptr->name); + if (cache_opt) { + match_analyzer.set_roi(*cache_opt); + } + } + + const auto& result_opt = match_analyzer.analyze(); + if (!result_opt) { + return std::nullopt; + } + if (use_cache && !result_opt->empty()) { + status()->set_rect(match_task_ptr->name, result_opt->front().rect); + } + + return result_opt; +} diff --git a/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.h b/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.h index 8730510999..dbaf412cb4 100644 --- a/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.h +++ b/src/MaaCore/Vision/Miscellaneous/PipelineAnalyzer.h @@ -7,6 +7,7 @@ #include "Common/AsstTypes.h" +#include "Vision/FeatureMatcher.h" #include "Vision/Matcher.h" #include "Vision/OCRer.h" @@ -18,7 +19,7 @@ public: struct Result { std::shared_ptr task_ptr; - std::variant result; + std::variant result; Rect rect; }; @@ -35,6 +36,7 @@ public: private: Matcher::ResultOpt match(const std::shared_ptr& task_ptr) const; OCRer::ResultsVecOpt ocr(const std::shared_ptr& task_ptr) const; + FeatureMatcher::ResultsVecOpt feature_match(const std::shared_ptr& task_ptr) const; std::vector m_tasks_name; };