mirror of
https://github.com/RapidAI/RapidOCR.git
synced 2026-11-10 21:10:55 +08:00
添加cpp项目和jvm项目
This commit is contained in:
1 parent
21a3bd6f3e
commit
a4f91467ba
55 files changed
+9096
-4
No files matched your search
+2
-2
@@ -132,6 +132,6 @@ dmypy.json
|
||||
|
||||
#idea
|
||||
.idea
|
||||
/test_imgs
|
||||
/images
|
||||
/models
|
||||
|
||||
/release
|
||||
@@ -0,0 +1,142 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Python template
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
#idea
|
||||
.idea
|
||||
cmake-build-debug/
|
||||
build/
|
||||
build-lib/
|
||||
/models/
|
||||
onnxruntime-shared/
|
||||
onnxruntime-static/
|
||||
opencv-static/
|
||||
opencv-shared/
|
||||
@@ -0,0 +1,123 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(BaiPiaoOcrOnnx)
|
||||
|
||||
option(BAIPIAO_LIB "OcrLite Jni Support" OFF)
|
||||
option(BAIPIAO_STATIC "Use Static Librarys For Build" ON)
|
||||
option(BAIPIAO_OPENMP "OcrLite Enable OpenMP" ON)
|
||||
option(BAIPIAO_BENCHMARK "build benchmark" ON)
|
||||
#set(BAIPIAO_LIB ON)
|
||||
#set(BAIPIAO_STATIC OFF)
|
||||
#set(BAIPIAO_OPENMP OFF)
|
||||
set(BAIPIAO_BENCHMARK ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions("-Wall -g -O0")
|
||||
else ()
|
||||
add_definitions("-Wall")
|
||||
endif ()
|
||||
|
||||
# OpenMP flags for MACOS
|
||||
if (APPLE)
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set(OpenMP_C "${CMAKE_C_COMPILER}")
|
||||
set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp -I/usr/local/opt/libomp/include")
|
||||
set(OpenMP_C_LIB_NAMES "omp")
|
||||
set(OpenMP_omp_LIBRARY ${OpenMP_C_LIB_NAMES})
|
||||
endif ()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(OpenMP_CXX "${CMAKE_CXX_COMPILER}")
|
||||
set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I/usr/local/opt/libomp/include")
|
||||
set(OpenMP_CXX_LIB_NAMES "omp")
|
||||
set(OpenMP_omp_LIBRARY ${OpenMP_C_LIB_NAMES})
|
||||
endif ()
|
||||
link_directories("/usr/local/opt/libomp/lib")
|
||||
endif ()
|
||||
|
||||
# OpenMP
|
||||
find_package(OpenMP REQUIRED)
|
||||
if (OPENMP_FOUND)
|
||||
message("OPENMP FOUND")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
|
||||
else ()
|
||||
message(FATAL_ERROR "OpenMP Not Found!")
|
||||
endif ()
|
||||
|
||||
# OnnxRuntime
|
||||
if (BAIPIAO_STATIC)
|
||||
set(OnnxRuntime_DIR "${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-static")
|
||||
else ()
|
||||
set(OnnxRuntime_DIR "${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-shared")
|
||||
endif ()
|
||||
find_package(OnnxRuntime REQUIRED)
|
||||
if (OnnxRuntime_FOUND)
|
||||
message(STATUS "OnnxRuntime_LIBS: ${OnnxRuntime_LIBS}")
|
||||
message(STATUS "OnnxRuntime_INCLUDE_DIRS: ${OnnxRuntime_INCLUDE_DIRS}")
|
||||
else ()
|
||||
message(FATAL_ERROR "onnxruntime Not Found!")
|
||||
endif (OnnxRuntime_FOUND)
|
||||
|
||||
# OpenCV
|
||||
if (BAIPIAO_STATIC)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/opencv-static/OpenCVWrapperConfig.cmake)
|
||||
else ()
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/opencv-shared/OpenCVWrapperConfig.cmake)
|
||||
endif ()
|
||||
find_package(OpenCV REQUIRED)
|
||||
if (OpenCV_FOUND)
|
||||
message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}")
|
||||
message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}")
|
||||
else ()
|
||||
message(FATAL_ERROR "opencv Not Found!")
|
||||
endif (OpenCV_FOUND)
|
||||
|
||||
# JNI
|
||||
if (BAIPIAO_LIB)
|
||||
find_package(JNI REQUIRED)
|
||||
if (JNI_FOUND)
|
||||
message("JNI FOUND")
|
||||
message(STATUS "JNI_LIBS: ${JNI_LIBS}")
|
||||
message(STATUS "JNI_INCLUDE_DIRS: ${JNI_INCLUDE_DIRS}")
|
||||
include_directories(${JNI_INCLUDE_DIRS})
|
||||
else ()
|
||||
message(FATAL_ERROR "JNI Not Found!")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# project include
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# source
|
||||
file(GLOB OCR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
|
||||
set(OCR_COMPILE_CODE ${OCR_SRC})
|
||||
|
||||
if (BAIPIAO_LIB)
|
||||
add_library(BaiPiaoOcrOnnx SHARED ${OCR_COMPILE_CODE})
|
||||
target_compile_definitions(BaiPiaoOcrOnnx PRIVATE __JNI__)
|
||||
target_link_libraries(BaiPiaoOcrOnnx ${OnnxRuntime_LIBS} ${OpenCV_LIBS} ${JNI_LIBS} ${OpenMP_CXX_LIB_NAMES})
|
||||
else ()
|
||||
add_executable(BaiPiaoOcrOnnx ${OCR_COMPILE_CODE})
|
||||
target_link_libraries(BaiPiaoOcrOnnx ${OnnxRuntime_LIBS} ${OpenCV_LIBS} ${OpenMP_CXX_LIB_NAMES})
|
||||
endif ()
|
||||
if (BAIPIAO_OPENMP)
|
||||
target_compile_definitions(BaiPiaoOcrOnnx PRIVATE __OPENMP__)
|
||||
endif ()
|
||||
|
||||
# benchmark
|
||||
if (BAIPIAO_BENCHMARK AND NOT BAIPIAO_LIB)
|
||||
add_executable(benchmark benchmark/benchmark.cpp
|
||||
src/AngleNet.cpp
|
||||
src/clipper.cpp
|
||||
src/CrnnNet.cpp
|
||||
src/DbNet.cpp
|
||||
src/getopt.cpp
|
||||
src/OcrLite.cpp
|
||||
src/OcrUtils.cpp)
|
||||
target_link_libraries(benchmark ${OnnxRuntime_LIBS} ${OpenCV_LIBS} ${OpenMP_CXX_LIB_NAMES})
|
||||
if (BAIPIAO_OPENMP)
|
||||
target_compile_definitions(benchmark PRIVATE __OPENMP__)
|
||||
endif ()
|
||||
endif ()
|
||||
@@ -0,0 +1,133 @@
|
||||
# BaiPiaoOcrOnnx
|
||||
|
||||
### Project下载
|
||||
* 有整合好源码和依赖库的完整工程项目,文件比较大,可到Q群共享内下载,找以Project开头的压缩包文件
|
||||
* 如果想自己折腾,则请继续阅读本说明
|
||||
|
||||
### Demo下载(win、mac、linux)
|
||||
编译好的demo文件比较大,可以到Q群共享内下载
|
||||
|
||||
### 介绍
|
||||
白富美OCR onnxruntime推理
|
||||
|
||||
onnxruntime框架[https://github.com/microsoft/onnxruntime](https://github.com/microsoft/onnxruntime)
|
||||
|
||||
### 关于模型:
|
||||
* det模型用于分割文字块,有两种模型(server和mobile),其中server体积大且较慢但效果好,mobile体积小且较快但效果差点。
|
||||
* cls模型用于检测文字方向,只有一种模型
|
||||
* rec模型用于文字识别,虽然有两种模型(server和mobile),但mobile由于转换失败,目前只有server模型。未来转换工具成熟后可以直接支持mobile模型。
|
||||
|
||||
### 模型下载
|
||||
[模型下载地址](https://github.com/znsoftm/BaiPiaoOCR/tree/main/models)
|
||||
也可以到Q群共享内下载,下载后解压到项目根目录
|
||||
```
|
||||
BaiPiaoOcrOnnx/models
|
||||
├── ch_ppocr_mobile_v2.0_cls_infer.onnx
|
||||
├── ch_ppocr_mobile_v2.0_det_infer.onnx
|
||||
├── ch_ppocr_server_v2.0_det_infer.onnx
|
||||
├── ch_ppocr_server_v2.0_rec_infer.onnx
|
||||
└── ppocr_keys_v1.txt
|
||||
```
|
||||
|
||||
### 依赖的第三方库下载
|
||||
下载opencv和onnxruntime,[下载地址](https://gitee.com/benjaminwan/ocr-lite-onnx/releases/v1.0)
|
||||
* OpenCv动态库:opencv-(版本号)-sharedLib.7z
|
||||
* OpenCv静态库:opencv-(版本号)-staticLib.7z
|
||||
* OnnxRuntime动态库:onnxruntime-(版本号)-sharedLib.7z
|
||||
* OnnxRuntime静态库:onnxruntime-(版本号)-staticLib.7z
|
||||
* 可以选择只下载两者的动态库或两者的静态库(要么都是静态库要么都是动态库),或者4种全部下载
|
||||
* 把压缩包解压到项目根目录,解压后目录结构
|
||||
```
|
||||
BaiPiaoOcrOnnx
|
||||
├── onnxruntime-shared
|
||||
├── onnxruntime-static
|
||||
├── opencv-shared
|
||||
├── opencv-static
|
||||
```
|
||||
|
||||
### 编译环境
|
||||
1. Windows 10 x64
|
||||
2. macOS 10.15
|
||||
3. Linux Ubuntu 1604
|
||||
|
||||
### Windows编译说明
|
||||
#### Windows nmake编译
|
||||
1. 安装VS2017或VS2019,安装时,至少选中'使用C++的桌面开发'
|
||||
2. cmake请自行下载&配置,[下载地址](https://cmake.org/download/)
|
||||
3. 开始菜单打开"x64 Native Tools Command Prompt for VS 2019"或"适用于 VS2017 的 x64 本机工具",并转到本项目根目录
|
||||
4. 运行```build.bat```并按照提示输入选项,最后选择'编译成可执行文件'
|
||||
5. 编译完成后运行```run-test.bat```进行测试
|
||||
6. 编译JNI动态运行库(可选,可用于java调用)
|
||||
* 下载jdk-8u221-windows-x64.exe,安装选项默认(确保“源代码”项选中),安装完成后,打开“系统”属性->高级->环境变量
|
||||
* 新建“系统变量”,变量名```JAVA_HOME``` ,变量值```C:\Program Files\Java\jdk1.8.0_221``
|
||||
* 新建“系统变量”,变量名```CLASSPATH``` ,变量值```.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;``
|
||||
* 编辑“系统变量”Path,Win7在变量值头部添加```%JAVA_HOME%\bin;``` ,win10直接添加一行```%JAVA_HOME%\bin```
|
||||
* 开始菜单打开"x64 Native Tools Command Prompt for VS 2019"或"适用于 VS2017 的 x64 本机工具",并转到本项目根目录
|
||||
* 运行```build.bat```并按照提示输入选项,最后选择'编译成JNI动态库'
|
||||
|
||||
#### Windows Visual Studio编译说明
|
||||
1. VS2017/VS2019,cmake……等安装配置参考上述步骤。
|
||||
2. 运行generate-vs-project.bat,输入数字选择要生成的visual studio项目解决方案版本。
|
||||
3. 根据你的编译环境,进入build-xxxx-x86或x64文件夹,打开BaiPiaoOcrOnnx.sln。
|
||||
4. 在顶部工具栏选择Release,在右边的"解决方案"窗口,右键选中"ALL_BUILD"->生成。要选择Debug,则您必须自行编译Debug版的opencv或onnxruntime。
|
||||
|
||||
#### Windows部署说明
|
||||
1. 编译选项选择第三方库为动态库时,部署的时候记得把dll复制到可执行文件目录。
|
||||
2. 部署时如果提示缺少"VCRUNTIME140_1.dll",下载安装适用于 Visual Studio 2015、2017 和 2019 的 Microsoft Visual C++ 可再发行软件包,
|
||||
[下载地址](https://support.microsoft.com/zh-cn/help/2977003/the-latest-supported-visual-c-downloads)
|
||||
|
||||
### Mac编译说明
|
||||
1. macOS Catalina 10.15.x,安装Xcode 12.1,并安装Xcode Command Line Tools, 终端运行```xcode-select –install```
|
||||
2. 自行下载安装HomeBrew,cmake >=3.1[下载地址](https://cmake.org/download/)
|
||||
3. libomp: ```brew install libomp```
|
||||
4. 终端打开项目根目录,```./build.sh```并按照提示输入选项,最后选择'编译成可执行文件'
|
||||
5. 测试:```./run-test.sh```
|
||||
6. 编译JNI动态运行库(可选,可用于java调用)
|
||||
* 下载jdk-8u221-macosx-x64.dmg,安装。
|
||||
* 编辑用户目录下的隐藏文件```.zshrc``` ,添加```export JAVA_HOME=$(/usr/libexec/java_home)```
|
||||
* 运行```build.sh```并按照提示输入选项,最后选择'编译成JNI动态库'
|
||||
|
||||
#### macOS部署说明
|
||||
opencv或onnxruntime使用动态库时,参考下列方法:
|
||||
* 把动态库所在路径加入DYLD_LIBRARY_PATH搜索路径
|
||||
* 把动态库复制或链接到到/usr/lib
|
||||
|
||||
### Linux编译说明
|
||||
1. Ubuntu16.04 LTS 或其它发行版
|
||||
2. ```sudo apt-get install build-essential```
|
||||
3. g++>=5,cmake>=3.1[下载地址](https://cmake.org/download/)
|
||||
4. 终端打开项目根目录,```./build.sh```并按照提示输入选项,最后选择'编译成可执行文件'
|
||||
5. 测试:```./run-test.sh```
|
||||
6. 编译JNI动态运行库(可选,可用于java调用)
|
||||
* 下载jdk-8u221并安装配置
|
||||
* 运行```build.sh```并按照提示输入选项,最后选择'编译成JNI动态库'
|
||||
|
||||
#### Linux部署说明
|
||||
opencv或onnxruntime使用动态库时,参考下列方法:
|
||||
* 把动态库所在路径加入LD_LIBRARY_PATH搜索路径
|
||||
* 把动态库复制或链接到到/usr/lib
|
||||
|
||||
### 编译参数说明
|
||||
build.sh编译参数:
|
||||
1. ```BAIPIAO_OPENMP=ON```:启用(ON)或禁用(OFF) ON时AngleNet和CrnnNet阶段使用OpenMP并行运算,OFF时单线程运算
|
||||
2. ```BAIPIAO_LIB=ON```: 启用(ON)或禁用(OFF) ON时编译为jni lib,OFF时编译为可执行文件
|
||||
3. ```BAIPIAO_STATIC=ON```: 启用(ON)或禁用(OFF) ON时选择opencv和onnxruntime的静态库进行编译,OFF时则选择动态库编译
|
||||
|
||||
### 输入参数说明
|
||||
* 请参考main.h中的命令行参数说明。
|
||||
* 每个参数有一个短参数名和一个长参数名,用短的或长的均可。
|
||||
1. ```-d或--models```:模型所在文件夹路径,可以相对路径也可以绝对路径。
|
||||
2. ```-1或--det```:det模型文件名
|
||||
3. ```-2或--cls```:cls模型文件名
|
||||
4. ```-3或--rec```:rec模型文件名
|
||||
5. ```-4或--keys```:keys.txt文件名
|
||||
6. ```-i或--image```:目标图片路径,可以相对路径也可以绝对路径。
|
||||
7. ```-t或--numThread```:线程数量。
|
||||
8. ```-p或--padding```:图像预处理,在图片外周添加白边,用于提升识别率,文字框没有正确框住所有文字时,增加此值。
|
||||
9. ```-s或--maxSideLen```:按图片最长边的长度,此值为0代表不缩放,例:1024,如果图片长边大于1024则把图像整体缩小到1024再进行图像分割计算,如果图片长边小于1024则不缩放,如果图片长边小于32,则缩放到32。
|
||||
6. ```-b或--boxScoreThresh```:文字框置信度门限,文字框没有正确框住所有文字时,减小此值。
|
||||
7. ```-o或--boxThresh```:请自行试验。
|
||||
9. ```-u或--unClipRatio```:单个文字框大小倍率,越大时单个文字框越大。此项与图片的大小相关,越大的图片此值应该越大。
|
||||
10. ```-a或--noAngle```:启用(1)/禁用(0) 文字方向检测,只有图片倒置的情况下(旋转90~270度的图片),才需要启用文字方向检测。
|
||||
11. ```-A或--mostAngle```:启用(1)/禁用(0) 角度投票(整张图片以最大可能文字方向来识别),当禁用文字方向检测时,此项也不起作用。
|
||||
12. ```-?或--help```:打印命令行帮助。
|
||||
@@ -0,0 +1,166 @@
|
||||
#include <omp.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "main.h"
|
||||
#include "version.h"
|
||||
#include "OcrLite.h"
|
||||
|
||||
void printHelp(FILE *out, char *argv0) {
|
||||
fprintf(out, " ------- Usage -------\n");
|
||||
fprintf(out, "%s %s", argv0, usageMsg);
|
||||
fprintf(out, " ------- Required Parameters -------\n");
|
||||
fprintf(out, "%s", requiredMsg);
|
||||
fprintf(out, " ------- Optional Parameters -------\n");
|
||||
fprintf(out, "%s", optionalMsg);
|
||||
fprintf(out, " ------- Other Parameters -------\n");
|
||||
fprintf(out, "%s", otherMsg);
|
||||
fprintf(out, " ------- Examples -------\n");
|
||||
fprintf(out, example1Msg, argv0);
|
||||
fprintf(out, example2Msg, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc <= 1) {
|
||||
printHelp(stderr, argv[0]);
|
||||
return -1;
|
||||
}
|
||||
std::string modelsDir = "../models";
|
||||
std::string modelDetPath, modelClsPath, modelRecPath, keysPath;
|
||||
std::string argImgPath = "../../test_imgs/1.jpg";
|
||||
std::string imgPath, imgName;
|
||||
int numThread = 4;
|
||||
int loopCount = 1;
|
||||
int padding = 0;
|
||||
int maxSideLen = 1024;
|
||||
float boxScoreThresh = 0.5f;
|
||||
float boxThresh = 0.3f;
|
||||
float unClipRatio = 2.0f;
|
||||
bool doAngle = true;
|
||||
int flagDoAngle = 1;
|
||||
bool mostAngle = false;
|
||||
int flagMostAngle = 0;
|
||||
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
while ((opt = getopt_long(argc, argv, "d:1:2:3:4:i:t:p:s:b:o:u:a:A:v:h:l", long_options, &optionIndex)) != -1) {
|
||||
//printf("option(-%c)=%s\n", opt, optarg);
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
modelsDir = optarg;
|
||||
printf("modelsPath=%s\n", modelsDir.c_str());
|
||||
break;
|
||||
case '1':
|
||||
modelDetPath = modelsDir + "/" + optarg;
|
||||
printf("model det path=%s\n", modelDetPath.c_str());
|
||||
break;
|
||||
case '2':
|
||||
modelClsPath = modelsDir + "/" + optarg;
|
||||
printf("model cls path=%s\n", modelClsPath.c_str());
|
||||
break;
|
||||
case '3':
|
||||
modelRecPath = modelsDir + "/" + optarg;
|
||||
printf("model rec path=%s\n", modelRecPath.c_str());
|
||||
break;
|
||||
case '4':
|
||||
keysPath = modelsDir + "/" + optarg;
|
||||
printf("keys path=%s\n", keysPath.c_str());
|
||||
break;
|
||||
case 'i':
|
||||
argImgPath.assign(optarg);
|
||||
imgPath.assign(argImgPath.substr(0, argImgPath.find_last_of('/') + 1));
|
||||
imgName.assign(argImgPath.substr(argImgPath.find_last_of('/') + 1));
|
||||
printf("imgPath=%s, imgName=%s\n", imgPath.c_str(), imgName.c_str());
|
||||
break;
|
||||
case 't':
|
||||
numThread = (int) strtol(optarg, NULL, 10);
|
||||
//printf("numThread=%d\n", numThread);
|
||||
break;
|
||||
case 'p':
|
||||
padding = (int) strtol(optarg, NULL, 10);
|
||||
//printf("padding=%d\n", padding);
|
||||
break;
|
||||
case 's':
|
||||
maxSideLen = (int) strtol(optarg, NULL, 10);
|
||||
//printf("maxSideLen=%d\n", maxSideLen);
|
||||
break;
|
||||
case 'b':
|
||||
boxScoreThresh = strtof(optarg, NULL);
|
||||
//printf("boxScoreThresh=%f\n", boxScoreThresh);
|
||||
break;
|
||||
case 'o':
|
||||
boxThresh = strtof(optarg, NULL);
|
||||
//printf("boxThresh=%f\n", boxThresh);
|
||||
break;
|
||||
case 'u':
|
||||
unClipRatio = strtof(optarg, NULL);
|
||||
//printf("unClipRatio=%f\n", unClipRatio);
|
||||
break;
|
||||
case 'a':
|
||||
flagDoAngle = (int) strtol(optarg, NULL, 10);
|
||||
if (flagDoAngle == 0) {
|
||||
doAngle = false;
|
||||
} else {
|
||||
doAngle = true;
|
||||
}
|
||||
//printf("doAngle=%d\n", doAngle);
|
||||
break;
|
||||
case 'A':
|
||||
flagMostAngle = (int) strtol(optarg, NULL, 10);
|
||||
if (flagMostAngle == 0) {
|
||||
mostAngle = false;
|
||||
} else {
|
||||
mostAngle = true;
|
||||
}
|
||||
//printf("mostAngle=%d\n", mostAngle);
|
||||
break;
|
||||
case 'v':
|
||||
printf("%s\n", VERSION);
|
||||
return 0;
|
||||
case 'h':
|
||||
printHelp(stdout, argv[0]);
|
||||
return 0;
|
||||
case 'l':
|
||||
loopCount = (int) strtol(optarg, NULL, 10);
|
||||
//printf("loopCount=%d\n", loopCount);
|
||||
break;
|
||||
default:
|
||||
printf("other option %c :%s\n", opt, optarg);
|
||||
}
|
||||
}
|
||||
|
||||
omp_set_num_threads(numThread);
|
||||
OcrLite ocrLite;
|
||||
ocrLite.setNumThread(numThread);
|
||||
ocrLite.initLogger(
|
||||
false,//isOutputConsole
|
||||
false,//isOutputPartImg
|
||||
false);//isOutputResultImg
|
||||
|
||||
ocrLite.enableResultTxt(imgPath.c_str(), imgName.c_str());
|
||||
printf("=====Input Params=====\n");
|
||||
printf(
|
||||
"numThread(%d),padding(%d),maxSideLen(%d),boxScoreThresh(%f),boxThresh(%f),unClipRatio(%f),doAngle(%d),mostAngle(%d)\n",
|
||||
numThread, padding, maxSideLen, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
|
||||
ocrLite.initModels(modelDetPath, modelClsPath, modelRecPath, keysPath);
|
||||
printf("=====warmup=====\n");
|
||||
OcrResult result = ocrLite.detect(imgPath.c_str(), imgName.c_str(), padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
printf("dbNetTime(%f) detectTime(%f)\n", result.dbNetTime, result.detectTime);
|
||||
double dbTime = 0.0f;
|
||||
double detectTime = 0.0f;
|
||||
for (int i = 0; i < loopCount; ++i) {
|
||||
printf("=====loop:%d=====\n", i + 1);
|
||||
OcrResult ocrResult = ocrLite.detect(imgPath.c_str(), imgName.c_str(),
|
||||
padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh,
|
||||
unClipRatio, doAngle, mostAngle);
|
||||
printf("dbNetTime(%f) detectTime(%f)\n", ocrResult.dbNetTime, ocrResult.detectTime);
|
||||
dbTime += ocrResult.dbNetTime;
|
||||
detectTime += ocrResult.detectTime;
|
||||
}
|
||||
printf("=====result=====\n");
|
||||
printf("average dbNetTime=%fms, average detectTime=%fms\n", dbTime / loopCount,
|
||||
detectTime / loopCount);
|
||||
return 0;
|
||||
}
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
@ECHO OFF
|
||||
chcp 65001
|
||||
cls
|
||||
@SETLOCAL
|
||||
echo "========请先参考README.md准备好编译环境========"
|
||||
echo.
|
||||
|
||||
echo "========编译选项========"
|
||||
echo "请注意:项目默认使用Release库,除非您自行编译Debug版的Onnxruntime和Opencv,否则请不要选择Debug编译"
|
||||
echo "请输入编译选项并回车: 1)Release, 2)Debug"
|
||||
set BUILD_TYPE=Release
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_TYPE=Release)^
|
||||
else if %flag% == 2 (set BUILD_TYPE=Debug)^
|
||||
else (echo 输入错误!Input Error!)
|
||||
echo.
|
||||
|
||||
echo "请输入OpenMP选项并回车: 1)启用OpenMP(Angle阶段和Crnn阶段多线程并行执行), 2)禁用OpenMP(Angle阶段和Crnn阶段单线程执行)"
|
||||
set BUILD_OPENMP=ON
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_OPENMP=ON)^
|
||||
else if %flag% == 2 (set BUILD_OPENMP=OFF)^
|
||||
else (echo 输入错误!Input Error!)
|
||||
echo.
|
||||
|
||||
echo "使用静态库时,编译出来的可执行文件较大,但部署起来比较方便。"
|
||||
echo "使用动态库时,编译出来的可执行文件较小,但部署的时候记得把dll复制到可执行文件目录"
|
||||
echo "请选择要使用的OnnxRuntime和Opencv库选项并回车: 1)Static静态库,2)Shared动态库"
|
||||
set BUILD_STATIC=ON
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_STATIC=ON)^
|
||||
else if %flag% == 2 (set BUILD_STATIC=OFF)^
|
||||
else (echo "输入错误!Input Error!")
|
||||
echo.
|
||||
|
||||
echo "请注意:如果选择2)编译为JNI动态库时,必须安装配置Oracle JDK"
|
||||
echo "请选择编译输出类型并回车: 1)编译成可执行文件,2)编译成JNI动态库"
|
||||
set BUILD_LIB=OFF
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_LIB=OFF)^
|
||||
else if %flag% == 2 (set BUILD_LIB=ON)^
|
||||
else (echo 输入错误!Input Error!)
|
||||
echo.
|
||||
if %BUILD_LIB% == OFF (call :makeExe)^
|
||||
else (call :makeLib)
|
||||
echo cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DBAIPIAO_OPENMP=%BUILD_OPENMP% -DBAIPIAO_LIB=%BUILD_LIB% -DBAIPIAO_STATIC=%BUILD_STATIC% ..
|
||||
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DBAIPIAO_OPENMP=%BUILD_OPENMP% -DBAIPIAO_LIB=%BUILD_LIB% -DBAIPIAO_STATIC=%BUILD_STATIC% ..
|
||||
nmake
|
||||
popd
|
||||
GOTO:EOF
|
||||
|
||||
:makeExe
|
||||
mkdir build
|
||||
pushd build
|
||||
GOTO:EOF
|
||||
|
||||
:makeLib
|
||||
mkdir build-lib
|
||||
pushd build-lib
|
||||
GOTO:EOF
|
||||
|
||||
@ENDLOCAL
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "========请先参考README.md准备好编译环境========"
|
||||
echo
|
||||
|
||||
echo "========编译选项========"
|
||||
echo "请输入编译选项并回车: 1)Release, 2)Debug"
|
||||
read -p "" BUILD_TYPE
|
||||
if [ $BUILD_TYPE == 1 ]; then
|
||||
BUILD_TYPE=Release
|
||||
elif [ $BUILD_TYPE == 2 ]; then
|
||||
BUILD_TYPE=Debug
|
||||
else
|
||||
echo -e "输入错误!Input Error!"
|
||||
fi
|
||||
|
||||
echo "请输入OpenMP选项并回车: 1)启用OpenMP(Angle阶段和Crnn阶段多线程并行执行), 2)禁用OpenMP(Angle阶段和Crnn阶段单线程执行)"
|
||||
read -p "" BUILD_OPENMP
|
||||
if [ $BUILD_OPENMP == 1 ]; then
|
||||
BUILD_OPENMP=ON
|
||||
elif [ $BUILD_OPENMP == 2 ]; then
|
||||
BUILD_OPENMP=OFF
|
||||
else
|
||||
echo -e "输入错误!Input Error!"
|
||||
fi
|
||||
|
||||
echo "使用静态库时,编译出来的可执行文件较大,但部署起来比较方便。"
|
||||
echo "使用动态库时,编译出来的可执行文件较小,但Linux部署时要配置LD_LIBRARY_PATH或链接|复制到/usr/lib。"
|
||||
echo "请选择要使用的OnnxRuntime和Opencv库选项并回车: 1)Static静态库,2)Shared动态库"
|
||||
read -p "" BUILD_STATIC
|
||||
if [ $BUILD_STATIC == 1 ]; then
|
||||
BUILD_STATIC=ON
|
||||
elif [ $BUILD_STATIC == 2 ]; then
|
||||
BUILD_STATIC=OFF
|
||||
else
|
||||
echo -e "输入错误!Input Error!"
|
||||
fi
|
||||
|
||||
echo "请注意:如果选择2)编译为JNI动态库时,必须安装配置Oracle JDK"
|
||||
echo "请选择编译输出类型并回车: 1)编译成可执行文件,2)编译成JNI动态库"
|
||||
read -p "" BUILD_LIB
|
||||
if [ $BUILD_LIB == 1 ]; then
|
||||
BUILD_LIB=OFF
|
||||
elif [ $BUILD_LIB == 2 ]; then
|
||||
BUILD_LIB=ON
|
||||
else
|
||||
echo -e "输入错误!Input Error!"
|
||||
fi
|
||||
|
||||
if [ $BUILD_LIB == OFF ]; then
|
||||
mkdir -p build
|
||||
pushd build
|
||||
else
|
||||
mkdir -p build-lib
|
||||
pushd build-lib
|
||||
fi
|
||||
|
||||
sysOS=`uname -s`
|
||||
NUM_THREADS=1
|
||||
if [ $sysOS == "Darwin" ];then
|
||||
#echo "I'm MacOS"
|
||||
NUM_THREADS=$(sysctl -n hw.ncpu)
|
||||
elif [ $sysOS == "Linux" ];then
|
||||
#echo "I'm Linux"
|
||||
NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l)
|
||||
else
|
||||
echo "Other OS: $sysOS"
|
||||
fi
|
||||
|
||||
echo "cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBAIPIAO_OPENMP=${BUILD_OPENMP} -DBAIPIAO_LIB=${BUILD_LIB} -DBAIPIAO_STATIC=${BUILD_STATIC} .."
|
||||
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBAIPIAO_OPENMP=$BUILD_OPENMP -DBAIPIAO_LIB=$BUILD_LIB -DBAIPIAO_STATIC=$BUILD_STATIC ..
|
||||
make -j $NUM_THREADS
|
||||
popd
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
@ECHO OFF
|
||||
chcp 65001
|
||||
cls
|
||||
@SETLOCAL
|
||||
echo "========请先参考README.md准备好编译环境========"
|
||||
echo.
|
||||
|
||||
echo "========编译选项========"
|
||||
echo "请注意:项目默认使用Release库,除非您自行编译Debug版的Onnxruntime和Opencv,否则请不要选择Debug编译"
|
||||
echo "请输入编译选项并回车: 1)Release, 2)Debug"
|
||||
set BUILD_TYPE=Release
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_TYPE=Release)^
|
||||
else if %flag% == 2 (set BUILD_TYPE=Debug)^
|
||||
else (echo 输入错误!Input Error!)
|
||||
echo.
|
||||
|
||||
echo "请输入OpenMP选项并回车: 1)启用OpenMP(Angle阶段和Crnn阶段多线程并行执行), 2)禁用OpenMP(Angle阶段和Crnn阶段单线程执行)"
|
||||
set BUILD_OPENMP=ON
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_OPENMP=ON)^
|
||||
else if %flag% == 2 (set BUILD_OPENMP=OFF)^
|
||||
else (echo 输入错误!Input Error!)
|
||||
echo.
|
||||
|
||||
echo "使用静态库时,编译出来的可执行文件较大,但部署起来比较方便。"
|
||||
echo "使用动态库时,编译出来的可执行文件较小,但部署的时候记得把dll复制到可执行文件目录"
|
||||
echo "请选择要使用的OnnxRuntime和Opencv库选项并回车: 1)Static静态库,2)Shared动态库"
|
||||
set BUILD_STATIC=ON
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_STATIC=ON)^
|
||||
else if %flag% == 2 (set BUILD_STATIC=OFF)^
|
||||
else (echo "输入错误!Input Error!")
|
||||
echo.
|
||||
|
||||
echo "请注意:如果选择2)编译为JNI动态库时,必须安装配置Oracle JDK"
|
||||
echo "请选择编译输出类型并回车: 1)编译成可执行文件,2)编译成JNI动态库"
|
||||
set BUILD_LIB=OFF
|
||||
set /p flag=
|
||||
if %flag% == 1 (set BUILD_LIB=OFF)^
|
||||
else if %flag% == 2 (set BUILD_LIB=ON)^
|
||||
else (echo 输入错误!Input Error!)
|
||||
echo.
|
||||
|
||||
echo "请输入选项并回车: 0)ALL, 1)vs2017-x86, 2)vs2017-x64, 3)vs2019-x86, 4)vs2019-x64:"
|
||||
set /p flag=
|
||||
if %flag% == 0 (call :buildALL)^
|
||||
else if %flag% == 1 (call :gen2017-x86)^
|
||||
else if %flag% == 2 (call :gen2017-x64)^
|
||||
else if %flag% == 3 (call :gen2019-x86)^
|
||||
else if %flag% == 4 (call :gen2019-x64)^
|
||||
else (echo "输入错误!Input Error!")
|
||||
GOTO:EOF
|
||||
|
||||
:buildALL
|
||||
call :gen2017-x86
|
||||
call :gen2017-x64
|
||||
call :gen2019-x86
|
||||
call :gen2019-x64
|
||||
GOTO:EOF
|
||||
|
||||
:gen2017-x86
|
||||
mkdir build-win-vs2017-x86
|
||||
pushd build-win-vs2017-x86
|
||||
call :cmakeParams "Visual Studio 15 2017" "Win32"
|
||||
popd
|
||||
GOTO:EOF
|
||||
|
||||
:gen2017-x64
|
||||
mkdir build-win-vs2017-x64
|
||||
pushd build-win-vs2017-x64
|
||||
call :cmakeParams "Visual Studio 15 2017" "x64"
|
||||
popd
|
||||
GOTO:EOF
|
||||
|
||||
:gen2019-x86
|
||||
mkdir build-win-vs2019-x86
|
||||
pushd build-win-vs2019-x86
|
||||
call :cmakeParams "Visual Studio 16 2019" "Win32"
|
||||
popd
|
||||
GOTO:EOF
|
||||
|
||||
:gen2019-x64
|
||||
mkdir build-win-vs2019-x64
|
||||
pushd build-win-vs2019-x64
|
||||
call :cmakeParams "Visual Studio 16 2019" "x64"
|
||||
popd
|
||||
GOTO:EOF
|
||||
|
||||
:cmakeParams
|
||||
echo cmake -G "%~1" -A "%~2" -DBAIPIAO_OPENMP=%BUILD_OPENMP% -DBAIPIAO_LIB=%BUILD_LIB% -DBAIPIAO_STATIC=%BUILD_STATIC% ..
|
||||
cmake -G "%~1" -A "%~2" -DBAIPIAO_OPENMP=%BUILD_OPENMP% -DBAIPIAO_LIB=%BUILD_LIB% -DBAIPIAO_STATIC=%BUILD_STATIC% ..
|
||||
GOTO:EOF
|
||||
|
||||
@ENDLOCAL
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef __OCR_ANGLENET_H__
|
||||
#define __OCR_ANGLENET_H__
|
||||
|
||||
#include "OcrStruct.h"
|
||||
#include "onnxruntime_cxx_api.h"
|
||||
#include <opencv/cv.hpp>
|
||||
|
||||
class AngleNet {
|
||||
public:
|
||||
AngleNet();
|
||||
|
||||
~AngleNet();
|
||||
|
||||
void setNumThread(int numOfThread);
|
||||
|
||||
void initModel(const std::string &pathStr);
|
||||
|
||||
std::vector<Angle> getAngles(std::vector<cv::Mat> &partImgs, const char *path,
|
||||
const char *imgName, bool doAngle, bool mostAngle);
|
||||
|
||||
private:
|
||||
bool isOutputAngleImg = false;
|
||||
|
||||
Ort::Session *session;
|
||||
Ort::Env env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "AngleNet");
|
||||
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
|
||||
int numThread = 0;
|
||||
|
||||
std::vector<char *> inputNames;
|
||||
std::vector<char *> outputNames;
|
||||
|
||||
const float meanValues[3] = {127.5, 127.5, 127.5};
|
||||
const float normValues[3] = {1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5};
|
||||
const int dstWidth = 192;
|
||||
const int dstHeight = 48;
|
||||
|
||||
Angle getAngle(cv::Mat &src);
|
||||
};
|
||||
|
||||
|
||||
#endif //__OCR_ANGLENET_H__
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef __OCR_CRNNNET_H__
|
||||
#define __OCR_CRNNNET_H__
|
||||
|
||||
#include "OcrStruct.h"
|
||||
#include "onnxruntime_cxx_api.h"
|
||||
#include <opencv/cv.hpp>
|
||||
|
||||
class CrnnNet {
|
||||
public:
|
||||
|
||||
CrnnNet();
|
||||
|
||||
~CrnnNet();
|
||||
|
||||
void setNumThread(int numOfThread);
|
||||
|
||||
void initModel(const std::string &pathStr, const std::string &keysPath);
|
||||
|
||||
std::vector<TextLine> getTextLines(std::vector<cv::Mat> &partImg, const char *path, const char *imgName);
|
||||
|
||||
private:
|
||||
bool isOutputDebugImg = false;
|
||||
Ort::Session *session;
|
||||
Ort::Env env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "CrnnNet");
|
||||
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
|
||||
int numThread = 0;
|
||||
|
||||
std::vector<char *> inputNames;
|
||||
std::vector<char *> outputNames;
|
||||
|
||||
const float meanValues[3] = {127.5, 127.5, 127.5};
|
||||
const float normValues[3] = {1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5};
|
||||
const int dstHeight = 32;
|
||||
|
||||
std::vector<std::string> keys;
|
||||
|
||||
TextLine scoreToTextLine(const std::vector<float> &outputData, int h, int w);
|
||||
|
||||
TextLine getTextLine(const cv::Mat &src);
|
||||
};
|
||||
|
||||
|
||||
#endif //__OCR_CRNNNET_H__
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef __OCR_DBNET_H__
|
||||
#define __OCR_DBNET_H__
|
||||
|
||||
#include "OcrStruct.h"
|
||||
#include "onnxruntime_cxx_api.h"
|
||||
#include <opencv/cv.hpp>
|
||||
|
||||
class DbNet {
|
||||
public:
|
||||
DbNet();
|
||||
|
||||
~DbNet();
|
||||
|
||||
void setNumThread(int numOfThread);
|
||||
|
||||
void initModel(const std::string &pathStr);
|
||||
|
||||
std::vector<TextBox> getTextBoxes(cv::Mat &src, ScaleParam &s, float boxScoreThresh,
|
||||
float boxThresh, float unClipRatio);
|
||||
|
||||
private:
|
||||
Ort::Session *session;
|
||||
Ort::Env env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "DbNet");
|
||||
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
|
||||
int numThread = 0;
|
||||
std::vector<char *> inputNames;
|
||||
std::vector<char *> outputNames;
|
||||
|
||||
const float meanValues[3] = {0.485f * 255, 0.456f * 255, 0.406f * 255};
|
||||
const float normValues[3] = {1.0 / 0.229f / 255.0, 1.0 / 0.224f / 255.0, 1.0 / 0.225f / 255.0};
|
||||
};
|
||||
|
||||
|
||||
#endif //__OCR_DBNET_H__
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef __OCR_LITE_H__
|
||||
#define __OCR_LITE_H__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "onnxruntime_cxx_api.h"
|
||||
#include "OcrStruct.h"
|
||||
#include "DbNet.h"
|
||||
#include "AngleNet.h"
|
||||
#include "CrnnNet.h"
|
||||
|
||||
class OcrLite {
|
||||
public:
|
||||
OcrLite();
|
||||
|
||||
~OcrLite();
|
||||
|
||||
void setNumThread(int numOfThread);
|
||||
|
||||
void initLogger(bool isConsole, bool isPartImg, bool isResultImg);
|
||||
|
||||
void enableResultTxt(const char *path, const char *imgName);
|
||||
|
||||
void initModels(const std::string &detPath, const std::string &clsPath,
|
||||
const std::string &recPath, const std::string &keysPath);
|
||||
|
||||
void Logger(const char *format, ...);
|
||||
|
||||
OcrResult detect(const char *path, const char *imgName,
|
||||
int padding, int maxSideLen,
|
||||
float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle);
|
||||
|
||||
private:
|
||||
bool isOutputConsole = false;
|
||||
bool isOutputPartImg = false;
|
||||
bool isOutputResultTxt = false;
|
||||
bool isOutputResultImg = false;
|
||||
FILE *resultTxt;
|
||||
DbNet dbNet;
|
||||
AngleNet angleNet;
|
||||
CrnnNet crnnNet;
|
||||
|
||||
std::vector<cv::Mat> getPartImages(cv::Mat &src, std::vector<TextBox> &textBoxes,
|
||||
const char *path, const char *imgName);
|
||||
|
||||
OcrResult detect(const char *path, const char *imgName,
|
||||
cv::Mat &src, cv::Rect &originRect, ScaleParam &scale,
|
||||
float boxScoreThresh = 0.6f, float boxThresh = 0.3f,
|
||||
float unClipRatio = 2.0f, bool doAngle = true, bool mostAngle = true);
|
||||
};
|
||||
|
||||
#endif //__OCR_LITE_H__
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifdef __JNI__
|
||||
#ifndef __OCR_RESULT_UTILS_H__
|
||||
#define __OCR_RESULT_UTILS_H__
|
||||
#include <jni.h>
|
||||
#include "OcrStruct.h"
|
||||
|
||||
class OcrResultUtils {
|
||||
public:
|
||||
OcrResultUtils(JNIEnv *env, OcrResult &ocrResult);
|
||||
|
||||
~OcrResultUtils();
|
||||
|
||||
jobject getJObject();
|
||||
|
||||
private:
|
||||
JNIEnv *jniEnv;
|
||||
jobject jOcrResult;
|
||||
|
||||
jclass newJListClass();
|
||||
|
||||
jmethodID getListConstructor(jclass clazz);
|
||||
|
||||
jobject getTextBlock(TextBlock &textBlock);
|
||||
|
||||
jobject getTextBlocks(std::vector<TextBlock> &textBlocks);
|
||||
|
||||
jobject newJPoint(cv::Point &point);
|
||||
|
||||
jobject newJBoxPoint(std::vector<cv::Point> &boxPoint);
|
||||
|
||||
jfloatArray newJScoreArray(std::vector<float> &scores);
|
||||
|
||||
};
|
||||
#endif //__OCR_RESULT_UTILS_H__
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef __OCR_STRUCT_H__
|
||||
#define __OCR_STRUCT_H__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include <vector>
|
||||
|
||||
struct ScaleParam {
|
||||
int srcWidth;
|
||||
int srcHeight;
|
||||
int dstWidth;
|
||||
int dstHeight;
|
||||
float ratioWidth;
|
||||
float ratioHeight;
|
||||
};
|
||||
|
||||
struct TextBox {
|
||||
std::vector<cv::Point> boxPoint;
|
||||
float score;
|
||||
};
|
||||
|
||||
struct Angle {
|
||||
int index;
|
||||
float score;
|
||||
double time;
|
||||
};
|
||||
|
||||
struct TextLine {
|
||||
std::string text;
|
||||
std::vector<float> charScores;
|
||||
double time;
|
||||
};
|
||||
|
||||
struct TextBlock {
|
||||
std::vector<cv::Point> boxPoint;
|
||||
float boxScore;
|
||||
int angleIndex;
|
||||
float angleScore;
|
||||
double angleTime;
|
||||
std::string text;
|
||||
std::vector<float> charScores;
|
||||
double crnnTime;
|
||||
double blockTime;
|
||||
};
|
||||
|
||||
struct OcrResult {
|
||||
double dbNetTime;
|
||||
std::vector<TextBlock> textBlocks;
|
||||
cv::Mat boxImg;
|
||||
double detectTime;
|
||||
std::string strRes;
|
||||
};
|
||||
|
||||
#endif //__OCR_STRUCT_H__
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef __OCR_UTILS_H__
|
||||
#define __OCR_UTILS_H__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include "OcrStruct.h"
|
||||
#include "onnxruntime_cxx_api.h"
|
||||
#include <numeric>
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
static std::unique_ptr<T> makeUnique(Ts &&... params) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static double getMean(std::vector<T> &input) {
|
||||
auto sum = accumulate(input.begin(), input.end(), 0.0);
|
||||
return sum / input.size();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static double getStdev(std::vector<T> &input, double mean) {
|
||||
if (input.size() <= 1) return 0;
|
||||
double accum = 0.0;
|
||||
for_each(input.begin(), input.end(), [&](const double d) {
|
||||
accum += (d - mean) * (d - mean);
|
||||
});
|
||||
double stdev = sqrt(accum / (input.size() - 1));
|
||||
return stdev;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T clamp(T x, T min, T max) {
|
||||
if (x > max)
|
||||
return max;
|
||||
if (x < min)
|
||||
return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
double getCurrentTime();
|
||||
|
||||
std::wstring strToWstr(std::string str);
|
||||
|
||||
ScaleParam getScaleParam(cv::Mat &src, const float scale);
|
||||
|
||||
ScaleParam getScaleParam(cv::Mat &src, const int targetSize);
|
||||
|
||||
std::vector<cv::Point2f> getBox(const cv::RotatedRect &rect);
|
||||
|
||||
int getThickness(cv::Mat &boxImg);
|
||||
|
||||
void drawTextBox(cv::Mat &boxImg, cv::RotatedRect &rect, int thickness);
|
||||
|
||||
void drawTextBox(cv::Mat &boxImg, const std::vector<cv::Point> &box, int thickness);
|
||||
|
||||
void drawTextBoxes(cv::Mat &boxImg, std::vector<TextBox> &textBoxes, int thickness);
|
||||
|
||||
cv::Mat matRotateClockWise180(cv::Mat src);
|
||||
|
||||
cv::Mat matRotateClockWise90(cv::Mat src);
|
||||
|
||||
cv::Mat getRotateCropImage(const cv::Mat &src, std::vector<cv::Point> box);
|
||||
|
||||
cv::Mat adjustTargetImg(cv::Mat &src, int dstWidth, int dstHeight);
|
||||
|
||||
std::vector<cv::Point2f> getMinBoxes(const cv::RotatedRect &boxRect, float &maxSideLen);
|
||||
|
||||
float boxScoreFast(const std::vector<cv::Point2f> &boxes, const cv::Mat &pred);
|
||||
|
||||
cv::RotatedRect unClip(std::vector<cv::Point2f> box, float unClipRatio);
|
||||
|
||||
std::vector<float> substractMeanNormalize(cv::Mat &src, const float *meanVals, const float *normVals);
|
||||
|
||||
std::vector<int> getAngleIndexes(std::vector<Angle> &angles);
|
||||
|
||||
std::vector<char *> getInputNames(Ort::Session *session);
|
||||
|
||||
std::vector<char *> getOutputNames(Ort::Session *session);
|
||||
|
||||
void saveImg(cv::Mat &img, const char *imgPath);
|
||||
|
||||
std::string getSrcImgFilePath(const char *path, const char *imgName);
|
||||
|
||||
std::string getResultTxtFilePath(const char *path, const char *imgName);
|
||||
|
||||
std::string getResultImgFilePath(const char *path, const char *imgName);
|
||||
|
||||
std::string getDebugImgFilePath(const char *path, const char *imgName, int i, const char *tag);
|
||||
|
||||
#endif //__OCR_UTILS_H__
|
||||
@@ -0,0 +1,406 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Author : Angus Johnson *
|
||||
* Version : 6.4.2 *
|
||||
* Date : 27 February 2017 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2017 *
|
||||
* *
|
||||
* License: *
|
||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||
* http://www.boost.org/LICENSE_1_0.txt *
|
||||
* *
|
||||
* Attributions: *
|
||||
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
|
||||
* "A generic solution to polygon clipping" *
|
||||
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
|
||||
* http://portal.acm.org/citation.cfm?id=129906 *
|
||||
* *
|
||||
* Computer graphics and geometric modeling: implementation and algorithms *
|
||||
* By Max K. Agoston *
|
||||
* Springer; 1 edition (January 4, 2005) *
|
||||
* http://books.google.com/books?q=vatti+clipping+agoston *
|
||||
* *
|
||||
* See also: *
|
||||
* "Polygon Offsetting by Computing Winding Numbers" *
|
||||
* Paper no. DETC2005-85513 pp. 565-575 *
|
||||
* ASME 2005 International Design Engineering Technical Conferences *
|
||||
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
|
||||
* September 24-28, 2005 , Long Beach, California, USA *
|
||||
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef clipper_hpp
|
||||
#define clipper_hpp
|
||||
|
||||
#define CLIPPER_VERSION "6.4.2"
|
||||
|
||||
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
|
||||
//improve performance but coordinate values are limited to the range +/- 46340
|
||||
//#define use_int32
|
||||
|
||||
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
|
||||
//#define use_xyz
|
||||
|
||||
//use_lines: Enables line clipping. Adds a very minor cost to performance.
|
||||
#define use_lines
|
||||
|
||||
//use_deprecated: Enables temporary support for the obsolete functions
|
||||
//#define use_deprecated
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
|
||||
namespace ClipperLib {
|
||||
|
||||
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
|
||||
enum PolyType { ptSubject, ptClip };
|
||||
//By far the most widely used winding rules for polygon filling are
|
||||
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
|
||||
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
|
||||
//see http://glprogramming.com/red/chapter11.html
|
||||
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
|
||||
|
||||
#ifdef use_int32
|
||||
typedef int cInt;
|
||||
static cInt const loRange = 0x7FFF;
|
||||
static cInt const hiRange = 0x7FFF;
|
||||
#else
|
||||
typedef signed long long cInt;
|
||||
static cInt const loRange = 0x3FFFFFFF;
|
||||
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
|
||||
typedef signed long long long64; //used by Int128 class
|
||||
typedef unsigned long long ulong64;
|
||||
|
||||
#endif
|
||||
|
||||
struct IntPoint {
|
||||
cInt X;
|
||||
cInt Y;
|
||||
#ifdef use_xyz
|
||||
cInt Z;
|
||||
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
|
||||
#else
|
||||
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
|
||||
#endif
|
||||
|
||||
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
|
||||
{
|
||||
return a.X == b.X && a.Y == b.Y;
|
||||
}
|
||||
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
|
||||
{
|
||||
return a.X != b.X || a.Y != b.Y;
|
||||
}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef std::vector< IntPoint > Path;
|
||||
typedef std::vector< Path > Paths;
|
||||
|
||||
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
|
||||
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
|
||||
|
||||
std::ostream& operator <<(std::ostream &s, const IntPoint &p);
|
||||
std::ostream& operator <<(std::ostream &s, const Path &p);
|
||||
std::ostream& operator <<(std::ostream &s, const Paths &p);
|
||||
|
||||
struct DoublePoint
|
||||
{
|
||||
double X;
|
||||
double Y;
|
||||
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
|
||||
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef use_xyz
|
||||
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
|
||||
#endif
|
||||
|
||||
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
|
||||
enum JoinType {jtSquare, jtRound, jtMiter};
|
||||
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
|
||||
|
||||
class PolyNode;
|
||||
typedef std::vector< PolyNode* > PolyNodes;
|
||||
|
||||
class PolyNode
|
||||
{
|
||||
public:
|
||||
PolyNode();
|
||||
virtual ~PolyNode(){};
|
||||
Path Contour;
|
||||
PolyNodes Childs;
|
||||
PolyNode* Parent;
|
||||
PolyNode* GetNext() const;
|
||||
bool IsHole() const;
|
||||
bool IsOpen() const;
|
||||
int ChildCount() const;
|
||||
private:
|
||||
//PolyNode& operator =(PolyNode& other);
|
||||
unsigned Index; //node index in Parent.Childs
|
||||
bool m_IsOpen;
|
||||
JoinType m_jointype;
|
||||
EndType m_endtype;
|
||||
PolyNode* GetNextSiblingUp() const;
|
||||
void AddChild(PolyNode& child);
|
||||
friend class Clipper; //to access Index
|
||||
friend class ClipperOffset;
|
||||
};
|
||||
|
||||
class PolyTree: public PolyNode
|
||||
{
|
||||
public:
|
||||
~PolyTree(){ Clear(); };
|
||||
PolyNode* GetFirst() const;
|
||||
void Clear();
|
||||
int Total() const;
|
||||
private:
|
||||
//PolyTree& operator =(PolyTree& other);
|
||||
PolyNodes AllNodes;
|
||||
friend class Clipper; //to access AllNodes
|
||||
};
|
||||
|
||||
bool Orientation(const Path &poly);
|
||||
double Area(const Path &poly);
|
||||
int PointInPolygon(const IntPoint &pt, const Path &path);
|
||||
|
||||
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
|
||||
|
||||
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
|
||||
void CleanPolygon(Path& poly, double distance = 1.415);
|
||||
void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
|
||||
void CleanPolygons(Paths& polys, double distance = 1.415);
|
||||
|
||||
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
|
||||
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
|
||||
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
|
||||
|
||||
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
|
||||
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
|
||||
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
|
||||
|
||||
void ReversePath(Path& p);
|
||||
void ReversePaths(Paths& p);
|
||||
|
||||
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
|
||||
|
||||
//enums that are used internally ...
|
||||
enum EdgeSide { esLeft = 1, esRight = 2};
|
||||
|
||||
//forward declarations (for stuff used internally) ...
|
||||
struct TEdge;
|
||||
struct IntersectNode;
|
||||
struct LocalMinimum;
|
||||
struct OutPt;
|
||||
struct OutRec;
|
||||
struct Join;
|
||||
|
||||
typedef std::vector < OutRec* > PolyOutList;
|
||||
typedef std::vector < TEdge* > EdgeList;
|
||||
typedef std::vector < Join* > JoinList;
|
||||
typedef std::vector < IntersectNode* > IntersectList;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//ClipperBase is the ancestor to the Clipper class. It should not be
|
||||
//instantiated directly. This class simply abstracts the conversion of sets of
|
||||
//polygon coordinates into edge objects that are stored in a LocalMinima list.
|
||||
class ClipperBase
|
||||
{
|
||||
public:
|
||||
ClipperBase();
|
||||
virtual ~ClipperBase();
|
||||
virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
|
||||
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
|
||||
virtual void Clear();
|
||||
IntRect GetBounds();
|
||||
bool PreserveCollinear() {return m_PreserveCollinear;};
|
||||
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
|
||||
protected:
|
||||
void DisposeLocalMinimaList();
|
||||
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
|
||||
virtual void Reset();
|
||||
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
|
||||
void InsertScanbeam(const cInt Y);
|
||||
bool PopScanbeam(cInt &Y);
|
||||
bool LocalMinimaPending();
|
||||
bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
|
||||
OutRec* CreateOutRec();
|
||||
void DisposeAllOutRecs();
|
||||
void DisposeOutRec(PolyOutList::size_type index);
|
||||
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
||||
void DeleteFromAEL(TEdge *e);
|
||||
void UpdateEdgeIntoAEL(TEdge *&e);
|
||||
|
||||
typedef std::vector<LocalMinimum> MinimaList;
|
||||
MinimaList::iterator m_CurrentLM;
|
||||
MinimaList m_MinimaList;
|
||||
|
||||
bool m_UseFullRange;
|
||||
EdgeList m_edges;
|
||||
bool m_PreserveCollinear;
|
||||
bool m_HasOpenPaths;
|
||||
PolyOutList m_PolyOuts;
|
||||
TEdge *m_ActiveEdges;
|
||||
|
||||
typedef std::priority_queue<cInt> ScanbeamList;
|
||||
ScanbeamList m_Scanbeam;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Clipper : public virtual ClipperBase
|
||||
{
|
||||
public:
|
||||
Clipper(int initOptions = 0);
|
||||
bool Execute(ClipType clipType,
|
||||
Paths &solution,
|
||||
PolyFillType fillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
Paths &solution,
|
||||
PolyFillType subjFillType,
|
||||
PolyFillType clipFillType);
|
||||
bool Execute(ClipType clipType,
|
||||
PolyTree &polytree,
|
||||
PolyFillType fillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
PolyTree &polytree,
|
||||
PolyFillType subjFillType,
|
||||
PolyFillType clipFillType);
|
||||
bool ReverseSolution() { return m_ReverseOutput; };
|
||||
void ReverseSolution(bool value) {m_ReverseOutput = value;};
|
||||
bool StrictlySimple() {return m_StrictSimple;};
|
||||
void StrictlySimple(bool value) {m_StrictSimple = value;};
|
||||
//set the callback function for z value filling on intersections (otherwise Z is 0)
|
||||
#ifdef use_xyz
|
||||
void ZFillFunction(ZFillCallback zFillFunc);
|
||||
#endif
|
||||
protected:
|
||||
virtual bool ExecuteInternal();
|
||||
private:
|
||||
JoinList m_Joins;
|
||||
JoinList m_GhostJoins;
|
||||
IntersectList m_IntersectList;
|
||||
ClipType m_ClipType;
|
||||
typedef std::list<cInt> MaximaList;
|
||||
MaximaList m_Maxima;
|
||||
TEdge *m_SortedEdges;
|
||||
bool m_ExecuteLocked;
|
||||
PolyFillType m_ClipFillType;
|
||||
PolyFillType m_SubjFillType;
|
||||
bool m_ReverseOutput;
|
||||
bool m_UsingPolyTree;
|
||||
bool m_StrictSimple;
|
||||
#ifdef use_xyz
|
||||
ZFillCallback m_ZFill; //custom callback
|
||||
#endif
|
||||
void SetWindingCount(TEdge& edge);
|
||||
bool IsEvenOddFillType(const TEdge& edge) const;
|
||||
bool IsEvenOddAltFillType(const TEdge& edge) const;
|
||||
void InsertLocalMinimaIntoAEL(const cInt botY);
|
||||
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
|
||||
void AddEdgeToSEL(TEdge *edge);
|
||||
bool PopEdgeFromSEL(TEdge *&edge);
|
||||
void CopyAELToSEL();
|
||||
void DeleteFromSEL(TEdge *e);
|
||||
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
|
||||
bool IsContributing(const TEdge& edge) const;
|
||||
bool IsTopHorz(const cInt XPos);
|
||||
void DoMaxima(TEdge *e);
|
||||
void ProcessHorizontals();
|
||||
void ProcessHorizontal(TEdge *horzEdge);
|
||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutRec* GetOutRec(int idx);
|
||||
void AppendPolygon(TEdge *e1, TEdge *e2);
|
||||
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
|
||||
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
OutPt* GetLastOutPt(TEdge *e);
|
||||
bool ProcessIntersections(const cInt topY);
|
||||
void BuildIntersectList(const cInt topY);
|
||||
void ProcessIntersectList();
|
||||
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
|
||||
void BuildResult(Paths& polys);
|
||||
void BuildResult2(PolyTree& polytree);
|
||||
void SetHoleState(TEdge *e, OutRec *outrec);
|
||||
void DisposeIntersectNodes();
|
||||
bool FixupIntersectionOrder();
|
||||
void FixupOutPolygon(OutRec &outrec);
|
||||
void FixupOutPolyline(OutRec &outrec);
|
||||
bool IsHole(TEdge *e);
|
||||
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
|
||||
void FixHoleLinkage(OutRec &outrec);
|
||||
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
|
||||
void ClearJoins();
|
||||
void ClearGhostJoins();
|
||||
void AddGhostJoin(OutPt *op, const IntPoint offPt);
|
||||
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
|
||||
void JoinCommonEdges();
|
||||
void DoSimplePolygons();
|
||||
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
|
||||
void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
#ifdef use_xyz
|
||||
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
|
||||
#endif
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ClipperOffset
|
||||
{
|
||||
public:
|
||||
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
|
||||
~ClipperOffset();
|
||||
void AddPath(const Path& path, JoinType joinType, EndType endType);
|
||||
void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
|
||||
void Execute(Paths& solution, double delta);
|
||||
void Execute(PolyTree& solution, double delta);
|
||||
void Clear();
|
||||
double MiterLimit;
|
||||
double ArcTolerance;
|
||||
private:
|
||||
Paths m_destPolys;
|
||||
Path m_srcPoly;
|
||||
Path m_destPoly;
|
||||
std::vector<DoublePoint> m_normals;
|
||||
double m_delta, m_sinA, m_sin, m_cos;
|
||||
double m_miterLim, m_StepsPerRad;
|
||||
IntPoint m_lowest;
|
||||
PolyNode m_polyNodes;
|
||||
|
||||
void FixOrientations();
|
||||
void DoOffset(double delta);
|
||||
void OffsetPoint(int j, int& k, JoinType jointype);
|
||||
void DoSquare(int j, int k);
|
||||
void DoMiter(int j, int k, double r);
|
||||
void DoRound(int j, int k);
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class clipperException : public std::exception
|
||||
{
|
||||
public:
|
||||
clipperException(const char* description): m_descr(description) {}
|
||||
virtual ~clipperException() throw() {}
|
||||
virtual const char* what() const throw() {return m_descr.c_str();}
|
||||
private:
|
||||
std::string m_descr;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} //ClipperLib namespace
|
||||
|
||||
#endif //clipper_hpp
|
||||
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* getopt - POSIX like getopt for Windows console Application
|
||||
*
|
||||
* win-c - Windows Console Library
|
||||
* Copyright (c) 2015 Koji Takami
|
||||
* Released under the MIT license
|
||||
* https://github.com/takamin/win-c/blob/master/LICENSE
|
||||
*/
|
||||
#ifndef _GETOPT_H_
|
||||
#define _GETOPT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
int getopt(int argc, char *const argv[],
|
||||
const char *optstring);
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt;
|
||||
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
struct option {
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
int getopt_long(int argc, char *const argv[],
|
||||
const char *optstring,
|
||||
const struct option *longopts, int *longindex);
|
||||
/****************************************************************************
|
||||
int getopt_long_only(int argc, char* const argv[],
|
||||
const char* optstring,
|
||||
const struct option* longopts, int* longindex);
|
||||
****************************************************************************/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
#endif // _GETOPT_H_
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#ifndef __MAIN_H__
|
||||
#define __MAIN_H__
|
||||
|
||||
#include "getopt.h"
|
||||
|
||||
static const struct option long_options[] = {
|
||||
{"models", required_argument, NULL, 'd'},
|
||||
{"det", required_argument, NULL, '1'},
|
||||
{"cls", required_argument, NULL, '2'},
|
||||
{"rec", required_argument, NULL, '3'},
|
||||
{"keys", required_argument, NULL, '4'},
|
||||
{"image", required_argument, NULL, 'i'},
|
||||
{"numThread", required_argument, NULL, 't'},
|
||||
{"padding", required_argument, NULL, 'p'},
|
||||
{"maxSideLen", required_argument, NULL, 's'},
|
||||
{"boxScoreThresh", required_argument, NULL, 'b'},
|
||||
{"boxThresh", required_argument, NULL, 'o'},
|
||||
{"unClipRatio", required_argument, NULL, 'u'},
|
||||
{"doAngle", required_argument, NULL, 'a'},
|
||||
{"mostAngle", required_argument, NULL, 'A'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"loopCount", required_argument, NULL, 'l'},
|
||||
{NULL, no_argument, NULL, 0}
|
||||
};
|
||||
|
||||
const char *usageMsg = "(-d --models) (-1 --det) (-2 --cls) (-3 --rec) (-4 --keys) (-i --image)\n"\
|
||||
"[-t --numThread] [-p --padding] [-s --maxSideLen]\n" \
|
||||
"[-b --boxScoreThresh] [-o --boxThresh] [-u --unClipRatio]\n" \
|
||||
"[-a --noAngle] [-A --mostAngle]\n\n";
|
||||
|
||||
const char *requiredMsg = "-d --models: models directory.\n" \
|
||||
"-1 --det: model file name of det.\n" \
|
||||
"-2 --cls: model file name of cls.\n" \
|
||||
"-3 --rec: model file name of rec.\n" \
|
||||
"-i --image: path of target image.\n\n";
|
||||
|
||||
const char *optionalMsg = "-t --numThread: value of numThread(int), default: 4\n" \
|
||||
"-p --padding: value of padding(int), default: 50\n" \
|
||||
"-s --maxSideLen: Long side of picture for resize(int), default: 1024\n" \
|
||||
"-b --boxScoreThresh: value of boxScoreThresh(float), default: 0.5\n" \
|
||||
"-o --boxThresh: value of boxThresh(float), default: 0.3\n" \
|
||||
"-u --unClipRatio: value of unClipRatio(float), default: 2.0\n" \
|
||||
"-a --doAngle: Enable(1)/Disable(0) Angle Net, default: Enable\n" \
|
||||
"-A --mostAngle: Enable(1)/Disable(0) Most Possible AngleIndex, default: Disable\n\n";
|
||||
|
||||
const char *otherMsg = "-v --version: show version\n" \
|
||||
"-h --help: print this help\n\n";
|
||||
|
||||
const char *example1Msg = "Example1: %s --models models --det det.onnx --cls cls.onnx --rec rec.onnx --image 1.jpg\n";
|
||||
const char *example2Msg = "Example2: %s -d models -1 det.onnx -2 cls.onnx -3 rec.onnx -i 1.jpg -t 4 -p 50 -s 0 -b 0.6 -o 0.3 -u 2.0 -a 1 -A 1\n";
|
||||
|
||||
#endif //__MAIN_H__
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef __OCR_VERSION_H__
|
||||
#define __OCR_VERSION_H__
|
||||
|
||||
#define VERSION "1.0.0"
|
||||
|
||||
#endif //__OCR_VERSION_H__
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
chcp 65001
|
||||
:: Set Param
|
||||
@ECHO OFF
|
||||
@SETLOCAL
|
||||
echo "Setting the Number of Threads=%NUMBER_OF_PROCESSORS% Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=%NUMBER_OF_PROCESSORS%
|
||||
|
||||
echo "请选择det模型: 1)server, 2)mobile"
|
||||
set /p flag=
|
||||
if %flag% == 1 (set DET_MODEL="ch_ppocr_server_v2.0_det_infer.onnx")^
|
||||
else if %flag% == 2 (set DET_MODEL="ch_ppocr_mobile_v2.0_det_infer.onnx")^
|
||||
else (echo 输入错误!Input Error!)
|
||||
|
||||
echo "请输入循环次数:"
|
||||
set /p LOOP_COUNT=
|
||||
|
||||
:: run Windows
|
||||
build\benchmark.exe --models models ^
|
||||
--det %DET_MODEL% ^
|
||||
--cls ch_ppocr_mobile_v2.0_cls_infer.onnx ^
|
||||
--rec ch_ppocr_server_v2.0_rec_infer.onnx ^
|
||||
--keys ppocr_keys_v1.txt ^
|
||||
--image ../../images/1.jpg ^
|
||||
--numThread %NUMBER_OF_PROCESSORS% ^
|
||||
--padding 0 ^
|
||||
--maxSideLen 1024 ^
|
||||
--boxScoreThresh 0.5 ^
|
||||
--boxThresh 0.3 ^
|
||||
--unClipRatio 1.5 ^
|
||||
--doAngle 1 ^
|
||||
--mostAngle 0 ^
|
||||
--loopCount %LOOP_COUNT%
|
||||
PAUSE
|
||||
@ENDLOCAL
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sysOS=`uname -s`
|
||||
NUM_THREADS=1
|
||||
if [ $sysOS == "Darwin" ];then
|
||||
#echo "I'm MacOS"
|
||||
NUM_THREADS=$(sysctl -n hw.ncpu)
|
||||
elif [ $sysOS == "Linux" ];then
|
||||
#echo "I'm Linux"
|
||||
NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l)
|
||||
else
|
||||
echo "Other OS: $sysOS"
|
||||
fi
|
||||
|
||||
echo "Setting the Number of Threads=$NUM_THREADS Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=$NUM_THREADS
|
||||
|
||||
echo "请输入循环次数:"
|
||||
read -p "" LOOP_COUNT
|
||||
|
||||
##### run test on MacOS or Linux
|
||||
./build/benchmark --models models \
|
||||
--det ch_ppocr_server_v2.0_det_infer.onnx \
|
||||
--cls ch_ppocr_mobile_v2.0_cls_infer.onnx \
|
||||
--rec ch_ppocr_server_v2.0_rec_infer.onnx \
|
||||
--keys ppocr_keys_v1.txt \
|
||||
--image ../../images/1.jpg \
|
||||
--numThread $NUM_THREADS \
|
||||
--padding 0 \
|
||||
--maxSideLen 1024 \
|
||||
--boxScoreThresh 0.5 \
|
||||
--boxThresh 0.3 \
|
||||
--unClipRatio 1.5 \
|
||||
--doAngle 1 \
|
||||
--mostAngle 0 \
|
||||
--loopCount $LOOP_COUNT
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
chcp 65001
|
||||
:: Set Param
|
||||
@ECHO OFF
|
||||
@SETLOCAL
|
||||
echo "Setting the Number of Threads=%NUMBER_OF_PROCESSORS% Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=%NUMBER_OF_PROCESSORS%
|
||||
|
||||
echo "请选择det模型: 1)server, 2)mobile"
|
||||
set /p flag=
|
||||
if %flag% == 1 (set DET_MODEL="ch_ppocr_server_v2.0_det_infer.onnx")^
|
||||
else if %flag% == 2 (set DET_MODEL="ch_ppocr_mobile_v2.0_det_infer.onnx")^
|
||||
else (echo 输入错误!Input Error!)
|
||||
|
||||
:: run Windows
|
||||
build\BaiPiaoOcrOnnx.exe --models models ^
|
||||
--det %DET_MODEL% ^
|
||||
--cls ch_ppocr_mobile_v2.0_cls_infer.onnx ^
|
||||
--rec ch_ppocr_server_v2.0_rec_infer.onnx ^
|
||||
--keys ppocr_keys_v1.txt ^
|
||||
--image ../../images/1.jpg ^
|
||||
--numThread %NUMBER_OF_PROCESSORS% ^
|
||||
--padding 0 ^
|
||||
--maxSideLen 1024 ^
|
||||
--boxScoreThresh 0.5 ^
|
||||
--boxThresh 0.3 ^
|
||||
--unClipRatio 1.5 ^
|
||||
--doAngle 1 ^
|
||||
--mostAngle 0
|
||||
PAUSE
|
||||
@ENDLOCAL
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sysOS=`uname -s`
|
||||
NUM_THREADS=1
|
||||
if [ $sysOS == "Darwin" ];then
|
||||
#echo "I'm MacOS"
|
||||
NUM_THREADS=$(sysctl -n hw.ncpu)
|
||||
elif [ $sysOS == "Linux" ];then
|
||||
#echo "I'm Linux"
|
||||
NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l)
|
||||
else
|
||||
echo "Other OS: $sysOS"
|
||||
fi
|
||||
|
||||
echo "Setting the Number of Threads=$NUM_THREADS Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=$NUM_THREADS
|
||||
|
||||
##### run test on MacOS or Linux
|
||||
./build/BaiPiaoOcrOnnx --models models \
|
||||
--det ch_ppocr_server_v2.0_det_infer.onnx \
|
||||
--cls ch_ppocr_mobile_v2.0_cls_infer.onnx \
|
||||
--rec ch_ppocr_server_v2.0_rec_infer.onnx \
|
||||
--keys ppocr_keys_v1.txt \
|
||||
--image ../../images/1.jpg \
|
||||
--numThread $NUM_THREADS \
|
||||
--padding 0 \
|
||||
--maxSideLen 1024 \
|
||||
--boxScoreThresh 0.5 \
|
||||
--boxThresh 0.3 \
|
||||
--unClipRatio 1.5 \
|
||||
--doAngle 1 \
|
||||
--mostAngle 0
|
||||
Executable
+139
@@ -0,0 +1,139 @@
|
||||
#include "AngleNet.h"
|
||||
#include "OcrUtils.h"
|
||||
#include <numeric>
|
||||
|
||||
AngleNet::AngleNet() {}
|
||||
|
||||
AngleNet::~AngleNet() {
|
||||
delete session;
|
||||
for (auto name : inputNames) {
|
||||
free(name);
|
||||
}
|
||||
for (auto name : outputNames) {
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
void AngleNet::setNumThread(int numOfThread) {
|
||||
numThread = numOfThread;
|
||||
//===session options===
|
||||
// Sets the number of threads used to parallelize the execution within nodes
|
||||
// A value of 0 means ORT will pick a default
|
||||
//sessionOptions.SetIntraOpNumThreads(numThread);
|
||||
//set OMP_NUM_THREADS=16
|
||||
|
||||
// Sets the number of threads used to parallelize the execution of the graph (across nodes)
|
||||
// If sequential execution is enabled this value is ignored
|
||||
// A value of 0 means ORT will pick a default
|
||||
sessionOptions.SetInterOpNumThreads(numThread);
|
||||
|
||||
// Sets graph optimization level
|
||||
// ORT_DISABLE_ALL -> To disable all optimizations
|
||||
// ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node removals)
|
||||
// ORT_ENABLE_EXTENDED -> To enable extended optimizations (Includes level 1 + more complex optimizations like node fusions)
|
||||
// ORT_ENABLE_ALL -> To Enable All possible opitmizations
|
||||
sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
|
||||
}
|
||||
|
||||
void AngleNet::initModel(const std::string &pathStr) {
|
||||
#ifdef _WIN32
|
||||
std::wstring clsPath = strToWstr(pathStr);
|
||||
session = new Ort::Session(env, clsPath.c_str(), sessionOptions);
|
||||
#else
|
||||
session = new Ort::Session(env, pathStr.c_str(), sessionOptions);
|
||||
#endif
|
||||
inputNames = getInputNames(session);
|
||||
outputNames = getOutputNames(session);
|
||||
}
|
||||
|
||||
Angle scoreToAngle(const std::vector<float> &outputData) {
|
||||
int maxIndex = 0;
|
||||
float maxScore = 0;
|
||||
for (int i = 0; i < outputData.size(); i++) {
|
||||
if (outputData[i] > maxScore) {
|
||||
maxScore = outputData[i];
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
return {maxIndex, maxScore};
|
||||
}
|
||||
|
||||
Angle AngleNet::getAngle(cv::Mat &src) {
|
||||
|
||||
std::vector<float> inputTensorValues = substractMeanNormalize(src, meanValues, normValues);
|
||||
|
||||
std::array<int64_t, 4> inputShape{1, src.channels(), src.rows, src.cols};
|
||||
|
||||
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
|
||||
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, inputTensorValues.data(),
|
||||
inputTensorValues.size(), inputShape.data(),
|
||||
inputShape.size());
|
||||
assert(inputTensor.IsTensor());
|
||||
|
||||
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, inputNames.data(), &inputTensor,
|
||||
inputNames.size(), outputNames.data(), outputNames.size());
|
||||
|
||||
assert(outputTensor.size() == 1 && outputTensor.front().IsTensor());
|
||||
|
||||
std::vector<int64_t> outputShape = outputTensor[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
|
||||
int64_t outputCount = std::accumulate(outputShape.begin(), outputShape.end(), 1,
|
||||
std::multiplies<int64_t>());
|
||||
|
||||
float *floatArray = outputTensor.front().GetTensorMutableData<float>();
|
||||
std::vector<float> outputData(floatArray, floatArray + outputCount);
|
||||
return scoreToAngle(outputData);
|
||||
}
|
||||
|
||||
std::vector<Angle> AngleNet::getAngles(std::vector<cv::Mat> &partImgs, const char *path,
|
||||
const char *imgName, bool doAngle, bool mostAngle) {
|
||||
int size = partImgs.size();
|
||||
std::vector<Angle> angles(size);
|
||||
if (doAngle) {
|
||||
#ifdef __OPENMP__
|
||||
#pragma omp parallel for num_threads(numThread)
|
||||
#endif
|
||||
for (int i = 0; i < size; ++i) {
|
||||
double startAngle = getCurrentTime();
|
||||
cv::Mat angleImg;
|
||||
cv::resize(partImgs[i], angleImg, cv::Size(dstWidth, dstHeight));
|
||||
//auto angleImg = adjustTargetImg(partImgs[i], dstWidth, dstHeight);
|
||||
Angle angle = getAngle(angleImg);
|
||||
double endAngle = getCurrentTime();
|
||||
angle.time = endAngle - startAngle;
|
||||
|
||||
angles[i] = angle;
|
||||
|
||||
//OutPut AngleImg
|
||||
if (isOutputAngleImg) {
|
||||
std::string angleImgFile = getDebugImgFilePath(path, imgName, i, "-angle-");
|
||||
saveImg(angleImg, angleImgFile.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
angles[i] = Angle{-1, 0.f};
|
||||
}
|
||||
}
|
||||
//Most Possible AngleIndex
|
||||
if (doAngle && mostAngle) {
|
||||
auto angleIndexes = getAngleIndexes(angles);
|
||||
double sum = std::accumulate(angleIndexes.begin(), angleIndexes.end(), 0.0);
|
||||
double halfPercent = angles.size() / 2.0f;
|
||||
int mostAngleIndex;
|
||||
if (sum < halfPercent) {//all angle set to 0
|
||||
mostAngleIndex = 0;
|
||||
} else {//all angle set to 1
|
||||
mostAngleIndex = 1;
|
||||
}
|
||||
printf("Set All Angle to mostAngleIndex(%d)\n", mostAngleIndex);
|
||||
for (int i = 0; i < angles.size(); ++i) {
|
||||
Angle angle = angles[i];
|
||||
angle.index = mostAngleIndex;
|
||||
angles.at(i) = angle;
|
||||
}
|
||||
}
|
||||
|
||||
return angles;
|
||||
}
|
||||
Executable
+149
@@ -0,0 +1,149 @@
|
||||
#include "CrnnNet.h"
|
||||
#include "OcrUtils.h"
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
|
||||
CrnnNet::CrnnNet() {}
|
||||
|
||||
CrnnNet::~CrnnNet() {
|
||||
delete session;
|
||||
for (auto name : inputNames) {
|
||||
free(name);
|
||||
}
|
||||
for (auto name : outputNames) {
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
void CrnnNet::setNumThread(int numOfThread) {
|
||||
numThread = numOfThread;
|
||||
//===session options===
|
||||
// Sets the number of threads used to parallelize the execution within nodes
|
||||
// A value of 0 means ORT will pick a default
|
||||
//sessionOptions.SetIntraOpNumThreads(numThread);
|
||||
//set OMP_NUM_THREADS=16
|
||||
|
||||
// Sets the number of threads used to parallelize the execution of the graph (across nodes)
|
||||
// If sequential execution is enabled this value is ignored
|
||||
// A value of 0 means ORT will pick a default
|
||||
sessionOptions.SetInterOpNumThreads(numThread);
|
||||
|
||||
// Sets graph optimization level
|
||||
// ORT_DISABLE_ALL -> To disable all optimizations
|
||||
// ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node removals)
|
||||
// ORT_ENABLE_EXTENDED -> To enable extended optimizations (Includes level 1 + more complex optimizations like node fusions)
|
||||
// ORT_ENABLE_ALL -> To Enable All possible opitmizations
|
||||
sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
|
||||
}
|
||||
|
||||
void CrnnNet::initModel(const std::string &pathStr, const std::string &keysPath) {
|
||||
#ifdef _WIN32
|
||||
std::wstring crnnPath = strToWstr(pathStr);
|
||||
session = new Ort::Session(env, crnnPath.c_str(), sessionOptions);
|
||||
#else
|
||||
session = new Ort::Session(env, pathStr.c_str(), sessionOptions);
|
||||
#endif
|
||||
inputNames = getInputNames(session);
|
||||
outputNames = getOutputNames(session);
|
||||
|
||||
//load keys
|
||||
std::ifstream in(keysPath.c_str());
|
||||
std::string line;
|
||||
if (in) {
|
||||
while (getline(in, line)) {// line中不包括每行的换行符
|
||||
keys.push_back(line);
|
||||
}
|
||||
} else {
|
||||
printf("The keys.txt file was not found\n");
|
||||
return;
|
||||
}
|
||||
if (keys.size() != 6623) {
|
||||
fprintf(stderr, "missing keys\n");
|
||||
}
|
||||
keys.insert(keys.begin(),
|
||||
"#"); // blank char for ctc
|
||||
keys.emplace_back(" ");
|
||||
printf("total keys size(%lu)\n", keys.size());
|
||||
}
|
||||
|
||||
template<class ForwardIterator>
|
||||
inline static size_t argmax(ForwardIterator first, ForwardIterator last) {
|
||||
return std::distance(first, std::max_element(first, last));
|
||||
}
|
||||
|
||||
TextLine CrnnNet::scoreToTextLine(const std::vector<float> &outputData, int h, int w) {
|
||||
int keySize = keys.size();
|
||||
std::string strRes;
|
||||
std::vector<float> scores;
|
||||
int lastIndex = 0;
|
||||
int maxIndex;
|
||||
float maxValue;
|
||||
|
||||
for (int i = 0; i < h; i++) {
|
||||
maxIndex = int(argmax(&outputData[i * w], &outputData[(i + 1) * w]));
|
||||
maxValue = float(*std::max_element(&outputData[i * w], &outputData[(i + 1) * w]));
|
||||
|
||||
if (maxIndex > 0 && maxIndex < keySize && (!(i > 0 && maxIndex == lastIndex))) {
|
||||
scores.push_back(maxValue);
|
||||
strRes.append(keys[maxIndex]);
|
||||
}
|
||||
lastIndex = maxIndex;
|
||||
}
|
||||
return {strRes, scores};
|
||||
}
|
||||
|
||||
TextLine CrnnNet::getTextLine(const cv::Mat &src) {
|
||||
float scale = (float) dstHeight / (float) src.rows;
|
||||
int dstWidth = int((float) src.cols * scale);
|
||||
|
||||
cv::Mat srcResize;
|
||||
resize(src, srcResize, cv::Size(dstWidth, dstHeight));
|
||||
|
||||
std::vector<float> inputTensorValues = substractMeanNormalize(srcResize, meanValues, normValues);
|
||||
|
||||
std::array<int64_t, 4> inputShape{1, srcResize.channels(), srcResize.rows, srcResize.cols};
|
||||
|
||||
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
|
||||
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, inputTensorValues.data(),
|
||||
inputTensorValues.size(), inputShape.data(),
|
||||
inputShape.size());
|
||||
assert(inputTensor.IsTensor());
|
||||
|
||||
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, inputNames.data(), &inputTensor,
|
||||
inputNames.size(), outputNames.data(), outputNames.size());
|
||||
|
||||
assert(outputTensor.size() == 1 && outputTensor.front().IsTensor());
|
||||
|
||||
std::vector<int64_t> outputShape = outputTensor[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
|
||||
int64_t outputCount = std::accumulate(outputShape.begin(), outputShape.end(), 1,
|
||||
std::multiplies<int64_t>());
|
||||
|
||||
float *floatArray = outputTensor.front().GetTensorMutableData<float>();
|
||||
std::vector<float> outputData(floatArray, floatArray + outputCount);
|
||||
return scoreToTextLine(outputData, outputShape[1], outputShape[2]);
|
||||
}
|
||||
|
||||
std::vector<TextLine> CrnnNet::getTextLines(std::vector<cv::Mat> &partImg, const char *path, const char *imgName) {
|
||||
int size = partImg.size();
|
||||
std::vector<TextLine> textLines(size);
|
||||
#ifdef __OPENMP__
|
||||
#pragma omp parallel for num_threads(numThread)
|
||||
#endif
|
||||
for (int i = 0; i < size; ++i) {
|
||||
//OutPut DebugImg
|
||||
if (isOutputDebugImg) {
|
||||
std::string debugImgFile = getDebugImgFilePath(path, imgName, i, "-debug-");
|
||||
saveImg(partImg[i], debugImgFile.c_str());
|
||||
}
|
||||
|
||||
//getTextLine
|
||||
double startCrnnTime = getCurrentTime();
|
||||
TextLine textLine = getTextLine(partImg[i]);
|
||||
double endCrnnTime = getCurrentTime();
|
||||
textLine.time = endCrnnTime - startCrnnTime;
|
||||
textLines[i] = textLine;
|
||||
}
|
||||
return textLines;
|
||||
}
|
||||
Executable
+157
@@ -0,0 +1,157 @@
|
||||
#include "DbNet.h"
|
||||
#include "OcrUtils.h"
|
||||
#include "clipper.hpp"
|
||||
|
||||
DbNet::DbNet() {}
|
||||
|
||||
DbNet::~DbNet() {
|
||||
delete session;
|
||||
for (auto name : inputNames) {
|
||||
free(name);
|
||||
}
|
||||
for (auto name : outputNames) {
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
void DbNet::setNumThread(int numOfThread) {
|
||||
numThread = numOfThread;
|
||||
//===session options===
|
||||
// Sets the number of threads used to parallelize the execution within nodes
|
||||
// A value of 0 means ORT will pick a default
|
||||
//sessionOptions.SetIntraOpNumThreads(numThread);
|
||||
//set OMP_NUM_THREADS=16
|
||||
|
||||
// Sets the number of threads used to parallelize the execution of the graph (across nodes)
|
||||
// If sequential execution is enabled this value is ignored
|
||||
// A value of 0 means ORT will pick a default
|
||||
sessionOptions.SetInterOpNumThreads(numThread);
|
||||
|
||||
// Sets graph optimization level
|
||||
// ORT_DISABLE_ALL -> To disable all optimizations
|
||||
// ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node removals)
|
||||
// ORT_ENABLE_EXTENDED -> To enable extended optimizations (Includes level 1 + more complex optimizations like node fusions)
|
||||
// ORT_ENABLE_ALL -> To Enable All possible opitmizations
|
||||
sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
|
||||
}
|
||||
|
||||
void DbNet::initModel(const std::string &pathStr) {
|
||||
#ifdef _WIN32
|
||||
std::wstring detPath = strToWstr(pathStr);
|
||||
session = new Ort::Session(env, detPath.c_str(), sessionOptions);
|
||||
#else
|
||||
session = new Ort::Session(env, pathStr.c_str(), sessionOptions);
|
||||
#endif
|
||||
inputNames = getInputNames(session);
|
||||
outputNames = getOutputNames(session);
|
||||
}
|
||||
|
||||
std::vector<TextBox> findRsBoxes(const cv::Mat &predMat, const cv::Mat &dilateMat, ScaleParam &s,
|
||||
const float boxScoreThresh, const float unClipRatio) {
|
||||
const int longSideThresh = 3;//minBox 长边门限
|
||||
const int maxCandidates = 1000;
|
||||
|
||||
std::vector<std::vector<cv::Point>> contours;
|
||||
std::vector<cv::Vec4i> hierarchy;
|
||||
|
||||
cv::findContours(dilateMat, contours, hierarchy, cv::RETR_LIST,
|
||||
cv::CHAIN_APPROX_SIMPLE);
|
||||
|
||||
int numContours = contours.size() >= maxCandidates ? maxCandidates : contours.size();
|
||||
|
||||
std::vector<TextBox> rsBoxes;
|
||||
|
||||
for (int i = 0; i < numContours; i++) {
|
||||
if (contours[i].size() <= 2) {
|
||||
continue;
|
||||
}
|
||||
cv::RotatedRect minAreaRect = cv::minAreaRect(contours[i]);
|
||||
|
||||
float longSide;
|
||||
std::vector<cv::Point2f> minBoxes = getMinBoxes(minAreaRect, longSide);
|
||||
|
||||
if (longSide < longSideThresh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float boxScore = boxScoreFast(minBoxes, predMat);
|
||||
if (boxScore < boxScoreThresh)
|
||||
continue;
|
||||
|
||||
//-----unClip-----
|
||||
cv::RotatedRect clipRect = unClip(minBoxes, unClipRatio);
|
||||
if (clipRect.size.height < 1.001 && clipRect.size.width < 1.001) {
|
||||
continue;
|
||||
}
|
||||
//-----unClip-----
|
||||
|
||||
std::vector<cv::Point2f> clipMinBoxes = getMinBoxes(clipRect, longSide);
|
||||
if (longSide < longSideThresh + 2)
|
||||
continue;
|
||||
|
||||
std::vector<cv::Point> intClipMinBoxes;
|
||||
|
||||
for (int p = 0; p < clipMinBoxes.size(); p++) {
|
||||
float x = clipMinBoxes[p].x / s.ratioWidth;
|
||||
float y = clipMinBoxes[p].y / s.ratioHeight;
|
||||
int ptX = (std::min)((std::max)(int(x), 0), s.srcWidth - 1);
|
||||
int ptY = (std::min)((std::max)(int(y), 0), s.srcHeight - 1);
|
||||
cv::Point point{ptX, ptY};
|
||||
intClipMinBoxes.push_back(point);
|
||||
}
|
||||
rsBoxes.push_back(TextBox{intClipMinBoxes, boxScore});
|
||||
}
|
||||
reverse(rsBoxes.begin(), rsBoxes.end());
|
||||
return rsBoxes;
|
||||
}
|
||||
|
||||
|
||||
std::vector<TextBox>
|
||||
DbNet::getTextBoxes(cv::Mat &src, ScaleParam &s, float boxScoreThresh, float boxThresh, float unClipRatio) {
|
||||
cv::Mat srcResize;
|
||||
resize(src, srcResize, cv::Size(s.dstWidth, s.dstHeight));
|
||||
std::vector<float> inputTensorValues = substractMeanNormalize(srcResize, meanValues, normValues);
|
||||
std::array<int64_t, 4> inputShape{1, srcResize.channels(), srcResize.rows, srcResize.cols};
|
||||
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, inputTensorValues.data(),
|
||||
inputTensorValues.size(), inputShape.data(),
|
||||
inputShape.size());
|
||||
assert(inputTensor.IsTensor());
|
||||
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, inputNames.data(), &inputTensor,
|
||||
inputNames.size(), outputNames.data(), outputNames.size());
|
||||
assert(outputTensor.size() == 1 && outputTensor.front().IsTensor());
|
||||
std::vector<int64_t> outputShape = outputTensor[0].GetTensorTypeAndShapeInfo().GetShape();
|
||||
int64_t outputCount = std::accumulate(outputShape.begin(), outputShape.end(), 1,
|
||||
std::multiplies<int64_t>());
|
||||
float *floatArray = outputTensor.front().GetTensorMutableData<float>();
|
||||
std::vector<float> outputData(floatArray, floatArray + outputCount);
|
||||
|
||||
//-----Data preparation-----
|
||||
int outHeight = outputShape[2];
|
||||
int outWidth = outputShape[3];
|
||||
int area = outHeight * outWidth;
|
||||
|
||||
std::vector<float> predData(area, 0.0);
|
||||
std::vector<unsigned char> cbufData(area, ' ');
|
||||
|
||||
for (int i = 0; i < area; i++) {
|
||||
predData[i] = float(outputData[i]);
|
||||
cbufData[i] = (unsigned char) ((outputData[i]) * 255);
|
||||
}
|
||||
|
||||
cv::Mat predMat(outHeight, outWidth, CV_32F, (float *) predData.data());
|
||||
cv::Mat cBufMat(outHeight, outWidth, CV_8UC1, (unsigned char *) cbufData.data());
|
||||
|
||||
//-----boxThresh-----
|
||||
const double maxValue = 255;
|
||||
const double threshold = boxThresh * 255;
|
||||
cv::Mat thresholdMat;
|
||||
cv::threshold(cBufMat, thresholdMat, threshold, maxValue, cv::THRESH_BINARY);
|
||||
|
||||
//-----dilate-----
|
||||
cv::Mat dilateMat;
|
||||
cv::Mat dilateElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));
|
||||
cv::dilate(thresholdMat, dilateMat, dilateElement);
|
||||
|
||||
return findRsBoxes(predMat, dilateMat, s, boxScoreThresh, unClipRatio);
|
||||
}
|
||||
Executable
+213
@@ -0,0 +1,213 @@
|
||||
#include "OcrLite.h"
|
||||
#include "OcrUtils.h"
|
||||
#include <stdarg.h> //windows&linux
|
||||
|
||||
OcrLite::OcrLite() {}
|
||||
|
||||
OcrLite::~OcrLite() {
|
||||
if (isOutputResultTxt) {
|
||||
fclose(resultTxt);
|
||||
}
|
||||
}
|
||||
|
||||
void OcrLite::setNumThread(int numOfThread) {
|
||||
dbNet.setNumThread(numOfThread);
|
||||
angleNet.setNumThread(numOfThread);
|
||||
crnnNet.setNumThread(numOfThread);
|
||||
}
|
||||
|
||||
void OcrLite::initLogger(bool isConsole, bool isPartImg, bool isResultImg) {
|
||||
isOutputConsole = isConsole;
|
||||
isOutputPartImg = isPartImg;
|
||||
isOutputResultImg = isResultImg;
|
||||
}
|
||||
|
||||
void OcrLite::enableResultTxt(const char *path, const char *imgName) {
|
||||
isOutputResultTxt = true;
|
||||
std::string resultTxtPath = getResultTxtFilePath(path, imgName);
|
||||
printf("resultTxtPath(%s)\n", resultTxtPath.c_str());
|
||||
resultTxt = fopen(resultTxtPath.c_str(), "w");
|
||||
}
|
||||
|
||||
void OcrLite::initModels(const std::string &detPath, const std::string &clsPath,
|
||||
const std::string &recPath, const std::string &keysPath) {
|
||||
Logger("=====Init Models=====\n");
|
||||
Logger("--- Init DbNet ---\n");
|
||||
dbNet.initModel(detPath);
|
||||
|
||||
Logger("--- Init AngleNet ---\n");
|
||||
angleNet.initModel(clsPath);
|
||||
|
||||
Logger("--- Init CrnnNet ---\n");
|
||||
crnnNet.initModel(recPath, keysPath);
|
||||
|
||||
Logger("Init Models Success!\n");
|
||||
}
|
||||
|
||||
void OcrLite::Logger(const char *format, ...) {
|
||||
if (!(isOutputConsole || isOutputResultTxt)) return;
|
||||
char *buffer = (char *) malloc(8192);
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsprintf(buffer, format, args);
|
||||
va_end(args);
|
||||
if (isOutputConsole) printf("%s", buffer);
|
||||
if (isOutputResultTxt) fprintf(resultTxt, "%s", buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
cv::Mat makePadding(cv::Mat &src, const int padding) {
|
||||
if (padding <= 0) return src;
|
||||
cv::Scalar paddingScalar = {255, 255, 255};
|
||||
cv::Mat paddingSrc;
|
||||
cv::copyMakeBorder(src, paddingSrc, padding, padding, padding, padding, cv::BORDER_ISOLATED, paddingScalar);
|
||||
return paddingSrc;
|
||||
}
|
||||
|
||||
OcrResult OcrLite::detect(const char *path, const char *imgName,
|
||||
const int padding, const int maxSideLen,
|
||||
float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle) {
|
||||
std::string imgFile = getSrcImgFilePath(path, imgName);
|
||||
|
||||
cv::Mat originSrc = imread(imgFile, cv::IMREAD_COLOR);//default : BGR
|
||||
int originMaxSide = (std::max)(originSrc.cols, originSrc.rows);
|
||||
int resize;
|
||||
if (maxSideLen <= 0 || maxSideLen > originMaxSide) {
|
||||
resize = originMaxSide;
|
||||
} else {
|
||||
resize = maxSideLen;
|
||||
}
|
||||
resize += 2*padding;
|
||||
cv::Rect paddingRect(padding, padding, originSrc.cols, originSrc.rows);
|
||||
cv::Mat paddingSrc = makePadding(originSrc, padding);
|
||||
ScaleParam scale = getScaleParam(paddingSrc, resize);
|
||||
OcrResult result;
|
||||
result = detect(path, imgName, paddingSrc, paddingRect, scale,
|
||||
boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> OcrLite::getPartImages(cv::Mat &src, std::vector<TextBox> &textBoxes,
|
||||
const char *path, const char *imgName) {
|
||||
std::vector<cv::Mat> partImages;
|
||||
for (int i = 0; i < textBoxes.size(); ++i) {
|
||||
cv::Mat partImg = getRotateCropImage(src, textBoxes[i].boxPoint);
|
||||
partImages.emplace_back(partImg);
|
||||
//OutPut DebugImg
|
||||
if (isOutputPartImg) {
|
||||
std::string debugImgFile = getDebugImgFilePath(path, imgName, i, "-part-");
|
||||
saveImg(partImg, debugImgFile.c_str());
|
||||
}
|
||||
}
|
||||
return partImages;
|
||||
}
|
||||
|
||||
OcrResult OcrLite::detect(const char *path, const char *imgName,
|
||||
cv::Mat &src, cv::Rect &originRect, ScaleParam &scale,
|
||||
float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle) {
|
||||
|
||||
cv::Mat textBoxPaddingImg = src.clone();
|
||||
int thickness = getThickness(src);
|
||||
|
||||
Logger("=====Start detect=====\n");
|
||||
Logger("ScaleParam(sw:%d,sh:%d,dw:%d,dh:%d,%f,%f)\n", scale.srcWidth, scale.srcHeight,
|
||||
scale.dstWidth, scale.dstHeight,
|
||||
scale.ratioWidth, scale.ratioHeight);
|
||||
|
||||
Logger("---------- step: dbNet getTextBoxes ----------\n");
|
||||
double startTime = getCurrentTime();
|
||||
std::vector<TextBox> textBoxes = dbNet.getTextBoxes(src, scale, boxScoreThresh, boxThresh, unClipRatio);
|
||||
double endDbNetTime = getCurrentTime();
|
||||
double dbNetTime = endDbNetTime - startTime;
|
||||
Logger("dbNetTime(%fms)\n", dbNetTime);
|
||||
|
||||
for (int i = 0; i < textBoxes.size(); ++i) {
|
||||
Logger("TextBox[%d](+padding)[score(%f),[x: %d, y: %d], [x: %d, y: %d], [x: %d, y: %d], [x: %d, y: %d]]\n", i,
|
||||
textBoxes[i].score,
|
||||
textBoxes[i].boxPoint[0].x, textBoxes[i].boxPoint[0].y,
|
||||
textBoxes[i].boxPoint[1].x, textBoxes[i].boxPoint[1].y,
|
||||
textBoxes[i].boxPoint[2].x, textBoxes[i].boxPoint[2].y,
|
||||
textBoxes[i].boxPoint[3].x, textBoxes[i].boxPoint[3].y);
|
||||
}
|
||||
|
||||
Logger("---------- step: drawTextBoxes ----------\n");
|
||||
drawTextBoxes(textBoxPaddingImg, textBoxes, thickness);
|
||||
|
||||
//---------- getPartImages ----------
|
||||
std::vector<cv::Mat> partImages = getPartImages(src, textBoxes, path, imgName);
|
||||
|
||||
Logger("---------- step: angleNet getAngles ----------\n");
|
||||
std::vector<Angle> angles;
|
||||
angles = angleNet.getAngles(partImages, path, imgName, doAngle, mostAngle);
|
||||
|
||||
//Log Angles
|
||||
for (int i = 0; i < angles.size(); ++i) {
|
||||
Logger("angle[%d][index(%d), score(%f), time(%fms)]\n", i, angles[i].index, angles[i].score, angles[i].time);
|
||||
}
|
||||
|
||||
//Rotate partImgs
|
||||
for (int i = 0; i < partImages.size(); ++i) {
|
||||
if (angles[i].index == 1) {
|
||||
partImages.at(i) = matRotateClockWise180(partImages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Logger("---------- step: crnnNet getTextLine ----------\n");
|
||||
std::vector<TextLine> textLines = crnnNet.getTextLines(partImages, path, imgName);
|
||||
//Log TextLines
|
||||
for (int i = 0; i < textLines.size(); ++i) {
|
||||
Logger("textLine[%d](%s)\n", i, textLines[i].text.c_str());
|
||||
std::ostringstream txtScores;
|
||||
for (int s = 0; s < textLines[i].charScores.size(); ++s) {
|
||||
if (s == 0) {
|
||||
txtScores << textLines[i].charScores[s];
|
||||
} else {
|
||||
txtScores << " ," << textLines[i].charScores[s];
|
||||
}
|
||||
}
|
||||
Logger("textScores[%d]{%s}\n", i, std::string(txtScores.str()).c_str());
|
||||
Logger("crnnTime[%d](%fms)\n", i, textLines[i].time);
|
||||
}
|
||||
|
||||
std::vector<TextBlock> textBlocks;
|
||||
for (int i = 0; i < textLines.size(); ++i) {
|
||||
std::vector<cv::Point> boxPoint = std::vector<cv::Point>(4);
|
||||
int padding = originRect.x;//padding conversion
|
||||
boxPoint[0] = cv::Point(textBoxes[i].boxPoint[0].x - padding, textBoxes[i].boxPoint[0].y - padding);
|
||||
boxPoint[1] = cv::Point(textBoxes[i].boxPoint[1].x - padding, textBoxes[i].boxPoint[1].y - padding);
|
||||
boxPoint[2] = cv::Point(textBoxes[i].boxPoint[2].x - padding, textBoxes[i].boxPoint[2].y - padding);
|
||||
boxPoint[3] = cv::Point(textBoxes[i].boxPoint[3].x - padding, textBoxes[i].boxPoint[3].y - padding);
|
||||
TextBlock textBlock{boxPoint, textBoxes[i].score, angles[i].index, angles[i].score,
|
||||
angles[i].time, textLines[i].text, textLines[i].charScores, textLines[i].time,
|
||||
angles[i].time + textLines[i].time};
|
||||
textBlocks.emplace_back(textBlock);
|
||||
}
|
||||
|
||||
double endTime = getCurrentTime();
|
||||
double fullTime = endTime - startTime;
|
||||
Logger("=====End detect=====\n");
|
||||
Logger("FullDetectTime(%fms)\n", fullTime);
|
||||
|
||||
//cropped to original size
|
||||
cv::Mat textBoxImg;
|
||||
|
||||
if (originRect.x > 0 && originRect.y > 0) {
|
||||
textBoxPaddingImg(originRect).copyTo(textBoxImg);
|
||||
} else {
|
||||
textBoxImg = textBoxPaddingImg;
|
||||
}
|
||||
|
||||
//Save result.jpg
|
||||
if (isOutputResultImg) {
|
||||
std::string resultImgFile = getResultImgFilePath(path, imgName);
|
||||
imwrite(resultImgFile, textBoxImg);
|
||||
}
|
||||
|
||||
std::string strRes;
|
||||
for (int i = 0; i < textBlocks.size(); ++i) {
|
||||
strRes.append(textBlocks[i].text);
|
||||
strRes.append("\n");
|
||||
}
|
||||
|
||||
return OcrResult{dbNetTime, textBlocks, textBoxImg, fullTime, strRes};
|
||||
}
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#ifdef __JNI__
|
||||
|
||||
#include "version.h"
|
||||
#include <jni.h>
|
||||
#include "OcrLite.h"
|
||||
#include "OcrResultUtils.h"
|
||||
|
||||
static OcrLite *ocrLite;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
ocrLite = new OcrLite();
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
JNI_OnUnload(JavaVM *vm, void *reserved) {
|
||||
//printf("JNI_OnUnload\n");
|
||||
delete ocrLite;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
char *jstringToChar(JNIEnv *env, jstring jstr) {
|
||||
char *rtn = NULL;
|
||||
jclass clsstring = env->FindClass("java/lang/String");
|
||||
jstring strencode = env->NewStringUTF("gbk");
|
||||
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
|
||||
jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
|
||||
jsize alen = env->GetArrayLength(barr);
|
||||
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
|
||||
if (alen > 0) {
|
||||
rtn = (char *) malloc(alen + 1);
|
||||
memcpy(rtn, ba, alen);
|
||||
rtn[alen] = 0;
|
||||
}
|
||||
env->ReleaseByteArrayElements(barr, ba, 0);
|
||||
return rtn;
|
||||
}
|
||||
#else
|
||||
|
||||
char *jstringToChar(JNIEnv *env, jstring input) {
|
||||
char *str = NULL;
|
||||
jclass clsstring = env->FindClass("java/lang/String");
|
||||
jstring strencode = env->NewStringUTF("utf-8");
|
||||
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
|
||||
jbyteArray barr = (jbyteArray) env->CallObjectMethod(input, mid, strencode);
|
||||
jsize alen = env->GetArrayLength(barr);
|
||||
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
|
||||
if (alen > 0) {
|
||||
str = (char *) malloc(alen + 1);
|
||||
memcpy(str, ba, alen);
|
||||
str[alen] = 0;
|
||||
}
|
||||
env->ReleaseByteArrayElements(barr, ba, 0);
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_com_benjaminwan_ocrlibrary_OcrEngine_getVersion(JNIEnv *env, jobject thiz) {
|
||||
jstring ver = env->NewStringUTF(VERSION);
|
||||
return ver;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_benjaminwan_ocrlibrary_OcrEngine_setNumThread(JNIEnv *env, jobject thiz, jint numThread) {
|
||||
ocrLite->setNumThread(numThread);
|
||||
printf("numThread=%d\n", numThread);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_benjaminwan_ocrlibrary_OcrEngine_initLogger(JNIEnv *env, jobject thiz, jboolean isConsole,
|
||||
jboolean isPartImg, jboolean isResultImg) {
|
||||
ocrLite->initLogger(isConsole,//isOutputConsole
|
||||
isPartImg,//isOutputPartImg
|
||||
isResultImg);//isOutputResultImg
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_benjaminwan_ocrlibrary_OcrEngine_enableResultText(JNIEnv *env, jobject thiz, jstring input) {
|
||||
std::string argImgPath = jstringToChar(env, input);
|
||||
std::string imgPath = argImgPath.substr(0, argImgPath.find_last_of('/') + 1);
|
||||
std::string imgName = argImgPath.substr(argImgPath.find_last_of('/') + 1);
|
||||
ocrLite->enableResultTxt(imgPath.c_str(), imgName.c_str());
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_benjaminwan_ocrlibrary_OcrEngine_initModels(JNIEnv *env, jobject thiz, jstring path,
|
||||
jstring det, jstring cls, jstring rec, jstring keys) {
|
||||
std::string modelsDir = jstringToChar(env, path);
|
||||
std::string detName = jstringToChar(env, det);
|
||||
std::string clsName = jstringToChar(env, cls);
|
||||
std::string recName = jstringToChar(env, rec);
|
||||
std::string keysName = jstringToChar(env, keys);
|
||||
std::string detPath = modelsDir + "/" + detName;
|
||||
std::string clsPath = modelsDir + "/" + clsName;
|
||||
std::string recPath = modelsDir + "/" + recName;
|
||||
std::string keysPath = modelsDir + "/" + keysName;
|
||||
printf("modelsDir=%s\ndet=%s\ncls=%s\nrec=%s\nkeys=%s\n", modelsDir.c_str(), detName.c_str(), clsName.c_str(),
|
||||
recName.c_str(), keysName.c_str());
|
||||
ocrLite->initModels(detPath, clsPath, recPath, keysPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_com_benjaminwan_ocrlibrary_OcrEngine_detect(JNIEnv *env, jobject thiz, jstring input, jint padding, jint maxSideLen,
|
||||
jfloat boxScoreThresh, jfloat boxThresh, jfloat unClipRatio,
|
||||
jboolean doAngle, jboolean mostAngle
|
||||
) {
|
||||
std::string argImgPath = jstringToChar(env, input);
|
||||
std::string imgPath = argImgPath.substr(0, argImgPath.find_last_of('/') + 1);
|
||||
std::string imgName = argImgPath.substr(argImgPath.find_last_of('/') + 1);
|
||||
printf("imgPath=%s, imgName=%s\n", imgPath.c_str(), imgName.c_str());
|
||||
OcrResult result = ocrLite->detect(imgPath.c_str(), imgName.c_str(), padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
return OcrResultUtils(env, result).getJObject();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
#ifdef __JNI__
|
||||
#include <OcrUtils.h>
|
||||
#include "OcrResultUtils.h"
|
||||
|
||||
OcrResultUtils::OcrResultUtils(JNIEnv *env, OcrResult &ocrResult) {
|
||||
jniEnv = env;
|
||||
|
||||
jclass jOcrResultClass = env->FindClass("com/benjaminwan/ocrlibrary/OcrResult");
|
||||
|
||||
if (jOcrResultClass == NULL) {
|
||||
printf("OcrResult class is null\n");
|
||||
}
|
||||
|
||||
jmethodID jOcrResultConstructor = env->GetMethodID(jOcrResultClass, "<init>",
|
||||
"(DLjava/util/ArrayList;DLjava/lang/String;)V");
|
||||
|
||||
jobject textBlocks = getTextBlocks(ocrResult.textBlocks);
|
||||
jdouble dbNetTime = (jdouble) ocrResult.dbNetTime;
|
||||
jdouble detectTime = (jdouble) ocrResult.detectTime;
|
||||
jstring jStrRest = jniEnv->NewStringUTF(ocrResult.strRes.c_str());
|
||||
|
||||
jOcrResult = env->NewObject(jOcrResultClass, jOcrResultConstructor, dbNetTime,
|
||||
textBlocks, detectTime, jStrRest);
|
||||
}
|
||||
|
||||
OcrResultUtils::~OcrResultUtils() {
|
||||
jniEnv = NULL;
|
||||
}
|
||||
|
||||
jobject OcrResultUtils::getJObject() {
|
||||
return jOcrResult;
|
||||
}
|
||||
|
||||
jclass OcrResultUtils::newJListClass() {
|
||||
jclass clazz = jniEnv->FindClass("java/util/ArrayList");
|
||||
if (clazz == NULL) {
|
||||
printf("ArrayList class is null\n");
|
||||
return NULL;
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
jmethodID OcrResultUtils::getListConstructor(jclass clazz) {
|
||||
jmethodID constructor = jniEnv->GetMethodID(clazz, "<init>", "()V");
|
||||
return constructor;
|
||||
}
|
||||
|
||||
jobject OcrResultUtils::newJPoint(cv::Point &point) {
|
||||
jclass clazz = jniEnv->FindClass("com/benjaminwan/ocrlibrary/Point");
|
||||
if (clazz == NULL) {
|
||||
printf("Point class is null\n");
|
||||
return NULL;
|
||||
}
|
||||
jmethodID constructor = jniEnv->GetMethodID(clazz, "<init>", "(II)V");
|
||||
jobject obj = jniEnv->NewObject(clazz, constructor, point.x, point.y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
jobject OcrResultUtils::newJBoxPoint(std::vector<cv::Point> &boxPoint) {
|
||||
jclass jListClass = newJListClass();
|
||||
jmethodID jListConstructor = getListConstructor(jListClass);
|
||||
jobject jList = jniEnv->NewObject(jListClass, jListConstructor);
|
||||
jmethodID jListAdd = jniEnv->GetMethodID(jListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
|
||||
for (auto point : boxPoint) {
|
||||
jobject jPoint = newJPoint(point);
|
||||
jniEnv->CallBooleanMethod(jList, jListAdd, jPoint);
|
||||
}
|
||||
return jList;
|
||||
}
|
||||
|
||||
jobject OcrResultUtils::getTextBlock(TextBlock &textBlock) {
|
||||
jobject jBoxPint = newJBoxPoint(textBlock.boxPoint);
|
||||
jfloat jBoxScore = (jfloat) textBlock.boxScore;
|
||||
jfloat jAngleScore = (jfloat) textBlock.angleScore;
|
||||
jdouble jAngleTime = (jdouble) textBlock.angleTime;
|
||||
jstring jText = jniEnv->NewStringUTF(textBlock.text.c_str());
|
||||
jobject jCharScores = newJScoreArray(textBlock.charScores);
|
||||
jdouble jCrnnTime = (jdouble) textBlock.crnnTime;
|
||||
jdouble jBlockTime = (jdouble) textBlock.blockTime;
|
||||
jclass clazz = jniEnv->FindClass("com/benjaminwan/ocrlibrary/TextBlock");
|
||||
if (clazz == NULL) {
|
||||
printf("TextBlock class is null\n");
|
||||
return NULL;
|
||||
}
|
||||
jmethodID constructor = jniEnv->GetMethodID(clazz, "<init>",
|
||||
"(Ljava/util/ArrayList;FIFDLjava/lang/String;[FDD)V");
|
||||
jobject obj = jniEnv->NewObject(clazz, constructor, jBoxPint, jBoxScore, textBlock.angleIndex,
|
||||
jAngleScore, jAngleTime, jText, jCharScores, jCrnnTime,
|
||||
jBlockTime);
|
||||
return obj;
|
||||
}
|
||||
|
||||
jobject OcrResultUtils::getTextBlocks(std::vector<TextBlock> &textBlocks) {
|
||||
jclass jListClass = newJListClass();
|
||||
jmethodID jListConstructor = getListConstructor(jListClass);
|
||||
jobject jList = jniEnv->NewObject(jListClass, jListConstructor);
|
||||
jmethodID jListAdd = jniEnv->GetMethodID(jListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
|
||||
for (int i = 0; i < textBlocks.size(); ++i) {
|
||||
auto textBlock = textBlocks[i];
|
||||
jobject jTextBlock = getTextBlock(textBlock);
|
||||
jniEnv->CallBooleanMethod(jList, jListAdd, jTextBlock);
|
||||
}
|
||||
return jList;
|
||||
}
|
||||
|
||||
jfloatArray OcrResultUtils::newJScoreArray(std::vector<float> &scores) {
|
||||
jfloatArray jScores = jniEnv->NewFloatArray(scores.size());
|
||||
jniEnv->SetFloatArrayRegion(jScores, 0, scores.size(), (jfloat *) scores.data());
|
||||
return jScores;
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+401
@@ -0,0 +1,401 @@
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <numeric>
|
||||
#include "OcrUtils.h"
|
||||
#include "clipper.hpp"
|
||||
|
||||
double getCurrentTime() {
|
||||
return (static_cast<double>(cv::getTickCount())) / cv::getTickFrequency() * 1000;//单位毫秒
|
||||
}
|
||||
|
||||
//onnxruntime init windows
|
||||
std::wstring strToWstr(std::string str) {
|
||||
if (str.length() == 0)
|
||||
return L"";
|
||||
std::wstring wstr;
|
||||
wstr.assign(str.begin(), str.end());
|
||||
return wstr;
|
||||
}
|
||||
|
||||
ScaleParam getScaleParam(cv::Mat &src, const float scale) {
|
||||
int srcWidth = src.cols;
|
||||
int srcHeight = src.rows;
|
||||
int dstWidth = int((float) srcWidth * scale);
|
||||
int dstHeight = int((float) srcHeight * scale);
|
||||
if (dstWidth % 32 != 0) {
|
||||
dstWidth = (dstWidth / 32 - 1) * 32;
|
||||
dstWidth = (std::max)(dstWidth, 32);
|
||||
}
|
||||
if (dstHeight % 32 != 0) {
|
||||
dstHeight = (dstHeight / 32 - 1) * 32;
|
||||
dstHeight = (std::max)(dstHeight, 32);
|
||||
}
|
||||
float scaleWidth = (float) dstWidth / (float) srcWidth;
|
||||
float scaleHeight = (float) dstHeight / (float) srcHeight;
|
||||
return {srcWidth, srcHeight, dstWidth, dstHeight, scaleWidth, scaleHeight};
|
||||
}
|
||||
|
||||
ScaleParam getScaleParam(cv::Mat &src, const int targetSize) {
|
||||
int srcWidth, srcHeight, dstWidth, dstHeight;
|
||||
srcWidth = dstWidth = src.cols;
|
||||
srcHeight = dstHeight = src.rows;
|
||||
|
||||
float ratio = 1.f;
|
||||
if (srcWidth > srcHeight) {
|
||||
ratio = float(targetSize) / float(srcWidth);
|
||||
} else {
|
||||
ratio = float(targetSize) / float(srcHeight);
|
||||
}
|
||||
dstWidth = int(float(srcWidth) * ratio);
|
||||
dstHeight = int(float(srcHeight) * ratio);
|
||||
if (dstWidth % 32 != 0) {
|
||||
dstWidth = (dstWidth / 32) * 32;
|
||||
dstWidth = (std::max)(dstWidth, 32);
|
||||
}
|
||||
if (dstHeight % 32 != 0) {
|
||||
dstHeight = (dstHeight / 32) * 32;
|
||||
dstHeight = (std::max)(dstHeight, 32);
|
||||
}
|
||||
float ratioWidth = (float) dstWidth / (float) srcWidth;
|
||||
float ratioHeight = (float) dstHeight / (float) srcHeight;
|
||||
return {srcWidth, srcHeight, dstWidth, dstHeight, ratioWidth, ratioHeight};
|
||||
}
|
||||
|
||||
std::vector<cv::Point2f> getBox(const cv::RotatedRect &rect) {
|
||||
cv::Point2f vertices[4];
|
||||
rect.points(vertices);
|
||||
//std::vector<cv::Point2f> ret(4);
|
||||
std::vector<cv::Point2f> ret2(vertices, vertices + sizeof(vertices) / sizeof(vertices[0]));
|
||||
//memcpy(vertices, &ret[0], ret.size() * sizeof(ret[0]));
|
||||
return ret2;
|
||||
}
|
||||
|
||||
int getThickness(cv::Mat &boxImg) {
|
||||
int minSize = boxImg.cols > boxImg.rows ? boxImg.rows : boxImg.cols;
|
||||
int thickness = minSize / 1000 + 2;
|
||||
return thickness;
|
||||
}
|
||||
|
||||
void drawTextBox(cv::Mat &boxImg, cv::RotatedRect &rect, int thickness) {
|
||||
cv::Point2f vertices[4];
|
||||
rect.points(vertices);
|
||||
for (int i = 0; i < 4; i++)
|
||||
cv::line(boxImg, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 0, 255), thickness);
|
||||
//cv::polylines(srcmat, textpoint, true, cv::Scalar(0, 255, 0), 2);
|
||||
}
|
||||
|
||||
void drawTextBox(cv::Mat &boxImg, const std::vector<cv::Point> &box, int thickness) {
|
||||
auto color = cv::Scalar(0, 0, 255);// B(0) G(0) R(255)
|
||||
cv::line(boxImg, box[0], box[1], color, thickness);
|
||||
cv::line(boxImg, box[1], box[2], color, thickness);
|
||||
cv::line(boxImg, box[2], box[3], color, thickness);
|
||||
cv::line(boxImg, box[3], box[0], color, thickness);
|
||||
}
|
||||
|
||||
void drawTextBoxes(cv::Mat &boxImg, std::vector<TextBox> &textBoxes, int thickness) {
|
||||
for (int i = 0; i < textBoxes.size(); ++i) {
|
||||
drawTextBox(boxImg, textBoxes[i].boxPoint, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat matRotateClockWise180(cv::Mat src) {
|
||||
flip(src, src, 0);
|
||||
flip(src, src, 1);
|
||||
return src;
|
||||
}
|
||||
|
||||
cv::Mat matRotateClockWise90(cv::Mat src) {
|
||||
transpose(src, src);
|
||||
flip(src, src, 1);
|
||||
return src;
|
||||
}
|
||||
|
||||
cv::Mat getRotateCropImage(const cv::Mat &src, std::vector<cv::Point> box) {
|
||||
cv::Mat image;
|
||||
src.copyTo(image);
|
||||
std::vector<cv::Point> points = box;
|
||||
|
||||
int collectX[4] = {box[0].x, box[1].x, box[2].x, box[3].x};
|
||||
int collectY[4] = {box[0].y, box[1].y, box[2].y, box[3].y};
|
||||
int left = int(*std::min_element(collectX, collectX + 4));
|
||||
int right = int(*std::max_element(collectX, collectX + 4));
|
||||
int top = int(*std::min_element(collectY, collectY + 4));
|
||||
int bottom = int(*std::max_element(collectY, collectY + 4));
|
||||
|
||||
cv::Mat imgCrop;
|
||||
image(cv::Rect(left, top, right - left, bottom - top)).copyTo(imgCrop);
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
points[i].x -= left;
|
||||
points[i].y -= top;
|
||||
}
|
||||
|
||||
int imgCropWidth = int(sqrt(pow(points[0].x - points[1].x, 2) +
|
||||
pow(points[0].y - points[1].y, 2)));
|
||||
int imgCropHeight = int(sqrt(pow(points[0].x - points[3].x, 2) +
|
||||
pow(points[0].y - points[3].y, 2)));
|
||||
|
||||
cv::Point2f ptsDst[4];
|
||||
ptsDst[0] = cv::Point2f(0., 0.);
|
||||
ptsDst[1] = cv::Point2f(imgCropWidth, 0.);
|
||||
ptsDst[2] = cv::Point2f(imgCropWidth, imgCropHeight);
|
||||
ptsDst[3] = cv::Point2f(0.f, imgCropHeight);
|
||||
|
||||
cv::Point2f ptsSrc[4];
|
||||
ptsSrc[0] = cv::Point2f(points[0].x, points[0].y);
|
||||
ptsSrc[1] = cv::Point2f(points[1].x, points[1].y);
|
||||
ptsSrc[2] = cv::Point2f(points[2].x, points[2].y);
|
||||
ptsSrc[3] = cv::Point2f(points[3].x, points[3].y);
|
||||
|
||||
cv::Mat M = cv::getPerspectiveTransform(ptsSrc, ptsDst);
|
||||
|
||||
cv::Mat partImg;
|
||||
cv::warpPerspective(imgCrop, partImg, M,
|
||||
cv::Size(imgCropWidth, imgCropHeight),
|
||||
cv::BORDER_REPLICATE);
|
||||
|
||||
if (float(partImg.rows) >= float(partImg.cols) * 1.5) {
|
||||
cv::Mat srcCopy = cv::Mat(partImg.rows, partImg.cols, partImg.depth());
|
||||
cv::transpose(partImg, srcCopy);
|
||||
cv::flip(srcCopy, srcCopy, 0);
|
||||
return srcCopy;
|
||||
} else {
|
||||
return partImg;
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat adjustTargetImg(cv::Mat &src, int dstWidth, int dstHeight) {
|
||||
cv::Mat srcResize;
|
||||
float scale = (float) dstHeight / (float) src.rows;
|
||||
int angleWidth = int((float) src.cols * scale);
|
||||
cv::resize(src, srcResize, cv::Size(angleWidth, dstHeight));
|
||||
cv::Mat srcFit = cv::Mat(dstHeight, dstWidth, CV_8UC3, cv::Scalar(255, 255, 255));
|
||||
if (angleWidth < dstWidth) {
|
||||
cv::Rect rect(0, 0, srcResize.cols, srcResize.rows);
|
||||
srcResize.copyTo(srcFit(rect));
|
||||
} else {
|
||||
cv::Rect rect(0, 0, dstWidth, dstHeight);
|
||||
srcResize(rect).copyTo(srcFit);
|
||||
}
|
||||
return srcFit;
|
||||
}
|
||||
|
||||
bool cvPointCompare(cv::Point a, cv::Point b) {
|
||||
return a.x < b.x;
|
||||
}
|
||||
|
||||
std::vector<cv::Point2f> getMinBoxes(const cv::RotatedRect &boxRect, float &maxSideLen) {
|
||||
maxSideLen = std::max(boxRect.size.width, boxRect.size.height);
|
||||
std::vector<cv::Point2f> boxPoint = getBox(boxRect);
|
||||
std::sort(boxPoint.begin(), boxPoint.end(), cvPointCompare);
|
||||
int index1, index2, index3, index4;
|
||||
if (boxPoint[1].y > boxPoint[0].y) {
|
||||
index1 = 0;
|
||||
index4 = 1;
|
||||
} else {
|
||||
index1 = 1;
|
||||
index4 = 0;
|
||||
}
|
||||
if (boxPoint[3].y > boxPoint[2].y) {
|
||||
index2 = 2;
|
||||
index3 = 3;
|
||||
} else {
|
||||
index2 = 3;
|
||||
index3 = 2;
|
||||
}
|
||||
std::vector<cv::Point2f> minBox(4);
|
||||
minBox[0] = boxPoint[index1];
|
||||
minBox[1] = boxPoint[index2];
|
||||
minBox[2] = boxPoint[index3];
|
||||
minBox[3] = boxPoint[index4];
|
||||
return minBox;
|
||||
}
|
||||
|
||||
float boxScoreFast(const std::vector<cv::Point2f> &boxes, const cv::Mat &pred) {
|
||||
int width = pred.cols;
|
||||
int height = pred.rows;
|
||||
|
||||
float arrayX[4] = {boxes[0].x, boxes[1].x, boxes[2].x, boxes[3].x};
|
||||
float arrayY[4] = {boxes[0].y, boxes[1].y, boxes[2].y, boxes[3].y};
|
||||
|
||||
int minX = clamp(int(std::floor(*(std::min_element(arrayX, arrayX + 4)))), 0, width - 1);
|
||||
int maxX = clamp(int(std::ceil(*(std::max_element(arrayX, arrayX + 4)))), 0, width - 1);
|
||||
int minY = clamp(int(std::floor(*(std::min_element(arrayY, arrayY + 4)))), 0, height - 1);
|
||||
int maxY = clamp(int(std::ceil(*(std::max_element(arrayY, arrayY + 4)))), 0, height - 1);
|
||||
|
||||
cv::Mat mask = cv::Mat::zeros(maxY - minY + 1, maxX - minX + 1, CV_8UC1);
|
||||
|
||||
cv::Point box[4];
|
||||
box[0] = cv::Point(int(boxes[0].x) - minX, int(boxes[0].y) - minY);
|
||||
box[1] = cv::Point(int(boxes[1].x) - minX, int(boxes[1].y) - minY);
|
||||
box[2] = cv::Point(int(boxes[2].x) - minX, int(boxes[2].y) - minY);
|
||||
box[3] = cv::Point(int(boxes[3].x) - minX, int(boxes[3].y) - minY);
|
||||
const cv::Point *pts[1] = {box};
|
||||
int npts[] = {4};
|
||||
cv::fillPoly(mask, pts, npts, 1, cv::Scalar(1));
|
||||
|
||||
cv::Mat croppedImg;
|
||||
pred(cv::Rect(minX, minY, maxX - minX + 1, maxY - minY + 1))
|
||||
.copyTo(croppedImg);
|
||||
|
||||
auto score = cv::mean(croppedImg, mask)[0];
|
||||
return score;
|
||||
}
|
||||
|
||||
float getContourArea(const std::vector<cv::Point2f> &box, float unClipRatio) {
|
||||
int size = box.size();
|
||||
float area = 0.0f;
|
||||
float dist = 0.0f;
|
||||
for (int i = 0; i < size; i++) {
|
||||
area += box[i].x * box[(i + 1) % size].y -
|
||||
box[i].y * box[(i + 1) % size].x;
|
||||
dist += sqrtf((box[i].x - box[(i + 1) % size].x) *
|
||||
(box[i].x - box[(i + 1) % size].x) +
|
||||
(box[i].y - box[(i + 1) % size].y) *
|
||||
(box[i].y - box[(i + 1) % size].y));
|
||||
}
|
||||
area = fabs(float(area / 2.0));
|
||||
|
||||
return area * unClipRatio / dist;
|
||||
}
|
||||
|
||||
cv::RotatedRect unClip(std::vector<cv::Point2f> box, float unClipRatio) {
|
||||
float distance = getContourArea(box, unClipRatio);
|
||||
|
||||
ClipperLib::ClipperOffset offset;
|
||||
ClipperLib::Path p;
|
||||
p << ClipperLib::IntPoint(int(box[0].x), int(box[0].y))
|
||||
<< ClipperLib::IntPoint(int(box[1].x), int(box[1].y))
|
||||
<< ClipperLib::IntPoint(int(box[2].x), int(box[2].y))
|
||||
<< ClipperLib::IntPoint(int(box[3].x), int(box[3].y));
|
||||
offset.AddPath(p, ClipperLib::jtRound, ClipperLib::etClosedPolygon);
|
||||
|
||||
ClipperLib::Paths soln;
|
||||
offset.Execute(soln, distance);
|
||||
std::vector<cv::Point2f> points;
|
||||
|
||||
for (int j = 0; j < soln.size(); j++) {
|
||||
for (int i = 0; i < soln[soln.size() - 1].size(); i++) {
|
||||
points.emplace_back(soln[j][i].X, soln[j][i].Y);
|
||||
}
|
||||
}
|
||||
cv::RotatedRect res;
|
||||
if (points.empty()) {
|
||||
res = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(1, 1), 0);
|
||||
} else {
|
||||
res = cv::minAreaRect(points);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<float> substractMeanNormalize(cv::Mat &src, const float *meanVals, const float *normVals) {
|
||||
auto inputTensorSize = src.cols * src.rows * src.channels();
|
||||
std::vector<float> inputTensorValues(inputTensorSize);
|
||||
size_t numChannels = src.channels();
|
||||
size_t imageSize = src.cols * src.rows;
|
||||
|
||||
for (size_t pid = 0; pid < imageSize; pid++) {
|
||||
for (size_t ch = 0; ch < numChannels; ++ch) {
|
||||
float data = (float) (src.data[pid * numChannels + ch] * normVals[ch] - meanVals[ch] * normVals[ch]);
|
||||
inputTensorValues[ch * imageSize + pid] = data;
|
||||
}
|
||||
}
|
||||
return inputTensorValues;
|
||||
}
|
||||
|
||||
std::vector<int> getAngleIndexes(std::vector<Angle> &angles) {
|
||||
std::vector<int> angleIndexes;
|
||||
angleIndexes.reserve(angles.size());
|
||||
for (int i = 0; i < angles.size(); ++i) {
|
||||
angleIndexes.push_back(angles[i].index);
|
||||
}
|
||||
return angleIndexes;
|
||||
}
|
||||
|
||||
std::vector<char *> getInputNames(Ort::Session *session) {
|
||||
Ort::AllocatorWithDefaultOptions allocator;
|
||||
size_t numInputNodes = session->GetInputCount();
|
||||
std::vector<char *> inputNodeNames(numInputNodes);
|
||||
std::vector<int64_t> inputNodeDims;
|
||||
|
||||
//printf("Number of inputs = %zu\n", numInputNodes);
|
||||
|
||||
for (int i = 0; i < numInputNodes; i++) {
|
||||
// print input node names
|
||||
char *inputName = session->GetInputName(i, allocator);
|
||||
printf("InputName[%d]=%s\n", i, inputName);
|
||||
inputNodeNames[i] = inputName;
|
||||
|
||||
// print input node types
|
||||
//Ort::TypeInfo typeInfo = session->GetInputTypeInfo(i);
|
||||
//auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
||||
|
||||
//ONNXTensorElementDataType type = tensorInfo.GetElementType();
|
||||
//printf("Input[%d] type=%d\n", i, type);
|
||||
|
||||
// print input shapes/dims
|
||||
//inputNodeDims = tensorInfo.GetShape();
|
||||
//printf("Input[%d] num_dims=%zu\n", i, inputNodeDims.size());
|
||||
/*for (int j = 0; j < inputNodeDims.size(); j++)
|
||||
printf("Input[%d] dim%d=%jd\n", i, j, inputNodeDims[j]);*/
|
||||
}
|
||||
return inputNodeNames;
|
||||
}
|
||||
|
||||
std::vector<char *> getOutputNames(Ort::Session *session) {
|
||||
Ort::AllocatorWithDefaultOptions allocator;
|
||||
size_t numOutputNodes = session->GetOutputCount();
|
||||
std::vector<char *> outputNodeNames(numOutputNodes);
|
||||
//std::vector<int64_t> outputNodeDims;
|
||||
|
||||
//printf("Number of outputs = %zu\n", numOutputNodes);
|
||||
|
||||
for (int i = 0; i < numOutputNodes; i++) {
|
||||
// print input node names
|
||||
char *outputName = session->GetOutputName(i, allocator);
|
||||
printf("OutputName[%d]=%s\n", i, outputName);
|
||||
outputNodeNames[i] = outputName;
|
||||
|
||||
// print input node types
|
||||
//Ort::TypeInfo type_info = session->GetOutputTypeInfo(i);
|
||||
//auto tensorInfo = type_info.GetTensorTypeAndShapeInfo();
|
||||
|
||||
//ONNXTensorElementDataType type = tensorInfo.GetElementType();
|
||||
//printf("Output %d : type=%d\n", i, type);
|
||||
|
||||
// print input shapes/dims
|
||||
//outputNodeDims = tensorInfo.GetShape();
|
||||
//printf("Output %d : num_dims=%zu\n", i, outputNodeDims.size());
|
||||
/*for (int j = 0; j < outputNodeDims.size(); j++)
|
||||
printf("Output %d : dim %d=%jd\n", i, j, outputNodeDims[j]);*/
|
||||
}
|
||||
return outputNodeNames;
|
||||
}
|
||||
|
||||
void saveImg(cv::Mat &img, const char *imgPath) {
|
||||
cv::imwrite(imgPath, img);
|
||||
}
|
||||
|
||||
std::string getSrcImgFilePath(const char *path, const char *imgName) {
|
||||
std::string filePath;
|
||||
filePath.append(path).append(imgName);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
std::string getResultTxtFilePath(const char *path, const char *imgName) {
|
||||
std::string filePath;
|
||||
filePath.append(path).append(imgName).append("-result.txt");
|
||||
return filePath;
|
||||
}
|
||||
|
||||
std::string getResultImgFilePath(const char *path, const char *imgName) {
|
||||
std::string filePath;
|
||||
filePath.append(path).append(imgName).append("-result.jpg");
|
||||
return filePath;
|
||||
}
|
||||
|
||||
std::string getDebugImgFilePath(const char *path, const char *imgName, int i, const char *tag) {
|
||||
std::string filePath;
|
||||
filePath.append(path).append(imgName).append(tag).append(std::to_string(i)).append(".jpg");
|
||||
return filePath;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Executable
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* getopt - POSIX like getopt for Windows console Application
|
||||
*
|
||||
* win-c - Windows Console Library
|
||||
* Copyright (c) 2015 Koji Takami
|
||||
* Released under the MIT license
|
||||
* https://github.com/takamin/win-c/blob/master/LICENSE
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "getopt.h"
|
||||
|
||||
char *optarg = 0;
|
||||
int optind = 1;
|
||||
int opterr = 1;
|
||||
int optopt = 0;
|
||||
|
||||
int postpone_count = 0;
|
||||
int nextchar = 0;
|
||||
|
||||
static void postpone(int argc, char *const argv[], int index) {
|
||||
char **nc_argv = (char **) argv;
|
||||
char *p = nc_argv[index];
|
||||
int j = index;
|
||||
for (; j < argc - 1; j++) {
|
||||
nc_argv[j] = nc_argv[j + 1];
|
||||
}
|
||||
nc_argv[argc - 1] = p;
|
||||
}
|
||||
|
||||
static int postpone_noopt(int argc, char *const argv[], int index) {
|
||||
int i = index;
|
||||
for (; i < argc; i++) {
|
||||
if (*(argv[i]) == '-') {
|
||||
postpone(argc, argv, index);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _getopt_(int argc, char *const argv[],
|
||||
const char *optstring,
|
||||
const struct option *longopts, int *longindex) {
|
||||
while (1) {
|
||||
int c;
|
||||
const char *optptr = 0;
|
||||
if (optind >= argc - postpone_count) {
|
||||
c = 0;
|
||||
optarg = 0;
|
||||
break;
|
||||
}
|
||||
c = *(argv[optind] + nextchar);
|
||||
if (c == '\0') {
|
||||
nextchar = 0;
|
||||
++optind;
|
||||
continue;
|
||||
}
|
||||
if (nextchar == 0) {
|
||||
if (optstring[0] != '+' && optstring[0] != '-') {
|
||||
while (c != '-') {
|
||||
/* postpone non-opt parameter */
|
||||
if (!postpone_noopt(argc, argv, optind)) {
|
||||
break; /* all args are non-opt param */
|
||||
}
|
||||
++postpone_count;
|
||||
c = *argv[optind];
|
||||
}
|
||||
}
|
||||
if (c != '-') {
|
||||
if (optstring[0] == '-') {
|
||||
optarg = argv[optind];
|
||||
nextchar = 0;
|
||||
++optind;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (strcmp(argv[optind], "--") == 0) {
|
||||
optind++;
|
||||
break;
|
||||
}
|
||||
++nextchar;
|
||||
if (longopts != 0 && *(argv[optind] + 1) == '-') {
|
||||
char const *spec_long = argv[optind] + 2;
|
||||
char const *pos_eq = strchr(spec_long, '=');
|
||||
int spec_len = (pos_eq == NULL ? strlen(spec_long) : pos_eq - spec_long);
|
||||
int index_search = 0;
|
||||
int index_found = -1;
|
||||
const struct option *optdef = 0;
|
||||
while (longopts->name != 0) {
|
||||
if (strncmp(spec_long, longopts->name, spec_len) == 0) {
|
||||
if (optdef != 0) {
|
||||
if (opterr) {
|
||||
fprintf(stderr, "ambiguous option: %s\n", spec_long);
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
optdef = longopts;
|
||||
index_found = index_search;
|
||||
}
|
||||
longopts++;
|
||||
index_search++;
|
||||
}
|
||||
if (optdef == 0) {
|
||||
if (opterr) {
|
||||
fprintf(stderr, "no such a option: %s\n", spec_long);
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
switch (optdef->has_arg) {
|
||||
case no_argument:
|
||||
optarg = 0;
|
||||
if (pos_eq != 0) {
|
||||
if (opterr) {
|
||||
fprintf(stderr, "no argument for %s\n", optdef->name);
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
break;
|
||||
case required_argument:
|
||||
if (pos_eq == NULL) {
|
||||
++optind;
|
||||
optarg = argv[optind];
|
||||
} else {
|
||||
optarg = (char *) pos_eq + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++optind;
|
||||
nextchar = 0;
|
||||
if (longindex != 0) {
|
||||
*longindex = index_found;
|
||||
}
|
||||
if (optdef->flag != 0) {
|
||||
*optdef->flag = optdef->val;
|
||||
return 0;
|
||||
}
|
||||
return optdef->val;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
optptr = strchr(optstring, c);
|
||||
if (optptr == NULL) {
|
||||
optopt = c;
|
||||
if (opterr) {
|
||||
fprintf(stderr,
|
||||
"%s: invalid option -- %c\n",
|
||||
argv[0], c);
|
||||
}
|
||||
++nextchar;
|
||||
return '?';
|
||||
}
|
||||
if (*(optptr + 1) != ':') {
|
||||
nextchar++;
|
||||
if (*(argv[optind] + nextchar) == '\0') {
|
||||
++optind;
|
||||
nextchar = 0;
|
||||
}
|
||||
optarg = 0;
|
||||
} else {
|
||||
nextchar++;
|
||||
if (*(argv[optind] + nextchar) != '\0') {
|
||||
optarg = argv[optind] + nextchar;
|
||||
} else {
|
||||
++optind;
|
||||
if (optind < argc - postpone_count) {
|
||||
optarg = argv[optind];
|
||||
} else {
|
||||
optopt = c;
|
||||
if (opterr) {
|
||||
fprintf(stderr,
|
||||
"%s: option requires an argument -- %c\n",
|
||||
argv[0], c);
|
||||
}
|
||||
if (optstring[0] == ':' ||
|
||||
(optstring[0] == '-' || optstring[0] == '+') &&
|
||||
optstring[1] == ':') {
|
||||
c = ':';
|
||||
} else {
|
||||
c = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
++optind;
|
||||
nextchar = 0;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* end of option analysis */
|
||||
|
||||
/* fix the order of non-opt params to original */
|
||||
while ((argc - optind - postpone_count) > 0) {
|
||||
postpone(argc, argv, optind);
|
||||
++postpone_count;
|
||||
}
|
||||
|
||||
nextchar = 0;
|
||||
postpone_count = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getopt(int argc, char *const argv[],
|
||||
const char *optstring) {
|
||||
return _getopt_(argc, argv, optstring, 0, 0);
|
||||
}
|
||||
|
||||
int getopt_long(int argc, char *const argv[],
|
||||
const char *optstring,
|
||||
const struct option *longopts, int *longindex) {
|
||||
return _getopt_(argc, argv, optstring, longopts, longindex);
|
||||
}
|
||||
/********************************************************
|
||||
int getopt_long_only(int argc, char* const argv[],
|
||||
const char* optstring,
|
||||
const struct option* longopts, int* longindex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
********************************************************/
|
||||
@@ -0,0 +1,145 @@
|
||||
#include <omp.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "main.h"
|
||||
#include "version.h"
|
||||
#include "OcrLite.h"
|
||||
|
||||
void printHelp(FILE *out, char *argv0) {
|
||||
fprintf(out, " ------- Usage -------\n");
|
||||
fprintf(out, "%s %s", argv0, usageMsg);
|
||||
fprintf(out, " ------- Required Parameters -------\n");
|
||||
fprintf(out, "%s", requiredMsg);
|
||||
fprintf(out, " ------- Optional Parameters -------\n");
|
||||
fprintf(out, "%s", optionalMsg);
|
||||
fprintf(out, " ------- Other Parameters -------\n");
|
||||
fprintf(out, "%s", otherMsg);
|
||||
fprintf(out, " ------- Examples -------\n");
|
||||
fprintf(out, example1Msg, argv0);
|
||||
fprintf(out, example2Msg, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc <= 1) {
|
||||
printHelp(stderr, argv[0]);
|
||||
return -1;
|
||||
}
|
||||
std::string modelsDir = "../models";
|
||||
std::string modelDetPath, modelClsPath, modelRecPath, keysPath;
|
||||
std::string argImgPath = "../../test_imgs/1.jpg";
|
||||
std::string imgPath, imgName;
|
||||
int numThread = 4;
|
||||
int padding = 0;
|
||||
int maxSideLen = 1024;
|
||||
float boxScoreThresh = 0.5f;
|
||||
float boxThresh = 0.3f;
|
||||
float unClipRatio = 2.0f;
|
||||
bool doAngle = true;
|
||||
int flagDoAngle = 1;
|
||||
bool mostAngle = false;
|
||||
int flagMostAngle = 0;
|
||||
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
while ((opt = getopt_long(argc, argv, "d:1:2:3:4:i:t:p:s:b:o:u:a:A:v:h", long_options, &optionIndex)) != -1) {
|
||||
//printf("option(-%c)=%s\n", opt, optarg);
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
modelsDir = optarg;
|
||||
printf("modelsPath=%s\n", modelsDir.c_str());
|
||||
break;
|
||||
case '1':
|
||||
modelDetPath = modelsDir + "/" + optarg;
|
||||
printf("model det path=%s\n", modelDetPath.c_str());
|
||||
break;
|
||||
case '2':
|
||||
modelClsPath = modelsDir + "/" + optarg;
|
||||
printf("model cls path=%s\n", modelClsPath.c_str());
|
||||
break;
|
||||
case '3':
|
||||
modelRecPath = modelsDir + "/" + optarg;
|
||||
printf("model rec path=%s\n", modelRecPath.c_str());
|
||||
break;
|
||||
case '4':
|
||||
keysPath = modelsDir + "/" + optarg;
|
||||
printf("keys path=%s\n", keysPath.c_str());
|
||||
break;
|
||||
case 'i':
|
||||
argImgPath.assign(optarg);
|
||||
imgPath.assign(argImgPath.substr(0, argImgPath.find_last_of('/') + 1));
|
||||
imgName.assign(argImgPath.substr(argImgPath.find_last_of('/') + 1));
|
||||
printf("imgPath=%s, imgName=%s\n", imgPath.c_str(), imgName.c_str());
|
||||
break;
|
||||
case 't':
|
||||
numThread = (int) strtol(optarg, NULL, 10);
|
||||
//printf("numThread=%d\n", numThread);
|
||||
break;
|
||||
case 'p':
|
||||
padding = (int) strtol(optarg, NULL, 10);
|
||||
//printf("padding=%d\n", padding);
|
||||
break;
|
||||
case 's':
|
||||
maxSideLen = (int) strtol(optarg, NULL, 10);
|
||||
//printf("maxSideLen=%d\n", maxSideLen);
|
||||
break;
|
||||
case 'b':
|
||||
boxScoreThresh = strtof(optarg, NULL);
|
||||
//printf("boxScoreThresh=%f\n", boxScoreThresh);
|
||||
break;
|
||||
case 'o':
|
||||
boxThresh = strtof(optarg, NULL);
|
||||
//printf("boxThresh=%f\n", boxThresh);
|
||||
break;
|
||||
case 'u':
|
||||
unClipRatio = strtof(optarg, NULL);
|
||||
//printf("unClipRatio=%f\n", unClipRatio);
|
||||
break;
|
||||
case 'a':
|
||||
flagDoAngle = (int) strtol(optarg, NULL, 10);
|
||||
if (flagDoAngle == 0) {
|
||||
doAngle = false;
|
||||
} else {
|
||||
doAngle = true;
|
||||
}
|
||||
//printf("doAngle=%d\n", doAngle);
|
||||
break;
|
||||
case 'A':
|
||||
flagMostAngle = (int) strtol(optarg, NULL, 10);
|
||||
if (flagMostAngle == 0) {
|
||||
mostAngle = false;
|
||||
} else {
|
||||
mostAngle = true;
|
||||
}
|
||||
//printf("mostAngle=%d\n", mostAngle);
|
||||
break;
|
||||
case 'v':
|
||||
printf("%s\n", VERSION);
|
||||
return 0;
|
||||
case 'h':
|
||||
printHelp(stdout, argv[0]);
|
||||
return 0;
|
||||
default:
|
||||
printf("other option %c :%s\n", opt, optarg);
|
||||
}
|
||||
}
|
||||
omp_set_num_threads(numThread);
|
||||
OcrLite ocrLite;
|
||||
ocrLite.setNumThread(numThread);
|
||||
ocrLite.initLogger(
|
||||
true,//isOutputConsole
|
||||
false,//isOutputPartImg
|
||||
true);//isOutputResultImg
|
||||
|
||||
ocrLite.enableResultTxt(imgPath.c_str(), imgName.c_str());
|
||||
ocrLite.Logger("=====Input Params=====\n");
|
||||
ocrLite.Logger(
|
||||
"numThread(%d),padding(%d),maxSideLen(%d),boxScoreThresh(%f),boxThresh(%f),unClipRatio(%f),doAngle(%d),mostAngle(%d)\n",
|
||||
numThread, padding, maxSideLen, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
|
||||
ocrLite.initModels(modelDetPath, modelClsPath, modelRecPath, keysPath);
|
||||
|
||||
OcrResult result = ocrLite.detect(imgPath.c_str(), imgName.c_str(), padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
ocrLite.Logger("%s\n", result.strRes.c_str());
|
||||
return 0;
|
||||
}
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
## script for 内存泄露检查
|
||||
# ========== macOS ==========
|
||||
# https://github.com/LouisBrunner/valgrind-macos
|
||||
# brew tap LouisBrunner/valgrind
|
||||
# brew install --HEAD LouisBrunner/valgrind/valgrind
|
||||
# ========== linux ==========
|
||||
# https://www.valgrind.org/
|
||||
# apt install valgrind
|
||||
|
||||
NUM_THREADS=1
|
||||
|
||||
set OMP_NUM_THREADS=$NUM_THREADS
|
||||
|
||||
##### run test on MacOS or Linux
|
||||
valgrind --tool=memcheck --leak-check=full --leak-resolution=med --track-origins=yes --vgdb=no --log-file=valgrind-memcheck.txt \
|
||||
./build/BaiPiaoOcrOnnx --models models \
|
||||
--det ch_ppocr_mobile_v2.0_det_infer.onnx \
|
||||
--cls ch_ppocr_mobile_v2.0_cls_infer.onnx \
|
||||
--rec ch_ppocr_server_v2.0_rec_infer.onnx \
|
||||
--keys ppocr_keys_v1.txt \
|
||||
--image ../../test_imgs/1.jpg \
|
||||
--numThread $NUM_THREADS \
|
||||
--padding 0 \
|
||||
--maxSideLen 1024 \
|
||||
--boxScoreThresh 0.5 \
|
||||
--boxThresh 0.3 \
|
||||
--unClipRatio 1.5 \
|
||||
--doAngle 1 \
|
||||
--mostAngle 0
|
||||
@@ -0,0 +1,5 @@
|
||||
# C++项目
|
||||
|
||||
## 简介
|
||||
本目录存放C++相关demo项目
|
||||
1. BaiPiaoOcrOnnx项目: 模型源自paddleOCR,转为onnx格式后,使用onnxruntime框架进行推理。
|
||||
@@ -1 +0,0 @@
|
||||
placeholder
|
||||
@@ -0,0 +1,145 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Python template
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
.gradle
|
||||
|
||||
#idea
|
||||
.idea
|
||||
build/
|
||||
/run-test/images/
|
||||
/run-test/models/
|
||||
/run-test/*.dll
|
||||
/run-test/*.so
|
||||
/run-test/*.dylib
|
||||
/run-test/*.jar
|
||||
/run-test/valgrind-memcheck.txt
|
||||
/out
|
||||
@@ -0,0 +1,47 @@
|
||||
# BaiPiaoOcrOnnxJvm
|
||||
|
||||
### Project下载
|
||||
* 有整合好源码和依赖库的完整工程项目,文件比较大,可到Q群共享内下载,找以Project开头的压缩包文件
|
||||
* 如果想自己折腾,则请继续阅读本说明
|
||||
|
||||
### Demo下载(win、mac、linux)
|
||||
编译好的demo文件比较大,可以到Q群共享内下载
|
||||
|
||||
### 介绍
|
||||
* 本项目为java或kotlin通过jni调用BaiPiaoOcrOnnx JNI动态运行库的范例。
|
||||
|
||||
### 编译环境
|
||||
1. jdk 1.8(参考动态运行库编译说明安装)
|
||||
2. gradle(可由IDE自动安装)
|
||||
|
||||
### 调试&编译说明
|
||||
1. 开始JVM部分编译之前,请先完成编译BaiPiaoOcrOnnx动态运行库,参考[此页面](https://github.com/znsoftm/BaiPiaoOCR/tree/main/cpp/BaiPiaoOcrOnnx)的编译说明。
|
||||
2. 把预先编译好的动态运行库复制到run-test文件夹,根据您选择的编译类型,macOS、linux可能还需要额外配置动态库的搜索路径
|
||||
3. 从第1步的说明中找到模型下载地址,放到run-test/models文件夹,测试的目标图片放到run-test/images文件夹
|
||||
4. 用IDEA打开本项目
|
||||
5. Main.java为java版调用范例,main.kt为kotlin版调用范例,之后以kotlin为范例来说明
|
||||
6. IDEA中直接在在main方法左边点击绿色的运行图标,可以直接调试运行范例,但此时没有输入参数。
|
||||
* 编辑运行参数:顶部工具栏“运行”图标左边,点开下拉菜单“Edit Configurations”
|
||||
* VM options:添加```-Djava.library.path=run-test```,把run-test文件夹加到java的lib搜索路径
|
||||
* Program arguments:添加``run-test/models ch_ppocr_server_v2.0_det_infer.onnx ch_ppocr_mobile_v2.0_cls_infer.onnx ch_ppocr_server_v2.0_rec_infer.onnx ppocr_keys_v1.txt run-test/images/1.jpg```,添加命令行输入参数,传入models文件夹和目标图片
|
||||
* 点击运行,正常的话就可以输出识别结果。
|
||||
8. 编译为jar包(以Kotlin为例):在菜单栏找到Project Structure
|
||||
* 转到“Artifacts”选项卡
|
||||
* 按“+”号新建配置,Add->Jar->Empty
|
||||
* 编辑Name:BaiPiaoOcrOnnxJvm,下面列表左边的名称也会对应改为“BaiPiaoOcrOnnxJvm.jar”,这是最终输出的文件名
|
||||
* 选中左边列表的“BaiPiaoOcrOnnxJvm.jar”,底部出现两个按钮,选择“Use Existing Manifest...”,选择src/main/resources/META-INF/MANIFEST.MF
|
||||
* 展开右边列表,找到BaiPiaoOcrOnnxJvm/main/“BaiPiaoOcrOnnxJvm.main compile output”,右键“Put into Output Root”
|
||||
* 选中右边列表的三个Gradle依赖包,分别是“kotlin-stdlib-common:1.3.72”/“kotlin-stdlib-1.3.72”/“annotations:13.0”,右键“Extract Into Output Root”
|
||||
* 按“OK”关闭项目设置窗口
|
||||
* 打开菜单栏Build->Build Artifacts,找到刚才的配置,并选Build
|
||||
* 找到out/artifacts/BaiPiaoOcrOnnxJvm/BaiPiaoOcrOnnxJvm.jar,并复制到run-test文件夹
|
||||
* 测试jar包是否正确编译,在run-test文件夹中运行
|
||||
```
|
||||
windows:run-test-java.bat
|
||||
mac或linx:./run-test-java.sh
|
||||
```
|
||||
|
||||
### 其它问题
|
||||
windows部署时如果运行显示错误can’t find dependent libraries
|
||||
* 检查是否安装C++运行环境,https://support.microsoft.com/zh-cn/help/2977003/the-latest-supported-visual-c-downloads
|
||||
下载安装:vc_redist.x64.exe
|
||||
@@ -0,0 +1,19 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
|
||||
}
|
||||
|
||||
group 'com.benjaminwan.baipiaoocr'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||
maven { url 'https://maven.aliyun.com/repository/jcenter' }
|
||||
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
kotlin.code.style=official
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://code.aliyun.com/kar/gradle-bin-zip/raw/master/gradle-6.3-bin.zip
|
||||
#distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+104
@@ -0,0 +1,104 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
chcp 65001
|
||||
:: Set Param
|
||||
@ECHO OFF
|
||||
@SETLOCAL
|
||||
echo "Setting the Number of Threads=%NUMBER_OF_PROCESSORS% Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=%NUMBER_OF_PROCESSORS%
|
||||
|
||||
:: run Windows
|
||||
java -Djava.library.path=. -Dfile.encoding=UTF-8 -jar BaiPiaoOcrOnnxJvm.jar models ^
|
||||
ch_ppocr_server_v2.0_det_infer.onnx \
|
||||
ch_ppocr_mobile_v2.0_cls_infer.onnx \
|
||||
ch_ppocr_server_v2.0_rec_infer.onnx \
|
||||
ppocr_keys_v1.txt \
|
||||
images/1.jpg \
|
||||
%NUMBER_OF_PROCESSORS% \
|
||||
50 \
|
||||
1024 \
|
||||
0.5 \
|
||||
0.3 \
|
||||
1.6 \
|
||||
1 \
|
||||
0
|
||||
|
||||
::models
|
||||
::det
|
||||
::cls
|
||||
::rec
|
||||
::keys
|
||||
::image
|
||||
::numThread
|
||||
::padding
|
||||
::maxSideLen
|
||||
::boxScoreThresh
|
||||
::boxThresh
|
||||
::unClipRatio
|
||||
::doAngle
|
||||
::mostAngle
|
||||
|
||||
PAUSE
|
||||
@ENDLOCAL
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sysOS=`uname -s`
|
||||
NUM_THREADS=1
|
||||
if [ $sysOS == "Darwin" ];then
|
||||
#echo "I'm MacOS"
|
||||
NUM_THREADS=$(sysctl -n hw.ncpu)
|
||||
elif [ $sysOS == "Linux" ];then
|
||||
#echo "I'm Linux"
|
||||
NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l)
|
||||
else
|
||||
echo "Other OS: $sysOS"
|
||||
fi
|
||||
|
||||
echo "Setting the Number of Threads=$NUM_THREADS Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=$NUM_THREADS
|
||||
|
||||
##### run test on MacOS or Linux
|
||||
java -Djava.library.path=. -jar BaiPiaoOcrOnnxJvm.jar models \
|
||||
ch_ppocr_server_v2.0_det_infer.onnx \
|
||||
ch_ppocr_mobile_v2.0_cls_infer.onnx \
|
||||
ch_ppocr_server_v2.0_rec_infer.onnx \
|
||||
ppocr_keys_v1.txt \
|
||||
images/1.jpg \
|
||||
$NUM_THREADS \
|
||||
50 \
|
||||
1024 \
|
||||
0.5 \
|
||||
0.3 \
|
||||
1.6 \
|
||||
1 \
|
||||
0
|
||||
|
||||
#models
|
||||
#det
|
||||
#cls
|
||||
#rec
|
||||
#keys
|
||||
#image
|
||||
#numThread
|
||||
#padding
|
||||
#maxSideLen
|
||||
#boxScoreThresh
|
||||
#boxThresh
|
||||
#unClipRatio
|
||||
#doAngle
|
||||
#mostAngle
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
## script for 内存泄露检查
|
||||
# ========== macOS ==========
|
||||
# https://github.com/LouisBrunner/valgrind-macos
|
||||
# brew tap LouisBrunner/valgrind
|
||||
# brew install --HEAD LouisBrunner/valgrind/valgrind
|
||||
# ========== linux ==========
|
||||
# https://www.valgrind.org/
|
||||
# apt install valgrind
|
||||
|
||||
NUM_THREADS=1
|
||||
echo "Setting the Number of Threads=$NUM_THREADS Using an OpenMP Environment Variable"
|
||||
set OMP_NUM_THREADS=$NUM_THREADS
|
||||
|
||||
##### run test on MacOS or Linux
|
||||
valgrind --trace-children=yes --tool=memcheck --leak-check=full --leak-resolution=med --track-origins=yes --vgdb=no --log-file=valgrind-memcheck.txt \
|
||||
java -Djava.library.path=. -jar BaiPiaoOcrOnnxJvm.jar models \
|
||||
ch_ppocr_server_v2.0_det_infer.onnx \
|
||||
ch_ppocr_mobile_v2.0_cls_infer.onnx \
|
||||
ch_ppocr_server_v2.0_rec_infer.onnx \
|
||||
ppocr_keys_v1.txt \
|
||||
images/1.jpg \
|
||||
$NUM_THREADS \
|
||||
0 \
|
||||
1024 \
|
||||
0.5 \
|
||||
0.3 \
|
||||
1.6 \
|
||||
1 \
|
||||
0
|
||||
|
||||
#models
|
||||
#det
|
||||
#cls
|
||||
#rec
|
||||
#keys
|
||||
#image
|
||||
#numThread
|
||||
#padding
|
||||
#maxSideLen
|
||||
#boxScoreThresh
|
||||
#boxThresh
|
||||
#unClipRatio
|
||||
#doAngle
|
||||
#mostAngle
|
||||
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'BaiPiaoOcrOnnxJvm'
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.benjaminwan.ocr;
|
||||
|
||||
import com.benjaminwan.ocrlibrary.OcrEngine;
|
||||
import com.benjaminwan.ocrlibrary.OcrResult;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
String jniLibDir = System.getProperty("java.library.path");
|
||||
System.out.println("java.library.path=" + jniLibDir);
|
||||
|
||||
if (args.length < 6) {
|
||||
System.out.println("Usage: models/dir det/name cls/name rec/name keys/name image/path");
|
||||
return;
|
||||
}
|
||||
|
||||
//------- init models/dir image/path -------
|
||||
String modelsDir = args[0];
|
||||
String detName = args[1];
|
||||
String clsName = args[2];
|
||||
String recName = args[3];
|
||||
String keysName = args[4];
|
||||
String imagePath = args[5];
|
||||
System.out.printf("modelsDir=%s, detName=%s, clsName=%s, recName=%s, keysName=%s, imagePath=%s", modelsDir, detName, clsName, recName, keysName, imagePath);
|
||||
|
||||
//------- numThread -------
|
||||
int numThread = 4;
|
||||
if (args.length > 6 && args[6] != null && args[6].length() > 0) {
|
||||
try {
|
||||
numThread = Integer.parseInt(args[6].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//------- padding -------
|
||||
int padding = 0;
|
||||
if (args.length > 7 && args[7] != null && args[7].length() > 0) {
|
||||
try {
|
||||
padding = Integer.parseInt(args[7].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//------- maxSideLen -------
|
||||
int maxSideLen = 1024;
|
||||
if (args.length > 8 && args[8] != null && args[8].length() > 0) {
|
||||
try {
|
||||
maxSideLen = Integer.parseInt(args[8].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//------- boxScoreThresh -------
|
||||
float boxScoreThresh = 0.5f;
|
||||
if (args.length > 9 && args[9] != null && args[9].length() > 0) {
|
||||
try {
|
||||
boxScoreThresh = Float.parseFloat(args[9].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//------- boxThresh -------
|
||||
float boxThresh = 0.3f;
|
||||
if (args.length > 10 && args[10] != null && args[10].length() > 0) {
|
||||
try {
|
||||
boxThresh = Float.parseFloat(args[10].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//------- unClipRatio -------
|
||||
float unClipRatio = 1.6f;
|
||||
if (args.length > 11 && args[11] != null && args[11].length() > 0) {
|
||||
try {
|
||||
unClipRatio = Float.parseFloat(args[11].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//------- doAngle -------
|
||||
int doAngleFlag = 1;
|
||||
if (args.length > 12 && args[12] != null && args[12].length() > 0) {
|
||||
try {
|
||||
doAngleFlag = Integer.parseInt(args[12].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
boolean doAngle = (doAngleFlag == 1);
|
||||
|
||||
//------- mostAngle -------
|
||||
int mostAngleFlag = 0;
|
||||
if (args.length > 13 && args[13] != null && args[13].length() > 0) {
|
||||
try {
|
||||
mostAngleFlag = Integer.parseInt(args[13].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
boolean mostAngle = (mostAngleFlag == 1);
|
||||
|
||||
//------- get jni version -------
|
||||
OcrEngine ocrEngine = new OcrEngine();
|
||||
String version = ocrEngine.getVersion();
|
||||
System.out.println("version=" + version);
|
||||
|
||||
//------- setNumThread -------
|
||||
ocrEngine.setNumThread(numThread);
|
||||
|
||||
//------- init Logger -------
|
||||
ocrEngine.initLogger(
|
||||
true,
|
||||
true,
|
||||
true
|
||||
);
|
||||
ocrEngine.enableResultText(imagePath);
|
||||
|
||||
//------- init Models -------
|
||||
boolean ret = ocrEngine.initModels(modelsDir, detName, clsName, recName, keysName);
|
||||
System.out.println("init Models " + ret);
|
||||
|
||||
//------- set param -------
|
||||
System.out.printf("padding(%d) boxScoreThresh(%f) boxThresh(%f) unClipRatio(%f) doAngle(%b) mostAngle(%b)", padding, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
ocrEngine.setPadding(padding); //图像外接白框,用于提升识别率,文字框没有正确框住所有文字时,增加此值。
|
||||
ocrEngine.setBoxScoreThresh(boxScoreThresh); //文字框置信度门限,文字框没有正确框住所有文字时,减小此值
|
||||
ocrEngine.setBoxThresh(boxThresh); //请自行试验
|
||||
ocrEngine.setUnClipRatio(unClipRatio); //单个文字框大小倍率,越大时单个文字框越大
|
||||
ocrEngine.setDoAngle(doAngle); //启用(1)/禁用(0) 文字方向检测,只有图片倒置的情况下(旋转90~270度的图片),才需要启用文字方向检测
|
||||
ocrEngine.setMostAngle(mostAngle); //启用(1)/禁用(0) 角度投票(整张图片以最大可能文字方向来识别),当禁用文字方向检测时,此项也不起作用
|
||||
//------- start detect -------
|
||||
OcrResult ocrResult = ocrEngine.detect(imagePath, maxSideLen); //按图像长边进行总体缩放,放大增加识别耗时但精度更高,缩小减小耗时但精度降低,maxSideLen=0代表不缩放
|
||||
|
||||
//使用native方法,可以让OcrEngine成为单例
|
||||
//OcrResult ocrResult = ocrEngine.detect(imagePath, padding, maxSideLen, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle);
|
||||
|
||||
//------- print result -------
|
||||
System.out.println(ocrResult.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.benjaminwan.ocr
|
||||
|
||||
import com.benjaminwan.ocrlibrary.OcrEngine
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val jniLibDir = System.getProperty("java.library.path")
|
||||
println("java.library.path=$jniLibDir")
|
||||
if (args.isEmpty() || args.size < 6) {
|
||||
println("Usage: models/dir det/name cls/name rec/name keys/name image/path")
|
||||
return
|
||||
}
|
||||
|
||||
//------- init models/dir image/path -------
|
||||
val modelsDir = args[0]
|
||||
val detName = args[1]
|
||||
val clsName = args[2]
|
||||
val recName = args[3]
|
||||
val keysName = args[4]
|
||||
val imagePath = args[5]
|
||||
println("modelsDir=$modelsDir, detName=$detName, clsName=$clsName, recName=$recName, keysName=$keysName, imagePath=$imagePath")
|
||||
|
||||
//------- numThread -------
|
||||
val numThread = if (args.size > 6) args[6].trim().toIntOrNull() ?: 4 else 4
|
||||
|
||||
//------- padding -------
|
||||
val padding = if (args.size > 7) args[7].trim().toIntOrNull() ?: 0 else 0
|
||||
|
||||
//------- maxSideLen -------
|
||||
val maxSideLen = if (args.size > 8) args[8].trim().toIntOrNull() ?: 1024 else 1024
|
||||
|
||||
//------- boxScoreThresh -------
|
||||
val boxScoreThresh = if (args.size > 9) args[9].trim().toFloatOrNull() ?: 0.5f else 0.5f
|
||||
|
||||
//------- boxThresh -------
|
||||
val boxThresh = if (args.size > 10) args[10].trim().toFloatOrNull() ?: 0.3f else 0.3f
|
||||
|
||||
//------- unClipRatio -------
|
||||
val unClipRatio = if (args.size > 11) args[11].trim().toFloatOrNull() ?: 1.6f else 1.6f
|
||||
|
||||
//------- doAngle -------
|
||||
val doAngleFlag = if (args.size > 12) args[12].trim().toIntOrNull() ?: 1 else 1
|
||||
val doAngle = (doAngleFlag == 1)
|
||||
|
||||
//------- mostAngle -------
|
||||
val mostAngleFlag = if (args.size > 13) args[13].trim().toIntOrNull() ?: 0 else 0
|
||||
val mostAngle = (mostAngleFlag == 1)
|
||||
|
||||
//------- get jni version -------
|
||||
val ocrEngine = OcrEngine()
|
||||
val version = ocrEngine.getVersion()
|
||||
println("version=$version")
|
||||
|
||||
//------- setNumThread -------
|
||||
ocrEngine.setNumThread(numThread)
|
||||
|
||||
//------- init Logger -------
|
||||
ocrEngine.initLogger(
|
||||
isConsole = true,//jni启用命令行输出
|
||||
isPartImg = true,
|
||||
isResultImg = true
|
||||
)
|
||||
ocrEngine.enableResultText(imagePath)
|
||||
|
||||
//------- init Models -------
|
||||
val ret = ocrEngine.initModels(modelsDir, detName, clsName, recName, keysName)
|
||||
println("init Models $ret")
|
||||
|
||||
//------- set param -------
|
||||
println("padding($padding) boxScoreThresh($boxScoreThresh) boxThresh($boxThresh) unClipRatio($unClipRatio) doAngle($doAngle) mostAngle($mostAngle)")
|
||||
ocrEngine.padding = padding //图像外接白框,用于提升识别率,文字框没有正确框住所有文字时,增加此值。
|
||||
ocrEngine.boxScoreThresh = boxScoreThresh //文字框置信度门限,文字框没有正确框住所有文字时,减小此值
|
||||
ocrEngine.boxThresh = boxThresh //请自行试验
|
||||
ocrEngine.unClipRatio = unClipRatio //单个文字框大小倍率,越大时单个文字框越大
|
||||
ocrEngine.doAngle = doAngle //启用(1)/禁用(0) 文字方向检测,只有图片倒置的情况下(旋转90~270度的图片),才需要启用文字方向检测
|
||||
ocrEngine.mostAngle = mostAngle //启用(1)/禁用(0) 角度投票(整张图片以最大可能文字方向来识别),当禁用文字方向检测时,此项也不起作用
|
||||
|
||||
//------- start detect -------
|
||||
val ocrResult =
|
||||
ocrEngine.detect(imagePath, maxSideLen = maxSideLen) //按图像长边进行总体缩放,放大增加识别耗时但精度更高,缩小减小耗时但精度降低,maxSideLen=0代表不缩放
|
||||
//使用native方法,可以让OcrEngine成为单例
|
||||
/*val ocrResult =
|
||||
ocrEngine.detect(imagePath, padding, maxSideLen, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle)*/
|
||||
//------- print result -------
|
||||
println(ocrResult.toString())
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.benjaminwan.ocrlibrary
|
||||
|
||||
class OcrEngine() {
|
||||
init {
|
||||
try {
|
||||
System.loadLibrary("BaiPiaoOcrOnnx")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
var padding: Int = 0
|
||||
var boxScoreThresh: Float = 0.5f
|
||||
var boxThresh: Float = 0.3f
|
||||
var unClipRatio: Float = 1.6f
|
||||
var doAngle: Boolean = true
|
||||
var mostAngle: Boolean = false
|
||||
|
||||
fun detect(input: String, maxSideLen: Int) = detect(
|
||||
input, padding, maxSideLen,
|
||||
boxScoreThresh, boxThresh,
|
||||
unClipRatio, doAngle, mostAngle
|
||||
)
|
||||
|
||||
external fun setNumThread(numThread: Int): Boolean
|
||||
|
||||
external fun initLogger(
|
||||
isConsole: Boolean,
|
||||
isPartImg: Boolean,
|
||||
isResultImg: Boolean
|
||||
)
|
||||
|
||||
external fun enableResultText(imagePath: String)
|
||||
|
||||
external fun initModels(
|
||||
modelsDir: String,
|
||||
detName: String,
|
||||
clsName: String,
|
||||
recName: String,
|
||||
keysName: String
|
||||
): Boolean
|
||||
|
||||
external fun getVersion(): String
|
||||
|
||||
external fun detect(
|
||||
input: String, padding: Int, maxSideLen: Int,
|
||||
boxScoreThresh: Float, boxThresh: Float,
|
||||
unClipRatio: Float, doAngle: Boolean, mostAngle: Boolean
|
||||
): OcrResult
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.benjaminwan.ocrlibrary
|
||||
|
||||
sealed class OcrOutput
|
||||
|
||||
object OcrStop : OcrOutput()
|
||||
object OcrFailed : OcrOutput()
|
||||
|
||||
data class OcrResult(
|
||||
val dbNetTime: Double,
|
||||
val textBlocks: ArrayList<TextBlock>,
|
||||
var detectTime: Double,
|
||||
var strRes: String
|
||||
) : OcrOutput()
|
||||
|
||||
data class Point(var x: Int, var y: Int)
|
||||
|
||||
data class TextBlock(
|
||||
val boxPoint: ArrayList<Point>, var boxScore: Float,
|
||||
val angleIndex: Int, val angleScore: Float, val angleTime: Double,
|
||||
val text: String, val charScores: FloatArray, val crnnTime: Double,
|
||||
val blockTime: Double
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TextBlock
|
||||
|
||||
if (boxPoint != other.boxPoint) return false
|
||||
if (boxScore != other.boxScore) return false
|
||||
if (angleIndex != other.angleIndex) return false
|
||||
if (angleScore != other.angleScore) return false
|
||||
if (angleTime != other.angleTime) return false
|
||||
if (text != other.text) return false
|
||||
if (!charScores.contentEquals(other.charScores)) return false
|
||||
if (crnnTime != other.crnnTime) return false
|
||||
if (blockTime != other.blockTime) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = boxPoint.hashCode()
|
||||
result = 31 * result + boxScore.hashCode()
|
||||
result = 31 * result + angleIndex
|
||||
result = 31 * result + angleScore.hashCode()
|
||||
result = 31 * result + angleTime.hashCode()
|
||||
result = 31 * result + text.hashCode()
|
||||
result = 31 * result + charScores.contentHashCode()
|
||||
result = 31 * result + crnnTime.hashCode()
|
||||
result = 31 * result + blockTime.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: com.benjaminwan.ocr.MainKt
|
||||
Class-Path: kotlin-stdlib-1.3.72.jar annotations-13.0.jar kotlin-stdlib-
|
||||
common-1.3.72.jar
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# JVM项目
|
||||
|
||||
## 简介
|
||||
本目录存放Jvm相关demo项目
|
||||
1. BaiPiaoOcrOnnxJvm项目: 通过jni调用BaiPiaoOcrOnnx项目编译的动态运行库范例,内含java和kotlin调用范例,附调试和编译相关说明
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
Windows /Linux/ IOS/ android/Java
|
||||
|
||||
白富美OCR, 模型来源于百度,富人家的千金 -- 白富美。
|
||||
白富美OCR(名称由来:白富美→又白又漂亮→白漂), 模型来源于百度,富人家的千金 -- 白富美。
|
||||
|
||||
基于百度的开源PaddleOCR,任何人可以使用本推理库,也可以根据自己的需求使用百度的paddlepaddle 框架进行模型优化。
|
||||
|
||||
|
||||
Reference in new issue
Block a user