C++项目升级v1.0.2,修复free方法造成win7崩溃问题。编译脚本添加预设项。

This commit is contained in:
benjaminwan committed 2021-01-30 09:29:21 +08:00
1 parent fc3918ff51
commit c14befcdab
13 files changed
+151 -66

No files matched your search

+30 -1
View File
@@ -1,25 +1,31 @@
# 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),其中server体积大且较慢但效果好,mobile体积小且较快但效果差点。
### 模型下载
[模型下载地址](https://github.com/znsoftm/BaiPiaoOCR/tree/main/models)
也可以到Q群共享内下载,下载后解压到项目根目录
```
BaiPiaoOcrOnnx/models
├── ch_ppocr_mobile_v2.0_cls_infer.onnx
@@ -31,13 +37,16 @@ BaiPiaoOcrOnnx/models
```
### 依赖的第三方库下载
下载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
@@ -47,19 +56,24 @@ BaiPiaoOcrOnnx
```
### 编译环境
1. Windows 10 x64
2. macOS 10.15
3. Linux Ubuntu 1604 x64
**注意:以下说明仅适用于本机编译。如果需要交叉编译为arm等其它平台(参考android),则需要先交叉编译所有第三方依赖库(onnxruntime、opencv),然后再把依赖库整合替换到本项目里。**
### 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;``
@@ -68,56 +82,70 @@ BaiPiaoOcrOnnx
* 运行```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. ```OCR_OPENMP=ON```:启用(ON)或禁用(OFF) ON时AngleNet和CrnnNet阶段使用OpenMP并行运算,OFF时单线程运算
2. ```OCR_LIB=ON```: 启用(ON)或禁用(OFF) ON时编译为jni lib,OFF时编译为可执行文件
3. ```OCR_STATIC=ON```: 启用(ON)或禁用(OFF) ON时选择opencv和onnxruntime的静态库进行编译,OFF时则选择动态库编译
### 输入参数说明
* 请参考main.h中的命令行参数说明。
* 每个参数有一个短参数名和一个长参数名,用短的或长的均可。
1. ```-d或--models```:模型所在文件夹路径,可以相对路径也可以绝对路径。
2. ```-1或--det```:det模型文件名(含扩展名)
3. ```-2或--cls```:cls模型文件名(含扩展名)
@@ -126,7 +154,8 @@ build.sh编译参数:
6. ```-i或--image```:目标图片路径,可以相对路径也可以绝对路径。
7. ```-t或--numThread```:线程数量。
8. ```-p或--padding```:图像预处理,在图片外周添加白边,用于提升识别率,文字框没有正确框住所有文字时,增加此值。
9. ```-s或--maxSideLen```:按图片最长边的长度,此值为0代表不缩放,例:1024,如果图片长边大于1024则把图像整体缩小到1024再进行图像分割计算,如果图片长边小于1024则不缩放,如果图片长边小于32,则缩放到32。
9. ```-s或--maxSideLen```
:按图片最长边的长度,此值为0代表不缩放,例:1024,如果图片长边大于1024则把图像整体缩小到1024再进行图像分割计算,如果图片长边小于1024则不缩放,如果图片长边小于32,则缩放到32。
10. ```-b或--boxScoreThresh```:文字框置信度门限,文字框没有正确框住所有文字时,减小此值。
11. ```-o或--boxThresh```:请自行试验。
12. ```-u或--unClipRatio```:单个文字框大小倍率,越大时单个文字框越大。此项与图片的大小相关,越大的图片此值应该越大。
+15 -1
View File
@@ -7,11 +7,12 @@ echo.
echo "========编译选项========"
echo "请注意:项目默认使用Release库,除非您自行编译Debug版的Onnxruntime和Opencv,否则请不要选择Debug编译"
echo "请输入编译选项并回车: 1)Release, 2)Debug"
echo "请输入编译选项并回车: 1)Release, 2)Debug, 3)预设"
set BUILD_TYPE=Release
set /p flag=
if %flag% == 1 (set BUILD_TYPE=Release)^
else if %flag% == 2 (set BUILD_TYPE=Debug)^
else if %flag% == 3 (goto :makeAllExe)^
else (echo 输入错误!Input Error!)
echo.
@@ -59,4 +60,17 @@ mkdir build-lib
pushd build-lib
GOTO:EOF
:makeAllExe
mkdir win-%VSCMD_ARG_TGT_ARCH%
pushd win-%VSCMD_ARG_TGT_ARCH%
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DOCR_OPENMP=ON -DOCR_LIB=OFF -DOCR_STATIC=ON ..
nmake
popd
mkdir win-lib-%VSCMD_ARG_TGT_ARCH%
pushd win-lib-%VSCMD_ARG_TGT_ARCH%
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DOCR_OPENMP=ON -DOCR_LIB=ON -DOCR_STATIC=ON ..
nmake
popd
@ENDLOCAL
+43 -26
View File
@@ -1,15 +1,44 @@
#!/usr/bin/env bash
function buildAll() {
mkdir -p ${sysOS}
pushd ${sysOS}
cmake -DCMAKE_BUILD_TYPE=Release -DOCR_OPENMP=ON -DOCR_LIB=OFF -DOCR_STATIC=ON ..
make -j $NUM_THREADS
popd
mkdir -p ${sysOS}-Lib
pushd ${sysOS}-Lib
cmake -DCMAKE_BUILD_TYPE=Release -DOCR_OPENMP=ON -DOCR_LIB=ON -DOCR_STATIC=ON ..
make -j $NUM_THREADS
popd
}
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 "========请先参考README.md准备好编译环境========"
echo
echo "========编译选项========"
echo "请输入编译选项并回车: 1)Release, 2)Debug"
echo "请输入编译选项并回车: 1)Release, 2)Debug, 3)预设"
read -p "" BUILD_TYPE
if [ $BUILD_TYPE == 1 ]; then
BUILD_TYPE=Release
BUILD_TYPE=Release
elif [ $BUILD_TYPE == 2 ]; then
BUILD_TYPE=Debug
BUILD_TYPE=Debug
elif [ $BUILD_TYPE == 3 ]; then
buildAll
exit
else
echo -e "输入错误!Input Error!"
fi
@@ -17,9 +46,9 @@ fi
echo "请输入OpenMP选项并回车: 1)启用OpenMP(Angle阶段和Crnn阶段多线程并行执行), 2)禁用OpenMP(Angle阶段和Crnn阶段单线程执行)"
read -p "" BUILD_OPENMP
if [ $BUILD_OPENMP == 1 ]; then
BUILD_OPENMP=ON
BUILD_OPENMP=ON
elif [ $BUILD_OPENMP == 2 ]; then
BUILD_OPENMP=OFF
BUILD_OPENMP=OFF
else
echo -e "输入错误!Input Error!"
fi
@@ -29,9 +58,9 @@ echo "使用动态库时,编译出来的可执行文件较小,但Linux部署
echo "请选择要使用的OnnxRuntime和Opencv库选项并回车: 1)Static静态库,2)Shared动态库"
read -p "" BUILD_STATIC
if [ $BUILD_STATIC == 1 ]; then
BUILD_STATIC=ON
BUILD_STATIC=ON
elif [ $BUILD_STATIC == 2 ]; then
BUILD_STATIC=OFF
BUILD_STATIC=OFF
else
echo -e "输入错误!Input Error!"
fi
@@ -40,34 +69,22 @@ echo "请注意:如果选择2)编译为JNI动态库时,必须安装配置Ora
echo "请选择编译输出类型并回车: 1)编译成可执行文件,2)编译成JNI动态库"
read -p "" BUILD_LIB
if [ $BUILD_LIB == 1 ]; then
BUILD_LIB=OFF
BUILD_LIB=OFF
elif [ $BUILD_LIB == 2 ]; then
BUILD_LIB=ON
BUILD_LIB=ON
else
echo -e "输入错误!Input Error!"
fi
if [ $BUILD_LIB == OFF ]; then
mkdir -p build
pushd build
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"
mkdir -p build-lib
pushd build-lib
fi
echo "cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOCR_OPENMP=${BUILD_OPENMP} -DOCR_LIB=${BUILD_LIB} -DOCR_STATIC=${BUILD_STATIC} .."
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DOCR_OPENMP=$BUILD_OPENMP -DOCR_LIB=$BUILD_LIB -DOCR_STATIC=$BUILD_STATIC ..
make -j $NUM_THREADS
popd
popd
+2 -2
View File
@@ -26,8 +26,8 @@ private:
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
int numThread = 0;
std::vector<char *> inputNames;
std::vector<char *> outputNames;
char *inputName;
char *outputName;
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};
+2 -2
View File
@@ -25,8 +25,8 @@ private:
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
int numThread = 0;
std::vector<char *> inputNames;
std::vector<char *> outputNames;
char *inputName;
char *outputName;
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};
+2 -2
View File
@@ -23,8 +23,8 @@ private:
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;
char *inputName;
char *outputName;
const float meanValues[3] = {0.485 * 255, 0.456 * 255, 0.406 * 255};
const float normValues[3] = {1.0 / 0.229 / 255.0, 1.0 / 0.224 / 255.0, 1.0 / 0.225 / 255.0};
+16
View File
@@ -45,6 +45,18 @@ inline bool isFileExists(const std::string &name) {
return (stat(name.c_str(), &buffer) == 0);
}
#ifdef _WIN32
#define my_strtol wcstol
#define my_strrchr wcsrchr
#define my_strcasecmp _wcsicmp
#define my_strdup _strdup
#else
#define my_strtol strtol
#define my_strrchr strrchr
#define my_strcasecmp strcasecmp
#define my_strdup strdup
#endif
std::wstring strToWstr(std::string str);
ScaleParam getScaleParam(cv::Mat &src, const float scale);
@@ -83,6 +95,10 @@ std::vector<char *> getInputNames(Ort::Session *session);
std::vector<char *> getOutputNames(Ort::Session *session);
void getInputName(Ort::Session *session, char *&inputName);
void getOutputName(Ort::Session *session, char *&outputName);
void saveImg(cv::Mat &img, const char *imgPath);
std::string getSrcImgFilePath(const char *path, const char *imgName);
+1 -1
View File
@@ -1,6 +1,6 @@
#ifndef __OCR_VERSION_H__
#define __OCR_VERSION_H__
#define VERSION "1.0.1"
#define VERSION "1.0.2"
#endif //__OCR_VERSION_H__
+5 -10
View File
@@ -6,12 +6,8 @@ AngleNet::AngleNet() {}
AngleNet::~AngleNet() {
delete session;
for (auto name : inputNames) {
free(name);
}
for (auto name : outputNames) {
free(name);
}
free(inputName);
free(outputName);
}
void AngleNet::setNumThread(int numOfThread) {
@@ -42,8 +38,8 @@ void AngleNet::initModel(const std::string &pathStr) {
#else
session = new Ort::Session(env, pathStr.c_str(), sessionOptions);
#endif
inputNames = getInputNames(session);
outputNames = getOutputNames(session);
getInputName(session,inputName);
getOutputName(session,outputName);
}
Angle scoreToAngle(const std::vector<float> &outputData) {
@@ -71,8 +67,7 @@ Angle AngleNet::getAngle(cv::Mat &src) {
inputShape.size());
assert(inputTensor.IsTensor());
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, inputNames.data(), &inputTensor,
inputNames.size(), outputNames.data(), outputNames.size());
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, &inputName, &inputTensor, 1, &outputName, 1);
assert(outputTensor.size() == 1 && outputTensor.front().IsTensor());
+5 -10
View File
@@ -7,12 +7,8 @@ CrnnNet::CrnnNet() {}
CrnnNet::~CrnnNet() {
delete session;
for (auto name : inputNames) {
free(name);
}
for (auto name : outputNames) {
free(name);
}
free(inputName);
free(outputName);
}
void CrnnNet::setNumThread(int numOfThread) {
@@ -43,8 +39,8 @@ void CrnnNet::initModel(const std::string &pathStr, const std::string &keysPath)
#else
session = new Ort::Session(env, pathStr.c_str(), sessionOptions);
#endif
inputNames = getInputNames(session);
outputNames = getOutputNames(session);
getInputName(session, inputName);
getOutputName(session, outputName);
//load keys
std::ifstream in(keysPath.c_str());
@@ -109,8 +105,7 @@ TextLine CrnnNet::getTextLine(const cv::Mat &src) {
inputShape.size());
assert(inputTensor.IsTensor());
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, inputNames.data(), &inputTensor,
inputNames.size(), outputNames.data(), outputNames.size());
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, &inputName, &inputTensor, 1, &outputName, 1);
assert(outputTensor.size() == 1 && outputTensor.front().IsTensor());
+5 -10
View File
@@ -5,12 +5,8 @@ DbNet::DbNet() {}
DbNet::~DbNet() {
delete session;
for (auto name : inputNames) {
free(name);
}
for (auto name : outputNames) {
free(name);
}
free(inputName);
free(outputName);
}
void DbNet::setNumThread(int numOfThread) {
@@ -41,8 +37,8 @@ void DbNet::initModel(const std::string &pathStr) {
#else
session = new Ort::Session(env, pathStr.c_str(), sessionOptions);
#endif
inputNames = getInputNames(session);
outputNames = getOutputNames(session);
getInputName(session, inputName);
getOutputName(session, outputName);
}
std::vector<TextBox> findRsBoxes(const cv::Mat &predMat, const cv::Mat &dilateMat, ScaleParam &s,
@@ -115,8 +111,7 @@ DbNet::getTextBoxes(cv::Mat &src, ScaleParam &s, float boxScoreThresh, float box
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());
auto outputTensor = session->Run(Ort::RunOptions{nullptr}, &inputName, &inputTensor, 1, &outputName, 1);
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,
+1 -1
View File
@@ -58,7 +58,7 @@ void OcrLite::Logger(const char *format, ...) {
cv::Mat makePadding(cv::Mat &src, const int padding) {
if (padding <= 0) return src;
cv::Scalar paddingScalar = {255, 255, 255}; //white color
cv::Scalar paddingScalar = {255, 255, 255};
cv::Mat paddingSrc;
cv::copyMakeBorder(src, paddingSrc, padding, padding, padding, padding, cv::BORDER_ISOLATED, paddingScalar);
return paddingSrc;
+24
View File
@@ -372,6 +372,30 @@ std::vector<char *> getOutputNames(Ort::Session *session) {
return outputNodeNames;
}
void getInputName(Ort::Session *session, char *&inputName) {
size_t numInputNodes = session->GetInputCount();
if (numInputNodes > 0) {
Ort::AllocatorWithDefaultOptions allocator;
{
char *t = session->GetInputName(0, allocator);
inputName = my_strdup(t);
allocator.Free(t);
}
}
}
void getOutputName(Ort::Session *session, char *&outputName) {
size_t numOutputNodes = session->GetInputCount();
if (numOutputNodes > 0) {
Ort::AllocatorWithDefaultOptions allocator;
{
char *t = session->GetOutputName(0, allocator);
outputName = my_strdup(t);
allocator.Free(t);
}
}
}
void saveImg(cv::Mat &img, const char *imgPath) {
cv::imwrite(imgPath, img);
}