添加cpp项目和jvm项目

This commit is contained in:
benjaminwan committed 2021-01-13 15:40:58 +08:00
1 parent 21a3bd6f3e
commit a4f91467ba
55 files changed
+9096 -4

No files matched your search

+2 -2
View File
@@ -132,6 +132,6 @@ dmypy.json
#idea
.idea
/test_imgs
/images
/models
/release
+142
View File
@@ -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/
+123
View File
@@ -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 ()
+133
View File
@@ -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```:打印命令行帮助。
+166
View File
@@ -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;
}
+62
View File
@@ -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
+73
View File
@@ -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
+95
View File
@@ -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
+41
View File
@@ -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__
+43
View File
@@ -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__
+34
View File
@@ -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__
+51
View File
@@ -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
+53
View File
@@ -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__
+90
View File
@@ -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__
+406
View File
@@ -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
+44
View File
@@ -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_
+53
View File
@@ -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__
+6
View File
@@ -0,0 +1,6 @@
#ifndef __OCR_VERSION_H__
#define __OCR_VERSION_H__
#define VERSION "1.0.0"
#endif //__OCR_VERSION_H__
+34
View File
@@ -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
+36
View File
@@ -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
+30
View File
@@ -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
+32
View File
@@ -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
+139
View File
@@ -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;
}
+149
View File
@@ -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;
}
+157
View File
@@ -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);
}
+213
View File
@@ -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};
}
+120
View File
@@ -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
+114
View File
@@ -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
+401
View File
@@ -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
+222
View File
@@ -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;
}
********************************************************/
+145
View File
@@ -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;
}
+30
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
# C++项目
## 简介
本目录存放C++相关demo项目
1. BaiPiaoOcrOnnx项目: 模型源自paddleOCR,转为onnx格式后,使用onnxruntime框架进行推理。
-1
View File
@@ -1 +0,0 @@
placeholder
+145
View File
@@ -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
+47
View File
@@ -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
+19
View File
@@ -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'
}
+1
View File
@@ -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
View File
@@ -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" "$@"
+104
View File
@@ -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
View File
@@ -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
View File
@@ -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
+45
View File
@@ -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
+2
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
# JVM项目
## 简介
本目录存放Jvm相关demo项目
1. BaiPiaoOcrOnnxJvm项目: 通过jni调用BaiPiaoOcrOnnx项目编译的动态运行库范例,内含java和kotlin调用范例,附调试和编译相关说明
+1 -1
View File
@@ -6,7 +6,7 @@
Windows /Linux/ IOS/ android/Java
白富美OCR, 模型来源于百度,富人家的千金 -- 白富美。
白富美OCR(名称由来:白富美→又白又漂亮→白漂), 模型来源于百度,富人家的千金 -- 白富美。
基于百度的开源PaddleOCR,任何人可以使用本推理库,也可以根据自己的需求使用百度的paddlepaddle 框架进行模型优化。