From 1113234c75ace5d05834396402eb11acdb586dfd Mon Sep 17 00:00:00 2001 From: wallbreaker2 <25576317+WallBreaker2@users.noreply.github.com> Date: Mon, 17 Jul 2023 17:01:15 +0800 Subject: [PATCH 1/5] remove useless files --- lua/CMakeLists.txt | 16 ---------------- python/CMakeLists.txt | 17 ----------------- python/test.py | 24 ------------------------ 3 files changed, 57 deletions(-) delete mode 100644 lua/CMakeLists.txt delete mode 100644 python/CMakeLists.txt delete mode 100644 python/test.py diff --git a/lua/CMakeLists.txt b/lua/CMakeLists.txt deleted file mode 100644 index 9fc373a..0000000 --- a/lua/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -ADD_DEFINITIONS(-D _UNICODE) -ADD_DEFINITIONS(-D UNICODE) - -ADD_DEFINITIONS(-D _CMAKE_BUILD) - -#SET(SRC_FILES "main.cpp") - - - -#find_library(OP_LIB op ../bin/Release NO_DEFAULT_PATH) - -#add_executable(test ${SRC_FILES}) - -#target_link_libraries(test ${OP_LIB}) \ No newline at end of file diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt deleted file mode 100644 index e8020ab..0000000 --- a/python/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -ADD_DEFINITIONS(-D _UNICODE) -ADD_DEFINITIONS(-D UNICODE) - -ADD_DEFINITIONS(-D _CMAKE_BUILD) - -#SET(SRC_FILES "main.cpp") - - - -#find_library(OP_LIB op ../bin/Release NO_DEFAULT_PATH) - -#add_executable(test ${SRC_FILES}) - -#target_link_libraries(test ${OP_LIB}) -install(FILES test.py DESTINATION "${PROJECT_SOURCE_DIR}/bin") diff --git a/python/test.py b/python/test.py deleted file mode 100644 index 1f846e3..0000000 --- a/python/test.py +++ /dev/null @@ -1,24 +0,0 @@ -# import moudles -from win32com.client import Dispatch -import sys -import time; -# create op instance -op = Dispatch("op.opsoft"); - -print(op.Ver()) -print(op.GetBasePath()) -print(op.MoveTo(3,30)) -hwnd = op.FindWindow("SDL_app","M2007J3SC"); - -print(hwnd) -if hwnd: - ret = op.BindWindow(hwnd,"normal","windows","windows",0); - if ret == 1: - print("bind ok"); - c = 0; - - else: - print("bind false"); -#op.RunApp("notepad",0); - - From d47a1490852ebfc5a4e8931f271e8f0d60b4fe5a Mon Sep 17 00:00:00 2001 From: wallbreaker2 <25576317+WallBreaker2@users.noreply.github.com> Date: Mon, 17 Jul 2023 17:01:36 +0800 Subject: [PATCH 2/5] add comments of SetScreenDataMode;addtess ocr engine --- CMakeLists.txt | 10 +-- CMakeSettings.json | 21 ++--- README.md | 28 +++---- include/libop.h | 2 +- libop/CMakeLists.txt | 14 +++- libop/imageProc/ImageLoc.cpp | 9 +- libop/imageProc/ImageLoc.h | 6 +- libop/imageProc/ImageProc.cpp | 152 ++++++++++++++++++---------------- libop/imageProc/ImageProc.h | 3 +- libop/imageProc/tess_ocr.cpp | 86 +++++++++++++++++++ libop/imageProc/tess_ocr.h | 22 +++++ libop/libop.cpp | 7 +- libop/libop.h | 2 +- qttool/CMakeLists.txt | 5 ++ tests/CMakeLists.txt | 4 +- tests/main.cpp | 67 +++++++++++++-- tests/test.h | 6 +- 17 files changed, 317 insertions(+), 127 deletions(-) create mode 100644 libop/imageProc/tess_ocr.cpp create mode 100644 libop/imageProc/tess_ocr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9173e54..fd4b97d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,9 @@ # cmake_minimum_required (VERSION 3.0) -if(NOT $ENV{VCPKG_ROOT} STREQUAL "") - set(${CMAKE_TOOLCHAIN_FILE} $ENV{VCPKG_ROOT}) -endif() +#if(NOT $ENV{VCPKG_ROOT} STREQUAL "") +# set(${CMAKE_TOOLCHAIN_FILE} $ENV{VCPKG_ROOT}) +#endif() if(NOT $ENV{BLACKBONE_ROOT} STREQUAL "") set(blackbone_include_path "$ENV{BLACKBONE_ROOT}/src") @@ -86,10 +86,6 @@ include_directories( # 包含子项目。 ## libop主项目 add_subdirectory ("libop") -## python测试项目 -add_subdirectory("python") -## lua测试项目 -add_subdirectory("lua") ## tools项目 add_subdirectory("tools") ## 测试项目 diff --git a/CMakeSettings.json b/CMakeSettings.json index 650f212..6392613 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -7,7 +7,7 @@ "inheritEnvironments": [ "msvc_x64_x64" ], "buildRoot": "${projectDir}\\out\\build\\${name}", "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=D:/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake", "buildCommandArgs": "", "ctestCommandArgs": "" }, @@ -16,7 +16,7 @@ "generator": "Ninja", "configurationType": "Release", "buildRoot": "${projectDir}\\out\\build\\${name}", - "cmakeCommandArgs": "", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=D:/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x86-windows-static", "buildCommandArgs": "", "ctestCommandArgs": "", "inheritEnvironments": [ "msvc_x86" ], @@ -32,9 +32,9 @@ "type": "BOOL" }, { - "name": "CMAKE_BUILD_TYPE", - "value": "Release", - "type": "STRING" + "name": "Qt5_DIR", + "value": "D:/workspace/dev/qt/5.12.12/msvc2017", + "type": "PATH" } ] }, @@ -44,7 +44,7 @@ "configurationType": "Debug", "buildRoot": "${projectDir}\\out\\build\\${name}", "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=D:/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake", "buildCommandArgs": "", "ctestCommandArgs": "", "inheritEnvironments": [ "msvc_x86" ] @@ -52,10 +52,10 @@ { "name": "x64-Release", "generator": "Ninja", - "configurationType": "RelWithDebInfo", + "configurationType": "Release", "buildRoot": "${projectDir}\\out\\build\\${name}", "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=D:/workspace/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static", "buildCommandArgs": "", "ctestCommandArgs": "", "inheritEnvironments": [ "msvc_x64_x64" ], @@ -69,11 +69,6 @@ "name": "build_swig_py", "value": "True", "type": "BOOL" - }, - { - "name": "CMAKE_BUILD_TYPE", - "value": "Release", - "type": "STRING" } ] } diff --git a/README.md b/README.md index 8aff079..2da6b10 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,29 @@ Overview =========== OP(operator & open)是一个开源插件(类似大漠插件).主要功能有:Windows消息模拟,后台截图,找图,字符识别(OCR)等。使用c++编写,源代码可编译为32/64位dll.op插件提供了两类接口:1)原生c++接口,可以让c/c++开发者方便调用;2)com接口,支持大多数编译型语言(c++,c#,vb,delphi等 以及脚本语言(python,lua等)的调用 + +OP插件是为了满足Windows平台下各种自动化操作和图像处理的需求而开发的一个轻量级、高效、易用的工具。它可以帮助开发者和用户实现各种复杂的任务,例如模拟键鼠操作、后台截图、图像识别、文字识别等。它适用于各种场景,例如办公自动化、软件测试、数据采集、图像处理等。 ![ava](doc/class_struct.svg) ## 功能特色 -1. Windows消息模拟,常见的键盘消息和鼠标消息模拟。 -2. 支持常见的截图方式,gdi,dx(包括d3d9,d3d10,d3d11),opengl截图,支持常见模拟器(雷电,夜神)的最小化截图 -3. 找色找图,支持偏色,支持模糊识别 -4. 字符识别(OCR),最大支持255 X 255 超大点阵,支持偏色,支持模糊识别,支持系统字库 -5. 插件有32位和64位版本,支持32/64位绑定 -6. 项目完全开源,无后门无病毒,可放心使用 +- Windows消息模拟,常见的键盘消息和鼠标消息模拟。 +- 支持常见的截图方式,gdi,dx(包括d3d9,d3d10,d3d11),opengl截图,支持常见模拟器(雷电,夜神)的最小化截图 +- 找色找图,支持偏色,支持模糊识别 +- 字符识别(OCR),最大支持255 X 255 超大点阵,支持偏色,支持模糊识别,支持系统字库 +- 插件有32位和64位版本,支持32/64位绑定 +- 项目完全开源,无后门无病毒,可放心使用 ## Download 包含32位和64位插件,tool工具以及必要的第三方库等文件 -下载地址:[https://github.com/WallBreaker2/op/releases](https://github.com/WallBreaker2/op/releases) 或[gitee](https://gitee.com/wallbreaker2/op/tags) - +下载地址:[GitHub](https://github.com/WallBreaker2/op/releases) ## 教程 -所有函数说明以及Demo可在以在[wiki](https://github.com/WallBreaker2/op/wiki)或[gitlab](https://gitee.com/wallbreaker2/op/wikis) - +所有函数说明以及Demo可在以在[wiki](https://github.com/WallBreaker2/op/wiki)中获取 ## 编译 ### 编译环境 * 操作系统: windows 10 64位 -* 编译器: vs2019 MSVC2017 32/64 -* 工具: cmake 3.0以上 +* 编译器: vs2022 MSVC 32/64 +* 工具: cmake 3.24以上 * DirectX SDK: 最新的即可 ### 第三方库 * [blackbone](https://github.com/DarthTon/Blackbone.git)(静态编译,链接方式MT) @@ -39,5 +39,5 @@ OP(operator & open)是一个开源插件(类似大漠插件).主要功能有:Win ## 参考 --- -[1] TSPLUG源码,TC company -[2] [Kiero](https://github.com/Rebzzel/kiero.git) +[1] [TSPLUG源码,TC company](https://github.com/tcplugins/tsplug) +[2] [Kiero](https://github.com/Rebzzel/kiero.git) \ No newline at end of file diff --git a/include/libop.h b/include/libop.h index 9a42c1e..3f46f2c 100644 --- a/include/libop.h +++ b/include/libop.h @@ -87,7 +87,7 @@ class OP_API libop{ void EnablePicCache(long enable, long* ret); //取上次操作的图色区域,保存为file(24位位图) void CapturePre(const wchar_t* file_name, long* ret); - + //设置屏幕数据模式,0:从上到下(默认),1:从下到上 void SetScreenDataMode(long mode, long* ret); //---------------------algorithm------------------------------- //A星算法 diff --git a/libop/CMakeLists.txt b/libop/CMakeLists.txt index 160e57c..740fdb1 100644 --- a/libop/CMakeLists.txt +++ b/libop/CMakeLists.txt @@ -57,9 +57,9 @@ SET(SRC_FILES "./core/opEnv.cpp" -"./imageProc/imageloc.cpp" -"./imageProc/imageproc.cpp" - + "./imageProc/imageloc.cpp" + "./imageProc/imageproc.cpp" + "imageProc/tess_ocr.cpp" "./winapi/injecter.cpp" "./winapi/memoryEx.cpp" "./winapi/query_api.cpp" @@ -112,7 +112,7 @@ add_custom_command( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -add_library(${op_com} SHARED ${SRC_FILES} ${COM_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/./com/op.rc) +add_library(${op_com} SHARED ${SRC_FILES} ${COM_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/./com/op.rc "imageProc/tess_ocr.cpp") SET_TARGET_PROPERTIES(${op_com} PROPERTIES LINK_FLAGS "/DEF:\"op.def\" /NODEFAULTLIB:\"mfc110d\"") # add_custom_command(TARGET ${op_com} PRE_BUILD COMMAND midl ./com/op.idl /iid "./com/op_i.c" /env win32 /h "./com/op_i.h" /W1 /char signed /tlb "./com/op.tlb" /Oicf /target "NT60" /D "NDEBUG" /robust /nologo /proxy "op_p.c" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) @@ -129,6 +129,12 @@ include_directories( ./core ) +## for tesseract lib +find_package(Tesseract) +include_directories(${Tesseract_INCLUDE_DIRS}) +target_link_libraries(${op_com} Tesseract::libtesseract) + + # 7.add link library #TARGET_LINK_LIBRARIES(libop BlackBone.lib) #TARGET_LINK_LIBRARIES(libop minhook.lib) diff --git a/libop/imageProc/ImageLoc.cpp b/libop/imageProc/ImageLoc.cpp index b4f3dbe..c7e008e 100644 --- a/libop/imageProc/ImageLoc.cpp +++ b/libop/imageProc/ImageLoc.cpp @@ -8,6 +8,7 @@ #include "../core/helpfunc.h" #include "imageView.hpp" + using std::to_wstring; //检查是否为透明图,返回透明像素个数, 四角颜色相同且透明颜色数量在50%-99%范围内 int check_transparent(Image *img) { @@ -126,11 +127,13 @@ enum PicMatchType { PicMatchRGB = 0, PicMatchGray = 1, PicMatchTrans = 2 }; //} ImageBase::ImageBase() : m_threadPool(std::thread::hardware_concurrency()) { - _x1 = _y1 = 0; - _dx = _dy = 0; + _x1 = _y1 = 0; + _dx = _dy = 0; + } -ImageBase::~ImageBase() {} +ImageBase::~ImageBase() { +} void ImageBase::set_offset(int x1, int y1) { _x1 = x1; diff --git a/libop/imageProc/ImageLoc.h b/libop/imageProc/ImageLoc.h index 3c0d0e6..477467b 100644 --- a/libop/imageProc/ImageLoc.h +++ b/libop/imageProc/ImageLoc.h @@ -11,6 +11,7 @@ #include "../include/Dict.h" #include "../include/color.h" #include "./compute/ThreadPool.h" + inline int HEX2INT(wchar_t c) { if (L'0' <= c && c <= L'9') return c - L'0'; @@ -51,6 +52,8 @@ struct gray_diff_t{ unsigned char gray; unsigned char diff; }; + + /* 此类用于实现一些图像功能,如图像定位,简单ocr等 */ @@ -172,11 +175,12 @@ class ImageBase ImageBin _record; ImageBin _binary; Image _sum; -private: //起始点 int _x1, _y1; //偏移 int _dx, _dy; +private: + ThreadPool m_threadPool; }; diff --git a/libop/imageProc/ImageProc.cpp b/libop/imageProc/ImageProc.cpp index 5dae778..050162f 100644 --- a/libop/imageProc/ImageProc.cpp +++ b/libop/imageProc/ImageProc.cpp @@ -15,7 +15,7 @@ ImageProc::~ImageProc() { } -long ImageProc::Capture(const std::wstring &file) +long ImageProc::Capture(const std::wstring& file) { wstring fpath = file; if (fpath.find(L'\\') == -1) @@ -24,14 +24,14 @@ long ImageProc::Capture(const std::wstring &file) return _src.write(fpath.data()); } -long ImageProc::CmpColor(long x, long y, const std::wstring &scolor, double sim) +long ImageProc::CmpColor(long x, long y, const std::wstring& scolor, double sim) { std::vector vcolor; str2colordfs(scolor, vcolor); return ImageBase::CmpColor(_src.at(0, 0), vcolor, sim); } -long ImageProc::FindColor(const wstring &color, double sim, long dir, long &x, long &y) +long ImageProc::FindColor(const wstring& color, double sim, long dir, long& x, long& y) { std::vector colors; str2colordfs(color, colors); @@ -40,14 +40,14 @@ long ImageProc::FindColor(const wstring &color, double sim, long dir, long &x, l return ImageBase::FindColor(colors, dir, x, y); } -long ImageProc::FindColoEx(const wstring &color, double sim, long dir, wstring &retstr) +long ImageProc::FindColoEx(const wstring& color, double sim, long dir, wstring& retstr) { std::vector colors; str2colordfs(color, colors); return ImageBase::FindColorEx(colors, retstr); } -long ImageProc::FindMultiColor(const wstring &first_color, const wstring &offset_color, double sim, long dir, long &x, long &y) +long ImageProc::FindMultiColor(const wstring& first_color, const wstring& offset_color, double sim, long dir, long& x, long& y) { std::vector vfirst_color; str2colordfs(first_color, vfirst_color); @@ -55,7 +55,7 @@ long ImageProc::FindMultiColor(const wstring &first_color, const wstring &offset split(offset_color, vseconds, L","); std::vector voffset_cr; pt_cr_df_t tp; - for (auto &it : vseconds) + for (auto& it : vseconds) { size_t id1, id2; id1 = it.find(L'|'); @@ -73,7 +73,7 @@ long ImageProc::FindMultiColor(const wstring &first_color, const wstring &offset return ImageBase::FindMultiColor(vfirst_color, voffset_cr, sim, dir, x, y); } -long ImageProc::FindMultiColorEx(const wstring &first_color, const wstring &offset_color, double sim, long dir, wstring &retstr) +long ImageProc::FindMultiColorEx(const wstring& first_color, const wstring& offset_color, double sim, long dir, wstring& retstr) { std::vector vfirst_color; str2colordfs(first_color, vfirst_color); @@ -81,7 +81,7 @@ long ImageProc::FindMultiColorEx(const wstring &first_color, const wstring &offs split(offset_color, vseconds, L","); std::vector voffset_cr; pt_cr_df_t tp; - for (auto &it : vseconds) + for (auto& it : vseconds) { size_t id1, id2; id1 = it.find(L'|'); @@ -99,9 +99,9 @@ long ImageProc::FindMultiColorEx(const wstring &first_color, const wstring &offs return ImageBase::FindMultiColorEx(vfirst_color, voffset_cr, sim, dir, retstr); } //图形定位 -long ImageProc::FindPic(const std::wstring &files, const wstring &delta_colors, double sim, long dir, long &x, long &y) +long ImageProc::FindPic(const std::wstring& files, const wstring& delta_colors, double sim, long dir, long& x, long& y) { - vector vpic; + vector vpic; //vector vcolor; color_t dfcolor; vector vpic_name; @@ -117,9 +117,9 @@ long ImageProc::FindPic(const std::wstring &files, const wstring &delta_colors, return ret; } // -long ImageProc::FindPicEx(const std::wstring &files, const wstring &delta_colors, double sim, long dir, wstring &retstr, bool returnID) +long ImageProc::FindPicEx(const std::wstring& files, const wstring& delta_colors, double sim, long dir, wstring& retstr, bool returnID) { - vector vpic; + vector vpic; vpoint_desc_t vpd; //vector vcolor; color_t dfcolor; @@ -128,17 +128,17 @@ long ImageProc::FindPicEx(const std::wstring &files, const wstring &delta_colors dfcolor.str2color(delta_colors); sim = 0.5 + sim / 2; long ret = ImageBase::FindPicExTh(vpic, dfcolor, sim, dir, vpd); - std::wstringstream ss(std::wstringstream::in|std::wstringstream::out); + std::wstringstream ss(std::wstringstream::in | std::wstringstream::out); if (returnID) { - for (auto &it : vpd) + for (auto& it : vpd) { ss << it.id << L"," << it.pos << L"|"; } } else { - for (auto &it : vpd) + for (auto& it : vpd) { ss << vpic_name[it.id] << L"," << it.pos << L"|"; } @@ -152,7 +152,7 @@ long ImageProc::FindPicEx(const std::wstring &files, const wstring &delta_colors return ret; } -long ImageProc::FindColorBlock(const wstring &color, double sim, long count, long height, long width, long &x, long &y) +long ImageProc::FindColorBlock(const wstring& color, double sim, long count, long height, long width, long& x, long& y) { vector colors; @@ -167,7 +167,7 @@ long ImageProc::FindColorBlock(const wstring &color, double sim, long count, lon return ImageBase::FindColorBlock(sim, count, height, width, x, y); } -long ImageProc::FindColorBlockEx(const wstring &color, double sim, long count, long height, long width, wstring &retstr) +long ImageProc::FindColorBlockEx(const wstring& color, double sim, long count, long height, long width, wstring& retstr) { vector colors; if (str2colordfs(color, colors) == 0) @@ -181,7 +181,7 @@ long ImageProc::FindColorBlockEx(const wstring &color, double sim, long count, l return ImageBase::FindColorBlockEx(sim, count, height, width, retstr); } -long ImageProc::SetDict(int idx, const wstring &file_name) +long ImageProc::SetDict(int idx, const wstring& file_name) { if (idx < 0 || idx >= _max_dict) return 0; @@ -202,12 +202,12 @@ long ImageProc::SetDict(int idx, const wstring &file_name) return _dicts[idx].empty() ? 0 : 1; } -long ImageProc::SetMemDict(int idx, void *data, long size) +long ImageProc::SetMemDict(int idx, void* data, long size) { if (idx < 0 || idx >= _max_dict) return 0; _dicts[idx].clear(); - _dicts[idx].read_memory_dict_dm((const char *)data, size); + _dicts[idx].read_memory_dict_dm((const char*)data, size); return _dicts[idx].empty() ? 0 : 1; } @@ -219,7 +219,7 @@ long ImageProc::UseDict(int idx) return 1; } -long ImageProc::OCR(const wstring &color, double sim, std::wstring &out_str) +long ImageProc::OCR(const wstring& color, double sim, std::wstring& out_str) { out_str.clear(); vector colors; @@ -233,8 +233,20 @@ long ImageProc::OCR(const wstring &color, double sim, std::wstring &out_str) } if (sim < 0. || sim > 1.) sim = 1.; - long s; - s = ImageBase::Ocr(_dicts[_curr_idx], sim, out_str); + long s = 0; + if (_dicts[_curr_idx].size() == 0) { + vector res; + m_tess_ocr.ocr(_gray.data(), _gray.width, _gray.height, 1, res); + for (auto& it : res) { + if (it.confidenc >= sim - 1e-9) { + out_str += it.text; + } + } + } + else { + s = ImageBase::Ocr(_dicts[_curr_idx], sim, out_str); + } + return s; } @@ -251,7 +263,7 @@ wstring ImageProc::GetColor(long x, long y) } } -int ImageProc::str2colordfs(const wstring &color_str, std::vector &colors) +int ImageProc::str2colordfs(const wstring& color_str, std::vector& colors) { std::vector vstr, vstr2; color_df_t cr; @@ -266,7 +278,7 @@ int ImageProc::str2colordfs(const wstring &color_str, std::vector &c ret = 1; } split(ret ? color_str.substr(1) : color_str, vstr, L"|"); - for (auto &it : vstr) + for (auto& it : vstr) { split(it, vstr2, L"-"); cr.color.str2color(vstr2[0]); @@ -276,27 +288,27 @@ int ImageProc::str2colordfs(const wstring &color_str, std::vector &c return ret; } -void ImageProc::str2colors(const wstring &color, std::vector &vcolor) +void ImageProc::str2colors(const wstring& color, std::vector& vcolor) { std::vector vstr, vstr2; color_t cr; vcolor.clear(); split(color, vstr, L"|"); - for (auto &it : vstr) + for (auto& it : vstr) { cr.str2color(it); vcolor.push_back(cr); } } -long ImageProc::LoadPic(const wstring &files) +long ImageProc::LoadPic(const wstring& files) { //std::vectorvstr, vstr2; std::vector vstr; int loaded = 0; split(files, vstr, L"|"); wstring tp; - for (auto &it : vstr) + for (auto& it : vstr) { //路径转化 if (!Path2GlobalPath(it, _curr_path, tp)) @@ -312,13 +324,13 @@ long ImageProc::LoadPic(const wstring &files) return loaded; } -long ImageProc::FreePic(const wstring &files) +long ImageProc::FreePic(const wstring& files) { std::vector vstr; int loaded = 0; split(files, vstr, L"|"); wstring tp; - for (auto &it : vstr) + for (auto& it : vstr) { //看当前目录 auto cache_it = _pic_cache.find(it); @@ -338,7 +350,7 @@ long ImageProc::FreePic(const wstring &files) return loaded; } -long ImageProc::LoadMemPic(const wstring &file_name, void *data, long size) +long ImageProc::LoadMemPic(const wstring& file_name, void* data, long size) { try { @@ -354,14 +366,14 @@ long ImageProc::LoadMemPic(const wstring &file_name, void *data, long size) return 1; } -void ImageProc::files2mats(const wstring &files, std::vector &vpic, std::vector &vstr) +void ImageProc::files2mats(const wstring& files, std::vector& vpic, std::vector& vstr) { //std::vectorvstr, vstr2; - Image *pm; + Image* pm; vpic.clear(); split(files, vstr, L"|"); wstring tp; - for (auto &it : vstr) + for (auto& it : vstr) { //先在缓存中查找是否已加载,包括从内存中加载的文件 if (_pic_cache.count(it)) @@ -388,9 +400,9 @@ void ImageProc::files2mats(const wstring &files, std::vector &vpic, std } } -long ImageProc::OcrEx(const wstring &color, double sim, std::wstring &out_str) +long ImageProc::OcrEx(const wstring& color, double sim, std::wstring& retstr) { - out_str.clear(); + retstr.clear(); vector colors; if (str2colordfs(color, colors) == 0) { @@ -402,11 +414,34 @@ long ImageProc::OcrEx(const wstring &color, double sim, std::wstring &out_str) } if (sim < 0. || sim > 1.) sim = 1.; + if (_dicts[_curr_idx].size() == 0) { + vector res; + int find_ct = 0; + m_tess_ocr.ocr(_gray.data(), _gray.width, _gray.height, 1, res); + for (auto& it : res) { + if (it.confidenc >= sim - 1e-9) { + retstr += std::to_wstring(it.left_top.x + _x1 + _dx); + retstr += L","; + retstr += std::to_wstring(it.left_top.y + _y1 + _dy); + retstr += L","; + retstr += it.text; + retstr += L"|"; + ++find_ct; + if (find_ct > _max_return_obj_ct) break; + + + } + } + if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); + return find_ct; + } + else { + return ImageBase::OcrEx(_dicts[_curr_idx], sim, retstr); + } - return ImageBase::OcrEx(_dicts[_curr_idx], sim, out_str); } -long ImageProc::FindStr(const wstring &str, const wstring &color, double sim, long &retx, long &rety) +long ImageProc::FindStr(const wstring& str, const wstring& color, double sim, long& retx, long& rety) { vector vstr; vector colors; @@ -424,7 +459,7 @@ long ImageProc::FindStr(const wstring &str, const wstring &color, double sim, lo return ImageBase::FindStr(_dicts[_curr_idx], vstr, sim, retx, rety); } -long ImageProc::FindStrEx(const wstring &str, const wstring &color, double sim, std::wstring &out_str) +long ImageProc::FindStrEx(const wstring& str, const wstring& color, double sim, std::wstring& out_str) { out_str.clear(); vector vstr; @@ -443,21 +478,12 @@ long ImageProc::FindStrEx(const wstring &str, const wstring &color, double sim, return ImageBase::FindStrEx(_dicts[_curr_idx], vstr, sim, out_str); } -long ImageProc::OcrAuto(double sim, std::wstring &retstr) +long ImageProc::OcrAuto(double sim, std::wstring& retstr) { - retstr.clear(); - - if (sim < 0. || sim > 1.) - sim = 1.; - vector colors; - bgr2binarybk(colors); - return ImageBase::Ocr(_dicts[_curr_idx], sim, retstr); - //_tes.SetImage(_src.pdata, _src.width, _src.height, 4, _src.width * 4); - //_tes.gette - return 0; + return OCR(L"", sim, retstr); } -long ImageProc::OcrFromFile(const wstring &files, const wstring &color, double sim, std::wstring &retstr) +long ImageProc::OcrFromFile(const wstring& files, const wstring& color, double sim, std::wstring& retstr) { retstr.clear(); if (sim < 0. || sim > 1.) @@ -468,23 +494,12 @@ long ImageProc::OcrFromFile(const wstring &files, const wstring &color, double s if (Path2GlobalPath(files, _curr_path, fullpath)) { _src.read(fullpath.data()); - if (str2colordfs(color, colors) == 0) - { - bgr2binary(colors); - } - else - { - bgr2binarybk(colors); - } - if (sim < 0. || sim > 1.) - sim = 1.; - - return ImageBase::Ocr(_dicts[_curr_idx], sim, retstr); + return OCR(L"", sim, retstr); } return 0; } -long ImageProc::OcrAutoFromFile(const wstring &files, double sim, std::wstring &retstr) +long ImageProc::OcrAutoFromFile(const wstring& files, double sim, std::wstring& retstr) { retstr.clear(); if (sim < 0. || sim > 1.) @@ -494,15 +509,12 @@ long ImageProc::OcrAutoFromFile(const wstring &files, double sim, std::wstring & if (Path2GlobalPath(files, _curr_path, fullpath)) { _src.read(fullpath.data()); - vector colors; - bgr2binarybk(colors); - - return ImageBase::Ocr(_dicts[_curr_idx], sim, retstr); + return OCR(L"", sim, retstr); } return 0; } -long ImageProc::FindLine(const wstring &color, double sim, wstring &retStr) +long ImageProc::FindLine(const wstring& color, double sim, wstring& retStr) { retStr.clear(); vector colors; diff --git a/libop/imageProc/ImageProc.h b/libop/imageProc/ImageProc.h index 670c5fe..5638522 100644 --- a/libop/imageProc/ImageProc.h +++ b/libop/imageProc/ImageProc.h @@ -2,6 +2,7 @@ #include #include "ImageLoc.h" #include +#include "tess_ocr.h" //#include using std::wstring; /* @@ -83,7 +84,7 @@ class ImageProc:public ImageBase //是否使用图片缓存,默认开启 int _enable_cache; - //tesseract::TessBaseAPI _tes; + tess_ocr m_tess_ocr; private: diff --git a/libop/imageProc/tess_ocr.cpp b/libop/imageProc/tess_ocr.cpp new file mode 100644 index 0000000..e77b312 --- /dev/null +++ b/libop/imageProc/tess_ocr.cpp @@ -0,0 +1,86 @@ +#include "tess_ocr.h" +#include +#include +#include "../core/helpfunc.h" +tess_ocr::tess_ocr(): m_api(nullptr) { + init(); +} +tess_ocr::~tess_ocr() { + release(); +} +int tess_ocr::init() { + // Tesseract OCR + m_api = new tesseract::TessBaseAPI(); + // ʼTesseract OCR + if (m_api->Init("./tess_model", "chi_sim")) { + setlog("Could not initialize tesseract.\n"); + release(); + } + return 0; +} +int tess_ocr::release() { + if (m_api) { + // ɾTesseract OCR + delete m_api; + } + m_api = nullptr; + return 0; +} + +static string utf8_to_ansi(string strUTF8); + +int tess_ocr::ocr(byte* data, int w, int h, int bpp, vector& result) { + result.clear(); + if (m_api == nullptr)return -1; + + // ͼøTesseract OCR + m_api->SetImage(data, w, h, bpp, w * bpp); + + // ִʶ + m_api->Recognize(0); + + // ȡ + tesseract::ResultIterator* ri = m_api->GetIterator(); + tesseract::PageIteratorLevel level = tesseract::RIL_WORD; + //std::setlocale() + // Чѭ + if (ri != 0) { + do { + // ȡʶĵʺŶ + const char* word = ri->GetUTF8Text(level); + int x1, x2, y1, y2; + ri->BoundingBox(level, &x1, &y1, &x2, &y2); + float conf = ri->Confidence(level); + + // ʾʺŶ + //printf("word: '%s'; \tconf: %.2f box=[%d,%d %d,%d]; \n", word, conf, x1, y1, x2, y2); + //std::wcout << L"xxx:" << utf82ws(word) << std::endl; + tess_rec_info ts; + ts.confidenc = conf; + ts.left_top = point_t(x1, y1); + ts.right_bottom = point_t(x2, y2); + ts.text = _s2wstring(utf8_to_ansi(word)); + result.push_back(ts); + // ͷŵʵڴ + delete[] word; + } while (ri->Next(level)); + } + //Pix* px = m_api->GetInputImage(); + //pixWrite("test_oux.bmp", px, IFF_BMP); + return result.size(); +} + +string utf8_to_ansi(string strUTF8) { + UINT nLen = MultiByteToWideChar(CP_UTF8, NULL, strUTF8.c_str(), -1, NULL, NULL); + WCHAR* wszBuffer = new WCHAR[nLen + 1]; + nLen = MultiByteToWideChar(CP_UTF8, NULL, strUTF8.c_str(), -1, wszBuffer, nLen); + wszBuffer[nLen] = 0; + nLen = WideCharToMultiByte(936, NULL, wszBuffer, -1, NULL, NULL, NULL, NULL); + CHAR* szBuffer = new CHAR[nLen + 1]; + nLen = WideCharToMultiByte(936, NULL, wszBuffer, -1, szBuffer, nLen, NULL, NULL); + szBuffer[nLen] = 0; + strUTF8 = szBuffer; + delete[]szBuffer; + delete[]wszBuffer; + return strUTF8; +} \ No newline at end of file diff --git a/libop/imageProc/tess_ocr.h b/libop/imageProc/tess_ocr.h new file mode 100644 index 0000000..0e674bf --- /dev/null +++ b/libop/imageProc/tess_ocr.h @@ -0,0 +1,22 @@ +#pragma once +#include "../core/optype.h" +namespace tesseract { + class TessBaseAPI; +}; +struct tess_rec_info { + point_t left_top; + point_t right_bottom; + wstring text; + float confidenc; +}; + +class tess_ocr { +public: + tess_ocr(); + ~tess_ocr(); + int init(); + int release(); + int ocr(byte* data, int w, int h, int bpp, vector& result); +private: + tesseract::TessBaseAPI* m_api; +}; \ No newline at end of file diff --git a/libop/libop.cpp b/libop/libop.cpp index 7fbe9e1..dbefd3d 100644 --- a/libop/libop.cpp +++ b/libop/libop.cpp @@ -25,10 +25,11 @@ const int small_block_size = 10; int libop::s_id = 0; -const int SC_DATA_BOTTOM = 0; -const int SC_DATA_TOP = 1; +const int SC_DATA_TOP = 0; +const int SC_DATA_BOTTOM = 1; -libop::libop():m_screen_data_mode(0) + +libop::libop():m_screen_data_mode(SC_DATA_TOP) { _winapi = new WinApi; _bkproc = new opBackground; diff --git a/libop/libop.h b/libop/libop.h index 9a42c1e..3f46f2c 100644 --- a/libop/libop.h +++ b/libop/libop.h @@ -87,7 +87,7 @@ class OP_API libop{ void EnablePicCache(long enable, long* ret); //取上次操作的图色区域,保存为file(24位位图) void CapturePre(const wchar_t* file_name, long* ret); - + //设置屏幕数据模式,0:从上到下(默认),1:从下到上 void SetScreenDataMode(long mode, long* ret); //---------------------algorithm------------------------------- //A星算法 diff --git a/qttool/CMakeLists.txt b/qttool/CMakeLists.txt index e7a77dd..9584272 100644 --- a/qttool/CMakeLists.txt +++ b/qttool/CMakeLists.txt @@ -63,6 +63,7 @@ SET(SRC_FILES "cap_dialog.h" "../libop/imageProc/imageProc.cpp" "../libop/imageProc/imageLoc.cpp" +"../libop/imageProc/tess_ocr.cpp" "../libop/core/globalVar.cpp" "../libop/core/helpfunc.cpp" "../libop/core/pipe.cpp" @@ -83,6 +84,10 @@ add_executable(qttool ${SRC_FILES} Tool.qrc) Set_Target_Properties(qttool PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") +## for tesseract lib +find_package(Tesseract) +include_directories(${Tesseract_INCLUDE_DIRS}) +target_link_libraries(qttool Tesseract::libtesseract) #target_link_libraries(qttool PRIVATE Qt5::Widgets) target_link_libraries(qttool ScintillaEdit4.lib) target_link_libraries(qttool lua) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 215e82b..00c6845 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,8 @@ ADD_DEFINITIONS(-D UNICODE) ADD_DEFINITIONS(-D _CMAKE_BUILD) +find_package(Tesseract) +include_directories(${Tesseract_INCLUDE_DIRS}) #include_directories("E:/git_pro/vcpkg/installed/x86-windows/include") SET(SRC_FILES "main.cpp" @@ -20,7 +22,7 @@ include_directories( add_executable(tests ${SRC_FILES} ) target_link_libraries(tests ${op_com}) - +target_link_libraries(tests Tesseract::libtesseract) IF(CMAKE_CL_64) install(TARGETS tests DESTINATION "${PROJECT_SOURCE_DIR}/bin/x64") ELSE(CMAKE_CL_64) diff --git a/tests/main.cpp b/tests/main.cpp index 4a880da..42d0ec6 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,7 +1,8 @@ // ConsoleTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // +#include +#include -#include #include #include @@ -195,6 +196,7 @@ void test_fs(); void test_threadPool(); void test_rect(); +int test_tess(int w, int h, unsigned char* data); int main(int argc, char *argv[]) { int a[2] = {0, 1}; @@ -207,9 +209,9 @@ int main(int argc, char *argv[]) { std::cout << add(1, 2); // test_shared(); CoInitialize(NULL); - // test_unique(); - // test_invoke(); - test_com(); + //test_unique(); + //test_invoke(); + //test_com(); test *ptest = new test; ptest->do_test(); delete ptest; @@ -219,10 +221,65 @@ int main(int argc, char *argv[]) { test_threadPool(); test_rect(); - + return 0; } +int test_tess(int w, int h, unsigned char* data) { + SetConsoleOutputCP(CP_UTF8); + // 创建Tesseract OCR对象 + tesseract::TessBaseAPI* api = new tesseract::TessBaseAPI(); + + // 初始化Tesseract OCR + // 第一个参数是数据文件的路径(空字符串表示默认路径) + // 第二个参数是使用的语言(英语为"eng") + // 第三个参数是OCR模式(默认为OEM_LSTM_ONLY) + if (api->Init("", "chi_sim")) { + fprintf(stderr, "Could not initialize tesseract.\n"); + exit(1); + } + + // 从图像文件读取像素数据 + //Pix* image = pixRead("C:/Users/pans/Desktop/test.bmp"); + + // 将图像数据设置给Tesseract OCR + api->SetImage(data,w,h,4,w*4); + + // 执行文字识别 + api->Recognize(0); + + // 获取结果迭代器 + tesseract::ResultIterator* ri = api->GetIterator(); + tesseract::PageIteratorLevel level = tesseract::RIL_WORD; + //std::setlocale() + // 如果结果迭代器有效,则循环 + if (ri != 0) { + do { + // 获取识别出的单词和置信度 + const char* word = ri->GetUTF8Text(level); + int x1, x2, y1, y2; + ri->BoundingBox(level, &x1, &y1, &x2, &y2); + float conf = ri->Confidence(level); + + // 显示单词和置信度 + printf("word: '%s'; \tconf: %.2f box=[%d,%d %d,%d]; \n", word, conf, x1, y1, x2, y2); + //std::wcout << L"xxx:" << utf82ws(word) << std::endl; + + // 释放单词的内存 + delete[] word; + } while (ri->Next(level)); + } + Pix* px = api->GetInputImage(); + pixWrite("test_oux.bmp", px, IFF_BMP); + // 删除Tesseract OCR对象 + delete api; + + // 删除图像数据 + //pixDestroy(&image); + + return 0; +} + void test_fs() { namespace fs = std::filesystem; std::cout << "current path:" << fs::current_path() << std::endl; diff --git a/tests/test.h b/tests/test.h index e52f18f..d2fa937 100644 --- a/tests/test.h +++ b/tests/test.h @@ -134,9 +134,9 @@ class test { // ***************** check ocr function ****************** - m_op->SetDict(0, L"st10.dict", &lret); - op_check(SetDict, lret == 1); - m_op->Ocr(0, 0, 2000, 2000, L"000000", 1.0, str); + //m_op->SetDict(0, L"st10.dict", &lret); + //op_check(SetDict, lret == 1); + m_op->Ocr(0, 0, 2000, 2000, L"000000", 0.9, str); std::wcout << L"ocr:" << str << std::endl; op_check(Ocr, str.length() > 0); m_op->GetWindowState((long)(::GetDesktopWindow()), 2, &lret); From 4056b8870cfad3684d81f7ed19bac69d24da0bf7 Mon Sep 17 00:00:00 2001 From: wallbreaker2 <25576317+WallBreaker2@users.noreply.github.com> Date: Sat, 22 Jul 2023 18:10:36 +0800 Subject: [PATCH 3/5] add tess lib desc --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2da6b10..3d31d21 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,14 @@ OP(operator & open)是一个开源插件(类似大漠插件).主要功能有:Win OP插件是为了满足Windows平台下各种自动化操作和图像处理的需求而开发的一个轻量级、高效、易用的工具。它可以帮助开发者和用户实现各种复杂的任务,例如模拟键鼠操作、后台截图、图像识别、文字识别等。它适用于各种场景,例如办公自动化、软件测试、数据采集、图像处理等。 ![ava](doc/class_struct.svg) ## 功能特色 -- Windows消息模拟,常见的键盘消息和鼠标消息模拟。 +- Windows消息模拟,支持常见的键盘消息和鼠标消息模拟。 - 支持常见的截图方式,gdi,dx(包括d3d9,d3d10,d3d11),opengl截图,支持常见模拟器(雷电,夜神)的最小化截图 - 找色找图,支持偏色,支持模糊识别 -- 字符识别(OCR),最大支持255 X 255 超大点阵,支持偏色,支持模糊识别,支持系统字库 -- 插件有32位和64位版本,支持32/64位绑定 -- 项目完全开源,无后门无病毒,可放心使用 +- 字符识别(OCR) + >1. 传统识别算法:最大支持255 X 255 超大点阵,支持偏色,支持模糊识别,支持系统字库 + + >2. 内部接入主流 ocr引擎(例如google的tesseract),无需繁琐配置,一条命令即可完成识别 +- 插件有32位和64位版本,支持32/64位程序调用 ## Download 包含32位和64位插件,tool工具以及必要的第三方库等文件 @@ -28,6 +30,7 @@ OP插件是为了满足Windows平台下各种自动化操作和图像处理的 ### 第三方库 * [blackbone](https://github.com/DarthTon/Blackbone.git)(静态编译,链接方式MT) 编译完成后,设置环境变量BLACKBONE_ROOT为源码根目录(例如D:\workspace\Blackbone) +* [tesseract](https://github.com/tesseract-ocr/tesseract)(依赖库较多,推荐vcpkg编译) * [kiero](https://github.com/Rebzzel/kiero.git)(已在源码内,无需安装) * [minhook](https://github.com/TsudaKageyu/minhook.git)(已在源码内,无需安装) * [QT5.12](https://download.qt.io/archive/qt/5.12/5.12.12/)(可选) 安装完成后设置环境变量QT_ROOT为Qt安装目录下的版本目录,例如D:\workspace\QT\5.12.12 From fa4797d721c318f344f3286b95c5ed65a9bf8476 Mon Sep 17 00:00:00 2001 From: wallbreaker2 <25576317+WallBreaker2@users.noreply.github.com> Date: Sat, 22 Jul 2023 18:11:44 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=9B=B4=E6=94=B9ocr=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libop/core/optype.h | 14 ++++++++++++++ libop/imageProc/tess_ocr.cpp | 9 +++++---- libop/imageProc/tess_ocr.h | 8 +------- qttool/MainWindow.cpp | 8 ++++---- qttool/Tool.cpp | 4 ++-- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/libop/core/optype.h b/libop/core/optype.h index 6459e8a..8f2334f 100644 --- a/libop/core/optype.h +++ b/libop/core/optype.h @@ -48,6 +48,7 @@ struct rect_t { int x2, y2; int width() const { return x2 - x1; } int height() const { return y2 - y1; } + int area() const { return width() * height(); } rect_t& shrinkRect(int w, int h) { x2 -= w; y2 -= h; @@ -91,5 +92,18 @@ struct point_desc_t { }; using vpoint_desc_t = std::vector; +// ocr result +struct ocr_rec_t { + // BBox of the text + point_t left_top; + point_t right_bottom; + // content of the text + wstring text; + // confidence of the text + float confidence; +}; + +using vocr_rec_t = std::vector; + #endif \ No newline at end of file diff --git a/libop/imageProc/tess_ocr.cpp b/libop/imageProc/tess_ocr.cpp index e77b312..cb43c6d 100644 --- a/libop/imageProc/tess_ocr.cpp +++ b/libop/imageProc/tess_ocr.cpp @@ -2,6 +2,7 @@ #include #include #include "../core/helpfunc.h" +#include "../core/opEnv.h" tess_ocr::tess_ocr(): m_api(nullptr) { init(); } @@ -12,7 +13,7 @@ int tess_ocr::init() { // Tesseract OCR m_api = new tesseract::TessBaseAPI(); // ʼTesseract OCR - if (m_api->Init("./tess_model", "chi_sim")) { + if (m_api->Init(_ws2string( opEnv::getBasePath()+L"/tess_model").c_str(), "chi_sim")) { setlog("Could not initialize tesseract.\n"); release(); } @@ -29,7 +30,7 @@ int tess_ocr::release() { static string utf8_to_ansi(string strUTF8); -int tess_ocr::ocr(byte* data, int w, int h, int bpp, vector& result) { +int tess_ocr::ocr(byte* data, int w, int h, int bpp, vocr_rec_t& result) { result.clear(); if (m_api == nullptr)return -1; @@ -55,8 +56,8 @@ int tess_ocr::ocr(byte* data, int w, int h, int bpp, vector& resu // ʾʺŶ //printf("word: '%s'; \tconf: %.2f box=[%d,%d %d,%d]; \n", word, conf, x1, y1, x2, y2); //std::wcout << L"xxx:" << utf82ws(word) << std::endl; - tess_rec_info ts; - ts.confidenc = conf; + ocr_rec_t ts; + ts.confidence = conf; ts.left_top = point_t(x1, y1); ts.right_bottom = point_t(x2, y2); ts.text = _s2wstring(utf8_to_ansi(word)); diff --git a/libop/imageProc/tess_ocr.h b/libop/imageProc/tess_ocr.h index 0e674bf..80b6699 100644 --- a/libop/imageProc/tess_ocr.h +++ b/libop/imageProc/tess_ocr.h @@ -3,12 +3,6 @@ namespace tesseract { class TessBaseAPI; }; -struct tess_rec_info { - point_t left_top; - point_t right_bottom; - wstring text; - float confidenc; -}; class tess_ocr { public: @@ -16,7 +10,7 @@ class tess_ocr { ~tess_ocr(); int init(); int release(); - int ocr(byte* data, int w, int h, int bpp, vector& result); + int ocr(byte* data, int w, int h, int bpp, vocr_rec_t& result); private: tesseract::TessBaseAPI* m_api; }; \ No newline at end of file diff --git a/qttool/MainWindow.cpp b/qttool/MainWindow.cpp index 2de057d..a563660 100644 --- a/qttool/MainWindow.cpp +++ b/qttool/MainWindow.cpp @@ -272,12 +272,12 @@ void MainWindow::sci_init() { m_sci->send(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE); m_sci->send(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE); - // �۵���ǩ��ɫ + m_sci->send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERSUB, 0xa0a0a0); m_sci->send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERMIDTAIL, 0xa0a0a0); m_sci->send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERTAIL, 0xa0a0a0); - m_sci->send(SCI_SETFOLDFLAGS, 16 | 4, 0); //����۵������۵��е����¸���һ������ + m_sci->send(SCI_SETFOLDFLAGS, 16 | 4, 0); //m_sci->send(SCI_SETMARGINMASKN, 0, 0x01); } @@ -435,7 +435,7 @@ void MainWindow::getBindScreen(){ lua_getglobal(_L, "ret"); int ret = lua_tointeger(_L, -1); lua_getglobal(_L, "data"); - unsigned int data = lua_tointeger(_L, -1); + size_t data = lua_tointeger(_L, -1); lua_getglobal(_L, "size"); int size = lua_tointeger(_L, -1); char buffer[256] = { 0 }; @@ -461,7 +461,7 @@ void MainWindow::getBindScreen(){ //m_bindWindow->setScaledContents(true); - m_bindWindowPixmap = QPixmap::fromImage(img.mirrored(false)); + m_bindWindowPixmap = QPixmap::fromImage(img); /*QPainter pant(&m_bindWindowPixmap); diff --git a/qttool/Tool.cpp b/qttool/Tool.cpp index 0ec36d3..d123000 100644 --- a/qttool/Tool.cpp +++ b/qttool/Tool.cpp @@ -206,12 +206,12 @@ void Tool::toBinary() { //_imgloc._binary.write(L"_binary.bmp"); //_qbinary.load("_binary.bmp"); ui.label_bin->setPixmap(QPixmap::fromImage(_qbinary)); - std::map ps; + std::map ps; std::wstring ss; auto tempDict = file_dict; tempDict.sort_dict(); _imgloc.bin_ocr(tempDict, _ocr_sim, ps); - for (auto& it : ps)ss += it.second; + for (auto& it : ps)ss += it.second.text; ui.textEdit->setText(QString::fromStdWString(ss)); From f1a405b1cf3f363bda63bab9378d3b7f8f007305 Mon Sep 17 00:00:00 2001 From: wallbreaker2 <25576317+WallBreaker2@users.noreply.github.com> Date: Sat, 22 Jul 2023 18:12:24 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=89=80=E6=9C=89ocr?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libop/imageProc/ImageLoc.cpp | 2551 +++++++++++++++++---------------- libop/imageProc/ImageLoc.h | 14 +- libop/imageProc/ImageProc.cpp | 38 +- 3 files changed, 1322 insertions(+), 1281 deletions(-) diff --git a/libop/imageProc/ImageLoc.cpp b/libop/imageProc/ImageLoc.cpp index c7e008e..698b2ce 100644 --- a/libop/imageProc/ImageLoc.cpp +++ b/libop/imageProc/ImageLoc.cpp @@ -11,70 +11,71 @@ using std::to_wstring; //检查是否为透明图,返回透明像素个数, 四角颜色相同且透明颜色数量在50%-99%范围内 -int check_transparent(Image *img) { - if (img->width < 2 || img->height < 2) return 0; - uint c0 = *img->begin(); - bool x = c0 == img->at(0, img->width - 1) && - c0 == img->at(img->height - 1, 0) && - c0 == img->at(img->height - 1, img->width - 1); - if (!x) return 0; - - int ct = 0; - for (auto it : *img) - if (it == c0) ++ct; - int total = img->height * img->width; - return total * 0.5 <= ct && ct < total ? ct : 0; +int check_transparent(Image* img) { + if (img->width < 2 || img->height < 2) return 0; + uint c0 = *img->begin(); + bool x = c0 == img->at(0, img->width - 1) && + c0 == img->at(img->height - 1, 0) && + c0 == img->at(img->height - 1, img->width - 1); + if (!x) return 0; + + int ct = 0; + for (auto it : *img) + if (it == c0) ++ct; + int total = img->height * img->width; + return total * 0.5 <= ct && ct < total ? ct : 0; } -void get_match_points(const Image &img, vector &points) { - points.clear(); - uint cbk = *img.begin(); - for (int i = 0; i < img.height; ++i) { - for (int j = 0; j < img.width; ++j) - if (cbk != img.at(i, j)) points.push_back((i << 16) | j); - } +void get_match_points(const Image& img, vector& points) { + points.clear(); + uint cbk = *img.begin(); + for (int i = 0; i < img.height; ++i) { + for (int j = 0; j < img.width; ++j) + if (cbk != img.at(i, j)) points.push_back((i << 16) | j); + } } -void gen_next(const Image &img, vector &next) { - next.resize(img.width * img.height); - - auto t = img.ptr(0); - auto p = next.data(); - p[0] = -1; - int k = -1, j = 0; - while (j < next.size() - 1) { - if (k == -1 || t[k] == t[j]) { - k++; - j++; - p[j] = k; - } else { - k = p[k]; - } - } +void gen_next(const Image& img, vector& next) { + next.resize(img.width * img.height); + + auto t = img.ptr(0); + auto p = next.data(); + p[0] = -1; + int k = -1, j = 0; + while (j < next.size() - 1) { + if (k == -1 || t[k] == t[j]) { + k++; + j++; + p[j] = k; + } + else { + k = p[k]; + } + } } -void Connectivity(const ImageBin &bin, ImageBin &rec) {} - -void extractConnectivity(const ImageBin &src, int threshold, - std::vector &out) { - ImageBin bin = src; - for (auto &it : bin) { - it = it > threshold ? 0xffu : 0; - } - ImageBin rec; - rec.create(bin.width, bin.height); - for (auto &it : rec) { - it = 0; - } +void Connectivity(const ImageBin& bin, ImageBin& rec) {} + +void extractConnectivity(const ImageBin& src, int threshold, + std::vector& out) { + ImageBin bin = src; + for (auto& it : bin) { + it = it > threshold ? 0xffu : 0; + } + ImageBin rec; + rec.create(bin.width, bin.height); + for (auto& it : rec) { + it = 0; + } } struct MatchContext { - imageView view; - Image *pic; - ImageBin *gray; - color_t dfcolor; - int tnorm; - vector &points; - int use_ts_match; + imageView view; + Image* pic; + ImageBin* gray; + color_t dfcolor; + int tnorm; + vector& points; + int use_ts_match; }; enum PicMatchType { PicMatchRGB = 0, PicMatchGray = 1, PicMatchTrans = 2 }; @@ -127,1103 +128,1108 @@ enum PicMatchType { PicMatchRGB = 0, PicMatchGray = 1, PicMatchTrans = 2 }; //} ImageBase::ImageBase() : m_threadPool(std::thread::hardware_concurrency()) { - _x1 = _y1 = 0; - _dx = _dy = 0; - + _x1 = _y1 = 0; + _dx = _dy = 0; + } ImageBase::~ImageBase() { } void ImageBase::set_offset(int x1, int y1) { - _x1 = x1; - _y1 = y1; + _x1 = x1; + _y1 = y1; } -long ImageBase::GetPixel(long x, long y, color_t &cr) { - auto p = _src.ptr(0); - // setlog("%d", _src.width); - // static_assert(sizeof(color_t) == 4); - cr = *p; - return 1; +long ImageBase::GetPixel(long x, long y, color_t& cr) { + auto p = _src.ptr(0); + // setlog("%d", _src.width); + // static_assert(sizeof(color_t) == 4); + cr = *p; + return 1; } -long ImageBase::CmpColor(color_t color, std::vector &colors, - double sim) { - for (auto &it : colors) { - if (IN_RANGE(color, it.color, it.df)) return 1; - } +long ImageBase::CmpColor(color_t color, std::vector& colors, + double sim) { + for (auto& it : colors) { + if (IN_RANGE(color, it.color, it.df)) return 1; + } - return 0; + return 0; } -struct opRange{ - int x1; - int x2; +struct opRange { + int x1; + int x2; }; -struct opRange2D{ - int x1; - int x2; - int y1; - int y2; - int stepX; - int stepY; +struct opRange2D { + int x1; + int x2; + int y1; + int y2; + int stepX; + int stepY; }; -static void gen_rangeyx(int dir,const opRange2D& range, opRange2D& out) { -if(dir == 0){ - out.x1 = range.x1; - out.x2 = range.x2; - out.y1 = range.y1; - out.y2 = range.y2; - out.stepX = 1; - out.stepY = 1; -}else if(dir == 1){ - out.x1 = range.x2-1; - out.x2 = range.x1-1; - out.y1 = range.y1; - out.y2 = range.y2; - out.stepX = -1; - out.stepY = 1; -}else if(dir == 2){ - out.x1 = range.x1; - out.x2 = range.x2; - out.y1 = range.y2-1; - out.y2 = range.y1-1; - out.stepX = 1; - out.stepY = -1; -}else{ - out.x1 = range.x2-1; - out.x2 = range.x1-1; - out.y1 = range.y2-1; - out.y2 = range.y1-1; - out.stepX = -1; - out.stepY = -1; -} +static void gen_rangeyx(int dir, const opRange2D& range, opRange2D& out) { + if (dir == 0) { + out.x1 = range.x1; + out.x2 = range.x2; + out.y1 = range.y1; + out.y2 = range.y2; + out.stepX = 1; + out.stepY = 1; + } + else if (dir == 1) { + out.x1 = range.x2 - 1; + out.x2 = range.x1 - 1; + out.y1 = range.y1; + out.y2 = range.y2; + out.stepX = -1; + out.stepY = 1; + } + else if (dir == 2) { + out.x1 = range.x1; + out.x2 = range.x2; + out.y1 = range.y2 - 1; + out.y2 = range.y1 - 1; + out.stepX = 1; + out.stepY = -1; + } + else { + out.x1 = range.x2 - 1; + out.x2 = range.x1 - 1; + out.y1 = range.y2 - 1; + out.y2 = range.y1 - 1; + out.stepX = -1; + out.stepY = -1; + } } -long ImageBase::FindColor(vector &colors, int dir, long &x, - long &y) { - opRange2D rng={0,_src.width,0,_src.height,0,0},range; - gen_rangeyx(dir,rng,range); - for (auto &it : colors) { //对每个颜色描述 - - for (int i = range.y1; i !=range.y2; i+=range.stepY) { - auto p = _src.ptr(i); - for (int j = range.x1; j !=range.x2; j+=range.stepX) { - if (IN_RANGE(*p, it.color, it.df)) { - x = j + _x1 + _dx; - y = i + _y1 + _dy; - return 1; - } - p++; - } - } - } - - x = y = -1; - return 0; +long ImageBase::FindColor(vector& colors, int dir, long& x, + long& y) { + opRange2D rng = { 0,_src.width,0,_src.height,0,0 }, range; + gen_rangeyx(dir, rng, range); + for (auto& it : colors) { //对每个颜色描述 + + for (int i = range.y1; i != range.y2; i += range.stepY) { + auto p = _src.ptr(i); + for (int j = range.x1; j != range.x2; j += range.stepX) { + if (IN_RANGE(*p, it.color, it.df)) { + x = j + _x1 + _dx; + y = i + _y1 + _dy; + return 1; + } + p++; + } + } + } + + x = y = -1; + return 0; } -long ImageBase::FindColorEx(vector &colors, std::wstring &retstr) { - retstr.clear(); - int find_ct = 0; - for (int i = 0; i < _src.height; ++i) { - auto p = _src.ptr(i); - for (int j = 0; j < _src.width; ++j) { - for (auto &it : colors) { //对每个颜色描述 - if (IN_RANGE(*p, it.color, it.df)) { - retstr += std::to_wstring(j + _x1 + _dx) + L"," + - std::to_wstring(i + _y1 + _dy); - retstr += L"|"; - ++find_ct; - // return 1; - if (find_ct > _max_return_obj_ct) goto _quick_break; - break; - } - } - p++; - } - } +long ImageBase::FindColorEx(vector& colors, std::wstring& retstr) { + retstr.clear(); + int find_ct = 0; + for (int i = 0; i < _src.height; ++i) { + auto p = _src.ptr(i); + for (int j = 0; j < _src.width; ++j) { + for (auto& it : colors) { //对每个颜色描述 + if (IN_RANGE(*p, it.color, it.df)) { + retstr += std::to_wstring(j + _x1 + _dx) + L"," + + std::to_wstring(i + _y1 + _dy); + retstr += L"|"; + ++find_ct; + // return 1; + if (find_ct > _max_return_obj_ct) goto _quick_break; + break; + } + } + p++; + } + } _quick_break: - if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); - return find_ct; + if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); + return find_ct; } -long ImageBase::FindMultiColor(std::vector &first_color, - std::vector &offset_color, - double sim, long dir, long &x, long &y) { - int max_err_ct = offset_color.size() * (1. - sim); - int err_ct; - opRange2D rng={0,_src.width,0,_src.height,0,0},range; - gen_rangeyx(dir,rng,range); - for (int i = range.y1; i !=range.y2; i+=range.stepY) { - auto p = _src.ptr(i); - for (int j = range.x1; j !=range.x2; j+=range.stepX) { - // step 1. find first color - for (auto &it : first_color) { //对每个颜色描述 - if (IN_RANGE(*p, it.color, it.df)) { - //匹配其他坐标 - err_ct = 0; - for (auto &off_cr : offset_color) { - int ptX = j + off_cr.x; - int ptY = i + off_cr.y; - if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height) { - color_t currentColor = _src.at(ptY, ptX); - if (!CmpColor(currentColor, off_cr.crdfs, sim)) ++err_ct; - } else { - ++err_ct; - } - if (err_ct > max_err_ct) goto _quick_break; - } - // ok - x = j + _x1 + _dx, y = i + _y1 + _dy; - return 1; - } - } - _quick_break: - p++; - } - } - x = y = -1; - return 0; +long ImageBase::FindMultiColor(std::vector& first_color, + std::vector& offset_color, + double sim, long dir, long& x, long& y) { + int max_err_ct = offset_color.size() * (1. - sim); + int err_ct; + opRange2D rng = { 0,_src.width,0,_src.height,0,0 }, range; + gen_rangeyx(dir, rng, range); + for (int i = range.y1; i != range.y2; i += range.stepY) { + auto p = _src.ptr(i); + for (int j = range.x1; j != range.x2; j += range.stepX) { + // step 1. find first color + for (auto& it : first_color) { //对每个颜色描述 + if (IN_RANGE(*p, it.color, it.df)) { + //匹配其他坐标 + err_ct = 0; + for (auto& off_cr : offset_color) { + int ptX = j + off_cr.x; + int ptY = i + off_cr.y; + if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height) { + color_t currentColor = _src.at(ptY, ptX); + if (!CmpColor(currentColor, off_cr.crdfs, sim)) ++err_ct; + } + else { + ++err_ct; + } + if (err_ct > max_err_ct) goto _quick_break; + } + // ok + x = j + _x1 + _dx, y = i + _y1 + _dy; + return 1; + } + } + _quick_break: + p++; + } + } + x = y = -1; + return 0; } -long ImageBase::FindMultiColorEx(std::vector &first_color, - std::vector &offset_color, - double sim, long dir, std::wstring &retstr) { - int max_err_ct = offset_color.size() * (1. - sim); - int err_ct; - int find_ct = 0; - opRange2D rng = { 0,_src.width,0,_src.height,0,0 }, range; - gen_rangeyx(dir, rng, range); - - for (int i = range.y1; i != range.y2; i += range.stepY) { - auto p = _src.ptr(i); - for (int j = range.x1; j != range.x2; j += range.stepX) { - // step 1. find first color - for (auto &it : first_color) { //对每个颜色描述 - if (IN_RANGE(*p, it.color, it.df)) { - //匹配其他坐标 - err_ct = 0; - for (auto &off_cr : offset_color) { - /* color_t currentColor = _src.at(j + off_cr.x, i + - off_cr.y); if (!CmpColor(currentColor, off_cr.crdfs, sim)) - ++err_ct; if (err_ct > max_err_ct) goto _quick_break;*/ - - // - int ptX = j + off_cr.x; - int ptY = i + off_cr.y; - if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height) { - color_t currentColor = _src.at(ptY, ptX); - if (!CmpColor(currentColor, off_cr.crdfs, sim)) ++err_ct; - } else { - ++err_ct; - } - if (err_ct > max_err_ct) goto _quick_break; - } - - // ok - retstr += - to_wstring(j + _x1 + _dx) + L"," + to_wstring(i + _y1 + _dy); - retstr += L"|"; - ++find_ct; - if (find_ct > _max_return_obj_ct) - goto _quick_return; - else - goto _quick_break; - } - } - _quick_break: - p++; - } - } +long ImageBase::FindMultiColorEx(std::vector& first_color, + std::vector& offset_color, + double sim, long dir, std::wstring& retstr) { + int max_err_ct = offset_color.size() * (1. - sim); + int err_ct; + int find_ct = 0; + opRange2D rng = { 0,_src.width,0,_src.height,0,0 }, range; + gen_rangeyx(dir, rng, range); + + for (int i = range.y1; i != range.y2; i += range.stepY) { + auto p = _src.ptr(i); + for (int j = range.x1; j != range.x2; j += range.stepX) { + // step 1. find first color + for (auto& it : first_color) { //对每个颜色描述 + if (IN_RANGE(*p, it.color, it.df)) { + //匹配其他坐标 + err_ct = 0; + for (auto& off_cr : offset_color) { + /* color_t currentColor = _src.at(j + off_cr.x, i + + off_cr.y); if (!CmpColor(currentColor, off_cr.crdfs, sim)) + ++err_ct; if (err_ct > max_err_ct) goto _quick_break;*/ + + // + int ptX = j + off_cr.x; + int ptY = i + off_cr.y; + if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height) { + color_t currentColor = _src.at(ptY, ptX); + if (!CmpColor(currentColor, off_cr.crdfs, sim)) ++err_ct; + } + else { + ++err_ct; + } + if (err_ct > max_err_ct) goto _quick_break; + } + + // ok + retstr += + to_wstring(j + _x1 + _dx) + L"," + to_wstring(i + _y1 + _dy); + retstr += L"|"; + ++find_ct; + if (find_ct > _max_return_obj_ct) + goto _quick_return; + else + goto _quick_break; + } + } + _quick_break: + p++; + } + } _quick_return: - if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); - return find_ct; - // x = y = -1; + if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); + return find_ct; + // x = y = -1; } -long ImageBase::FindPic(std::vector &pics, color_t dfcolor, double sim, long dir, - long &x, long &y) { - x = y = -1; - vector points; - int match_ret = 0; - ImageBin gimg; - _gray.fromImage4(_src); - record_sum(_gray); - int tnorm; - opRange2D rng = { 0,_src.width,0,_src.height,0,0 },range; - gen_rangeyx(dir, rng, range); - //将小循环放在最外面,提高处理速度 - for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { - auto pic = pics[pic_id]; - int use_ts_match = check_transparent(pic); - if (use_ts_match) - get_match_points(*pic, points); - else { - gimg.fromImage4(*pic); - tnorm = sum(gimg.begin(), gimg.end()); - } - - for (int i = range.y1; i != range.y2; i += range.stepY) { - for (int j = range.x1; j != range.x2; j += range.stepX) { - // step 1. 边界检查 - if (i + pic->height > _src.height || j + pic->width > _src.width) - continue; - // step 2. 计算最大误差 - int max_err_ct = - (pic->height * pic->width - use_ts_match) * (1.0 - sim); - // step 3. 开始匹配 - - /*match_ret = (use_ts_match ? trans_match(j, i, pic, dfcolor, - points, max_err_ct) : simple_match(j, i, pic, dfcolor, - max_err_ct));*/ - match_ret = (use_ts_match ? trans_match(j, i, pic, dfcolor, - points, max_err_ct) - : real_match(j, i, &gimg, tnorm, sim)); - // simple_match(j, i, pic, dfcolor,tnorm, sim)); - if (match_ret) { - x = j + _x1 + _dx; - y = i + _y1 + _dy; - return pic_id; - } - - } // end for j - } // end for i - } // end for pics - return -1; +long ImageBase::FindPic(std::vector& pics, color_t dfcolor, double sim, long dir, + long& x, long& y) { + x = y = -1; + vector points; + int match_ret = 0; + ImageBin gimg; + _gray.fromImage4(_src); + record_sum(_gray); + int tnorm; + opRange2D rng = { 0,_src.width,0,_src.height,0,0 }, range; + gen_rangeyx(dir, rng, range); + //将小循环放在最外面,提高处理速度 + for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { + auto pic = pics[pic_id]; + int use_ts_match = check_transparent(pic); + if (use_ts_match) + get_match_points(*pic, points); + else { + gimg.fromImage4(*pic); + tnorm = sum(gimg.begin(), gimg.end()); + } + + for (int i = range.y1; i != range.y2; i += range.stepY) { + for (int j = range.x1; j != range.x2; j += range.stepX) { + // step 1. 边界检查 + if (i + pic->height > _src.height || j + pic->width > _src.width) + continue; + // step 2. 计算最大误差 + int max_err_ct = + (pic->height * pic->width - use_ts_match) * (1.0 - sim); + // step 3. 开始匹配 + + /*match_ret = (use_ts_match ? trans_match(j, i, pic, dfcolor, + points, max_err_ct) : simple_match(j, i, pic, dfcolor, + max_err_ct));*/ + match_ret = (use_ts_match ? trans_match(j, i, pic, dfcolor, + points, max_err_ct) + : real_match(j, i, &gimg, tnorm, sim)); + // simple_match(j, i, pic, dfcolor,tnorm, sim)); + if (match_ret) { + x = j + _x1 + _dx; + y = i + _y1 + _dy; + return pic_id; + } + + } // end for j + } // end for i + } // end for pics + return -1; } -long ImageBase::FindPicTh(std::vector &pics, color_t dfcolor, - double sim, long dir, long &x, long &y) { - x = y = -1; - vector points; - int match_ret = 0; - ImageBin gimg; - _gray.fromImage4(_src); - record_sum(_gray); - int tnorm; - std::vector blocks; - //将小循环放在最外面,提高处理速度 - for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { - auto pic = pics[pic_id]; - int use_ts_match = check_transparent(pic); - if (use_ts_match) - get_match_points(*pic, points); - else { - gimg.fromImage4(*pic); - tnorm = sum(gimg.begin(), gimg.end()); - } - auto pgimg = &gimg; - rect_t matchRect(0, 0, _src.width, _src.height); - matchRect.shrinkRect(pic->width, pic->height); - if (!matchRect.valid()) continue; - matchRect.divideBlock(m_threadPool.getThreadNum(), - matchRect.width() > matchRect.height(), blocks); - std::vector> results; - bool stop = false; - for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i) { - results.push_back(m_threadPool.enqueue( - [this, dfcolor, points, pgimg, tnorm](rect_t &block, Image *pic, - int use_ts_match, double sim, - bool *stop) { - // 计算最大误差 - int max_err_ct = - (pic->height * pic->width - use_ts_match) * (1.0 - sim); - for (int i = block.y1; i < block.y2; ++i) { - for (int j = block.x1; j < block.x2; ++j) { - if (*stop) return point_t(-1, -1); - // 开始匹配 - int match_ret = - (use_ts_match ? trans_match(j, i, pic, dfcolor, - points, max_err_ct) - : real_match(j, i, pgimg, tnorm, sim)); - // simple_match(j, i, pic, dfcolor,tnorm, sim)); - if (match_ret) { - *stop = true; - return point_t(j + _x1 + _dx, i + _y1 + _dy); - } - - } // end for j - } // end for i - return point_t(-1, -1); - }, - blocks[i], pic, use_ts_match, sim, &stop)); - // results.push_back(r); - } - // wait all - for (auto &&f : results) { - point_t p = f.get(); - if (p.x != -1) { - x = p.x; - y = p.y; - // return pic_id; - } - } - if (x != -1) { - return pic_id; - } - - } // end for pics - return -1; +long ImageBase::FindPicTh(std::vector& pics, color_t dfcolor, + double sim, long dir, long& x, long& y) { + x = y = -1; + vector points; + int match_ret = 0; + ImageBin gimg; + _gray.fromImage4(_src); + record_sum(_gray); + int tnorm; + std::vector blocks; + //将小循环放在最外面,提高处理速度 + for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { + auto pic = pics[pic_id]; + int use_ts_match = check_transparent(pic); + if (use_ts_match) + get_match_points(*pic, points); + else { + gimg.fromImage4(*pic); + tnorm = sum(gimg.begin(), gimg.end()); + } + auto pgimg = &gimg; + rect_t matchRect(0, 0, _src.width, _src.height); + matchRect.shrinkRect(pic->width, pic->height); + if (!matchRect.valid()) continue; + matchRect.divideBlock(m_threadPool.getThreadNum(), + matchRect.width() > matchRect.height(), blocks); + std::vector> results; + bool stop = false; + for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i) { + results.push_back(m_threadPool.enqueue( + [this, dfcolor, points, pgimg, tnorm](rect_t& block, Image* pic, + int use_ts_match, double sim, + bool* stop) { + // 计算最大误差 + int max_err_ct = + (pic->height * pic->width - use_ts_match) * (1.0 - sim); + for (int i = block.y1; i < block.y2; ++i) { + for (int j = block.x1; j < block.x2; ++j) { + if (*stop) return point_t(-1, -1); + // 开始匹配 + int match_ret = + (use_ts_match ? trans_match(j, i, pic, dfcolor, + points, max_err_ct) + : real_match(j, i, pgimg, tnorm, sim)); + // simple_match(j, i, pic, dfcolor,tnorm, sim)); + if (match_ret) { + *stop = true; + return point_t(j + _x1 + _dx, i + _y1 + _dy); + } + + } // end for j + } // end for i + return point_t(-1, -1); + }, + blocks[i], pic, use_ts_match, sim, &stop)); + // results.push_back(r); + } + // wait all + for (auto&& f : results) { + point_t p = f.get(); + if (p.x != -1) { + x = p.x; + y = p.y; + // return pic_id; + } + } + if (x != -1) { + return pic_id; + } + + } // end for pics + return -1; } -long ImageBase::FindPicEx(std::vector &pics, color_t dfcolor, - double sim, long dir, vpoint_desc_t &vpd) { - int obj_ct = 0; - vpd.clear(); - vector points; - bool nodfcolor = color2uint(dfcolor) == 0; - int match_ret = 0; - ImageBin gimg; - _gray.fromImage4(_src); - record_sum(_gray); - int tnorm; - for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { - auto pic = pics[pic_id]; - int use_ts_match = check_transparent(pic); - - if (use_ts_match) - get_match_points(*pic, points); - else { - gimg.fromImage4(*pic); - tnorm = sum(gimg.begin(), gimg.end()); - } - for (int i = 0; i < _src.height; ++i) { - for (int j = 0; j < _src.width; ++j) { - // step 1. 边界检查 - if (i + pic->height > _src.height || j + pic->width > _src.width) - continue; - // step 2. 计算最大误差 - int max_err_ct = - (pic->height * pic->width - use_ts_match) * (1.0 - sim); - // step 3. 开始匹配 - - match_ret = (use_ts_match ? trans_match(j, i, pic, dfcolor, - points, max_err_ct) - : real_match(j, i, &gimg, tnorm, sim)); - if (match_ret) { - point_desc_t pd = {pic_id, point_t(j + _x1 + _dx, i + _y1 + _dy)}; - - vpd.push_back(pd); - ++obj_ct; - if (obj_ct > _max_return_obj_ct) goto _quick_return; - } - - } // end for j - } // end for i - } // end for pics +long ImageBase::FindPicEx(std::vector& pics, color_t dfcolor, + double sim, long dir, vpoint_desc_t& vpd) { + int obj_ct = 0; + vpd.clear(); + vector points; + bool nodfcolor = color2uint(dfcolor) == 0; + int match_ret = 0; + ImageBin gimg; + _gray.fromImage4(_src); + record_sum(_gray); + int tnorm; + for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { + auto pic = pics[pic_id]; + int use_ts_match = check_transparent(pic); + + if (use_ts_match) + get_match_points(*pic, points); + else { + gimg.fromImage4(*pic); + tnorm = sum(gimg.begin(), gimg.end()); + } + for (int i = 0; i < _src.height; ++i) { + for (int j = 0; j < _src.width; ++j) { + // step 1. 边界检查 + if (i + pic->height > _src.height || j + pic->width > _src.width) + continue; + // step 2. 计算最大误差 + int max_err_ct = + (pic->height * pic->width - use_ts_match) * (1.0 - sim); + // step 3. 开始匹配 + + match_ret = (use_ts_match ? trans_match(j, i, pic, dfcolor, + points, max_err_ct) + : real_match(j, i, &gimg, tnorm, sim)); + if (match_ret) { + point_desc_t pd = { pic_id, point_t(j + _x1 + _dx, i + _y1 + _dy) }; + + vpd.push_back(pd); + ++obj_ct; + if (obj_ct > _max_return_obj_ct) goto _quick_return; + } + + } // end for j + } // end for i + } // end for pics _quick_return: - return obj_ct; + return obj_ct; } -long ImageBase::FindPicExTh(std::vector &pics, color_t dfcolor, - double sim, long dir, vpoint_desc_t &vpd) { - vpd.clear(); - int obj_ct = 0; - vector points; - int match_ret = 0; - ImageBin gimg; - _gray.fromImage4(_src); - record_sum(_gray); - int tnorm; - std::vector blocks; - //将小循环放在最外面,提高处理速度 - for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { - auto pic = pics[pic_id]; - int use_ts_match = check_transparent(pic); - if (use_ts_match) - get_match_points(*pic, points); - else { - gimg.fromImage4(*pic); - tnorm = sum(gimg.begin(), gimg.end()); - } - auto pgimg = &gimg; - rect_t matchRect(0, 0, _src.width, _src.height); - matchRect.shrinkRect(pic->width, pic->height); - if (!matchRect.valid()) continue; - matchRect.divideBlock(m_threadPool.getThreadNum(), - matchRect.width() > matchRect.height(), blocks); - std::vector> results; - for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i) { - results.push_back(m_threadPool.enqueue( - [this, dfcolor, points, pgimg, tnorm](rect_t &block, Image *pic, - int use_ts_match, - double sim) -> vpoint_t { - vpoint_t vp; - // 计算最大误差 - int max_err_ct = - (pic->height * pic->width - use_ts_match) * (1.0 - sim); - for (int i = block.y1; i < block.y2; ++i) { - for (int j = block.x1; j < block.x2; ++j) { - // 开始匹配 - int match_ret = - (use_ts_match ? trans_match(j, i, pic, dfcolor, - points, max_err_ct) - : real_match(j, i, pgimg, tnorm, sim)); - // simple_match(j, i, pic, dfcolor,tnorm, sim)); - if (match_ret) { - vp.push_back(point_t(j + _x1 + _dx, i + _y1 + _dy)); - } - - } // end for j - } // end for i - return vp; - }, - blocks[i], pic, use_ts_match, sim)); - // results.push_back(r); - } - // wait all - for (auto &&f : results) { - vpoint_t vp = f.get(); - if (vp.size() > 0) { - for (auto &p : vp) { - if (obj_ct < _max_return_obj_ct) { - point_desc_t pd = {pic_id, p}; - - vpd.push_back(pd); - ++obj_ct; - } - - } - - // return pic_id; - } - } - } - - return obj_ct; +long ImageBase::FindPicExTh(std::vector& pics, color_t dfcolor, + double sim, long dir, vpoint_desc_t& vpd) { + vpd.clear(); + int obj_ct = 0; + vector points; + int match_ret = 0; + ImageBin gimg; + _gray.fromImage4(_src); + record_sum(_gray); + int tnorm; + std::vector blocks; + //将小循环放在最外面,提高处理速度 + for (int pic_id = 0; pic_id < pics.size(); ++pic_id) { + auto pic = pics[pic_id]; + int use_ts_match = check_transparent(pic); + if (use_ts_match) + get_match_points(*pic, points); + else { + gimg.fromImage4(*pic); + tnorm = sum(gimg.begin(), gimg.end()); + } + auto pgimg = &gimg; + rect_t matchRect(0, 0, _src.width, _src.height); + matchRect.shrinkRect(pic->width, pic->height); + if (!matchRect.valid()) continue; + matchRect.divideBlock(m_threadPool.getThreadNum(), + matchRect.width() > matchRect.height(), blocks); + std::vector> results; + for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i) { + results.push_back(m_threadPool.enqueue( + [this, dfcolor, points, pgimg, tnorm](rect_t& block, Image* pic, + int use_ts_match, + double sim) -> vpoint_t { + vpoint_t vp; + // 计算最大误差 + int max_err_ct = + (pic->height * pic->width - use_ts_match) * (1.0 - sim); + for (int i = block.y1; i < block.y2; ++i) { + for (int j = block.x1; j < block.x2; ++j) { + // 开始匹配 + int match_ret = + (use_ts_match ? trans_match(j, i, pic, dfcolor, + points, max_err_ct) + : real_match(j, i, pgimg, tnorm, sim)); + // simple_match(j, i, pic, dfcolor,tnorm, sim)); + if (match_ret) { + vp.push_back(point_t(j + _x1 + _dx, i + _y1 + _dy)); + } + + } // end for j + } // end for i + return vp; + }, + blocks[i], pic, use_ts_match, sim)); + // results.push_back(r); + } + // wait all + for (auto&& f : results) { + vpoint_t vp = f.get(); + if (vp.size() > 0) { + for (auto& p : vp) { + if (obj_ct < _max_return_obj_ct) { + point_desc_t pd = { pic_id, p }; + + vpd.push_back(pd); + ++obj_ct; + } + + } + + // return pic_id; + } + } + } + + return obj_ct; } long ImageBase::FindColorBlock(double sim, long count, long height, long width, - long &x, long &y) { - x = y = -1; - record_sum(_binary); - for (int i = 0; i <= _binary.height - height; ++i) { - for (int j = 0; j < _binary.width - width; ++j) { - if (region_sum(j, i, j + width, i + height) >= count) { - x = j + _x1 + _dx; - y = i + _y1 + _dy; - return 1; - } - } - } - return -1; + long& x, long& y) { + x = y = -1; + record_sum(_binary); + for (int i = 0; i <= _binary.height - height; ++i) { + for (int j = 0; j < _binary.width - width; ++j) { + if (region_sum(j, i, j + width, i + height) >= count) { + x = j + _x1 + _dx; + y = i + _y1 + _dy; + return 1; + } + } + } + return -1; } long ImageBase::FindColorBlockEx(double sim, long count, long height, - long width, std::wstring &retstr) { - record_sum(_binary); - int cnt = 0; - for (int i = 0; i <= _binary.height - height; ++i) { - for (int j = 0; j < _binary.width - width; ++j) { - if (region_sum(j, i, j + width, i + height) >= count) { - wchar_t buff[20]; - wsprintfW(buff, L"%d,%d|", j + _x1 + _dx, i + _y1 + _dy); - retstr += buff; - ++cnt; - if (cnt > _max_return_obj_ct) goto _quick_return; - } - } - } + long width, std::wstring& retstr) { + record_sum(_binary); + int cnt = 0; + for (int i = 0; i <= _binary.height - height; ++i) { + for (int j = 0; j < _binary.width - width; ++j) { + if (region_sum(j, i, j + width, i + height) >= count) { + wchar_t buff[20]; + wsprintfW(buff, L"%d,%d|", j + _x1 + _dx, i + _y1 + _dy); + retstr += buff; + ++cnt; + if (cnt > _max_return_obj_ct) goto _quick_return; + } + } + } _quick_return: - if (cnt) { - retstr.pop_back(); - } - return cnt; + if (cnt) { + retstr.pop_back(); + } + return cnt; } -long ImageBase::Ocr(Dict &dict, double sim, wstring &retstr) { - retstr.clear(); - std::map ps; - bin_ocr(dict, sim, ps); - for (auto &it : ps) { - retstr += it.second; - } - return 1; +long ImageBase::Ocr(Dict& dict, double sim, wstring& retstr) { + retstr.clear(); + std::map ps; + bin_ocr(dict, sim, ps); + for (auto& it : ps) { + retstr += it.second.text; + } + return 1; } -long ImageBase::OcrEx(Dict &dict, double sim, std::wstring &retstr) { - retstr.clear(); - std::map ps; - bin_ocr(dict, sim, ps); - // x1,y1,str....|x2,y2,str2...|... - int find_ct = 0; - for (auto &it : ps) { - retstr += std::to_wstring(it.first.x + _x1 + _dx); - retstr += L","; - retstr += std::to_wstring(it.first.y + _y1 + _dy); - retstr += L","; - retstr += it.second; - retstr += L"|"; - ++find_ct; - if (find_ct > _max_return_obj_ct) break; - } - if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); - return find_ct; +long ImageBase::OcrEx(Dict& dict, double sim, std::wstring& retstr) { + retstr.clear(); + std::map ps; + bin_ocr(dict, sim, ps); + // x1,y1,str....|x2,y2,str2...|... + int find_ct = 0; + for (auto& it : ps) { + retstr += std::to_wstring(it.first.x + _x1 + _dx); + retstr += L","; + retstr += std::to_wstring(it.first.y + _y1 + _dy); + retstr += L","; + retstr += it.second.text; + retstr += L"|"; + ++find_ct; + if (find_ct > _max_return_obj_ct) break; + } + if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); + return find_ct; } -long ImageBase::FindStr(Dict &dict, const vector &vstr, double sim, - long &retx, long &rety) { - retx = rety = -1; - - //查找字符 返回坐标 - // step 1. 找出频幕中所有字符及其坐标信息 - std::map ps; - bin_ocr(dict, sim, ps); - // step 2. 拼接字符 形成完整字符串 - wstring str; - for (auto &it : ps) str.append(it.second); - // step 3. 在完整字符中查找目标字符串,并记录 索引 - int idx = -1; - int oneIndex = -1; - for (int i = 0; i < vstr.size(); ++i) { - idx = str.find(vstr[i]); - if (idx != -1) { - oneIndex = i; - break; - } - } - // step 4.根据索引给出对应 坐标 并返回 - if (idx != -1) { // locate it - int curr_len = 0; - for (auto &it : ps) { - curr_len += it.second.length(); - if (curr_len < idx + 1) continue; - if (it.second.find(str[idx]) != -1) { - retx = it.first.x + _x1 + _dx; - rety = it.first.y + _y1 + _dy; - return oneIndex; - } - } - //这里进行断言,表示不会走到这一步 - assert(0); - } - - return -1; +long ImageBase::FindStr(std::map& ps, const vector& vstr, double sim, + long& retx, long& rety) { + retx = rety = -1; + + //查找字符 返回坐标 + // step 1. 找出频幕中所有字符及其坐标信息 + //std::map ps; + //bin_ocr(dict, sim, ps); + // step 2. 拼接字符 形成完整字符串 + wstring str; + for (auto& it : ps) str.append(it.second.text); + // step 3. 在完整字符中查找目标字符串,并记录 索引 + int idx = -1; + int oneIndex = -1; + for (int i = 0; i < vstr.size(); ++i) { + idx = str.find(vstr[i]); + if (idx != -1) { + oneIndex = i; + break; + } + } + // step 4.根据索引给出对应 坐标 并返回 + if (idx != -1) { // locate it + int curr_len = 0; + for (auto& it : ps) { + curr_len += it.second.text.length(); + if (curr_len < idx + 1) continue; + if (it.second.text.find(str[idx]) != -1) { + retx = it.first.x + _x1 + _dx; + rety = it.first.y + _y1 + _dy; + return oneIndex; + } + } + //这里进行断言,表示不会走到这一步 + assert(0); + } + + return -1; } -long ImageBase::FindStrEx(Dict &dict, const vector &vstr, double sim, - std::wstring &retstr) { - //描述:查找屏幕指定位置的字符(或者字符串)位置,返回所有出现的坐标(注意与FindStr接口的区别)!!! - //----------------------步骤----------------- - // step 1. 获取指定位置的字符及坐标信息 - // step 2. 拼接字符,形成完整字符串 str - // step 2.对每个目标字符 ti , 查找其在str中的位置,并记录 index(如果存在) - // step 4.根据index ,获取其坐标,并将坐标转化为字符串,拼接到返回值 - // step 5. 回到第3步 - - retstr.clear(); - std::map ps; - // step 1. - bin_ocr(dict, sim, ps); - // setp 2. - wstring str; - for (auto &it : ps) { - str.append(it.second); - } - // step 3. - int find_ct = 0; - for (int i = 0; i < vstr.size(); ++i) { - int index = -1, old = -1; - do { - index = str.find(vstr[i], old + 1); - if (index == -1) { // failed!! - break; - } - // step 4 - int current_len = 0; - for (auto &it : ps) { - //注意 字符长度要大于index 才记录坐标 - current_len += it.second.length(); - if (current_len < index + 1) continue; - if (it.second.find(str[index]) != -1) { - //记录坐标 - wchar_t buff[20] = {0}; - //注意加偏移 - wsprintf(buff, L"%d,%d,%d|", i, it.first.x + _x1 + _dx, - it.first.y + _y1 + _dy); - retstr.append(buff); - ++find_ct; - if (find_ct > _max_return_obj_ct) - goto _quick_return; - else - break; - // to do 这里还需要修改 - } - } // end for ps - old = index; - } while (1); - } +long ImageBase::FindStrEx(std::map& ps, const vector& vstr, double sim, + std::wstring& retstr) { + //描述:查找屏幕指定位置的字符(或者字符串)位置,返回所有出现的坐标(注意与FindStr接口的区别)!!! + //----------------------步骤----------------- + // step 1. 获取指定位置的字符及坐标信息 + // step 2. 拼接字符,形成完整字符串 str + // step 2.对每个目标字符 ti , 查找其在str中的位置,并记录 index(如果存在) + // step 4.根据index ,获取其坐标,并将坐标转化为字符串,拼接到返回值 + // step 5. 回到第3步 + + retstr.clear(); + + // setp 2. + wstring str; + for (auto& it : ps) { + str.append(it.second.text); + } + // step 3. + int find_ct = 0; + for (int i = 0; i < vstr.size(); ++i) { + int index = -1, old = -1; + do { + index = str.find(vstr[i], old + 1); + if (index == -1) { // failed!! + break; + } + // step 4 + int current_len = 0; + for (auto& it : ps) { + //注意 字符长度要大于index 才记录坐标 + current_len += it.second.text.length(); + if (current_len < index + 1) continue; + if (it.second.text.find(str[index]) != -1) { + //记录坐标 + wchar_t buff[20] = { 0 }; + //注意加偏移 + wsprintf(buff, L"%d,%d,%d|", i, it.first.x + _x1 + _dx, + it.first.y + _y1 + _dy); + retstr.append(buff); + ++find_ct; + if (find_ct > _max_return_obj_ct) + goto _quick_return; + else + break; + // to do 这里还需要修改 + } + } // end for ps + old = index; + } while (1); + } _quick_return: - if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); - return find_ct; + if (!retstr.empty() && retstr.back() == L'|') retstr.pop_back(); + return find_ct; } -long ImageBase::FindLine(double sim, std::wstring &outStr) { - outStr.clear(); - int h = - sqrt(_binary.width * _binary.width + _binary.height * _binary.height) + 2; - _sum.create(360, h); - //行:距离,列:角度 - _sum.fill(0); - for (int i = 0; i < _binary.height; i++) { - for (int j = 0; j < _binary.width; j++) { - if (_binary.at(i, j) == WORD_COLOR) { - for (int t = 0; t < 360; t++) { - int d = - j * cos(t * 0.0174532925) + i * sin(t * 0.0174532925); //可以优化 - assert(d <= h); - if (d >= 0) _sum.at(d, t)++; - } - } - } - } - int maxRow = 0, maxCol = 0; - int maxval = -1; - for (int i = 0; i < _sum.height; i++) { - for (int j = 0; j < _sum.width; j++) { - if (_sum.at(i, j) > maxval) { - maxRow = i; - maxCol = j; - maxval = _sum.at(i, j); - } - } - } - // setlog("degree=%d,dis=%d,val=%d", maxCol, maxRow, maxval); - wchar_t buffer[256]; - wsprintf(buffer, L"%d,%d", maxCol, maxRow); - outStr = buffer; - return maxval; +long ImageBase::FindLine(double sim, std::wstring& outStr) { + outStr.clear(); + int h = + sqrt(_binary.width * _binary.width + _binary.height * _binary.height) + 2; + _sum.create(360, h); + //行:距离,列:角度 + _sum.fill(0); + for (int i = 0; i < _binary.height; i++) { + for (int j = 0; j < _binary.width; j++) { + if (_binary.at(i, j) == WORD_COLOR) { + for (int t = 0; t < 360; t++) { + int d = + j * cos(t * 0.0174532925) + i * sin(t * 0.0174532925); //可以优化 + assert(d <= h); + if (d >= 0) _sum.at(d, t)++; + } + } + } + } + int maxRow = 0, maxCol = 0; + int maxval = -1; + for (int i = 0; i < _sum.height; i++) { + for (int j = 0; j < _sum.width; j++) { + if (_sum.at(i, j) > maxval) { + maxRow = i; + maxCol = j; + maxval = _sum.at(i, j); + } + } + } + // setlog("degree=%d,dis=%d,val=%d", maxCol, maxRow, maxval); + wchar_t buffer[256]; + wsprintf(buffer, L"%d,%d", maxCol, maxRow); + outStr = buffer; + return maxval; } template -long ImageBase::simple_match(long x, long y, Image *timg, color_t dfcolor, - int tnorm, double sim) { - int err_ct = 0; - // quick check - if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) > - (double)tnorm * (1.0 - sim)) - return 0; - int max_error = (1.0 - sim) * timg->size(); - uint *pscreen_top, *pscreen_bottom, *pimg_top, *pimg_bottom; - pscreen_top = _src.ptr(y) + x; - pscreen_bottom = _src.ptr(y + timg->height - 1) + x; - pimg_top = timg->ptr(0); - pimg_bottom = timg->ptr(timg->height - 1); - while (pscreen_top <= pscreen_bottom) { - auto ps1 = pscreen_top, ps2 = pscreen_top + timg->width - 1; - auto ps3 = pscreen_bottom, ps4 = pscreen_bottom + timg->width - 1; - auto pt1 = pimg_top, pt2 = pimg_top + timg->width - 1; - auto pt3 = pimg_bottom, pt4 = pimg_bottom + timg->width - 1; - while (ps1 <= ps2) { - if (nodfcolor) { - if (*ps1++ != *pt1++) ++err_ct; // top left - if (*ps2-- != *pt2--) ++err_ct; // top right - if (*ps3++ != *pt3++) ++err_ct; // bottom left - if (*ps4-- != *pt4--) ++err_ct; // bottom right - } else { - if (!IN_RANGE(*(color_t *)ps1++, *(color_t *)pt1++, dfcolor)) ++err_ct; - if (!IN_RANGE(*(color_t *)ps2--, *(color_t *)pt2--, dfcolor)) ++err_ct; - if (!IN_RANGE(*(color_t *)ps3++, *(color_t *)pt3++, dfcolor)) ++err_ct; - if (!IN_RANGE(*(color_t *)ps4--, *(color_t *)pt4--, dfcolor)) ++err_ct; - } - - if (err_ct > max_error) return 0; - } - pscreen_top += _src.width; - pscreen_bottom -= _src.width; - } - - return 1; +long ImageBase::simple_match(long x, long y, Image* timg, color_t dfcolor, + int tnorm, double sim) { + int err_ct = 0; + // quick check + if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) > + (double)tnorm * (1.0 - sim)) + return 0; + int max_error = (1.0 - sim) * timg->size(); + uint* pscreen_top, * pscreen_bottom, * pimg_top, * pimg_bottom; + pscreen_top = _src.ptr(y) + x; + pscreen_bottom = _src.ptr(y + timg->height - 1) + x; + pimg_top = timg->ptr(0); + pimg_bottom = timg->ptr(timg->height - 1); + while (pscreen_top <= pscreen_bottom) { + auto ps1 = pscreen_top, ps2 = pscreen_top + timg->width - 1; + auto ps3 = pscreen_bottom, ps4 = pscreen_bottom + timg->width - 1; + auto pt1 = pimg_top, pt2 = pimg_top + timg->width - 1; + auto pt3 = pimg_bottom, pt4 = pimg_bottom + timg->width - 1; + while (ps1 <= ps2) { + if (nodfcolor) { + if (*ps1++ != *pt1++) ++err_ct; // top left + if (*ps2-- != *pt2--) ++err_ct; // top right + if (*ps3++ != *pt3++) ++err_ct; // bottom left + if (*ps4-- != *pt4--) ++err_ct; // bottom right + } + else { + if (!IN_RANGE(*(color_t*)ps1++, *(color_t*)pt1++, dfcolor)) ++err_ct; + if (!IN_RANGE(*(color_t*)ps2--, *(color_t*)pt2--, dfcolor)) ++err_ct; + if (!IN_RANGE(*(color_t*)ps3++, *(color_t*)pt3++, dfcolor)) ++err_ct; + if (!IN_RANGE(*(color_t*)ps4--, *(color_t*)pt4--, dfcolor)) ++err_ct; + } + + if (err_ct > max_error) return 0; + } + pscreen_top += _src.width; + pscreen_bottom -= _src.width; + } + + return 1; } template -long ImageBase::trans_match(long x, long y, Image *timg, color_t dfcolor, - vector pts, int max_error) { - int err_ct = 0, k, dx, dy; - int left, right; - left = 0; - right = pts.size() - 1; - while (left <= right) { - auto it = pts[left]; - if (nodfcolor) { - if (_src.at(y + PTY(pts[left]), x + PTX(pts[left])) != - timg->at(PTY(pts[left]), PTX(pts[left]))) - ++err_ct; - if (_src.at(y + PTY(pts[right]), x + PTX(pts[right])) != - timg->at(PTY(pts[right]), PTX(pts[right]))) - ++err_ct; - } else { - color_t cr1, cr2; - cr1 = _src.at(y + PTY(pts[left]), x + PTX(pts[left])); - cr2 = timg->at(PTY(pts[left]), PTX(pts[left])); - if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct; - cr1 = _src.at(y + PTY(pts[right]), x + PTX(pts[right])); - cr2 = timg->at(PTY(pts[right]), PTX(pts[right])); - if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct; - } - - ++left; - --right; - if (err_ct > max_error) return 0; - } - return 1; +long ImageBase::trans_match(long x, long y, Image* timg, color_t dfcolor, + vector pts, int max_error) { + int err_ct = 0, k, dx, dy; + int left, right; + left = 0; + right = pts.size() - 1; + while (left <= right) { + auto it = pts[left]; + if (nodfcolor) { + if (_src.at(y + PTY(pts[left]), x + PTX(pts[left])) != + timg->at(PTY(pts[left]), PTX(pts[left]))) + ++err_ct; + if (_src.at(y + PTY(pts[right]), x + PTX(pts[right])) != + timg->at(PTY(pts[right]), PTX(pts[right]))) + ++err_ct; + } + else { + color_t cr1, cr2; + cr1 = _src.at(y + PTY(pts[left]), x + PTX(pts[left])); + cr2 = timg->at(PTY(pts[left]), PTX(pts[left])); + if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct; + cr1 = _src.at(y + PTY(pts[right]), x + PTX(pts[right])); + cr2 = timg->at(PTY(pts[right]), PTX(pts[right])); + if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct; + } + + ++left; + --right; + if (err_ct > max_error) return 0; + } + return 1; } -long ImageBase::real_match(long x, long y, ImageBin *timg, int tnorm, - double sim) { - // quick check - if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) / - (double)tnorm > - 1.0 - sim) - return 0; - int err = 0; - int maxErr = (1.0 - sim) * tnorm; - for (int i = 0; i < timg->height; i++) { - auto ptr = _gray.ptr(y + i) + x; - auto ptr2 = timg->ptr(i); - for (int j = 0; j < timg->width; j++) { - err += abs(*ptr - *ptr2); - ptr++; - ptr2++; - } - if (err > maxErr) return 0; - } - - return 1; +long ImageBase::real_match(long x, long y, ImageBin* timg, int tnorm, + double sim) { + // quick check + if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) / + (double)tnorm > + 1.0 - sim) + return 0; + int err = 0; + int maxErr = (1.0 - sim) * tnorm; + for (int i = 0; i < timg->height; i++) { + auto ptr = _gray.ptr(y + i) + x; + auto ptr2 = timg->ptr(i); + for (int j = 0; j < timg->width; j++) { + err += abs(*ptr - *ptr2); + ptr++; + ptr2++; + } + if (err > maxErr) return 0; + } + + return 1; } -void ImageBase::record_sum(const ImageBin &gray) { - //为了减少边界判断,这里多多加一行一列 - _sum.create(gray.width + 1, gray.height + 1); - _sum.fill(0); - int m = _sum.height; - int n = _sum.width; - for (int i = 1; i < m; i++) { - for (int j = 1; j < n; j++) { - int s = 0; - - s += _sum.at(i - 1, j); - - s += _sum.at(i, j - 1); - - s -= _sum.at(i - 1, j - 1); - s += (int)gray.at(i - 1, j - 1); - _sum.at(i, j) = s; - } - } +void ImageBase::record_sum(const ImageBin& gray) { + //为了减少边界判断,这里多多加一行一列 + _sum.create(gray.width + 1, gray.height + 1); + _sum.fill(0); + int m = _sum.height; + int n = _sum.width; + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + int s = 0; + + s += _sum.at(i - 1, j); + + s += _sum.at(i, j - 1); + + s -= _sum.at(i - 1, j - 1); + s += (int)gray.at(i - 1, j - 1); + _sum.at(i, j) = s; + } + } } int ImageBase::region_sum(int x1, int y1, int x2, int y2) { - int ans = _sum.at(y2, x2) - _sum.at(y2, x1) - _sum.at(y1, x2) + - _sum.at(y1, x1); - return ans; + int ans = _sum.at(y2, x2) - _sum.at(y2, x1) - _sum.at(y1, x2) + + _sum.at(y1, x1); + return ans; } constexpr int MIN_CUT_W = 5; constexpr int MIN_CUT_H = 2; int ImageBase::get_bk_color(inputbin bin) { - int y[256] = {0}; - auto ptr = bin.pixels.data(); - int n = bin.pixels.size(); - for (int i = 0; i < n; ++i) y[ptr[i]]++; - // scan max - int m = 0; - for (int i = 1; i < 256; ++i) { - if (y[i] > y[m]) m = i; - } - return m; + int y[256] = { 0 }; + auto ptr = bin.pixels.data(); + int n = bin.pixels.size(); + for (int i = 0; i < n; ++i) y[ptr[i]]++; + // scan max + int m = 0; + for (int i = 1; i < 256; ++i) { + if (y[i] > y[m]) m = i; + } + return m; } -void ImageBase::bgr2binary(vector &colors) { - if (_src.empty()) return; - int ncols = _src.width, nrows = _src.height; - _binary.fromImage4(_src); - for (int i = 0; i < nrows; ++i) { - auto psrc = _src.ptr(i); - - auto pbin = _binary.ptr(i); - for (int j = 0; j < ncols; ++j) { - uchar g1 = psrc->toGray(); - *pbin = WORD_BKCOLOR; - for (auto &it : colors) { //对每个颜色描述 - if (abs(g1 - it.color.toGray()) <= it.df.toGray()) { - *pbin = WORD_COLOR; - break; - } - } - ++pbin; - ++psrc; - } - } - //_binary.create(ncols, nrows); - // for (int i = 0; i < nrows; ++i) { - // auto psrc = _src.ptr(i); - // auto pbin = _binary.ptr(i); - // for (int j = 0; j < ncols; ++j) { - // *pbin = WORD_BKCOLOR; - // for (auto& it : colors) {//对每个颜色描述 - // if (IN_RANGE(*psrc, it.color, it.df)) { - // *pbin = WORD_COLOR; - // break; - // } - // } - // ++pbin; ++psrc; - // } - //} - // test - // cv::imwrite("src.png", _src); - // cv::imwrite("binary.png", _binary); +void ImageBase::bgr2binary(vector& colors) { + if (_src.empty()) return; + int ncols = _src.width, nrows = _src.height; + _binary.fromImage4(_src); + for (int i = 0; i < nrows; ++i) { + auto psrc = _src.ptr(i); + + auto pbin = _binary.ptr(i); + for (int j = 0; j < ncols; ++j) { + uchar g1 = psrc->toGray(); + *pbin = WORD_BKCOLOR; + for (auto& it : colors) { //对每个颜色描述 + if (abs(g1 - it.color.toGray()) <= it.df.toGray()) { + *pbin = WORD_COLOR; + break; + } + } + ++pbin; + ++psrc; + } + } + //_binary.create(ncols, nrows); + // for (int i = 0; i < nrows; ++i) { + // auto psrc = _src.ptr(i); + // auto pbin = _binary.ptr(i); + // for (int j = 0; j < ncols; ++j) { + // *pbin = WORD_BKCOLOR; + // for (auto& it : colors) {//对每个颜色描述 + // if (IN_RANGE(*psrc, it.color, it.df)) { + // *pbin = WORD_COLOR; + // break; + // } + // } + // ++pbin; ++psrc; + // } + //} + // test + // cv::imwrite("src.png", _src); + // cv::imwrite("binary.png", _binary); } //二值化 -void ImageBase::bgr2binarybk(const vector &bk_colors) { - //创建二值图 - _binary.create(_src.width, _src.height); - memset(_binary.pixels.data(), WORD_BKCOLOR, _binary.size()); - int n = _binary.size(); - auto pdst = _binary.data(); - if (bk_colors.size() == 0) { // auto - //转为灰度图 - _gray.fromImage4(_src); - - //获取背景颜色 - int bkcolor = get_bk_color(_gray); - - auto pgray = _gray.data(); - for (int i = 0; i < n; ++i) { - pdst[i] = - (std::abs((int)pgray[i] - bkcolor) < 20 ? WORD_BKCOLOR : WORD_COLOR); - } - } else { - for (auto bk : bk_colors) { - for (int i = 0; i < n; ++i) { - auto c = (color_t *)(_src.pdata + i * 4); - if (!IN_RANGE(*c, bk.color, bk.df)) pdst[i] = WORD_COLOR; - } - } - } +void ImageBase::bgr2binarybk(const vector& bk_colors) { + //创建二值图 + _binary.create(_src.width, _src.height); + memset(_binary.pixels.data(), WORD_BKCOLOR, _binary.size()); + int n = _binary.size(); + auto pdst = _binary.data(); + if (bk_colors.size() == 0) { // auto + //转为灰度图 + _gray.fromImage4(_src); + + //获取背景颜色 + int bkcolor = get_bk_color(_gray); + + auto pgray = _gray.data(); + for (int i = 0; i < n; ++i) { + pdst[i] = + (std::abs((int)pgray[i] - bkcolor) < 20 ? WORD_BKCOLOR : WORD_COLOR); + } + } + else { + for (auto bk : bk_colors) { + for (int i = 0; i < n; ++i) { + auto c = (color_t*)(_src.pdata + i * 4); + if (!IN_RANGE(*c, bk.color, bk.df)) pdst[i] = WORD_COLOR; + } + } + } } //垂直方向投影到x轴 -void ImageBase::binshadowx(const rect_t &rc, std::vector &out_put) { - // qDebug("in x rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2); - out_put.clear(); - // ys.clear(); - // Mat paintx(binary.size(), CV_8UC1, cv::Scalar(255)); - // //创建一个全白图片,用作显示 - - // int* blackcout = new int[binary.cols]; - std::vector vx; - vx.resize(_binary.width); - memset(&vx[0], 0, _binary.width * 4); - for (int j = rc.x1; j < rc.x2; j++) { - for (int i = rc.y1; i < rc.y2; i++) { - if (_binary.at(i, j) == WORD_COLOR) { - vx[j]++; //垂直投影按列在x轴进行投影 - } - } - } - - int startindex = 0; - int endindex = 0; - bool inblock = false; //是否遍历到字符位置 - rect_t roi; - for (int j = rc.x1; j < rc.x2; j++) { - if (!inblock && vx[j] != 0) //进入有字符区域 - { - inblock = true; - startindex = j; - // std::cout << "startindex:" << startindex << std::endl; - } - // if (inblock&&vx[j] == 0) //进入空白区 - else if (inblock && vx[j] == 0 && - j - startindex >= MIN_CUT_W) //进入空白区域,且宽度不小于1 - { - endindex = j; - inblock = false; - // Mat roi = binary.colRange(startindex, endindex + 1); - - roi.x1 = startindex; - roi.y1 = rc.y1; - roi.x2 = endindex; - roi.y2 = rc.y2; - // qDebug("out xrc:%d,%d,%d,%d", roi.x1, roi.y1, roi.x2, roi.y2); - out_put.push_back(roi); - } - } - // special case - if (inblock) { - roi.x1 = startindex; - roi.y1 = rc.y1; - roi.x2 = rc.x2; - roi.y2 = rc.y2; - out_put.push_back(roi); - } +void ImageBase::binshadowx(const rect_t& rc, std::vector& out_put) { + // qDebug("in x rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2); + out_put.clear(); + // ys.clear(); + // Mat paintx(binary.size(), CV_8UC1, cv::Scalar(255)); + // //创建一个全白图片,用作显示 + + // int* blackcout = new int[binary.cols]; + std::vector vx; + vx.resize(_binary.width); + memset(&vx[0], 0, _binary.width * 4); + for (int j = rc.x1; j < rc.x2; j++) { + for (int i = rc.y1; i < rc.y2; i++) { + if (_binary.at(i, j) == WORD_COLOR) { + vx[j]++; //垂直投影按列在x轴进行投影 + } + } + } + + int startindex = 0; + int endindex = 0; + bool inblock = false; //是否遍历到字符位置 + rect_t roi; + for (int j = rc.x1; j < rc.x2; j++) { + if (!inblock && vx[j] != 0) //进入有字符区域 + { + inblock = true; + startindex = j; + // std::cout << "startindex:" << startindex << std::endl; + } + // if (inblock&&vx[j] == 0) //进入空白区 + else if (inblock && vx[j] == 0 && + j - startindex >= MIN_CUT_W) //进入空白区域,且宽度不小于1 + { + endindex = j; + inblock = false; + // Mat roi = binary.colRange(startindex, endindex + 1); + + roi.x1 = startindex; + roi.y1 = rc.y1; + roi.x2 = endindex; + roi.y2 = rc.y2; + // qDebug("out xrc:%d,%d,%d,%d", roi.x1, roi.y1, roi.x2, roi.y2); + out_put.push_back(roi); + } + } + // special case + if (inblock) { + roi.x1 = startindex; + roi.y1 = rc.y1; + roi.x2 = rc.x2; + roi.y2 = rc.y2; + out_put.push_back(roi); + } } //投影到y轴 -void ImageBase::binshadowy(const rect_t &rc, std::vector &out_put) { - // qDebug("in y rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2); - out_put.clear(); - //是否为白色或者黑色根据二值图像的处理得来 - // Mat painty(binary.size(), CV_8UC1, cv::Scalar(255)); //初始化为全白 - //水平投影 - // int* pointcount = new int[binary.rows]; //在二值图片中记录行中特征点的个数 - std::vector vy; - vy.resize(_binary.height); - memset(&vy[0], 0, _binary.height * 4); //注意这里需要进行初始化 - - for (int i = rc.y1; i < rc.y2; i++) { - for (int j = rc.x1; j < rc.x2; j++) { - if (_binary.at(i, j) == WORD_COLOR) { - vy[i]++; //记录每行中黑色点的个数 //水平投影按行在y轴上的投影 - } - } - } - - // std::vector result; - int startindex = 0; - int endindex = 0; - bool inblock = false; //是否遍历到字符位置 - rect_t roi; - for (int i = rc.y1; i < rc.y2; i++) { - if (!inblock && vy[i] != 0) //进入有字符区域 - { - inblock = true; - startindex = i; - // std::cout << "startindex:" << startindex << std::endl; - } - // if (inblock&&vy[i] == 0) //进入空白区 - if (inblock && vy[i] == 0 && - i - startindex >= MIN_CUT_H) //进入空白区,且高度不小于1 - { - endindex = i; - inblock = false; - - roi.x1 = rc.x1; - roi.y1 = startindex; - roi.x2 = rc.x2; - roi.y2 = endindex; - out_put.push_back(roi); - } - } - - if (inblock) { - roi.x1 = rc.x1; - roi.y1 = startindex; - roi.x2 = rc.x2; - roi.y2 = rc.y2; - out_put.push_back(roi); - } +void ImageBase::binshadowy(const rect_t& rc, std::vector& out_put) { + // qDebug("in y rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2); + out_put.clear(); + //是否为白色或者黑色根据二值图像的处理得来 + // Mat painty(binary.size(), CV_8UC1, cv::Scalar(255)); //初始化为全白 + //水平投影 + // int* pointcount = new int[binary.rows]; //在二值图片中记录行中特征点的个数 + std::vector vy; + vy.resize(_binary.height); + memset(&vy[0], 0, _binary.height * 4); //注意这里需要进行初始化 + + for (int i = rc.y1; i < rc.y2; i++) { + for (int j = rc.x1; j < rc.x2; j++) { + if (_binary.at(i, j) == WORD_COLOR) { + vy[i]++; //记录每行中黑色点的个数 //水平投影按行在y轴上的投影 + } + } + } + + // std::vector result; + int startindex = 0; + int endindex = 0; + bool inblock = false; //是否遍历到字符位置 + rect_t roi; + for (int i = rc.y1; i < rc.y2; i++) { + if (!inblock && vy[i] != 0) //进入有字符区域 + { + inblock = true; + startindex = i; + // std::cout << "startindex:" << startindex << std::endl; + } + // if (inblock&&vy[i] == 0) //进入空白区 + if (inblock && vy[i] == 0 && + i - startindex >= MIN_CUT_H) //进入空白区,且高度不小于1 + { + endindex = i; + inblock = false; + + roi.x1 = rc.x1; + roi.y1 = startindex; + roi.x2 = rc.x2; + roi.y2 = endindex; + out_put.push_back(roi); + } + } + + if (inblock) { + roi.x1 = rc.x1; + roi.y1 = startindex; + roi.x2 = rc.x2; + roi.y2 = rc.y2; + out_put.push_back(roi); + } } -void ImageBase::bin_image_cut(int min_word_h, const rect_t &inrc, - rect_t &outrc) { - //水平裁剪,缩小高度 - std::vector v; - outrc = inrc; - int i, j; - v.resize(_binary.height); - for (auto &it : v) it = 0; - for (i = inrc.y1; i < inrc.y2; ++i) { - for (j = inrc.x1; j < inrc.x2; ++j) - v[i] += (_binary.at(i, j) == WORD_COLOR ? 1 : 0); - } - i = inrc.y1; - while (v[i] == 0) i++; - outrc.y1 = i; - i = inrc.y2 - 1; - while (v[i] == 0) i--; - if (i + 1 - outrc.y1 > min_word_h) outrc.y2 = i + 1; - //垂直裁剪.缩小宽度 - v.resize(_binary.width); - for (auto &it : v) it = 0; - - for (i = inrc.y1; i < inrc.y2; ++i) { - for (j = inrc.x1; j < inrc.x2; ++j) - v[j] += _binary.at(i, j) == WORD_COLOR ? 1 : 0; - } - i = inrc.x1; - while (v[i] == 0) i++; - outrc.x1 = i; - i = inrc.x2 - 1; - while (v[i] == 0) i--; - outrc.x2 = i + 1; +void ImageBase::bin_image_cut(int min_word_h, const rect_t& inrc, + rect_t& outrc) { + //水平裁剪,缩小高度 + std::vector v; + outrc = inrc; + int i, j; + v.resize(_binary.height); + for (auto& it : v) it = 0; + for (i = inrc.y1; i < inrc.y2; ++i) { + for (j = inrc.x1; j < inrc.x2; ++j) + v[i] += (_binary.at(i, j) == WORD_COLOR ? 1 : 0); + } + i = inrc.y1; + while (v[i] == 0) i++; + outrc.y1 = i; + i = inrc.y2 - 1; + while (v[i] == 0) i--; + if (i + 1 - outrc.y1 > min_word_h) outrc.y2 = i + 1; + //垂直裁剪.缩小宽度 + v.resize(_binary.width); + for (auto& it : v) it = 0; + + for (i = inrc.y1; i < inrc.y2; ++i) { + for (j = inrc.x1; j < inrc.x2; ++j) + v[j] += _binary.at(i, j) == WORD_COLOR ? 1 : 0; + } + i = inrc.x1; + while (v[i] == 0) i++; + outrc.x1 = i; + i = inrc.x2 - 1; + while (v[i] == 0) i--; + outrc.x2 = i + 1; } -void ImageBase::get_rois(int min_word_h, std::vector &vroi) { - vroi.clear(); - std::vector vrcx, vrcy; - rect_t rc; - rc.x1 = rc.y1 = 0; - rc.x2 = _binary.width; - rc.y2 = _binary.height; - binshadowy(rc, vrcy); - for (int i = 0; i < vrcy.size(); ++i) { - binshadowx(vrcy[i], vrcx); - for (int j = 0; j < vrcx.size(); j++) { - if (vrcx[j].width() >= min_word_h) - bin_image_cut(min_word_h, vrcx[j], vrcx[j]); - vroi.push_back(vrcx[j]); - } - } +void ImageBase::get_rois(int min_word_h, std::vector& vroi) { + vroi.clear(); + std::vector vrcx, vrcy; + rect_t rc; + rc.x1 = rc.y1 = 0; + rc.x2 = _binary.width; + rc.y2 = _binary.height; + binshadowy(rc, vrcy); + for (int i = 0; i < vrcy.size(); ++i) { + binshadowx(vrcy[i], vrcx); + for (int j = 0; j < vrcx.size(); j++) { + if (vrcx[j].width() >= min_word_h) + bin_image_cut(min_word_h, vrcx[j], vrcx[j]); + vroi.push_back(vrcx[j]); + } + } } -inline int full_match(const ImageBin &binary, rect_t &rc, const uint8_t *data) { - //匹配 - int idx = 0; - for (int x = rc.x1; x < rc.x2; ++x) { - for (int y = rc.y1; y < rc.y2; ++y) { - int val = GET_BIT(data[idx / 8], idx & 7); - if (binary.at(y, x) != val) return 0; - idx++; - } - } - return 1; +inline int full_match(const ImageBin& binary, rect_t& rc, const uint8_t* data) { + //匹配 + int idx = 0; + for (int x = rc.x1; x < rc.x2; ++x) { + for (int y = rc.y1; y < rc.y2; ++y) { + int val = GET_BIT(data[idx / 8], idx & 7); + if (binary.at(y, x) != val) return 0; + idx++; + } + } + return 1; } -inline int part_match(const ImageBin &binary, rect_t &rc, int max_error, - const uint8_t *data) { - //匹配 - //匹配 - int err_ct = 0; - int idx = 0; - for (int x = rc.x1; x < rc.x2; ++x) { - for (int y = rc.y1; y < rc.y2; ++y) { - int val = GET_BIT(data[idx / 8], idx & 7); - if (binary.at(y, x) != val) { - ++err_ct; - if (err_ct > max_error) return 0; - } - idx++; - } - } - return 1; +inline int part_match(const ImageBin& binary, rect_t& rc, int max_error, + const uint8_t* data) { + //匹配 + int err_ct = 0; + int idx = 0; + for (int x = rc.x1; x < rc.x2; ++x) { + for (int y = rc.y1; y < rc.y2; ++y) { + int val = GET_BIT(data[idx / 8], idx & 7); + if (binary.at(y, x) != val) { + ++err_ct; + if (err_ct > max_error) return err_ct; + } + idx++; + } + } + return err_ct; } -inline void fill_rect(ImageBin &record, const rect_t &rc) { - int w = rc.width(); - for (int y = rc.y1; y < rc.y2; ++y) { - uchar *p = record.ptr(y) + rc.x1; - memset(p, 1, sizeof(uchar) * w); - } +inline void fill_rect(ImageBin& record, const rect_t& rc) { + int w = rc.width(); + for (int y = rc.y1; y < rc.y2; ++y) { + uchar* p = record.ptr(y) + rc.x1; + memset(p, 1, sizeof(uchar) * w); + } } int binarySearch(const word1_t a[], int bidx, int eidx, int target) //循环实现 { - int low = bidx, high = eidx, middle; - while (low < high) { - middle = (low + high) / 2; - if (target == a[middle].info.bit_cnt) - return middle; - else if (target > a[middle].info.bit_cnt) - low = middle + 1; - else if (target < a[middle].info.bit_cnt) - high = middle; - } - return -1; + int low = bidx, high = eidx, middle; + while (low < high) { + middle = (low + high) / 2; + if (target == a[middle].info.bit_cnt) + return middle; + else if (target > a[middle].info.bit_cnt) + low = middle + 1; + else if (target < a[middle].info.bit_cnt) + high = middle; + } + return -1; }; //完全匹配 待识别文字不能含有任何噪声 @@ -1233,219 +1239,230 @@ f(p,size) 为从点p开始,大小为size的矩形块像素之和,这个函数 字库:D 字的像素范围:m-M 字的大小范围:size_min-size_max 识别图像:src for each point in src: - if f(p,size_max)M //像素太多 - continue; - //像素合适 - for each w in D - if f(p,w_size)==w_cnt - ok=match(...) //作最后的匹配 - if ok - add w to result; - delete rect(p,w_size); - else - continue;//to next w - endif - end - end + if f(p,size_max)M //像素太多 + continue; + //像素合适 + for each w in D + if f(p,w_size)==w_cnt + ok=match(...) //作最后的匹配 + if ok + add w to result; + delete rect(p,w_size); + else + continue;//to next w + endif + end + end end */ -void ImageBase::_bin_ocr(const Dict &dict, - std::map &ps) { - int px, py; - if (_binary.empty()) return; - // - record_sum(_binary); - // find cnt range - // find width and height range; - int cnt_min = 255 * 255, cnt_max = 0; - int w_min = 255, h_min = 255; - int w_max = 0, h_max = 0; - for (auto &it : dict.words) { - cnt_min = min(cnt_min, it.info.bit_cnt); - cnt_max = max(cnt_max, it.info.bit_cnt); - w_min = min(w_min, it.info.w); - h_min = min(h_min, it.info.h); - w_max = max(w_max, it.info.w); - h_max = max(h_max, it.info.h); - } - - //将所有字库按照大小分成几类,对于每个大小根据像素密度查找对应的符合字库 - auto makeinfo = [](int begin, int end, int szh, int szw) { - return std::make_pair(begin << 16 | end, szh << 8 | szw); - }; - vector> dict_sz; - auto &vword = dict.words; - // 32 begin(8) - dict_sz.push_back(makeinfo(0, 0, dict.words[0].info.h, dict.words[0].info.w)); - for (int i = 1; i < vword.size(); i++) { - int sz = vword[i].info.h << 8 | vword[i].info.w; - if (dict_sz.back().second != sz) { - dict_sz.back().first |= i; // fix old end - dict_sz.push_back(std::make_pair(i << 16, sz)); // add new begin - } - } - dict_sz.back().first |= vword.size(); - - //遍历行 - for (py = 0; py < _binary.height - h_min + 1; ++py) { - //遍历列 - for (px = 0; px < _binary.width - w_min + 1; ++px) { - if (_record.at(py, px)) continue; - //检测像素密度 - if (region_sum(px, py, min(px + w_max, _binary.width), - min(py + h_max, _binary.height)) < cnt_min) // too less - continue; - if (region_sum(px, py, px + w_min, py + h_min) > cnt_max) // too much - continue; - point_t pt; - pt.x = px; - pt.y = py; - int k = 0; - for (int k = 0; k < dict_sz.size(); k++) { - int h = dict_sz[k].second >> 8, w = dict_sz[k].second & 0xff; - rect_t crc; - crc.x1 = px; - crc.y1 = py; - crc.x2 = px + w; - crc.y2 = py + h; - //边界检查 - if (crc.y2 > _binary.height || crc.x2 > _binary.width) continue; - - int fidx = dict_sz[k].first >> 16, eidx = dict_sz[k].first & 0xffff; - - // quick check - int cnt_src = region_sum(crc.x1, crc.y1, crc.x2, crc.y2); - if (cnt_src < vword[fidx].info.bit_cnt || - cnt_src > vword[eidx - 1].info.bit_cnt) - continue; - int tidx = binarySearch(&vword[0], fidx, eidx, cnt_src); - if (tidx == -1) continue; - int tleft = tidx, tright = tidx; - while (tleft > 0 && vword[tleft - 1].info.bit_cnt == cnt_src) --tleft; - while (tright < eidx && vword[tright].info.bit_cnt == cnt_src) ++tright; - int matched = 0; - // match - tidx = tleft; - while (tidx < tright) { - auto &it = vword[tidx++]; - matched = full_match(_binary, crc, it.data.data()); - if (matched) { - // final check - // check right col is empty/background - int rs = 0; - if (crc.x2 < _binary.width) { - for (int k = crc.y1; k < crc.y2; k++) rs += _binary.at(k, crc.x2); - } - if (rs < it.info.h / 2) { - ps[pt] = it.info.name; - fill_rect(_record, crc); - // break;//words - break; - } else { - // not matched - matched = 0; - } - } - } - if (matched) break; - } // end for words - } // end for j - } // end for i +void ImageBase::_bin_ocr(const Dict& dict, + std::map& ps) { + int px, py; + if (_binary.empty()) return; + // + record_sum(_binary); + // find cnt range + // find width and height range; + int cnt_min = 255 * 255, cnt_max = 0; + int w_min = 255, h_min = 255; + int w_max = 0, h_max = 0; + for (auto& it : dict.words) { + cnt_min = min(cnt_min, it.info.bit_cnt); + cnt_max = max(cnt_max, it.info.bit_cnt); + w_min = min(w_min, it.info.w); + h_min = min(h_min, it.info.h); + w_max = max(w_max, it.info.w); + h_max = max(h_max, it.info.h); + } + + //将所有字库按照大小分成几类,对于每个大小根据像素密度查找对应的符合字库 + auto makeinfo = [](int begin, int end, int szh, int szw) { + return std::make_pair(begin << 16 | end, szh << 8 | szw); + }; + vector> dict_sz; + auto& vword = dict.words; + // 32 begin(8) + dict_sz.push_back(makeinfo(0, 0, dict.words[0].info.h, dict.words[0].info.w)); + for (int i = 1; i < vword.size(); i++) { + int sz = vword[i].info.h << 8 | vword[i].info.w; + if (dict_sz.back().second != sz) { + dict_sz.back().first |= i; // fix old end + dict_sz.push_back(std::make_pair(i << 16, sz)); // add new begin + } + } + dict_sz.back().first |= vword.size(); + + //遍历行 + for (py = 0; py < _binary.height - h_min + 1; ++py) { + //遍历列 + for (px = 0; px < _binary.width - w_min + 1; ++px) { + if (_record.at(py, px)) continue; + //检测像素密度 + if (region_sum(px, py, min(px + w_max, _binary.width), + min(py + h_max, _binary.height)) < cnt_min) // too less + continue; + if (region_sum(px, py, px + w_min, py + h_min) > cnt_max) // too much + continue; + point_t pt; + pt.x = px; + pt.y = py; + int k = 0; + for (int k = 0; k < dict_sz.size(); k++) { + int h = dict_sz[k].second >> 8, w = dict_sz[k].second & 0xff; + rect_t crc; + crc.x1 = px; + crc.y1 = py; + crc.x2 = px + w; + crc.y2 = py + h; + //边界检查 + if (crc.y2 > _binary.height || crc.x2 > _binary.width) continue; + + int fidx = dict_sz[k].first >> 16, eidx = dict_sz[k].first & 0xffff; + + // quick check + int cnt_src = region_sum(crc.x1, crc.y1, crc.x2, crc.y2); + if (cnt_src < vword[fidx].info.bit_cnt || + cnt_src > vword[eidx - 1].info.bit_cnt) + continue; + int tidx = binarySearch(&vword[0], fidx, eidx, cnt_src); + if (tidx == -1) continue; + int tleft = tidx, tright = tidx; + while (tleft > 0 && vword[tleft - 1].info.bit_cnt == cnt_src) --tleft; + while (tright < eidx && vword[tright].info.bit_cnt == cnt_src) ++tright; + int matched = 0; + // match + tidx = tleft; + while (tidx < tright) { + auto& it = vword[tidx++]; + matched = full_match(_binary, crc, it.data.data()); + if (matched) { + // final check + // check right col is empty/background + int rs = 0; + if (crc.x2 < _binary.width) { + for (int k = crc.y1; k < crc.y2; k++) rs += _binary.at(k, crc.x2); + } + if (rs < it.info.h / 2) { + ocr_rec_t ocr_res; + ocr_res.left_top = pt; + ocr_res.right_bottom = point_t(crc.x2, crc.y2); + ocr_res.text = it.info.name; + ocr_res.confidence = 1.0; + ps[pt] = ocr_res; + fill_rect(_record, crc); + // break;//words + break; + } + else { + // not matched + matched = 0; + } + } + } + if (matched) break; + } // end for words + } // end for j + } // end for i } //模糊匹配 待识别区域可以含有噪声 -void ImageBase::_bin_ocr(const Dict &dict, double sim, - std::map &ps) { - int px, py, y; - if (_binary.empty()) return; - // - record_sum(_binary); - // find cnt range - // find width and height range; - int cnt_min = 255 * 255, cnt_max = 0; - int w_min = 255, h_min = 255; - int w_max = 0, h_max = 0; - for (auto &it : dict.words) { - cnt_min = min(cnt_min, it.info.bit_cnt); - cnt_max = max(cnt_max, it.info.bit_cnt); - w_min = min(w_min, it.info.w); - h_min = min(h_min, it.info.h); - w_max = max(w_max, it.info.w); - h_max = max(h_max, it.info.h); - } - int matched = 0; - //遍历行 - for (py = 0; py < _binary.height - h_min + 1; ++py) { - //遍历列 - for (px = 0; px < _binary.width - w_min + 1; ++px) { - if (_record.at(py, px)) continue; - //检测像素密度 - if (region_sum(px, py, min(px + w_max, _binary.width), - min(py + h_max, _binary.height)) < - cnt_min * sim) // too less - continue; - if (region_sum(px, py, px + w_min, py + h_min) > cnt_max * (2 - sim)) - continue; - point_t pt; - pt.x = px; - pt.y = py; - //遍历字库 - // assert(i != 4 || j != 3); - int k = 0; - for (auto &it : dict.words) { - rect_t crc; - crc.x1 = px; - crc.y1 = py; - crc.x2 = px + it.info.w; - crc.y2 = py + it.info.h; - //边界检查 - if (crc.y2 > _binary.height || crc.x2 > _binary.width) continue; - // quick check - - int error = (1 - sim) * it.info.w * it.info.h; - if (abs(region_sum(crc.x1, crc.y1, crc.x2, crc.y2) - it.info.bit_cnt) > - error) - continue; - // match - matched = part_match(_binary, crc, error, it.data.data()); - - if (matched) { - // final check - // check right col is empty/background - int rs = 0; - if (crc.x2 < _binary.width) { - for (int k = crc.y1; k < crc.y2; k++) rs += _binary.at(k, crc.x2); - } - if (rs <= it.info.h / 2) { - ps[pt] = it.info.name; - fill_rect(_record, crc); - // break;//words - break; - } else { - // not matched - matched = 0; - } - } - } // end for words - // if (matched)break; - } // end for j - } // end for i +void ImageBase::_bin_ocr(const Dict& dict, double sim, + std::map& ps) { + int px, py, y; + if (_binary.empty()) return; + // + record_sum(_binary); + // find cnt range + // find width and height range; + int cnt_min = 255 * 255, cnt_max = 0; + int w_min = 255, h_min = 255; + int w_max = 0, h_max = 0; + for (auto& it : dict.words) { + cnt_min = min(cnt_min, it.info.bit_cnt); + cnt_max = max(cnt_max, it.info.bit_cnt); + w_min = min(w_min, it.info.w); + h_min = min(h_min, it.info.h); + w_max = max(w_max, it.info.w); + h_max = max(h_max, it.info.h); + } + //int matched = 0; + //遍历行 + for (py = 0; py < _binary.height - h_min + 1; ++py) { + //遍历列 + for (px = 0; px < _binary.width - w_min + 1; ++px) { + if (_record.at(py, px)) continue; + //检测像素密度 + if (region_sum(px, py, min(px + w_max, _binary.width), + min(py + h_max, _binary.height)) < + cnt_min * sim) // too less + continue; + if (region_sum(px, py, px + w_min, py + h_min) > cnt_max * (2 - sim)) + continue; + point_t pt; + pt.x = px; + pt.y = py; + //遍历字库 + // assert(i != 4 || j != 3); + int k = 0; + for (auto& it : dict.words) { + rect_t crc; + crc.x1 = px; + crc.y1 = py; + crc.x2 = px + it.info.w; + crc.y2 = py + it.info.h; + //边界检查 + if (crc.y2 > _binary.height || crc.x2 > _binary.width) continue; + // quick check + // error tolerance + int error_tolerance = (1 - sim) * it.info.w * it.info.h; + if (abs(region_sum(crc.x1, crc.y1, crc.x2, crc.y2) - it.info.bit_cnt) > + error_tolerance) + continue; + // match + int match_error = part_match(_binary, crc, error_tolerance, it.data.data()); + if (match_error <= error_tolerance) { + // final check + // check right col is empty/background + int rs = 0; + if (crc.x2 < _binary.width) { + for (int k = crc.y1; k < crc.y2; k++) rs += _binary.at(k, crc.x2); + } + if (rs <= it.info.h / 2) { + ocr_rec_t ocr_res; + ocr_res.left_top = pt; + ocr_res.right_bottom = point_t(crc.x2, crc.y2); + ocr_res.text = it.info.name; + ocr_res.confidence = (crc.area() - match_error) / (double)crc.area(); + ps[pt] = ocr_res; + fill_rect(_record, crc); + // break;//words + break; + } + else { + // not matched + } + } + } // end for words + // if (matched)break; + } // end for j + } // end for i } -void ImageBase::bin_ocr(const Dict &dict, double sim, - std::map &ps) { - ps.clear(); - if (dict.words.empty()) return; - if (_binary.empty()) return; - _record.create(_binary.width, _binary.height); - memset(_record.data(), 0, sizeof(uchar) * _record.width * _record.height); - - if (sim > 1.0 - 1e-5) { - _bin_ocr(dict, ps); - } else { - sim = 0.5 + sim / 2; - - _bin_ocr(dict, sim, ps); - } +void ImageBase::bin_ocr(const Dict& dict, double sim, + std::map& ps) { + ps.clear(); + if (dict.words.empty()) return; + if (_binary.empty()) return; + _record.create(_binary.width, _binary.height); + memset(_record.data(), 0, sizeof(uchar) * _record.width * _record.height); + + if (sim > 1.0 - 1e-5) { + _bin_ocr(dict, ps); + } + else { + sim = 0.5 + sim / 2; + + _bin_ocr(dict, sim, ps); + } } diff --git a/libop/imageProc/ImageLoc.h b/libop/imageProc/ImageLoc.h index 477467b..18ff00c 100644 --- a/libop/imageProc/ImageLoc.h +++ b/libop/imageProc/ImageLoc.h @@ -112,9 +112,9 @@ class ImageBase long OcrEx(Dict& dict, double sim, std::wstring& out_str); - long FindStr(Dict& dict, const vector& vstr, double sim, long& retx, long& rety); + long FindStr(std::map& ps, const vector& vstr, double sim, long& retx, long& rety); - long FindStrEx(Dict& dict, const vector& vstr, double sim, std::wstring& out_str); + long FindStrEx(std::map& ps, const vector& vstr, double sim, std::wstring& out_str); //描述:查找目标图像中的直线 //输入:精度 //输出:outStr:直线描述[法线角度,直线到原点的距离];ret:该直线上的点的数量 @@ -147,12 +147,10 @@ class ImageBase //ocr 完全匹配模式 - void _bin_ocr(const Dict& dict, std::map&ps); + void _bin_ocr(const Dict& dict, std::map&ps); //ocr 模糊匹配模式 - void _bin_ocr(const Dict& dict,double sim, std::map&ps); - //ocr wrapper - //template - //void bin_ocr(const Image& binary, Image& record, const Dict& dict, int* max_error, std::wstring& outstr); + void _bin_ocr(const Dict& dict,double sim, std::map&ps); + public: /* if(abs(cr-src)<=df) pixel=1; @@ -166,7 +164,7 @@ class ImageBase void get_rois(int min_word_h, std::vector& vroi); //ocr识别,返回识别到的字及对应坐标 - void bin_ocr(const Dict& dict, double sim, std::map&ps); + void bin_ocr(const Dict& dict, double sim, std::map&ps); public: diff --git a/libop/imageProc/ImageProc.cpp b/libop/imageProc/ImageProc.cpp index 050162f..e9b38f9 100644 --- a/libop/imageProc/ImageProc.cpp +++ b/libop/imageProc/ImageProc.cpp @@ -235,10 +235,10 @@ long ImageProc::OCR(const wstring& color, double sim, std::wstring& out_str) sim = 1.; long s = 0; if (_dicts[_curr_idx].size() == 0) { - vector res; + vocr_rec_t res; m_tess_ocr.ocr(_gray.data(), _gray.width, _gray.height, 1, res); for (auto& it : res) { - if (it.confidenc >= sim - 1e-9) { + if (it.confidence >= sim - 1e-9) { out_str += it.text; } } @@ -415,11 +415,11 @@ long ImageProc::OcrEx(const wstring& color, double sim, std::wstring& retstr) if (sim < 0. || sim > 1.) sim = 1.; if (_dicts[_curr_idx].size() == 0) { - vector res; + vocr_rec_t res; int find_ct = 0; m_tess_ocr.ocr(_gray.data(), _gray.width, _gray.height, 1, res); for (auto& it : res) { - if (it.confidenc >= sim - 1e-9) { + if (it.confidence >= sim - 1e-9) { retstr += std::to_wstring(it.left_top.x + _x1 + _dx); retstr += L","; retstr += std::to_wstring(it.left_top.y + _y1 + _dy); @@ -456,7 +456,20 @@ long ImageProc::FindStr(const wstring& str, const wstring& color, double sim, lo } if (sim < 0. || sim > 1.) sim = 1.; - return ImageBase::FindStr(_dicts[_curr_idx], vstr, sim, retx, rety); + std::map ocr_res; + if (_dicts[_curr_idx].size() == 0) { + vocr_rec_t res; + m_tess_ocr.ocr(_gray.data(), _gray.width, _gray.height, 1, res); + for(auto& it:res){ + if (it.confidence >= sim - 1e-9) { + ocr_res[it.left_top]=it; + } + } + } + else { + ImageBase::bin_ocr(_dicts[_curr_idx], sim, ocr_res); + } + return ImageBase::FindStr(ocr_res, vstr, sim, retx, rety); } long ImageProc::FindStrEx(const wstring& str, const wstring& color, double sim, std::wstring& out_str) @@ -475,7 +488,20 @@ long ImageProc::FindStrEx(const wstring& str, const wstring& color, double sim, } if (sim < 0. || sim > 1.) sim = 1.; - return ImageBase::FindStrEx(_dicts[_curr_idx], vstr, sim, out_str); + std::map ocr_res; + if (_dicts[_curr_idx].size() == 0) { + vocr_rec_t res; + m_tess_ocr.ocr(_gray.data(), _gray.width, _gray.height, 1, res); + for (auto& it : res) { + if (it.confidence >= sim - 1e-9) { + ocr_res[it.left_top] = it; + } + } + } + else { + ImageBase::bin_ocr(_dicts[_curr_idx], sim, ocr_res); + } + return ImageBase::FindStrEx(ocr_res, vstr, sim, out_str); } long ImageProc::OcrAuto(double sim, std::wstring& retstr)