feat: 干员识别增加等级和精英度 (#4438)

* core返回json object

* fix:冲突

* 等级ok

* 等级和精英度

* core:等级,精英度识别

* 优化识别方式

* fix:ci

* 添加潜能模板,fix ci

* 潜能识别

* clang

* fix:直接使用stoi

* 修改识别完成条件

* 修复等级判断条件

* fix

* 修改类继承,使用char_model,函数拆分

* 缩小名字识别框,添加替换字符。权宜之计 (

* 纠错,优化代码
This commit is contained in:
GD-GK
2023-04-25 20:08:48 +08:00
committed by GitHub
parent 06102d14d3
commit 037ee70e64
11 changed files with 322 additions and 36 deletions

View File

@@ -2327,6 +2327,30 @@
"[Uu]-[O0o]f{1,2}icial",
"U-Official"
],
[
"^4",
""
],
[
"^α",
""
],
[
"^-",
""
],
[
"—",
""
],
[
".*12F",
"12F"
],
[
"^_",
""
],
[
".*威龙陈",
"假日威龙陈"
@@ -9745,18 +9769,64 @@
327
]
},
"OperBoxFlagElite1": {
"doc": "精英一 模板"
},
"OperBoxFlagElite2": {
"doc": "精英二 模板"
},
"OperBoxFlagElite": {
"doc": "偏移,识别精英度,扩大两个像素点",
"template": "empty.png",
"templThreshold": 0.9,
"roi": [
-7,
-52,
24,
35
]
},
"OperBoxPotential": {
"doc": "偏移,识别潜能,扩大两个像素点",
"template": "empty.png",
"templThreshold": 0.85,
"roi": [
81,
-29,
24,
24
]
},
"OperBoxPotential1": {},
"OperBoxPotential2": {},
"OperBoxPotential3": {},
"OperBoxPotential4": {},
"OperBoxPotential5": {},
"OperBoxNameOCR": {
"algorithm": "OcrDetect",
"fullMatch": true,
"text": [],
"roi": [
-10,
5,
42,
120,
105,
20
],
"postDelay": 100
},
"OperBoxLevelOCR": {
"algorithm": "OcrDetect",
"fullMatch": true,
"isAscii": true,
"text": [],
"roi": [
-13,
7,
37,
23
],
"postDelay": 100
},
"RoguelikeRecruitSwipeMaxTime": {
"algorithm": "JustReturn",
"maxTimes_Doc": "这个参数是肉鸽招募时,向右划动的最大次数",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -23,7 +23,9 @@ bool asst::OperBoxRecognitionTask::swipe_and_analyze()
{
LogTraceFunction;
std::string current_page_last_oper_name = "";
std::string current_page_first_oper_name = "";
m_own_opers.clear();
while (!need_exit()) {
OperBoxImageAnalyzer analyzer(ctrler()->get_image());
@@ -33,10 +35,12 @@ bool asst::OperBoxRecognitionTask::swipe_and_analyze()
break;
}
const auto& opers_result = analyzer.get_result();
if (opers_result.back().name == current_page_last_oper_name) {
if (opers_result.back().name == current_page_last_oper_name &&
opers_result.front().name == current_page_first_oper_name) {
break;
}
else {
current_page_first_oper_name = opers_result.front().name;
current_page_last_oper_name = opers_result.back().name;
for (const auto& box_info : opers_result) {
m_own_opers.emplace(box_info.name, box_info);
@@ -72,13 +76,17 @@ void asst::OperBoxRecognitionTask::callback_analyze_result(bool done)
{ "name", name },
{ "own", own },
});
if (own) {
own_opers.emplace_back(json::object {
{ "id", BattleData.get_id(name) }, { "name", name }, { "own", own },
// TODO
// { "elite", 0 }, { "level", 0 },
});
}
}
for (const auto& [name,box_info] : m_own_opers) {
own_opers.emplace_back(json::object {
{ "id", box_info.id },
{ "name", box_info.name },
{ "own", box_info.own },
{ "elite", box_info.elite },
{ "level", box_info.level },
{ "potential", box_info.potential },
// TODO
});
}
callback(AsstMsg::SubTaskExtraInfo, info);

View File

@@ -5,48 +5,244 @@
#include "Config/Miscellaneous/BattleDataConfig.h"
#include "Config/TaskData.h"
#include "Utils/Logger.hpp"
#include "Vision/BestMatchImageAnalyzer.h"
#include "Vision/MatchImageAnalyzer.h"
#include "Vision/OcrWithFlagTemplImageAnalyzer.h"
#include "Vision/MultiMatchImageAnalyzer.h"
#include "Vision/OcrWithPreprocessImageAnalyzer.h"
bool asst::OperBoxImageAnalyzer::analyze()
{
LogTraceFunction;
m_current_page_opers.clear();
bool oper_box = analyzer_opers();
#ifdef ASST_DEBUG
m_image_draw = m_image_draw_oper;
#endif
bool oper_box = analyzer_oper_box();
save_img(utils::path("debug") / utils::path("oper"));
return oper_box;
}
bool asst::OperBoxImageAnalyzer::analyzer_opers()
int asst::OperBoxImageAnalyzer::level_num(const std::string &level)
{
std::vector<asst::TextRect> oper_names_result;
const auto& ocr_replace = Task.get<OcrTaskInfo>("CharsNameOcrReplace");
if (level.empty() || !ranges::all_of(level, [](const char& c) -> bool { return std::isdigit(c); })) {
return 1;
}
return std::stoi(level);
}
OcrWithFlagTemplImageAnalyzer oper_name_analyzer(m_image);
bool asst::OperBoxImageAnalyzer::analyzer_oper_box()
{
LogTraceFunction;
oper_name_analyzer.set_task_info("OperBoxFlagLV", "OperBoxNameOCR");
oper_name_analyzer.set_replace(ocr_replace->replace_map, ocr_replace->replace_full);
const auto& all_opers = BattleData.get_all_oper_names();
oper_name_analyzer.set_required(std::vector(all_opers.begin(), all_opers.end()));
if (!oper_name_analyzer.analyze()) {
#ifdef ASST_DEBUG
m_image_draw_oper = m_image_draw;
#endif // ASST_DEBUG
if (!lv_flag_analyzer()) {
return false;
}
for (auto& opername : oper_name_analyzer.get_result()) {
OperBoxInfo oper_info;
oper_info.name = std::move(opername.text);
m_current_page_opers.emplace_back(oper_info);
if (!names_analyzer()) {
return false;
}
if (!level_analyzer()) {
return false;
}
if (!elite_analyzer()) {
return false;
}
if (!potential_analyzer()) {
return false;
}
sort_oper_horizontal(m_current_page_opers);
for (const auto& box : m_current_page_opers) {
Log.trace("operBox{", "\"rect_lv\":[", box.rect.x, box.rect.y, box.rect.width, box.rect.height,
"], \"id\": ", box.id, ", \"name\": ", box.name, ", \"elite\": ", box.elite,
", \"level\": ", box.level, ", \"potential\": ", box.potential, "}");
}
#ifdef ASST_DEBUG
m_image_draw_oper = m_image;
cv::rectangle(m_image_draw_oper, make_rect<cv::Rect>(opername.rect), cv::Scalar(0, 255, 0), 2);
cv::putText(m_image_draw_oper, opername.text, cv::Point(opername.rect.x, opername.rect.y - 10),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 2);
m_image_draw = m_image_draw_oper;
#endif
return !m_current_page_opers.empty();
}
// 获取lv_flag,必须是第一步
bool asst::OperBoxImageAnalyzer::lv_flag_analyzer()
{
MultiMatchImageAnalyzer m_multi_match_image_analyzer(m_image);
m_multi_match_image_analyzer.set_task_info("OperBoxFlagLV");
m_multi_match_image_analyzer.analyze();
const auto& m_lv_flags = m_multi_match_image_analyzer.get_result();
if (m_lv_flags.empty()) {
return false;
}
// 填入rect
for (const auto& lv_flag : m_lv_flags) {
OperBoxInfo box;
box.rect = lv_flag.rect;
box.own = true;
m_current_page_opers.emplace_back(box);
#ifdef ASST_DEBUG
cv::rectangle(m_image_draw_oper, make_rect<cv::Rect>(lv_flag.rect), cv::Scalar(0, 255, 0), 1);
#endif // ASST_DEBUG
}
return true;
}
// 识别名字
bool asst::OperBoxImageAnalyzer::names_analyzer()
{
OcrWithPreprocessImageAnalyzer name_analyzer(m_image);
name_analyzer.set_task_info("OperBoxNameOCR");
const Rect name_roi = Task.get<OcrTaskInfo>("OperBoxNameOCR")->roi;
const auto& ocr_replace = Task.get<OcrTaskInfo>("CharsNameOcrReplace");
name_analyzer.set_replace(ocr_replace->replace_map, ocr_replace->replace_full);
for (auto& box : m_current_page_opers) {
Rect roi = box.rect.move(name_roi);
if (roi.x + roi.width >= WindowWidthDefault) {
// 超出范围提示,框width该缩小。
Log.error("name rect:[", roi.x, roi.y, roi.width, roi.height, " out of range ");
return false;
}
name_analyzer.set_roi(roi);
if (name_analyzer.analyze()) {
const auto& ocr_result = name_analyzer.get_result().front();
std::string name = ocr_result.text;
box.id = BattleData.get_id(name);
box.name = std::move(name);
#ifdef ASST_DEBUG
cv::rectangle(m_image_draw_oper, make_rect<cv::Rect>(ocr_result.rect), cv::Scalar(0, 255, 0), 1);
cv::putText(m_image_draw_oper, box.id, cv::Point(roi.x, roi.y + 35), cv::FONT_HERSHEY_SIMPLEX, 0.5,
cv::Scalar(0, 0, 255), 2);
#endif // ASST_DEBUG
}
}
return true;
}
// 识别等级
bool asst::OperBoxImageAnalyzer::level_analyzer()
{
OcrWithPreprocessImageAnalyzer level_analyzer(m_image);
level_analyzer.set_task_info("OperBoxLevelOCR");
const Rect level_roi = Task.get<OcrTaskInfo>("OperBoxLevelOCR")->roi;
const auto& ocr_replace_num = Task.get<OcrTaskInfo>("NumberOcrReplace");
level_analyzer.set_replace(ocr_replace_num->replace_map, ocr_replace_num->replace_full);
for (auto& box : m_current_page_opers) {
Rect roi = box.rect.move(level_roi);
if (roi.x < 0) {
// 等级在lv的左,lv的识别框x该右移
Log.error("level rect:[", roi.x, roi.y, roi.width, roi.height, " out of range ");
return false;
}
level_analyzer.set_roi(roi);
if (level_analyzer.analyze()) {
const auto& ocr_result = level_analyzer.get_result().front();
std::string level = ocr_result.text;
box.level = std::move(level_num(level));
#ifdef ASST_DEBUG
cv::rectangle(m_image_draw_oper, make_rect<cv::Rect>(ocr_result.rect), cv::Scalar(0, 255, 0), 1);
cv::putText(m_image_draw_oper, level, cv::Point(roi.x, roi.y - 10), cv::FONT_HERSHEY_SIMPLEX, 0.5,
cv::Scalar(0, 0, 255), 2);
#endif // ASST_DEBUG
}
}
return true;
}
// 识别精英度
bool asst::OperBoxImageAnalyzer::elite_analyzer()
{
const std::string task_name = "OperBoxFlagElite";
const Rect elite_roi = Task.get(task_name)->roi;
BestMatchImageAnalyzer elite_analyzer(m_image);
elite_analyzer.set_task_info(task_name);
elite_analyzer.append_templ("OperBoxFlagElite1.png");
elite_analyzer.append_templ("OperBoxFlagElite2.png");
for (auto& box : m_current_page_opers) {
Rect roi = box.rect.move(elite_roi);
if (roi.x < 0) {
// 等级在lv的左,lv的识别框x该右移
Log.error("elite rect:[", roi.x, roi.y, roi.width, roi.height, " out of range ");
return false;
}
elite_analyzer.set_roi(roi);
if (!elite_analyzer.analyze()) {
box.elite = 0;
}
else {
const auto& elite_templ = elite_analyzer.get_result();
std::string elite = elite_templ.name.substr(task_name.size(), 1);
box.elite = std::stoi(elite);
#ifdef ASST_DEBUG
cv::rectangle(m_image_draw_oper, make_rect<cv::Rect>(roi), cv::Scalar(0, 255, 0), 1);
#endif // ASST_DEBUG
}
#ifdef ASST_DEBUG
cv::putText(m_image_draw_oper, std::to_string(box.elite), cv::Point(roi.x, roi.y - 10),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 2);
#endif // ASST_DEBUG
}
return true;
}
// 识别潜能
bool asst::OperBoxImageAnalyzer::potential_analyzer()
{
const std::string task_name_p = "OperBoxPotential";
const Rect potential_roi = Task.get(task_name_p)->roi;
BestMatchImageAnalyzer potential_analyzer(m_image);
potential_analyzer.set_task_info(task_name_p);
for (int i = 1; i < 6; i++) {
std::string potential_temp_name = task_name_p + std::to_string(i) + ".png";
potential_analyzer.append_templ(potential_temp_name);
}
for (auto& box : m_current_page_opers) {
Rect roi = box.rect.move(potential_roi);
if (roi.x + roi.width < 0) {
// 潜能在lv的右,lv的识别框width该缩小
Log.error("level rect:[", roi.x, roi.y, roi.width, roi.height, " out of range ");
return false;
}
potential_analyzer.set_roi(roi);
if (!potential_analyzer.analyze()) {
box.potential = 0;
}
else {
const auto& poten_templ = potential_analyzer.get_result();
std::string potential = poten_templ.name.substr(task_name_p.size(), 1);
box.potential = std::stoi(potential);
#ifdef ASST_DEBUG
cv::rectangle(m_image_draw_oper, make_rect<cv::Rect>(roi), cv::Scalar(0, 255, 0), 1);
#endif // ASST_DEBUG
}
#ifdef ASST_DEBUG
cv::putText(m_image_draw_oper, std::to_string(box.potential), cv::Point(roi.x, roi.y - 10),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 2);
#endif // ASST_DEBUG
}
return true;
}
void asst::OperBoxImageAnalyzer::sort_oper_horizontal(std::vector<asst::OperBoxInfo> m_oper_boxs)
{
// 按位置排个序
ranges::sort(m_oper_boxs, [](const OperBoxInfo& lhs, const OperBoxInfo& rhs) -> bool {
if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的按x排序
return lhs.rect.x < rhs.rect.x;
}
else {
return lhs.rect.y < rhs.rect.y;
}
});
}

View File

@@ -6,11 +6,13 @@ namespace asst
{
struct OperBoxInfo
{
std::string name;
std::string id;
std::string name;
int level = 0; // 等级
int elite = 0; // 精英度
bool own = false;
int potential = 0; // 潜能
Rect rect;
};
class OperBoxImageAnalyzer final : public AbstractImageAnalyzer
{
@@ -21,9 +23,19 @@ namespace asst
const auto& get_result() const noexcept { return m_current_page_opers; }
private:
bool analyzer_opers();
std::vector<OperBoxInfo> m_current_page_opers;
int level_num(const std::string& level);
bool analyzer_oper_box();
// 获取lv rect
bool lv_flag_analyzer();
bool names_analyzer();
bool level_analyzer();
bool elite_analyzer();
bool potential_analyzer();
// 按位置排序
// 1 - 2 - 3
// 4 - 5 - 6
void sort_oper_horizontal(std::vector<asst::OperBoxInfo> m_oper_boxs);
std::vector<asst::OperBoxInfo> m_current_page_opers;
#ifdef ASST_DEBUG
cv::Mat m_image_draw_oper;
#endif