feat.集成企鹅物流识别库,删除特征点检测相关接口
@@ -837,74 +837,6 @@
|
||||
"endFlag_Doc": "识别到这里面的名字,就不继续往后识别了,一般用列表里的最后一个人名"
|
||||
}
|
||||
],
|
||||
"featureKey_Doc": "特征检测的关键字,如果OCR识别到了左边的内容,但是却没有右边的内容,则进行特征检测进一步确认",
|
||||
"featureKey": [
|
||||
[
|
||||
"森",
|
||||
"森蚺"
|
||||
],
|
||||
[
|
||||
"白面",
|
||||
"白面鸮"
|
||||
],
|
||||
[
|
||||
"灰",
|
||||
"灰烬"
|
||||
],
|
||||
[
|
||||
"早",
|
||||
"早露"
|
||||
],
|
||||
[
|
||||
"石",
|
||||
"燧石"
|
||||
],
|
||||
[
|
||||
"深",
|
||||
"深靛"
|
||||
],
|
||||
[
|
||||
"影",
|
||||
"傀影"
|
||||
],
|
||||
[
|
||||
"峨",
|
||||
"嵯峨"
|
||||
],
|
||||
[
|
||||
"利安",
|
||||
"卡涅利安"
|
||||
],
|
||||
[
|
||||
"星",
|
||||
"陨星"
|
||||
],
|
||||
[
|
||||
"冬",
|
||||
"凛冬"
|
||||
],
|
||||
[
|
||||
"狮",
|
||||
"狮蝎"
|
||||
]
|
||||
],
|
||||
"featureWhatever_Doc": "这里面的无论如何都进行特征检测",
|
||||
"featureWhatever": [
|
||||
"砾",
|
||||
"煌",
|
||||
"山",
|
||||
"黑",
|
||||
"W",
|
||||
"夕",
|
||||
"红",
|
||||
"芬",
|
||||
"梅",
|
||||
"宴",
|
||||
"孑",
|
||||
"吽",
|
||||
"空",
|
||||
"阿"
|
||||
],
|
||||
"allNames": [
|
||||
"桃金娘",
|
||||
"食铁兽",
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -84,20 +84,6 @@ Assistance::Assistance(AsstCallback callback, void* callback_arg)
|
||||
throw "resource broken";
|
||||
}
|
||||
|
||||
for (const auto& [key, name] : InfrastConfiger::get_instance().m_oper_name_feat) {
|
||||
ret = m_identify_ptr->add_text_image(name, GetResourceDir() + "operators\\" + Utf8ToGbk(name) + ".png");
|
||||
if (!ret) {
|
||||
callback_error(name);
|
||||
throw "resource broken";
|
||||
}
|
||||
}
|
||||
for (const auto& name : InfrastConfiger::get_instance().m_oper_name_feat_whatever) {
|
||||
ret = m_identify_ptr->add_text_image(name, GetResourceDir() + "operators\\" + Utf8ToGbk(name) + ".png");
|
||||
if (!ret) {
|
||||
callback_error(name);
|
||||
throw "resource broken";
|
||||
}
|
||||
}
|
||||
for (const auto& file : std::filesystem::directory_iterator(GetResourceDir() + "penguin-stats-recognize\\items")) {
|
||||
ret = m_identify_ptr->penguin_load_templ(file.path().stem().u8string(), file.path().u8string());
|
||||
if (!ret) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <filesystem>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <opencv2/xfeatures2d.hpp>
|
||||
#include <opencv2/imgproc/types_c.h>
|
||||
|
||||
namespace penguin {
|
||||
@@ -17,7 +16,6 @@ namespace penguin {
|
||||
|
||||
using namespace asst;
|
||||
using namespace cv;
|
||||
using namespace cv::xfeatures2d;
|
||||
|
||||
bool Identify::add_image(const std::string& name, const std::string& path)
|
||||
{
|
||||
@@ -29,18 +27,6 @@ bool Identify::add_image(const std::string& name, const std::string& path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::Identify::add_text_image(const std::string& text, const std::string& path)
|
||||
{
|
||||
Mat image = imread(path);
|
||||
if (image.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_feature_map.emplace(text, surf_detect(image));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Mat Identify::image_2_hist(const cv::Mat& src)
|
||||
{
|
||||
Mat src_hsv;
|
||||
@@ -66,165 +52,6 @@ double Identify::image_hist_comp(const cv::Mat& src, const cv::MatND& hist)
|
||||
return 1 - compareHist(image_2_hist(src), hist, CV_COMP_BHATTACHARYYA);
|
||||
}
|
||||
|
||||
std::pair<std::vector<cv::KeyPoint>, cv::Mat> asst::Identify::surf_detect(const cv::Mat& image)
|
||||
{
|
||||
// 灰度图转换
|
||||
cv::Mat mat_gray;
|
||||
cv::cvtColor(image, mat_gray, cv::COLOR_RGB2GRAY);
|
||||
|
||||
constexpr int min_hessian = 400;
|
||||
// SURF特征点检测
|
||||
static cv::Ptr<SURF> detector = SURF::create(min_hessian);
|
||||
std::vector<KeyPoint> keypoints;
|
||||
cv::Mat mat_vector;
|
||||
// 找到特征点并计算特征描述子(向量)
|
||||
detector->detectAndCompute(mat_gray, Mat(), keypoints, mat_vector);
|
||||
|
||||
return std::make_pair(std::move(keypoints), std::move(mat_vector));
|
||||
}
|
||||
|
||||
std::optional<asst::Rect> asst::Identify::feature_match(
|
||||
const std::vector<cv::KeyPoint>& query_keypoints, const cv::Mat& query_mat_vector,
|
||||
const std::vector<cv::KeyPoint>& train_keypoints, const cv::Mat& train_mat_vector
|
||||
#ifdef LOG_TRACE
|
||||
, const cv::Mat query_mat, const cv::Mat train_mat
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (query_mat_vector.empty() || train_mat_vector.empty()) {
|
||||
DebugTraceError("feature_match | input vector is empty");
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<cv::DMatch> matches;
|
||||
static FlannBasedMatcher matcher;
|
||||
matcher.match(query_mat_vector, train_mat_vector, matches);
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
//std::cout << matches.size() << " / " << query_keypoints.size() << std::endl;
|
||||
cv::Mat allmatch_mat;
|
||||
cv::drawMatches(query_mat, query_keypoints, train_mat, train_keypoints, matches, allmatch_mat);
|
||||
#endif
|
||||
|
||||
// 最大的距离
|
||||
auto max_iter = std::max_element(matches.cbegin(), matches.cend(),
|
||||
[](const cv::DMatch& lhs, const cv::DMatch& rhs) ->bool {
|
||||
return lhs.distance < rhs.distance;
|
||||
}); // 描述符欧式距离(knn)
|
||||
if (max_iter == matches.cend()) {
|
||||
return std::nullopt;;
|
||||
}
|
||||
float maxdist = max_iter->distance;
|
||||
|
||||
std::vector<cv::DMatch> approach_matches;
|
||||
std::vector<cv::KeyPoint> train_approach_keypoints;
|
||||
std::vector<cv::KeyPoint> query_approach_keypoints;
|
||||
std::vector<cv::Point> train_approach_points;
|
||||
std::vector<cv::Point> query_approach_points;
|
||||
// 利用距离进行一次逼近
|
||||
constexpr static const double MatchRatio = 0.4;
|
||||
int approach_index = 0;
|
||||
for (const cv::DMatch dmatch : matches) {
|
||||
if (dmatch.distance < maxdist * MatchRatio) {
|
||||
// 按理说不会越界,以防万一还是检查一下
|
||||
if (dmatch.queryIdx >= 0 && dmatch.queryIdx < query_keypoints.size()
|
||||
&& dmatch.trainIdx >= 0 && dmatch.trainIdx < train_keypoints.size()) {
|
||||
approach_matches.emplace_back(dmatch);
|
||||
train_approach_points.emplace_back(train_keypoints.at(dmatch.trainIdx).pt);
|
||||
query_approach_points.emplace_back(query_keypoints.at(dmatch.queryIdx).pt);
|
||||
train_approach_keypoints.emplace_back(train_keypoints.at(dmatch.trainIdx));
|
||||
query_approach_keypoints.emplace_back(query_keypoints.at(dmatch.queryIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
cv::Mat approach_mat;
|
||||
cv::drawMatches(query_mat, query_keypoints, train_mat, train_keypoints, approach_matches, approach_mat);
|
||||
#endif
|
||||
|
||||
if (query_approach_points.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 使用RANSAC剔除异常值
|
||||
std::vector<uchar> ransac_status;
|
||||
cv::Mat fundametal = cv::findFundamentalMat(query_approach_points, train_approach_points, ransac_status, cv::FM_RANSAC);
|
||||
|
||||
std::vector<cv::DMatch> ransac_matchs;
|
||||
std::vector<cv::KeyPoint> train_ransac_keypoints;
|
||||
std::vector<cv::KeyPoint> query_ransac_keypoints;
|
||||
int index = 0;
|
||||
for (size_t i = 0; i != ransac_status.size(); ++i) {
|
||||
if (ransac_status.at(i) != 0) {
|
||||
train_ransac_keypoints.emplace_back(train_approach_keypoints.at(i));
|
||||
query_ransac_keypoints.emplace_back(query_approach_keypoints.at(i));
|
||||
cv::DMatch dmatch = approach_matches.at(i);
|
||||
ransac_matchs.emplace_back(std::move(dmatch));
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
// 做一次算数均值滤波,过滤异常的点。这个算法有点蠢,TODO可以看下怎么改
|
||||
size_t point_size = train_ransac_keypoints.size();
|
||||
if (point_size == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
cv::Point sum_point = std::accumulate(
|
||||
train_ransac_keypoints.cbegin(), train_ransac_keypoints.cend(), cv::Point(),
|
||||
[](cv::Point sum, const cv::KeyPoint& rhs) -> cv::Point {
|
||||
return cv::Point(sum.x + rhs.pt.x, sum.y + rhs.pt.y);
|
||||
});
|
||||
cv::Point avg_point(sum_point.x / point_size, sum_point.y / point_size);
|
||||
std::vector<cv::DMatch> good_matchs;
|
||||
std::vector<cv::Point> good_points;
|
||||
// TODO,这个阈值需要根据分辨率进行缩放,而且最好写到配置文件里
|
||||
constexpr static int DistanceThreshold = 100;
|
||||
for (size_t i = 0; i != train_ransac_keypoints.size(); ++i) {
|
||||
// 没必要算距离,x y各算一下就行了,省点CPU时间
|
||||
//int distance = std::sqrt(std::pow(avg_point.x - cur_x, 2) + std::pow(avg_point.y - cur_y, 2));
|
||||
cv::Point2f& pt = train_ransac_keypoints.at(i).pt;
|
||||
int x_distance = std::abs(avg_point.x - pt.x);
|
||||
int y_distance = std::abs(avg_point.y - pt.y);
|
||||
if (x_distance < DistanceThreshold && y_distance < DistanceThreshold) {
|
||||
good_matchs.emplace_back(ransac_matchs.at(i));
|
||||
good_points.emplace_back(pt);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
|
||||
cv::Mat ransac_mat;
|
||||
cv::drawMatches(query_mat, query_keypoints, train_mat, train_keypoints, ransac_matchs, ransac_mat);
|
||||
|
||||
cv::Mat good_mat;
|
||||
cv::drawMatches(query_mat, query_keypoints, train_mat, train_keypoints, good_matchs, good_mat);
|
||||
|
||||
#endif
|
||||
|
||||
constexpr static const double MatchSizeRatioThreshold = 0.1;
|
||||
if (good_points.size() >= query_keypoints.size() * MatchSizeRatioThreshold) {
|
||||
Rect dst;
|
||||
int left = 0, right = 0, top = 0, bottom = 0;
|
||||
for (const cv::Point& pt : good_points) {
|
||||
if (pt.x < left || left == 0) {
|
||||
left = pt.x;
|
||||
}
|
||||
if (pt.x > right || right == 0) {
|
||||
right = pt.x;
|
||||
}
|
||||
if (pt.y < top || top == 0) {
|
||||
top = pt.y;
|
||||
}
|
||||
if (pt.y > bottom || bottom == 0) {
|
||||
bottom = pt.y;
|
||||
}
|
||||
}
|
||||
dst = { left, top, right - left, bottom - top };
|
||||
return dst;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<TextArea> asst::Identify::ocr_detect(const cv::Mat& image)
|
||||
{
|
||||
OcrResult ocr_results = m_ocr_lite.detect(image,
|
||||
@@ -360,34 +187,6 @@ std::vector<asst::Identify::FindImageResult> asst::Identify::find_all_images(
|
||||
return results;
|
||||
}
|
||||
|
||||
std::optional<TextArea> asst::Identify::feature_match(const cv::Mat& image, const std::string& templ_name)
|
||||
{
|
||||
//DebugTraceFunction;
|
||||
|
||||
if (m_feature_map.find(templ_name) == m_feature_map.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto&& [query_keypoints, query_mat_vector] = m_feature_map[templ_name];
|
||||
auto&& [train_keypoints, train_mat_vector] = surf_detect(image);
|
||||
|
||||
#ifdef LOG_TRACE
|
||||
cv::Mat query_mat = cv::imread(GetResourceDir() + "operators\\" + Utf8ToGbk(templ_name) + ".png");
|
||||
auto&& ret = feature_match(query_keypoints, query_mat_vector, train_keypoints, train_mat_vector,
|
||||
query_mat, image);
|
||||
#else
|
||||
auto&& ret = feature_match(query_keypoints, query_mat_vector, train_keypoints, train_mat_vector);
|
||||
#endif
|
||||
if (ret) {
|
||||
TextArea dst;
|
||||
dst.text = templ_name;
|
||||
dst.rect = std::move(ret.value());
|
||||
return dst;
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void Identify::clear_cache()
|
||||
{
|
||||
m_cache_map.clear();
|
||||
@@ -411,15 +210,7 @@ bool asst::Identify::ocr_init_models(const std::string& dir)
|
||||
const std::string rec_filename = dir + RecName;
|
||||
const std::string keys_filename = dir + KeysName;
|
||||
|
||||
if (std::filesystem::exists(dst_filename)
|
||||
&& std::filesystem::exists(cls_filename)
|
||||
&& std::filesystem::exists(rec_filename)
|
||||
&& std::filesystem::exists(keys_filename))
|
||||
{
|
||||
m_ocr_lite.initModels(dst_filename, cls_filename, rec_filename, keys_filename);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return m_ocr_lite.initModels(dst_filename, cls_filename, rec_filename, keys_filename);
|
||||
}
|
||||
|
||||
std::optional<asst::Rect> asst::Identify::find_text(const cv::Mat& image, const std::string& text)
|
||||
@@ -500,10 +291,8 @@ bool asst::Identify::penguin_load_templ(const std::string& item_id, const std::s
|
||||
|
||||
std::string asst::Identify::penguin_recognize(const cv::Mat& image)
|
||||
{
|
||||
cv::Mat resize_mat;
|
||||
cv::resize(image, resize_mat, cv::Size(1024, 768));
|
||||
std::vector<uchar> buf;
|
||||
cv::imencode(".png", resize_mat, buf);
|
||||
cv::imencode(".png", image, buf);
|
||||
return penguin::recognize(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace asst {
|
||||
|
||||
/*** OpenCV package ***/
|
||||
bool add_image(const std::string& name, const std::string& path);
|
||||
bool add_text_image(const std::string& text, const std::string& path);
|
||||
|
||||
constexpr static double NotAddCache = 999.0;
|
||||
FindImageResult find_image(
|
||||
@@ -41,8 +40,6 @@ namespace asst {
|
||||
std::vector<FindImageResult> find_all_images(
|
||||
const cv::Mat& image, const std::string& templ_name, double threshold = 0, bool rect_zoom = true) const;
|
||||
|
||||
std::optional<TextArea> feature_match(const cv::Mat& image, const std::string& key);
|
||||
|
||||
void clear_cache();
|
||||
|
||||
/*** OcrLite package ***/
|
||||
@@ -67,21 +64,8 @@ namespace asst {
|
||||
// return pair< suitability, raw opencv::point>
|
||||
std::pair<double, cv::Point> match_template(const cv::Mat& image, const cv::Mat& templ);
|
||||
|
||||
// return pair<特征点s,特征点描述子(向量)>
|
||||
std::pair<std::vector<cv::KeyPoint>, cv::Mat> surf_detect(const cv::Mat& mat);
|
||||
std::optional<Rect> feature_match(
|
||||
const std::vector<cv::KeyPoint>& query_keypoints, const cv::Mat& query_mat_vector,
|
||||
const std::vector<cv::KeyPoint>& train_keypoints, const cv::Mat& train_mat_vector
|
||||
#ifdef LOG_TRACE
|
||||
, const cv::Mat query_mat = cv::Mat(), const cv::Mat train_mat = cv::Mat()
|
||||
#endif
|
||||
);
|
||||
|
||||
std::unordered_map<std::string, cv::Mat> m_mat_map;
|
||||
bool m_use_cache = true; // 是否使用缓存——总开关
|
||||
std::unordered_map<std::string, std::pair<cv::Rect, cv::Mat>> m_cache_map; // 位置、直方图缓存
|
||||
// value: pair<特征点s,特征点描述子(向量)>
|
||||
std::unordered_map<std::string, std::pair<std::vector<cv::KeyPoint>, cv::Mat>> m_feature_map;
|
||||
|
||||
OcrLiteCaller m_ocr_lite;
|
||||
};
|
||||
|
||||
@@ -82,8 +82,6 @@ std::optional<std::unordered_map<std::string, OperInfrastInfo>> asst::IdentifyOp
|
||||
};
|
||||
m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg);
|
||||
|
||||
std::unordered_map<std::string, std::string> feature_cond = InfrastConfiger::get_instance().m_oper_name_feat;
|
||||
std::unordered_set<std::string> feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever;
|
||||
std::unordered_map<std::string, OperInfrastInfo> detected_opers;
|
||||
|
||||
int times = 0;
|
||||
@@ -100,7 +98,7 @@ std::optional<std::unordered_map<std::string, OperInfrastInfo>> asst::IdentifyOp
|
||||
std::future<bool> swipe_future = std::async(
|
||||
std::launch::async, &IdentifyOperTask::swipe, this, reverse, WinMacro::SwipeExtraDelayDefault);
|
||||
|
||||
auto cur_name_textarea = detect_operators_name(image, feature_cond, feature_whatever);
|
||||
auto cur_name_textarea = detect_operators_name(image);
|
||||
|
||||
int oper_numer = detected_opers.size();
|
||||
for (const TextArea& textarea : cur_name_textarea) {
|
||||
|
||||
@@ -124,11 +124,7 @@ bool asst::InfrastAbstractTask::append_task_to_back_to_infrast_home()
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<TextArea> asst::InfrastAbstractTask::detect_operators_name(
|
||||
const cv::Mat& image,
|
||||
std::unordered_map<std::string,
|
||||
std::string>& feature_cond,
|
||||
std::unordered_set<std::string>& feature_whatever)
|
||||
std::vector<TextArea> asst::InfrastAbstractTask::detect_operators_name(const cv::Mat& image)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
@@ -191,98 +187,6 @@ std::vector<TextArea> asst::InfrastAbstractTask::detect_operators_name(
|
||||
std::make_move_iterator(lower_part_names.begin()),
|
||||
std::make_move_iterator(lower_part_names.end()));
|
||||
|
||||
// 如果ocr结果中已经有某个干员了,就没必要再尝试对他特征检测了,直接删了
|
||||
for (const TextArea& textarea : all_opers_textarea) {
|
||||
auto cond_iter = std::find_if(feature_cond.begin(), feature_cond.end(),
|
||||
[&textarea](const auto& pair) -> bool {
|
||||
return textarea.text == pair.second;
|
||||
});
|
||||
if (cond_iter != feature_cond.end()) {
|
||||
feature_cond.erase(cond_iter);
|
||||
}
|
||||
|
||||
auto whatever_iter = std::find_if(feature_whatever.begin(), feature_whatever.end(),
|
||||
[&textarea](const std::string& str) -> bool {
|
||||
return textarea.text == str;
|
||||
});
|
||||
if (whatever_iter != feature_whatever.end()) {
|
||||
feature_whatever.erase(whatever_iter);
|
||||
}
|
||||
}
|
||||
|
||||
// 用特征检测再筛选一遍OCR识别漏了的——有关键字的
|
||||
for (const TextArea& textarea : all_text_area) {
|
||||
auto find_iter = std::find_if(all_opers_textarea.cbegin(), all_opers_textarea.cend(),
|
||||
[&textarea](const auto& rhs) -> bool {
|
||||
return textarea.text == rhs.text; });
|
||||
if (find_iter != all_opers_textarea.cend()) {
|
||||
continue;
|
||||
}
|
||||
for (auto iter = feature_cond.begin(); iter != feature_cond.end(); ++iter) {
|
||||
auto& [key, value] = *iter;
|
||||
// 识别到了key,但是没识别到value,这种情况就需要进行特征检测进一步确认了
|
||||
if (textarea.text.find(key) != std::string::npos
|
||||
&& textarea.text.find(value) == std::string::npos) {
|
||||
// 把key所在的矩形放大一点送去做特征检测,不需要把整张图片都送去检测
|
||||
Rect magnified_area = textarea.rect.center_zoom(2.0, image.cols, image.rows);
|
||||
// key是关键字而已,真正要识别的是value
|
||||
auto&& ret = OcrAbstractTask::m_identify_ptr->feature_match(
|
||||
image(make_rect<cv::Rect>(magnified_area)), value);
|
||||
if (ret) {
|
||||
// 匹配上了下次就不用再匹配这个了,直接删了
|
||||
all_opers_textarea.emplace_back(value, textarea.rect);
|
||||
iter = feature_cond.erase(iter);
|
||||
--iter;
|
||||
// 也从whatever里面删了
|
||||
auto whatever_iter = std::find_if(feature_whatever.begin(), feature_whatever.end(),
|
||||
[&textarea](const std::string& str) -> bool {
|
||||
return textarea.text == str;
|
||||
});
|
||||
if (whatever_iter != feature_whatever.end()) {
|
||||
feature_whatever.erase(whatever_iter);
|
||||
}
|
||||
// 顺便再涂黑了,避免后面被whatever特征检测的误识别
|
||||
// 注意这里是浅拷贝,原图image也会被涂黑
|
||||
//cv::rectangle(draw_image, cv_rect, cv::Scalar(0, 0, 0), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用特征检测再筛选一遍OCR识别漏了的——无论如何都进行识别的
|
||||
for (auto iter = feature_whatever.begin(); iter != feature_whatever.end(); ++iter) {
|
||||
// 上半部分长条形的图片
|
||||
auto&& upper_ret = OcrAbstractTask::m_identify_ptr->feature_match(upper_part_name_image, *iter);
|
||||
if (upper_ret) {
|
||||
TextArea temp = std::move(upper_ret.value());
|
||||
#ifdef LOG_TRACE // 也顺便涂黑一下,方便看谁没被识别出来
|
||||
// 注意这里是浅拷贝,原图image也会被涂黑
|
||||
cv::rectangle(upper_part_name_image, make_rect<cv::Rect>(temp.rect), cv::Scalar(0, 0, 0), -1);
|
||||
#endif
|
||||
// 因为图片是裁剪过的,所以对应原图的坐标要加上裁剪的参数
|
||||
temp.rect.y += cropped_upper_y;
|
||||
all_opers_textarea.emplace_back(std::move(temp));
|
||||
iter = feature_whatever.erase(iter);
|
||||
--iter;
|
||||
continue;
|
||||
}
|
||||
// 下半部分长条形的图片
|
||||
auto&& lower_ret = OcrAbstractTask::m_identify_ptr->feature_match(lower_part_name_image, *iter);
|
||||
if (lower_ret) {
|
||||
TextArea temp = std::move(lower_ret.value());
|
||||
#ifdef LOG_TRACE // 也顺便涂黑一下,方便看谁没被识别出来
|
||||
// 注意这里是浅拷贝,原图image也会被涂黑
|
||||
cv::rectangle(lower_part_name_image, make_rect<cv::Rect>(temp.rect), cv::Scalar(0, 0, 0), -1);
|
||||
#endif
|
||||
// 因为图片是裁剪过的,所以对应原图的坐标要加上裁剪的参数
|
||||
temp.rect.y += cropped_lower_y;
|
||||
all_opers_textarea.emplace_back(std::move(temp));
|
||||
iter = feature_whatever.erase(iter);
|
||||
--iter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return all_opers_textarea;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ namespace asst {
|
||||
virtual bool append_task_to_back_to_infrast_home(); // 添加返回主界面的任务
|
||||
|
||||
// 检测干员名
|
||||
virtual std::vector<TextArea> detect_operators_name(const cv::Mat& image,
|
||||
std::unordered_map<std::string, std::string>& feature_cond,
|
||||
std::unordered_set<std::string>& feature_whatever);
|
||||
virtual std::vector<TextArea> detect_operators_name(const cv::Mat& image);
|
||||
virtual bool enter_station(const std::vector<std::string>& templ_names, int index, double threshold = 0.8);
|
||||
virtual bool click_first_operator();
|
||||
|
||||
|
||||
@@ -15,12 +15,6 @@ bool InfrastConfiger::parse(const json::value& json)
|
||||
for (const json::value& name : json.at("allNames").as_array()) {
|
||||
m_all_opers_name.emplace(name.as_string());
|
||||
}
|
||||
for (const json::value& pair : json.at("featureKey").as_array()) {
|
||||
m_oper_name_feat.emplace(pair[0].as_string(), pair[1].as_string());
|
||||
}
|
||||
for (const json::value& name : json.at("featureWhatever").as_array()) {
|
||||
m_oper_name_feat_whatever.emplace(name.as_string());
|
||||
}
|
||||
|
||||
// 每个基建设施中的干员组合信息
|
||||
for (const json::value& facility : json.at("infrast").as_array()) {
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace asst {
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> m_all_opers_name; // 所有干员的名字
|
||||
std::unordered_map<std::string, std::string> m_oper_name_feat; // 根据关键字需要特征检测干员名:如果OCR识别到了key的内容但是却没有value的内容,则进行特征检测进一步确认
|
||||
std::unordered_set<std::string> m_oper_name_feat_whatever; // 无论如何都进行特征检测的干员名
|
||||
// 各个设施内的可能干员组合,注意值是vector,是有序的
|
||||
std::unordered_map<std::string, std::vector<OperInfrastComb>> m_infrast_combs;
|
||||
// 各个设施的识别结束标记,识别到这个名字就停止继续识别了,一般放列表中最后几个人名
|
||||
|
||||
@@ -123,9 +123,6 @@ std::optional<std::unordered_map<std::string, OperInfrastInfo>> asst::InfrastPro
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> feature_cond = InfrastConfiger::get_instance().m_oper_name_feat;
|
||||
std::unordered_set<std::string> feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever;
|
||||
|
||||
std::vector<std::string> end_flag_vec = InfrastConfiger::get_instance().m_infrast_end_flag[m_facility];
|
||||
|
||||
std::unordered_map<std::string, OperInfrastInfo> cur_opers_info;
|
||||
@@ -139,7 +136,7 @@ std::optional<std::unordered_map<std::string, OperInfrastInfo>> asst::InfrastPro
|
||||
std::future<bool> swipe_future = std::async(
|
||||
std::launch::async, &InfrastProductionTask::swipe, this, false, WinMacro::SwipeExtraDelayDefault);
|
||||
|
||||
auto cur_name_textarea = detect_operators_name(image, feature_cond, feature_whatever);
|
||||
auto cur_name_textarea = detect_operators_name(image);
|
||||
for (const TextArea& textarea : cur_name_textarea) {
|
||||
OperInfrastInfo info;
|
||||
// 考虑map中没有这个名字的情况:包括一开始识别漏了、抽到了新干员但没更新等,也有可能是本次识别错了
|
||||
@@ -255,15 +252,13 @@ bool asst::InfrastProductionTask::swipe_and_select(const std::vector<std::string
|
||||
swipe_to_the_left();
|
||||
|
||||
auto need_to_select = name_comb;
|
||||
std::unordered_map<std::string, std::string> feature_cond = InfrastConfiger::get_instance().m_oper_name_feat;
|
||||
std::unordered_set<std::string> feature_whatever = InfrastConfiger::get_instance().m_oper_name_feat_whatever;
|
||||
// 一边滑动一边点击最优解中的干员
|
||||
for (int i = 0; i != swipe_max_times; ++i) {
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
const cv::Mat& image = m_controller_ptr->get_image(true);
|
||||
auto cur_name_textarea = detect_operators_name(image, feature_cond, feature_whatever);
|
||||
auto cur_name_textarea = detect_operators_name(image);
|
||||
|
||||
for (TextArea& text_area : cur_name_textarea) {
|
||||
// 点过了就不会再点了,直接从最优解vector里面删了
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>libmeojson.lib;OcrLiteOnnx.lib;opencv_world3413.lib;penguin-stats-recognize.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>libmeojson.lib;OcrLiteOnnx.lib;opencv_world453.lib;penguin-stats-recognize.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)3rdparty\lib\</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
@@ -172,7 +172,7 @@ xcopy /e /y /i /c $(SolutionDir)3rdparty\resource $(TargetDir)resource</Command>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>libmeojson.lib;OcrLiteOnnx.lib;opencv_world3413.lib;penguin-stats-recognize.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>libmeojson.lib;OcrLiteOnnx.lib;opencv_world453.lib;penguin-stats-recognize.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)3rdparty\lib\</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
|
||||