├── images ├── 1.jpg ├── clear.cmd └── clear.sh ├── include ├── version.h ├── DbNet.h ├── OcrResultUtils.h ├── AngleNet.h ├── CrnnNet.h ├── OcrStruct.h ├── getopt.h ├── OcrLiteCApi.h ├── OcrLite.h ├── OcrUtils.h ├── main.h └── clipper.hpp ├── scripts ├── install-vulkan-linux.sh ├── build-ncnn-nmake.bat └── build-ncnn.sh ├── opencv-static └── OpenCVWrapperConfig.cmake ├── OcrCRTLinkage.cmake ├── valgrind-memcheck.sh ├── ncnn-static └── NcnnWrapperConfig.cmake ├── ncnn-vulkan-static └── NcnnWrapperConfig.cmake ├── run-test.sh ├── run-benchmark.sh ├── run-test.bat ├── run-benchmark.bat ├── generate-vs-project.bat ├── src ├── OcrLiteCApi.cpp ├── DbNet.cpp ├── AngleNet.cpp ├── CrnnNet.cpp ├── OcrResultUtils.cpp ├── OcrLiteJni.cpp ├── getopt.cpp ├── main.cpp ├── OcrLite.cpp └── OcrUtils.cpp ├── .gitignore ├── README.md ├── CMakeLists.txt ├── BUILD.md ├── benchmark └── benchmark.cpp ├── .github └── workflows │ └── builder.yml └── valgrind-memcheck.txt /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminwan/OcrLiteNcnn/HEAD/images/1.jpg -------------------------------------------------------------------------------- /include/version.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_VERSION_H__ 2 | #define __OCR_VERSION_H__ 3 | 4 | #define VERSION "1.8.1" 5 | 6 | #endif //__OCR_VERSION_H__ 7 | -------------------------------------------------------------------------------- /images/clear.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | echo Delete part img 3 | DEL /Q *-part-*.jpg 4 | 5 | echo Delete angle img 6 | DEL /Q *-angle-*.jpg 7 | 8 | echo Delete debug img 9 | DEL /Q *-debug-*.jpg 10 | 11 | echo Delete result img 12 | DEL /Q *-result.jpg 13 | 14 | echo Delete result txt 15 | DEL /Q *-result.txt 16 | -------------------------------------------------------------------------------- /images/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo Delete part img 4 | rm -f *-part-*.jpg 5 | 6 | echo Delete angle img 7 | rm -f *-angle-*.jpg 8 | 9 | echo Delete debug img 10 | rm -f *-debug-*.jpg 11 | 12 | echo Delete result img 13 | rm -f *-result.jpg 14 | 15 | echo Delete result txt 16 | rm -f *-result.txt 17 | -------------------------------------------------------------------------------- /scripts/install-vulkan-linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | VULKAN_SDK="$(dirname "$(readlink -f "${BASH_SOURCE:-$_}" )" )/x86_64" 3 | sudo cp -r $VULKAN_SDK/include/vulkan/ /usr/local/include/ 4 | sudo cp -P $VULKAN_SDK/lib/libvulkan.so* /usr/local/lib/ 5 | sudo cp $VULKAN_SDK/lib/libVkLayer_*.so /usr/local/lib/ 6 | sudo mkdir -p /usr/local/share/vulkan/explicit_layer.d 7 | sudo cp $VULKAN_SDK/etc/vulkan/explicit_layer.d/VkLayer_*.json /usr/local/share/vulkan/explicit_layer.d -------------------------------------------------------------------------------- /opencv-static/OpenCVWrapperConfig.cmake: -------------------------------------------------------------------------------- 1 | if (WIN32) 2 | if (CMAKE_CL_64) 3 | message("配置WINDOWS OpenCV x64 路径: ${CMAKE_CURRENT_LIST_DIR}/windows-x64") 4 | set(OpenCV_DIR "${CMAKE_CURRENT_LIST_DIR}/windows-x64") 5 | else () 6 | message("配置WINDOWS OpenCV x86 路径: ${CMAKE_CURRENT_LIST_DIR}/windows-x86") 7 | set(OpenCV_DIR "${CMAKE_CURRENT_LIST_DIR}/windows-x86") 8 | endif () 9 | elseif (APPLE) 10 | message("配置macOS OpenCV 路径: ${CMAKE_CURRENT_LIST_DIR}/macos/lib/cmake/opencv4") 11 | set(OpenCV_DIR "${CMAKE_CURRENT_LIST_DIR}/macos/lib/cmake/opencv4") 12 | elseif (UNIX) 13 | message("配置Linux OpenCV 路径: ${CMAKE_CURRENT_LIST_DIR}/linux/lib/cmake/opencv4") 14 | set(OpenCV_DIR "${CMAKE_CURRENT_LIST_DIR}/linux/lib/cmake/opencv4") 15 | endif () 16 | -------------------------------------------------------------------------------- /include/DbNet.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_DBNET_H__ 2 | #define __OCR_DBNET_H__ 3 | 4 | #include "OcrStruct.h" 5 | #include "ncnn/net.h" 6 | #include 7 | 8 | class DbNet { 9 | public: 10 | ~DbNet(); 11 | 12 | void setNumThread(int numOfThread); 13 | 14 | void setGpuIndex(int gpuIndex); 15 | 16 | bool initModel(const std::string &pathStr); 17 | 18 | std::vector getTextBoxes(cv::Mat &src, ScaleParam &s, float boxScoreThresh, 19 | float boxThresh, float unClipRatio); 20 | 21 | private: 22 | int numThread; 23 | ncnn::Net net; 24 | const float meanValues[3] = {0.485 * 255, 0.456 * 255, 0.406 * 255}; 25 | const float normValues[3] = {1.0 / 0.229 / 255.0, 1.0 / 0.224 / 255.0, 1.0 / 0.225 / 255.0}; 26 | }; 27 | 28 | 29 | #endif //__OCR_DBNET_H__ 30 | -------------------------------------------------------------------------------- /include/OcrResultUtils.h: -------------------------------------------------------------------------------- 1 | #ifdef __JNI__ 2 | #ifndef __OCR_RESULT_UTILS_H__ 3 | #define __OCR_RESULT_UTILS_H__ 4 | #include 5 | #include "OcrStruct.h" 6 | 7 | class OcrResultUtils { 8 | public: 9 | OcrResultUtils(JNIEnv *env, OcrResult &ocrResult); 10 | 11 | ~OcrResultUtils(); 12 | 13 | jobject getJObject(); 14 | 15 | private: 16 | JNIEnv *jniEnv; 17 | jobject jOcrResult; 18 | 19 | jclass newJListClass(); 20 | 21 | jmethodID getListConstructor(jclass clazz); 22 | 23 | jobject getTextBlock(TextBlock &textBlock); 24 | 25 | jobject getTextBlocks(std::vector &textBlocks); 26 | 27 | jobject newJPoint(cv::Point &point); 28 | 29 | jobject newJBoxPoint(std::vector &boxPoint); 30 | 31 | jfloatArray newJScoreArray(std::vector &scores); 32 | 33 | }; 34 | #endif //__OCR_RESULT_UTILS_H__ 35 | #endif 36 | -------------------------------------------------------------------------------- /scripts/build-ncnn-nmake.bat: -------------------------------------------------------------------------------- 1 | :: build opencv 3.4.x for windows by benjaminwan 2 | @ECHO OFF 3 | chcp 65001 4 | cls 5 | @SETLOCAL 6 | 7 | echo 请选择要生成的选项并回车: 1)Release,2)Debug: 8 | echo Please Select Build Type: 1)Release,2)Debug: 9 | set BUILD_TYPE=Release 10 | set /p flag= 11 | if %flag% == 0 (set BUILD_TYPE=Release)^ 12 | else if %flag% == 1 (set BUILD_TYPE=Release)^ 13 | else if %flag% == 2 (set BUILD_TYPE=Debug)^ 14 | else (echo "输入错误!Input Error!") 15 | call :cmakeParams %BUILD_TYPE% 16 | GOTO:EOF 17 | 18 | :cmakeParams 19 | mkdir "build-windows-%~1" 20 | pushd "build-windows-%~1" 21 | cmake -G "NMake Makefiles" ^ 22 | -DCMAKE_BUILD_TYPE="%~1" ^ 23 | -DNCNN_OPENMP=ON ^ 24 | -DNCNN_BUILD_BENCHMARK=OFF ^ 25 | -DNCNN_BUILD_EXAMPLES=OFF ^ 26 | -DNCNN_BUILD_TOOLS=OFF ^ 27 | -DNCNN_PYTHON=OFF ^ 28 | -DNCNN_VULKAN=OFF ^ 29 | .. 30 | nmake 31 | nmake install 32 | popd 33 | GOTO:EOF -------------------------------------------------------------------------------- /include/AngleNet.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_ANGLENET_H__ 2 | #define __OCR_ANGLENET_H__ 3 | 4 | #include "OcrStruct.h" 5 | #include "ncnn/net.h" 6 | #include 7 | 8 | class AngleNet { 9 | public: 10 | 11 | ~AngleNet(); 12 | 13 | void setNumThread(int numOfThread); 14 | 15 | void setGpuIndex(int gpuIndex); 16 | 17 | bool initModel(const std::string &pathStr); 18 | 19 | std::vector getAngles(std::vector &partImgs, const char *path, 20 | const char *imgName, bool doAngle, bool mostAngle); 21 | 22 | private: 23 | bool isOutputAngleImg = false; 24 | int numThread; 25 | ncnn::Net net; 26 | const float meanValues[3] = {127.5, 127.5, 127.5}; 27 | const float normValues[3] = {1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5}; 28 | 29 | const int dstWidth = 192; 30 | const int dstHeight = 32; 31 | 32 | Angle getAngle(cv::Mat &src); 33 | }; 34 | 35 | 36 | #endif //__OCR_ANGLENET_H__ 37 | -------------------------------------------------------------------------------- /include/CrnnNet.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_CRNNNET_H__ 2 | #define __OCR_CRNNNET_H__ 3 | 4 | #include "OcrStruct.h" 5 | #include "ncnn/net.h" 6 | #include 7 | 8 | class CrnnNet { 9 | public: 10 | 11 | ~CrnnNet(); 12 | 13 | void setNumThread(int numOfThread); 14 | 15 | void setGpuIndex(int gpuIndex); 16 | 17 | bool initModel(const std::string &pathStr, const std::string &keysPath); 18 | 19 | std::vector getTextLines(std::vector &partImg, const char *path, const char *imgName); 20 | 21 | private: 22 | bool isOutputDebugImg = false; 23 | int numThread; 24 | ncnn::Net net; 25 | 26 | const float meanValues[3] = {127.5, 127.5, 127.5}; 27 | const float normValues[3] = {1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5}; 28 | const int dstHeight = 32; 29 | 30 | std::vector keys; 31 | 32 | TextLine scoreToTextLine(const std::vector &outputData, int h, int w); 33 | 34 | TextLine getTextLine(const cv::Mat &src); 35 | }; 36 | 37 | 38 | #endif //__OCR_CRNNNET_H__ 39 | -------------------------------------------------------------------------------- /OcrCRTLinkage.cmake: -------------------------------------------------------------------------------- 1 | if (WIN32) 2 | foreach(flag_var 3 | CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE 4 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO 5 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 6 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 7 | if(${flag_var} MATCHES "/MD") 8 | string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 9 | endif() 10 | if(${flag_var} MATCHES "/MDd") 11 | string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") 12 | endif() 13 | endforeach(flag_var) 14 | 15 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:atlthunk.lib") 16 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:msvcrt.lib") 17 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:libcpmtd.lib /NODEFAULTLIB:msvcrtd.lib") 18 | message(STATUS "OCR_BUILD_CRT True") 19 | endif () 20 | 21 | -------------------------------------------------------------------------------- /valgrind-memcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## script for 内存泄露检查 3 | # ========== macOS ========== 4 | # https://github.com/LouisBrunner/valgrind-macos 5 | # brew tap LouisBrunner/valgrind 6 | # brew install --HEAD LouisBrunner/valgrind/valgrind 7 | # ========== linux ========== 8 | # https://www.valgrind.org/ 9 | # apt install valgrind 10 | 11 | NUM_THREADS=1 12 | 13 | set OMP_NUM_THREADS=$NUM_THREADS 14 | 15 | TARGET_IMG=../images/1.jpg 16 | if [ ! -f "$TARGET_IMG" ]; then 17 | echo "找不到待识别的目标图片:${TARGET_IMG},请打开本文件并编辑TARGET_IMG" 18 | exit 19 | fi 20 | 21 | sysOS=`uname -s` 22 | EXE_PATH=${sysOS}-BIN 23 | 24 | ##### run test on MacOS or Linux 25 | valgrind --tool=memcheck --leak-check=full --leak-resolution=med --track-origins=yes --vgdb=no --log-file=valgrind-memcheck.txt \ 26 | ./${EXE_PATH}/OcrLiteNcnn --models models \ 27 | --det dbnet.onnx \ 28 | --cls angle_net.onnx \ 29 | --rec crnn_lite_lstm.onnx \ 30 | --keys keys.txt \ 31 | --image $TARGET_IMG \ 32 | --numThread $NUM_THREADS \ 33 | --padding 50 \ 34 | --maxSideLen 1024 \ 35 | --boxScoreThresh 0.6 \ 36 | --boxThresh 0.3 \ 37 | --unClipRatio 2.0 \ 38 | --doAngle 1 \ 39 | --mostAngle 1 40 | -------------------------------------------------------------------------------- /include/OcrStruct.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_STRUCT_H__ 2 | #define __OCR_STRUCT_H__ 3 | 4 | #include "opencv2/core.hpp" 5 | #include 6 | 7 | struct ScaleParam { 8 | int srcWidth; 9 | int srcHeight; 10 | int dstWidth; 11 | int dstHeight; 12 | float ratioWidth; 13 | float ratioHeight; 14 | }; 15 | 16 | struct TextBox { 17 | std::vector boxPoint; 18 | float score; 19 | }; 20 | 21 | struct Angle { 22 | int index; 23 | float score; 24 | double time; 25 | }; 26 | 27 | struct TextLine { 28 | std::string text; 29 | std::vector charScores; 30 | double time; 31 | }; 32 | 33 | struct TextBlock { 34 | std::vector boxPoint; 35 | float boxScore; 36 | int angleIndex; 37 | float angleScore; 38 | double angleTime; 39 | std::string text; 40 | std::vector charScores; 41 | double crnnTime; 42 | double blockTime; 43 | }; 44 | 45 | struct OcrResult { 46 | double dbNetTime; 47 | std::vector textBlocks; 48 | cv::Mat boxImg; 49 | double detectTime; 50 | std::string strRes; 51 | }; 52 | 53 | #endif //__OCR_STRUCT_H__ 54 | -------------------------------------------------------------------------------- /ncnn-static/NcnnWrapperConfig.cmake: -------------------------------------------------------------------------------- 1 | if (APPLE) 2 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/macos/include") 3 | include_directories(${NCNN_INCLUDE_DIRS}) 4 | message("配置macOS ncnn 路径: ${CMAKE_CURRENT_LIST_DIR}/macos/lib/cmake/ncnn") 5 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/macos/lib/cmake/ncnn") 6 | elseif (WIN32) 7 | if (CMAKE_CL_64) 8 | message("配置WINDOWS ncnn x64 路径: ${CMAKE_CURRENT_LIST_DIR}/windows-x64/lib/cmake/ncnn") 9 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/windows-x64/lib/cmake/ncnn") 10 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/windows-x64/include") 11 | else () 12 | message("配置WINDOWS ncnn x86 路径: ${CMAKE_CURRENT_LIST_DIR}/windows-x86/lib/cmake/ncnn") 13 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/windows-x86/lib/cmake/ncnn") 14 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/windows-x86/include") 15 | endif () 16 | include_directories(${NCNN_INCLUDE_DIRS}) 17 | elseif (UNIX) 18 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/linux/include") 19 | include_directories(${NCNN_INCLUDE_DIRS}) 20 | message("配置Linux ncnn 路径: ${CMAKE_CURRENT_LIST_DIR}/linux/lib/cmake/ncnn") 21 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/linux/lib/cmake/ncnn") 22 | endif () 23 | 24 | -------------------------------------------------------------------------------- /ncnn-vulkan-static/NcnnWrapperConfig.cmake: -------------------------------------------------------------------------------- 1 | if (APPLE) 2 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/macos/include") 3 | include_directories(${NCNN_INCLUDE_DIRS}) 4 | message("配置macOS ncnn 路径: ${CMAKE_CURRENT_LIST_DIR}/macos/lib/cmake/ncnn") 5 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/macos/lib/cmake/ncnn") 6 | elseif (WIN32) 7 | if (CMAKE_CL_64) 8 | message("配置WINDOWS ncnn x64 路径: ${CMAKE_CURRENT_LIST_DIR}/windows-x64/lib/cmake/ncnn") 9 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/windows-x64/lib/cmake/ncnn") 10 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/windows-x64/include") 11 | else () 12 | message("配置WINDOWS ncnn x86 路径: ${CMAKE_CURRENT_LIST_DIR}/windows-x86/lib/cmake/ncnn") 13 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/windows-x86/lib/cmake/ncnn") 14 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/windows-x86/include") 15 | endif () 16 | include_directories(${NCNN_INCLUDE_DIRS}) 17 | elseif (UNIX) 18 | set(NCNN_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/linux/include") 19 | include_directories(${NCNN_INCLUDE_DIRS}) 20 | message("配置Linux ncnn 路径: ${CMAKE_CURRENT_LIST_DIR}/linux/lib/cmake/ncnn") 21 | set(ncnn_DIR "${CMAKE_CURRENT_LIST_DIR}/linux/lib/cmake/ncnn") 22 | endif () 23 | 24 | -------------------------------------------------------------------------------- /include/getopt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * getopt - POSIX like getopt for Windows console Application 3 | * 4 | * win-c - Windows Console Library 5 | * Copyright (c) 2015 Koji Takami 6 | * Released under the MIT license 7 | * https://github.com/takamin/win-c/blob/master/LICENSE 8 | */ 9 | #ifndef _GETOPT_H_ 10 | #define _GETOPT_H_ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif // __cplusplus 15 | 16 | int getopt(int argc, char *const argv[], 17 | const char *optstring); 18 | 19 | extern char *optarg; 20 | extern int optind, opterr, optopt; 21 | 22 | #define no_argument 0 23 | #define required_argument 1 24 | #define optional_argument 2 25 | 26 | struct option { 27 | const char *name; 28 | int has_arg; 29 | int *flag; 30 | int val; 31 | }; 32 | 33 | int getopt_long(int argc, char *const argv[], 34 | const char *optstring, 35 | const struct option *longopts, int *longindex); 36 | /**************************************************************************** 37 | int getopt_long_only(int argc, char* const argv[], 38 | const char* optstring, 39 | const struct option* longopts, int* longindex); 40 | ****************************************************************************/ 41 | #ifdef __cplusplus 42 | } 43 | #endif // __cplusplus 44 | #endif // _GETOPT_H_ 45 | -------------------------------------------------------------------------------- /include/OcrLiteCApi.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | #ifndef __OCR_LITE_C_API_H__ 3 | #define __OCR_LITE_C_API_H__ 4 | extern "C" 5 | { 6 | 7 | #ifdef WIN32 8 | #ifdef __CLIB__ 9 | #define _QM_OCR_API __declspec(dllexport) 10 | #else 11 | #define _QM_OCR_API __declspec(dllimport) 12 | #endif 13 | #else 14 | #define _QM_OCR_API 15 | #endif 16 | 17 | typedef void *OCR_HANDLE; 18 | typedef char OCR_BOOL; 19 | 20 | #ifndef NULL 21 | #define NULL 0 22 | #endif 23 | #define TRUE 1 24 | #define FALSE 0 25 | 26 | typedef struct __ocr_param { 27 | int padding; 28 | int maxSideLen; 29 | float boxScoreThresh; 30 | float boxThresh; 31 | float unClipRatio; 32 | int doAngle; // 1 means do 33 | int mostAngle; // 1 means true 34 | } OCR_PARAM; 35 | 36 | /* 37 | * nThreads should be the number of threads 38 | * gpuIndex: -1 means use cpu, 0 means use gpu0... 39 | */ 40 | _QM_OCR_API OCR_HANDLE 41 | OcrInit(const char *szDetModel, const char *szClsModel, const char *szRecModel, const char *szKeyPath, int nThreads, 42 | int gpuIndex); 43 | 44 | _QM_OCR_API OCR_BOOL 45 | OcrDetect(OCR_HANDLE handle, const char *imgPath, const char *imgName, OCR_PARAM *pParam); 46 | 47 | _QM_OCR_API int OcrGetLen(OCR_HANDLE handle); 48 | 49 | _QM_OCR_API OCR_BOOL OcrGetResult(OCR_HANDLE handle, char *szBuf, int nLen); 50 | 51 | _QM_OCR_API void OcrDestroy(OCR_HANDLE handle); 52 | 53 | }; 54 | #endif //__OCR_LITE_C_API_H__ 55 | #endif //__cplusplus 56 | -------------------------------------------------------------------------------- /run-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function PrepareVar(){ 4 | echo "Gpu版本测试前请先安装Vulkan SDK v1.2.162.0,https://vulkan.lunarg.com/sdk/home" 5 | echo "请输入测试选项并回车: 1)CPU, 2)GPU" 6 | read -p "" RUN_ARCH 7 | if [ $RUN_ARCH == 1 ]; then 8 | EXE_PATH=${sysOS}-BIN-CPU 9 | GPU_INDEX=-1 10 | elif [ $RUN_ARCH == 2 ]; then 11 | EXE_PATH=${sysOS}-BIN-GPU 12 | GPU_INDEX=0 13 | else 14 | echo -e "输入错误!Input Error!" 15 | fi 16 | } 17 | 18 | sysOS=`uname -s` 19 | NUM_THREADS=1 20 | if [ $sysOS == "Darwin" ];then 21 | #echo "I'm MacOS" 22 | NUM_THREADS=$(sysctl -n hw.ncpu) 23 | elif [ $sysOS == "Linux" ];then 24 | #echo "I'm Linux" 25 | NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l) 26 | else 27 | echo "Other OS: $sysOS" 28 | fi 29 | 30 | echo "Setting the Number of Threads=$NUM_THREADS Using an OpenMP Environment Variable" 31 | set OMP_NUM_THREADS=$NUM_THREADS 32 | 33 | PrepareVar 34 | 35 | TARGET_IMG=images/1.jpg 36 | if [ ! -f "$TARGET_IMG" ]; then 37 | echo "找不到待识别的目标图片:${TARGET_IMG},请打开本文件并编辑TARGET_IMG" 38 | exit 39 | fi 40 | 41 | ##### run test on MacOS or Linux 42 | ./${EXE_PATH}/OcrLiteNcnn --version 43 | ./${EXE_PATH}/OcrLiteNcnn --models models \ 44 | --det dbnet_op \ 45 | --cls angle_op \ 46 | --rec crnn_lite_op \ 47 | --keys keys.txt \ 48 | --image $TARGET_IMG \ 49 | --numThread $NUM_THREADS \ 50 | --padding 50 \ 51 | --maxSideLen 0 \ 52 | --boxScoreThresh 0.6 \ 53 | --boxThresh 0.3 \ 54 | --unClipRatio 2.0 \ 55 | --doAngle 1 \ 56 | --mostAngle 1 \ 57 | --GPU $GPU_INDEX 58 | -------------------------------------------------------------------------------- /run-benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function PrepareVar(){ 4 | echo "Gpu版本测试前请先安装Vulkan SDK v1.2.162.0,https://vulkan.lunarg.com/sdk/home" 5 | echo "请输入测试选项并回车: 1)CPU, 2)GPU" 6 | read -p "" RUN_ARCH 7 | if [ $RUN_ARCH == 1 ]; then 8 | EXE_PATH=${sysOS}-BIN-CPU 9 | GPU_INDEX=-1 10 | elif [ $RUN_ARCH == 2 ]; then 11 | EXE_PATH=${sysOS}-BIN-GPU 12 | GPU_INDEX=0 13 | else 14 | echo -e "输入错误!Input Error!" 15 | fi 16 | } 17 | 18 | sysOS=`uname -s` 19 | NUM_THREADS=1 20 | if [ $sysOS == "Darwin" ];then 21 | #echo "I'm MacOS" 22 | NUM_THREADS=$(sysctl -n hw.ncpu) 23 | elif [ $sysOS == "Linux" ];then 24 | #echo "I'm Linux" 25 | NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l) 26 | else 27 | echo "Other OS: $sysOS" 28 | fi 29 | 30 | echo "Setting the Number of Threads=$NUM_THREADS Using an OpenMP Environment Variable" 31 | set OMP_NUM_THREADS=$NUM_THREADS 32 | 33 | PrepareVar 34 | 35 | echo "请输入循环次数" 36 | read -p "" LOOP_COUNT 37 | 38 | TARGET_IMG=images/1.jpg 39 | if [ ! -f "$TARGET_IMG" ]; then 40 | echo "找不到待识别的目标图片:${TARGET_IMG},请打开本文件并编辑TARGET_IMG" 41 | exit 42 | fi 43 | 44 | ./${EXE_PATH}/benchmark --version 45 | ./${EXE_PATH}/benchmark --models models \ 46 | --det dbnet_op \ 47 | --cls angle_op \ 48 | --rec crnn_lite_op \ 49 | --keys keys.txt \ 50 | --image $TARGET_IMG \ 51 | --numThread $NUM_THREADS \ 52 | --padding 50 \ 53 | --maxSideLen 0 \ 54 | --boxScoreThresh 0.6 \ 55 | --boxThresh 0.3 \ 56 | --unClipRatio 2.0 \ 57 | --doAngle 1 \ 58 | --mostAngle 1 \ 59 | --GPU $GPU_INDEX \ 60 | --loopCount $LOOP_COUNT -------------------------------------------------------------------------------- /run-test.bat: -------------------------------------------------------------------------------- 1 | chcp 65001 2 | :: Set Param 3 | @ECHO OFF 4 | @SETLOCAL 5 | echo "Setting the Number of Threads=%NUMBER_OF_PROCESSORS% Using an OpenMP Environment Variable" 6 | set OMP_NUM_THREADS=%NUMBER_OF_PROCESSORS% 7 | 8 | :MainExec 9 | echo "Gpu版本测试前请先安装Vulkan SDK v1.2.162.0,https://vulkan.lunarg.com/sdk/home" 10 | echo "请输入测试选项并回车: 1)CPU-x64, 2)CPU-x86, 3)GPU-x64, 4)GPU-x86" 11 | set GPU_INDEX=-1 12 | set /p flag= 13 | if %flag% == 1 (call :PrepareCpuX64)^ 14 | else if %flag% == 2 (call :PrepareCpuX86)^ 15 | else if %flag% == 3 (call :PrepareGpuX64)^ 16 | else if %flag% == 4 (call :PrepareGpuX86)^ 17 | else (echo 输入错误!Input Error!) 18 | 19 | SET TARGET_IMG=images/1.jpg 20 | if not exist %TARGET_IMG% ( 21 | echo "找不到待识别的目标图片:%TARGET_IMG%,请打开本文件并编辑TARGET_IMG" 22 | PAUSE 23 | exit 24 | ) 25 | 26 | if exist %EXE_PATH%\install\bin ( 27 | SET EXE_PATH=%EXE_PATH%\install\bin 28 | ) 29 | 30 | %EXE_PATH%\OcrLiteNcnn.exe --version 31 | %EXE_PATH%\OcrLiteNcnn.exe --models models ^ 32 | --det dbnet_op ^ 33 | --cls angle_op ^ 34 | --rec crnn_lite_op ^ 35 | --keys keys.txt ^ 36 | --image %TARGET_IMG% ^ 37 | --numThread %NUMBER_OF_PROCESSORS% ^ 38 | --padding 50 ^ 39 | --maxSideLen 0 ^ 40 | --boxScoreThresh 0.6 ^ 41 | --boxThresh 0.3 ^ 42 | --unClipRatio 2.0 ^ 43 | --doAngle 1 ^ 44 | --mostAngle 1 ^ 45 | --GPU %GPU_INDEX% 46 | 47 | echo. 48 | GOTO:MainExec 49 | 50 | :PrepareCpuX64 51 | set EXE_PATH=win-BIN-CPU-x64 52 | set GPU_INDEX=-1 53 | GOTO:EOF 54 | 55 | :PrepareCpuX86 56 | set EXE_PATH=win-BIN-CPU-Win32 57 | set GPU_INDEX=-1 58 | GOTO:EOF 59 | 60 | :PrepareGpuX64 61 | set EXE_PATH=win-BIN-GPU-x64 62 | set GPU_INDEX=0 63 | GOTO:EOF 64 | 65 | :PrepareGpuX86 66 | set EXE_PATH=win-BIN-GPU-Win32 67 | set GPU_INDEX=0 68 | GOTO:EOF 69 | 70 | @ENDLOCAL 71 | -------------------------------------------------------------------------------- /run-benchmark.bat: -------------------------------------------------------------------------------- 1 | chcp 65001 2 | :: Set Param 3 | @ECHO OFF 4 | @SETLOCAL 5 | echo "Setting the Number of Threads=%NUMBER_OF_PROCESSORS% Using an OpenMP Environment Variable" 6 | set OMP_NUM_THREADS=%NUMBER_OF_PROCESSORS% 7 | 8 | :MainExec 9 | echo "Gpu版本测试前请先安装Vulkan SDK v1.2.162.0,https://vulkan.lunarg.com/sdk/home" 10 | echo "请输入测试选项并回车: 1)CPU-x64, 2)CPU-x86, 3)GPU-x64, 4)GPU-x86" 11 | set GPU_INDEX=-1 12 | set /p flag= 13 | if %flag% == 1 (call :PrepareCpuX64)^ 14 | else if %flag% == 2 (call :PrepareCpuX86)^ 15 | else if %flag% == 3 (call :PrepareGpuX64)^ 16 | else if %flag% == 4 (call :PrepareGpuX86)^ 17 | else (echo 输入错误!Input Error!) 18 | 19 | echo "请输入循环次数:" 20 | set /p LOOP_COUNT= 21 | 22 | SET TARGET_IMG=images/1.jpg 23 | if not exist %TARGET_IMG% ( 24 | echo "找不到待识别的目标图片:%TARGET_IMG%,请打开本文件并编辑TARGET_IMG" 25 | PAUSE 26 | exit 27 | ) 28 | 29 | if exist %EXE_PATH%\install\bin ( 30 | SET EXE_PATH=%EXE_PATH%\install\bin 31 | ) 32 | 33 | %EXE_PATH%\benchmark.exe --version 34 | %EXE_PATH%\benchmark.exe --models models ^ 35 | --det dbnet_op ^ 36 | --cls angle_op ^ 37 | --rec crnn_lite_op ^ 38 | --keys keys.txt ^ 39 | --image %TARGET_IMG% ^ 40 | --numThread %NUMBER_OF_PROCESSORS% ^ 41 | --padding 50 ^ 42 | --maxSideLen 0 ^ 43 | --boxScoreThresh 0.6 ^ 44 | --boxThresh 0.3 ^ 45 | --unClipRatio 2.0 ^ 46 | --doAngle 1 ^ 47 | --mostAngle 1 ^ 48 | --GPU %GPU_INDEX% ^ 49 | --loopCount %LOOP_COUNT% 50 | 51 | popd 52 | echo. 53 | GOTO:MainExec 54 | 55 | :PrepareCpuX64 56 | set EXE_PATH=win-BIN-CPU-x64 57 | set GPU_INDEX=-1 58 | GOTO:EOF 59 | 60 | :PrepareCpuX86 61 | set EXE_PATH=win-BIN-CPU-Win32 62 | set GPU_INDEX=-1 63 | GOTO:EOF 64 | 65 | :PrepareGpuX64 66 | set EXE_PATH=win-BIN-GPU-x64 67 | set GPU_INDEX=0 68 | GOTO:EOF 69 | 70 | :PrepareGpuX86 71 | set EXE_PATH=win-BIN-GPU-Win32 72 | set GPU_INDEX=0 73 | GOTO:EOF 74 | 75 | @ENDLOCAL 76 | -------------------------------------------------------------------------------- /include/OcrLite.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_LITE_H__ 2 | #define __OCR_LITE_H__ 3 | 4 | #include "opencv2/core.hpp" 5 | #include "ncnn/net.h" 6 | #include "OcrStruct.h" 7 | #include "DbNet.h" 8 | #include "AngleNet.h" 9 | #include "CrnnNet.h" 10 | 11 | class OcrLite { 12 | public: 13 | OcrLite(); 14 | 15 | ~OcrLite(); 16 | 17 | void setNumThread(int numOfThread); 18 | 19 | void initLogger(bool isConsole, bool isPartImg, bool isResultImg); 20 | 21 | void enableResultTxt(const char *path, const char *imgName); 22 | 23 | void setGpuIndex(int gpuIndex); 24 | 25 | bool initModels(const std::string &detPath, const std::string &clsPath, 26 | const std::string &recPath, const std::string &keysPath); 27 | 28 | void Logger(const char *format, ...); 29 | 30 | OcrResult detect(const char *path, const char *imgName, 31 | int padding, int maxSideLen, 32 | float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle); 33 | 34 | OcrResult detect(const cv::Mat& mat, 35 | int padding, int maxSideLen, 36 | float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle); 37 | private: 38 | bool isOutputConsole = false; 39 | bool isOutputPartImg = false; 40 | bool isOutputResultTxt = false; 41 | bool isOutputResultImg = false; 42 | FILE *resultTxt; 43 | DbNet dbNet; 44 | AngleNet angleNet; 45 | CrnnNet crnnNet; 46 | 47 | std::vector getPartImages(cv::Mat &src, std::vector &textBoxes, 48 | const char *path, const char *imgName); 49 | 50 | OcrResult detect(const char *path, const char *imgName, 51 | cv::Mat &src, cv::Rect &originRect, ScaleParam &scale, 52 | float boxScoreThresh = 0.6f, float boxThresh = 0.3f, 53 | float unClipRatio = 2.0f, bool doAngle = true, bool mostAngle = true); 54 | }; 55 | 56 | #endif //__OCR_LITE_H__ 57 | -------------------------------------------------------------------------------- /scripts/build-ncnn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function cmakeParamsMac(){ 4 | mkdir -p "build-mac-$1" 5 | pushd "build-mac-$1" 6 | cmake -DCMAKE_BUILD_TYPE=$1 \ 7 | -DNCNN_OPENMP=ON \ 8 | -DOpenMP_C_FLAGS="-Xpreprocessor -fopenmp -I/usr/local/opt/libomp/include" \ 9 | -DOpenMP_CXX_FLAGS="-Xpreprocessor -fopenmp -I/usr/local/opt/libomp/include" \ 10 | -DOpenMP_CXX_LIB_NAMES="omp" \ 11 | -DOpenMP_C_LIB_NAMES="omp" \ 12 | -DOpenMP_omp_LIBRARY=/usr/local/opt/libomp/lib/libomp.dylib \ 13 | -DNCNN_BUILD_BENCHMARK=OFF \ 14 | -DNCNN_BUILD_EXAMPLES=OFF \ 15 | -DNCNN_BUILD_TOOLS=OFF \ 16 | -DNCNN_PYTHON=OFF \ 17 | -DNCNN_VULKAN=OFF \ 18 | .. 19 | make -j $NUM_THREADS 20 | make install 21 | popd 22 | } 23 | 24 | function cmakeParamsLinux(){ 25 | mkdir -p "build-linux-$1" 26 | pushd "build-linux-$1" 27 | cmake -DCMAKE_BUILD_TYPE=$1 \ 28 | -DNCNN_OPENMP=ON \ 29 | -DNCNN_BUILD_BENCHMARK=OFF \ 30 | -DNCNN_BUILD_EXAMPLES=OFF \ 31 | -DNCNN_BUILD_TOOLS=OFF \ 32 | -DNCNN_PYTHON=OFF \ 33 | -DNCNN_VULKAN=OFF \ 34 | .. 35 | make -j $NUM_THREADS 36 | make install 37 | } 38 | 39 | sysOS=`uname -s` 40 | NUM_THREADS=1 41 | if [ $sysOS == "Darwin" ];then 42 | #echo "I'm MacOS" 43 | NUM_THREADS=$(sysctl -n hw.ncpu) 44 | elif [ $sysOS == "Linux" ];then 45 | #echo "I'm Linux" 46 | NUM_THREADS=$(grep ^processor /proc/cpuinfo | wc -l) 47 | else 48 | echo "Other OS: $sysOS" 49 | fi 50 | 51 | echo "请选择编译选项并回车: 1)Release,2)Debug" 52 | echo "Please Select Build Type: 1)Release,2)Debug" 53 | read -p "" BUILD_TYPE 54 | if [ $BUILD_TYPE == 0 ]; then 55 | BUILD_TYPE="Release" 56 | elif [ $BUILD_TYPE == 1 ]; then 57 | BUILD_TYPE="Release" 58 | elif [ $BUILD_TYPE == 2 ]; then 59 | BUILD_TYPE="Debug" 60 | else 61 | echo -e "输入错误!Input Error!" 62 | fi 63 | 64 | if [ $sysOS == "Darwin" ];then 65 | #echo "I'm MacOS" 66 | cmakeParamsMac $BUILD_TYPE 67 | elif [ $sysOS == "Linux" ];then 68 | #echo "I'm Linux" 69 | cmakeParamsLinux $BUILD_TYPE 70 | else 71 | echo "Other OS: $sysOS" 72 | fi -------------------------------------------------------------------------------- /include/OcrUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCR_UTILS_H__ 2 | #define __OCR_UTILS_H__ 3 | 4 | #include 5 | #include "OcrStruct.h" 6 | 7 | #include 8 | /*#define __ENABLE_CONSOLE__ true 9 | #define Logger(format, ...) {\ 10 | if(__ENABLE_CONSOLE__) printf(format,##__VA_ARGS__); \ 11 | }*/ 12 | 13 | double getCurrentTime(); 14 | 15 | inline bool isFileExists(const std::string &name) { 16 | struct stat buffer; 17 | return (stat(name.c_str(), &buffer) == 0); 18 | } 19 | 20 | std::wstring strToWstr(std::string str); 21 | 22 | ScaleParam getScaleParam(cv::Mat &src, const float scale); 23 | 24 | ScaleParam getScaleParam(cv::Mat &src, const int targetSize); 25 | 26 | std::vector getBox(const cv::RotatedRect &rect); 27 | 28 | int getThickness(cv::Mat &boxImg); 29 | 30 | void drawTextBox(cv::Mat &boxImg, cv::RotatedRect &rect, int thickness); 31 | 32 | void drawTextBox(cv::Mat &boxImg, const std::vector &box, int thickness); 33 | 34 | void drawTextBoxes(cv::Mat &boxImg, std::vector &textBoxes, int thickness); 35 | 36 | cv::Mat matRotateClockWise180(cv::Mat src); 37 | 38 | cv::Mat matRotateClockWise90(cv::Mat src); 39 | 40 | cv::Mat getRotateCropImage(const cv::Mat &src, std::vector box); 41 | 42 | cv::Mat adjustTargetImg(cv::Mat &src, int dstWidth, int dstHeight); 43 | 44 | std::vector getMinBoxes(const std::vector &inVec, float &minSideLen, float &allEdgeSize); 45 | 46 | float boxScoreFast(const cv::Mat &inMat, const std::vector &inBox); 47 | 48 | std::vector unClip(const std::vector &inBox, float perimeter, float unClipRatio); 49 | 50 | std::vector getAngleIndexes(std::vector &angles); 51 | 52 | void saveImg(cv::Mat &img, const char *imgPath); 53 | 54 | std::string getSrcImgFilePath(const char *path, const char *imgName); 55 | 56 | std::string getResultTxtFilePath(const char *path, const char *imgName); 57 | 58 | std::string getResultImgFilePath(const char *path, const char *imgName); 59 | 60 | std::string getDebugImgFilePath(const char *path, const char *imgName, int i, const char *tag); 61 | 62 | void printGpuInfo(); 63 | 64 | #endif //__OCR_UTILS_H__ 65 | -------------------------------------------------------------------------------- /generate-vs-project.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | chcp 65001 3 | cls 4 | @SETLOCAL 5 | echo "========请先参考README.md准备好编译环境========" 6 | echo. 7 | 8 | echo "========编译选项========" 9 | echo "请注意:项目默认使用Release库,除非您自行编译Debug版的Ncnn和Opencv,否则请不要选择Debug编译" 10 | echo "请输入编译选项并回车: 1)Release, 2)Debug" 11 | set BUILD_TYPE=Release 12 | set /p flag= 13 | if %flag% == 1 (set BUILD_TYPE=Release)^ 14 | else if %flag% == 2 (set BUILD_TYPE=Debug)^ 15 | else (echo 输入错误!Input Error!) 16 | echo. 17 | 18 | echo "请选择要使用的ncnn库选项并回车: 1)ncnn(CPU),2)ncnn(vulkan)" 19 | set BUILD_NCNN_VULKAN="CPU" 20 | set /p flag= 21 | if %flag% == 1 (set BUILD_NCNN_VULKAN="CPU")^ 22 | else if %flag% == 2 (set BUILD_NCNN_VULKAN="GPU")^ 23 | else (echo "输入错误!Input Error!") 24 | echo. 25 | 26 | echo "请注意:如果选择2)JNI动态库时,必须安装配置Oracle JDK" 27 | echo "请选择编译输出类型并回车: 1)BIN可执行文件,2)JNI动态库,3)C动态库" 28 | set /p flag= 29 | if %flag% == 1 (set BUILD_OUTPUT="BIN")^ 30 | else if %flag% == 2 (set BUILD_OUTPUT="JNI")^ 31 | else if %flag% == 3 (set BUILD_OUTPUT="CLIB")^ 32 | else (echo 输入错误!Input Error!) 33 | echo. 34 | 35 | echo "引用的库类型: 1)静态CRT(mt), 2)动态CRT(md)" 36 | echo "注意:范例工程默认集成mt版库" 37 | set /p flag= 38 | if %flag% == 1 ( 39 | set MT_ENABLED="True" 40 | )^ 41 | else (set MT_ENABLED="False") 42 | echo. 43 | 44 | echo "请输入选项并回车: 0)ALL, 1)vs2017-x86, 2)vs2017-x64, 3)vs2019-x86, 4)vs2019-x64:" 45 | set /p flag= 46 | if %flag% == 0 (call :buildALL)^ 47 | else if %flag% == 1 (call :gen2017-x86)^ 48 | else if %flag% == 2 (call :gen2017-x64)^ 49 | else if %flag% == 3 (call :gen2019-x86)^ 50 | else if %flag% == 4 (call :gen2019-x64)^ 51 | else (echo "输入错误!Input Error!") 52 | GOTO:EOF 53 | 54 | :buildALL 55 | call :gen2017-x86 56 | call :gen2017-x64 57 | call :gen2019-x86 58 | call :gen2019-x64 59 | GOTO:EOF 60 | 61 | :gen2017-x86 62 | mkdir build-win-vs2017-x86 63 | pushd build-win-vs2017-x86 64 | call :cmakeParams "Visual Studio 15 2017" "Win32" 65 | popd 66 | GOTO:EOF 67 | 68 | :gen2017-x64 69 | mkdir build-win-vs2017-x64 70 | pushd build-win-vs2017-x64 71 | call :cmakeParams "Visual Studio 15 2017" "x64" 72 | popd 73 | GOTO:EOF 74 | 75 | :gen2019-x86 76 | mkdir build-win-vs2019-x86 77 | pushd build-win-vs2019-x86 78 | call :cmakeParams "Visual Studio 16 2019" "Win32" 79 | popd 80 | GOTO:EOF 81 | 82 | :gen2019-x64 83 | mkdir build-win-vs2019-x64 84 | pushd build-win-vs2019-x64 85 | call :cmakeParams "Visual Studio 16 2019" "x64" 86 | popd 87 | GOTO:EOF 88 | 89 | :cmakeParams 90 | echo cmake -G "%~1" -A "%~2" -DOCR_OUTPUT=%BUILD_OUTPUT% -DOCR_VULKAN=%BUILD_NCNN_VULKAN% -DOCR_BUILD_CRT=%MT_ENABLED% .. 91 | cmake -G "%~1" -A "%~2" -DOCR_OUTPUT=%BUILD_OUTPUT% -DOCR_VULKAN=%BUILD_NCNN_VULKAN% -DOCR_BUILD_CRT=%MT_ENABLED% .. 92 | GOTO:EOF 93 | 94 | @ENDLOCAL 95 | -------------------------------------------------------------------------------- /src/OcrLiteCApi.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __CLIB__ 2 | 3 | #include "OcrLiteCApi.h" 4 | #include "OcrLite.h" 5 | 6 | extern "C" 7 | { 8 | typedef struct { 9 | OcrLite OcrObj; 10 | std::string strRes; 11 | } OCR_OBJ; 12 | 13 | _QM_OCR_API OCR_HANDLE 14 | OcrInit(const char *szDetModel, const char *szClsModel, const char *szRecModel, const char *szKeyPath, int nThreads, 15 | int gpuIndex) { 16 | 17 | OCR_OBJ *pOcrObj = new OCR_OBJ; 18 | if (pOcrObj) { 19 | pOcrObj->OcrObj.setNumThread(nThreads); 20 | pOcrObj->OcrObj.setGpuIndex(gpuIndex); 21 | pOcrObj->OcrObj.initModels(szDetModel, szClsModel, szRecModel, szKeyPath); 22 | 23 | return pOcrObj; 24 | } else { 25 | return nullptr; 26 | } 27 | 28 | } 29 | 30 | _QM_OCR_API OCR_BOOL 31 | OcrDetect(OCR_HANDLE handle, const char *imgPath, const char *imgName, OCR_PARAM *pParam) { 32 | 33 | OCR_OBJ *pOcrObj = (OCR_OBJ *) handle; 34 | if (!pOcrObj) 35 | return FALSE; 36 | 37 | OCR_PARAM Param = *pParam; 38 | if (Param.padding == 0) 39 | Param.padding = 50; 40 | 41 | if (Param.maxSideLen == 0) 42 | Param.maxSideLen = 1024; 43 | 44 | if (Param.boxScoreThresh == 0) 45 | Param.boxScoreThresh = 0.6; 46 | 47 | if (Param.boxThresh == 0) 48 | Param.boxThresh = 0.3f; 49 | 50 | if (Param.unClipRatio == 0) 51 | Param.unClipRatio = 2.0; 52 | 53 | if (Param.doAngle == 0) 54 | Param.doAngle = 1; 55 | 56 | if (Param.mostAngle == 0) 57 | Param.mostAngle = 1; 58 | 59 | OcrResult result = pOcrObj->OcrObj.detect(imgPath, imgName, Param.padding, Param.maxSideLen, 60 | Param.boxScoreThresh, Param.boxThresh, Param.unClipRatio, 61 | Param.doAngle != 0, Param.mostAngle != 0); 62 | if (result.strRes.length() > 0) { 63 | pOcrObj->strRes = result.strRes; 64 | return TRUE; 65 | } else 66 | return FALSE; 67 | } 68 | 69 | 70 | _QM_OCR_API int OcrGetLen(OCR_HANDLE handle) { 71 | OCR_OBJ *pOcrObj = (OCR_OBJ *) handle; 72 | if (!pOcrObj) 73 | return 0; 74 | return pOcrObj->strRes.size() + 1; 75 | } 76 | 77 | _QM_OCR_API OCR_BOOL OcrGetResult(OCR_HANDLE handle, char *szBuf, int nLen) { 78 | OCR_OBJ *pOcrObj = (OCR_OBJ *) handle; 79 | if (!pOcrObj) 80 | return FALSE; 81 | 82 | if (nLen > pOcrObj->strRes.size()) { 83 | strncpy(szBuf, pOcrObj->strRes.c_str(), pOcrObj->strRes.size()); 84 | szBuf[pOcrObj->strRes.size() - 1] = 0; 85 | } 86 | 87 | return pOcrObj->strRes.size(); 88 | } 89 | 90 | _QM_OCR_API void OcrDestroy(OCR_HANDLE handle) { 91 | OCR_OBJ *pOcrObj = (OCR_OBJ *) handle; 92 | if (pOcrObj) 93 | delete pOcrObj; 94 | } 95 | 96 | }; 97 | #endif 98 | 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | 133 | #idea 134 | .idea 135 | cmake-build-debug/ 136 | build/ 137 | build-win/ 138 | build-lib/ 139 | /models/*.bin 140 | /models/*.param 141 | /ncnn/include/ 142 | /ncnn/macos/ 143 | /ncnn/windows/ 144 | /ncnn/linux/ 145 | ncnn-static/* 146 | !/ncnn-static/NcnnWrapperConfig.cmake 147 | ncnn-vulkan-static/* 148 | !/ncnn-vulkan-static/NcnnWrapperConfig.cmake 149 | opencv-static/* 150 | !/opencv-static/OpenCVWrapperConfig.cmake 151 | /images/*-result.* -------------------------------------------------------------------------------- /include/main.h: -------------------------------------------------------------------------------- 1 | #ifndef __MAIN_H__ 2 | #define __MAIN_H__ 3 | 4 | #include "getopt.h" 5 | 6 | static const struct option long_options[] = { 7 | {"models", required_argument, NULL, 'd'}, 8 | {"det", required_argument, NULL, '1'}, 9 | {"cls", required_argument, NULL, '2'}, 10 | {"rec", required_argument, NULL, '3'}, 11 | {"keys", required_argument, NULL, '4'}, 12 | {"image", required_argument, NULL, 'i'}, 13 | {"numThread", required_argument, NULL, 't'}, 14 | {"padding", required_argument, NULL, 'p'}, 15 | {"maxSideLen", required_argument, NULL, 's'}, 16 | {"boxScoreThresh", required_argument, NULL, 'b'}, 17 | {"boxThresh", required_argument, NULL, 'o'}, 18 | {"unClipRatio", required_argument, NULL, 'u'}, 19 | {"doAngle", required_argument, NULL, 'a'}, 20 | {"mostAngle", required_argument, NULL, 'A'}, 21 | {"version", no_argument, NULL, 'v'}, 22 | {"help", no_argument, NULL, 'h'}, 23 | {"loopCount", required_argument, NULL, 'l'}, 24 | {"GPU", required_argument, NULL, 'G'}, 25 | {NULL, no_argument, NULL, 0} 26 | }; 27 | 28 | const char *usageMsg = "(-d --models) (-1 --det) (-2 --cls) (-3 --rec) (-4 --keys) (-i --image)\n"\ 29 | "[-t --numThread] [-p --padding] [-s --maxSideLen]\n" \ 30 | "[-b --boxScoreThresh] [-o --boxThresh] [-u --unClipRatio]\n" \ 31 | "[-a --noAngle] [-A --mostAngle] [-G --GPU]\n\n"; 32 | 33 | const char *requiredMsg = "-d --models: models directory.\n" \ 34 | "-1 --det: model file name of det.\n" \ 35 | "-2 --cls: model file name of cls.\n" \ 36 | "-3 --rec: model file name of rec.\n" \ 37 | "-4 --keys: keys file name.\n" \ 38 | "-i --image: path of target image.\n\n"; 39 | 40 | const char *optionalMsg = "-t --numThread: value of numThread(int), default: 4\n" \ 41 | "-p --padding: value of padding(int), default: 50\n" \ 42 | "-s --maxSideLen: Long side of picture for resize(int), default: 1024\n" \ 43 | "-b --boxScoreThresh: value of boxScoreThresh(float), default: 0.6\n" \ 44 | "-o --boxThresh: value of boxThresh(float), default: 0.3\n" \ 45 | "-u --unClipRatio: value of unClipRatio(float), default: 2.0\n" \ 46 | "-a --doAngle: Enable(1)/Disable(0) Angle Net, default: Enable\n" \ 47 | "-A --mostAngle: Enable(1)/Disable(0) Most Possible AngleIndex, default: Enable\n\n" \ 48 | "-G --GPU: Disable(-1)/GPU0(0)/GPU1(1)/... Use Vulkan GPU accelerate, default: Disable(-1)\n\n"; 49 | 50 | const char *otherMsg = "-v --version: show version\n" \ 51 | "-h --help: print this help\n\n"; 52 | 53 | const char *example1Msg = "Example1: %s --models models --det det --cls cls --rec rec --keys keys.txt --image 1.jpg --GPU 0\n"; 54 | const char *example2Msg = "Example2: %s -d models -1 det -2 cls -3 rec -4 keys.txt -i 1.jpg -t 4 -p 50 -s 0 -b 0.6 -o 0.3 -u 2.0 -a 1 -A 1 -G 0\n"; 55 | 56 | #endif //__MAIN_H__ 57 | -------------------------------------------------------------------------------- /src/DbNet.cpp: -------------------------------------------------------------------------------- 1 | #include "DbNet.h" 2 | #include "OcrUtils.h" 3 | 4 | void DbNet::setGpuIndex(int gpuIndex) { 5 | #ifdef __VULKAN__ 6 | if (gpuIndex >= 0) { 7 | net.opt.use_vulkan_compute = true; 8 | net.set_vulkan_device(gpuIndex); 9 | printf("dbNet try to use Gpu%d\n", gpuIndex); 10 | } else { 11 | net.opt.use_vulkan_compute = false; 12 | printf("dbNet use Cpu\n"); 13 | } 14 | #endif 15 | } 16 | 17 | DbNet::~DbNet() { 18 | net.clear(); 19 | } 20 | 21 | void DbNet::setNumThread(int numOfThread) { 22 | numThread = numOfThread; 23 | } 24 | 25 | bool DbNet::initModel(const std::string &pathStr) { 26 | int dbParam = net.load_param((pathStr + ".param").c_str()); 27 | int dbModel = net.load_model((pathStr + ".bin").c_str()); 28 | if (dbParam != 0 || dbModel != 0) { 29 | printf("DBNet load param(%d), model(%d)\n", dbParam, dbModel); 30 | return false; 31 | } else { 32 | return true; 33 | } 34 | } 35 | 36 | std::vector findRsBoxes(const cv::Mat &fMapMat, const cv::Mat &norfMapMat, ScaleParam &s, 37 | const float boxScoreThresh, const float unClipRatio) { 38 | float minArea = 3; 39 | std::vector rsBoxes; 40 | rsBoxes.clear(); 41 | std::vector> contours; 42 | findContours(norfMapMat, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); 43 | for (int i = 0; i < contours.size(); ++i) { 44 | float minSideLen, perimeter; 45 | std::vector minBox = getMinBoxes(contours[i], minSideLen, perimeter); 46 | if (minSideLen < minArea) 47 | continue; 48 | float score = boxScoreFast(fMapMat, contours[i]); 49 | if (score < boxScoreThresh) 50 | continue; 51 | //---use clipper start--- 52 | std::vector clipBox = unClip(minBox, perimeter, unClipRatio); 53 | std::vector clipMinBox = getMinBoxes(clipBox, minSideLen, perimeter); 54 | //---use clipper end--- 55 | 56 | if (minSideLen < minArea + 2) 57 | continue; 58 | 59 | for (int j = 0; j < clipMinBox.size(); ++j) { 60 | clipMinBox[j].x = (clipMinBox[j].x / s.ratioWidth); 61 | clipMinBox[j].x = (std::min)((std::max)(clipMinBox[j].x, 0), s.srcWidth); 62 | 63 | clipMinBox[j].y = (clipMinBox[j].y / s.ratioHeight); 64 | clipMinBox[j].y = (std::min)((std::max)(clipMinBox[j].y, 0), s.srcHeight); 65 | } 66 | 67 | rsBoxes.emplace_back(TextBox{clipMinBox, score}); 68 | } 69 | reverse(rsBoxes.begin(), rsBoxes.end()); 70 | return rsBoxes; 71 | } 72 | 73 | std::vector 74 | DbNet::getTextBoxes(cv::Mat &src, ScaleParam &s, float boxScoreThresh, float boxThresh, float unClipRatio) { 75 | cv::Mat srcResize; 76 | resize(src, srcResize, cv::Size(s.dstWidth, s.dstHeight)); 77 | ncnn::Mat input = ncnn::Mat::from_pixels(srcResize.data, ncnn::Mat::PIXEL_RGB, 78 | srcResize.cols, srcResize.rows); 79 | 80 | input.substract_mean_normalize(meanValues, normValues); 81 | ncnn::Extractor extractor = net.create_extractor(); 82 | extractor.set_num_threads(numThread); 83 | extractor.input("input0", input); 84 | ncnn::Mat out; 85 | extractor.extract("out1", out); 86 | //-----Data preparation----- 87 | cv::Mat fMapMat(srcResize.rows, srcResize.cols, CV_32FC1); 88 | memcpy(fMapMat.data, (float *) out.data, srcResize.rows * srcResize.cols * sizeof(float)); 89 | 90 | //-----boxThresh----- 91 | cv::Mat norfMapMat; 92 | norfMapMat = fMapMat > boxThresh; 93 | 94 | return findRsBoxes(fMapMat, norfMapMat, s, boxScoreThresh, unClipRatio); 95 | } 96 | -------------------------------------------------------------------------------- /src/AngleNet.cpp: -------------------------------------------------------------------------------- 1 | #include "AngleNet.h" 2 | #include "OcrUtils.h" 3 | #include 4 | 5 | void AngleNet::setGpuIndex(int gpuIndex) { 6 | #ifdef __VULKAN__ 7 | if (gpuIndex >= 0) { 8 | net.opt.use_vulkan_compute = true; 9 | net.set_vulkan_device(gpuIndex); 10 | printf("AngleNet try to use Gpu%d\n", gpuIndex); 11 | } else { 12 | net.opt.use_vulkan_compute = false; 13 | printf("AngleNet use Cpu\n"); 14 | } 15 | #endif 16 | } 17 | 18 | AngleNet::~AngleNet() { 19 | net.clear(); 20 | } 21 | 22 | void AngleNet::setNumThread(int numOfThread) { 23 | numThread = numOfThread; 24 | } 25 | 26 | bool AngleNet::initModel(const std::string &pathStr) { 27 | int ret_param = net.load_param((pathStr + ".param").c_str()); 28 | int ret_bin = net.load_model((pathStr + ".bin").c_str()); 29 | if (ret_param != 0 || ret_bin != 0) { 30 | printf("AngleNet load param(%d), model(%d)\n", ret_param, ret_bin); 31 | return false; 32 | } else { 33 | return true; 34 | } 35 | } 36 | 37 | Angle scoreToAngle(const std::vector &outputData) { 38 | int maxIndex = 0; 39 | float maxScore = -1000.0f; 40 | for (int i = 0; i < outputData.size(); i++) { 41 | if (i == 0)maxScore = outputData[i]; 42 | else if (outputData[i] > maxScore) { 43 | maxScore = outputData[i]; 44 | maxIndex = i; 45 | } 46 | } 47 | return {maxIndex, maxScore}; 48 | } 49 | 50 | Angle AngleNet::getAngle(cv::Mat &src) { 51 | ncnn::Mat input = ncnn::Mat::from_pixels( 52 | src.data, ncnn::Mat::PIXEL_RGB, 53 | src.cols, src.rows); 54 | input.substract_mean_normalize(meanValues, normValues); 55 | ncnn::Extractor extractor = net.create_extractor(); 56 | extractor.set_num_threads(numThread); 57 | extractor.input("input", input); 58 | ncnn::Mat out; 59 | extractor.extract("out", out); 60 | float *floatArray = (float *) out.data; 61 | std::vector outputData(floatArray, floatArray + out.w); 62 | return scoreToAngle(outputData); 63 | } 64 | 65 | std::vector AngleNet::getAngles(std::vector &partImgs, const char *path, 66 | const char *imgName, bool doAngle, bool mostAngle) { 67 | int size = partImgs.size(); 68 | std::vector angles(size); 69 | if (doAngle) { 70 | for (int i = 0; i < size; ++i) { 71 | double startAngle = getCurrentTime(); 72 | auto angleImg = adjustTargetImg(partImgs[i], dstWidth, dstHeight); 73 | Angle angle = getAngle(angleImg); 74 | double endAngle = getCurrentTime(); 75 | angle.time = endAngle - startAngle; 76 | 77 | angles[i] = angle; 78 | 79 | //OutPut AngleImg 80 | if (isOutputAngleImg) { 81 | std::string angleImgFile = getDebugImgFilePath(path, imgName, i, "-angle-"); 82 | saveImg(angleImg, angleImgFile.c_str()); 83 | } 84 | } 85 | } else { 86 | for (int i = 0; i < size; ++i) { 87 | angles[i] = Angle{-1, 0.f}; 88 | } 89 | } 90 | //Most Possible AngleIndex 91 | if (doAngle && mostAngle) { 92 | auto angleIndexes = getAngleIndexes(angles); 93 | double sum = std::accumulate(angleIndexes.begin(), angleIndexes.end(), 0.0); 94 | double halfPercent = angles.size() / 2.0f; 95 | int mostAngleIndex; 96 | if (sum < halfPercent) {//all angle set to 0 97 | mostAngleIndex = 0; 98 | } else {//all angle set to 1 99 | mostAngleIndex = 1; 100 | } 101 | //printf("Set All Angle to mostAngleIndex(%d)\n", mostAngleIndex); 102 | for (int i = 0; i < angles.size(); ++i) { 103 | Angle angle = angles[i]; 104 | angle.index = mostAngleIndex; 105 | angles.at(i) = angle; 106 | } 107 | } 108 | 109 | return angles; 110 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OcrLiteNcnn 2 | 3 | ### 联系方式 4 | 5 | * QQ①群:994929053(满) 6 | * QQ②群:820132154(满) 7 | * QQ③群:904091319(满) 8 | * QQ④群:615877948(满) 9 | * QQ⑤群:185905924(满) 10 | * QQ⑥群:628010752 11 | 12 | ### Project下载 13 | 14 | * 整合好源码和依赖库的完整工程项目,可以在Release中下载(github) 15 | * 可到Q群共享内下载,以Project开头的压缩包文件为源码工程,例:Project_OcrLiteNcnn-版本号.7z 16 | * 如果想自己折腾,则请继续阅读本说明 17 | 18 | ### Demo下载(win、mac、linux) 19 | 20 | * 编译好的demo,可以在release中下载,或者Q群共享内下载 21 | * 或者[Gitee下载](https://gitee.com/benjaminwan/ocr-lite-ncnn/releases) 22 | * 或者[Github下载](https://github.com/benjaminwan/OcrLiteNcnn/releases) 23 | * 各平台可执行文件:linux-bin.7z、macos-bin.7z、windows-bin.7z 24 | * 用于java的jni库:linux-jni.7z、macos-jni.7z、windows-jni.7z 25 | * 用于C的动态库:linux-clib.7z、macos-clib.7z、windows-clib.7z 26 | * C动态库调用范例:[OcrLiteNcnnLibTest](https://github.com/benjaminwan/OcrLiteNcnnLibTest) 27 | * 注意:linux编译平台为ubuntu18.04,如果你的linux版本无法运行demo,请自行从源码编译依赖库和完整项目。 28 | 29 | ### 介绍 30 | 31 | ChineseOcr Lite Ncnn,超轻量级中文OCR PC Demo,支持ncnn推理 32 | 33 | **对应chineseocr lite的onnx分支** 34 | 35 | 这个项目使用ncnn框架进行推理,模型是从onnx模型转成了ncnn模型格式。 36 | 37 | 详情请查看 [https://github.com/ouyanghuiyu/chineseocr_lite](https://github.com/ouyanghuiyu/chineseocr_lite) 38 | 39 | 采用ncnn神经网络前向计算框架[https://github.com/Tencent/ncnn](https://github.com/Tencent/ncnn) 40 | 41 | ### 更新说明 42 | 43 | #### 2021-10-13 update 44 | 45 | * opencv 4.5.4 46 | * ncnn 20210720 47 | * 优化编译脚本和测试脚本 48 | 49 | #### 2021-10-25 update 50 | 51 | * fix:win编译打包C动态库缺少lib文件 52 | * 编译c动态库添加打包include 53 | 54 | #### 2022-06-18 update 55 | 56 | * opencv 4.6.0 57 | * ncnn 20220420,模型格式变化,需重新转换db和angle模型 58 | * 修改c lib导出方法,支持C调用 59 | 60 | #### 2022-10-14 update 61 | 62 | * ncnn: 20220729 63 | * vulkan: 1.2.189.0 64 | * 范例工程windows版默认使用mt版依赖库 65 | 66 | #### 2023-02-14 update 67 | 68 | * ncnn: 20221128 69 | * vulkan: 1.3.236.0 70 | 71 | ### 模型下载 72 | 73 | [模型下载地址](https://github.com/ouyanghuiyu/chineseocr_lite/tree/onnx/models_ncnn) 74 | 下载后解压到项目根目录 75 | 76 | ``` 77 | OcrLiteNcnn/models 78 | ├── angle_op.bin 79 | ├── angle_op.param 80 | ├── crnn_lite_op.bin 81 | ├── crnn_lite_op.param 82 | ├── dbnet_op.bin 83 | ├── dbnet_op.param 84 | └── keys.txt 85 | ``` 86 | 87 | ### [编译说明](./BUILD.md) 88 | 89 | ### 测试说明 90 | 91 | 1. 根据系统下载对应的程序包linux-bin.7z、macos-bin.7z、windows-bin.7z,并解压. 92 | 2. 把上面的模型下载,解压到第一步解压的文件夹里. 93 | 3. 终端运行run-test.sh或命令行运行run-test.bat,查看识别结果. 94 | 4. 终端运行run-benchmark.sh或命令行运行run-benchmark.bat,查看识别过程平均耗时. 95 | 96 | ### FAQ 97 | 98 | #### macOS缺少openmp(从1.7.0开始已不再依赖openmp) 99 | 100 | ```brew install libomp``` 101 | 102 | #### gpu版程序运行出错,缺少vulkan sdk 103 | 104 | 参考[编译说明](./BUILD.md) 安装vulkan sdk 105 | 106 | #### windows提示缺少"VCRUNTIME140_1.dll" 107 | 108 | 下载安装适用于 Visual Studio 2015、2017 和 2019 的 Microsoft Visual C++ 可再发行软件包 109 | [下载地址](https://support.microsoft.com/zh-cn/help/2977003/the-latest-supported-visual-c-downloads) 110 | 111 | #### Windows7执行错误|中文乱码 112 | 113 | 1. cmd窗口左上角-属性 114 | 2. 字体选项卡-选择除了“点阵字体”以外的TrueType字体,例如:Lucida Console、宋体 115 | 3. 重新执行bat 116 | 117 | ### 输入参数说明 118 | 119 | * 请参考main.h中的命令行参数说明。 120 | * 每个参数有一个短参数名和一个长参数名,用短的或长的均可。 121 | 122 | 1. ```-d或--models```:模型所在文件夹路径,可以相对路径也可以绝对路径。 123 | 2. ```-1或--det```:dbNet模型文件名(不含扩展名) 124 | 3. ```-2或--cls```:angleNet模型文件名(不含扩展名) 125 | 4. ```-3或--rec```:crnnNet模型文件名(不含扩展名) 126 | 5. ```-4或--keys```:keys.txt文件名(含扩展名) 127 | 6. ```-i或--image```:目标图片路径,可以相对路径也可以绝对路径。 128 | 7. ```-t或--numThread```:线程数量。 129 | 8. ```-p或--padding```:图像预处理,在图片外周添加白边,用于提升识别率,文字框没有正确框住所有文字时,增加此值。 130 | 9. ```-s或--maxSideLen``` 131 | :按图片最长边的长度,此值为0代表不缩放,例:1024,如果图片长边大于1024则把图像整体缩小到1024再进行图像分割计算,如果图片长边小于1024则不缩放,如果图片长边小于32,则缩放到32。 132 | 10. ```-b或--boxScoreThresh```:文字框置信度门限,文字框没有正确框住所有文字时,减小此值。 133 | 11. ```-o或--boxThresh```:请自行试验。 134 | 12. ```-u或--unClipRatio```:单个文字框大小倍率,越大时单个文字框越大。此项与图片的大小相关,越大的图片此值应该越大。 135 | 13. ```-a或--doAngle```:启用(1)/禁用(0) 文字方向检测,只有图片倒置的情况下(旋转90~270度的图片),才需要启用文字方向检测。 136 | 14. ```-A或--mostAngle```:启用(1)/禁用(0) 角度投票(整张图片以最大可能文字方向来识别),当禁用文字方向检测时,此项也不起作用。 137 | 15. ```-h或--help```:打印命令行帮助。 138 | 16. ```-G或--GPU```:尝试使用gpu进行计算,-1(使用CPU)/0(使用GPU0)/1(使用GPU1)/...,GPU选择失败时,则使用CPU进行计算。 139 | 140 | ### 关于内存泄漏与valgrind 141 | 142 | * 项目根目录的valgrind-memcheck.sh用来检查内存泄漏(需要debug编译)。 143 | * valgrind-memcheck.txt是demo在linux平台的检查报告。 144 | * 报告中的"possibly lost"均发生在第三方库,possibly lost可能不一定是泄露,暂时不管。 -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (WIN32) 2 | cmake_minimum_required(VERSION 3.12) 3 | elseif (APPLE) 4 | cmake_minimum_required(VERSION 3.17) 5 | elseif (UNIX) 6 | cmake_minimum_required(VERSION 3.17) 7 | endif () 8 | project(OcrLiteNcnn) 9 | 10 | # Output BIN JNI CLIB 11 | if (NOT DEFINED OCR_OUTPUT) 12 | set(OCR_OUTPUT "BIN") 13 | message(STATUS "No OCR_OUTPUT, defaulting to BIN") 14 | endif () 15 | if (NOT DEFINED OCR_VULKAN) 16 | set(OCR_VULKAN "CPU") 17 | message(STATUS "No OCR_VULKAN, defaulting to CPU") 18 | endif () 19 | option(OCR_BENCHMARK "build benchmark" ON) 20 | set(OCR_BENCHMARK ON) 21 | #set(OCR_OUTPUT "BIN") 22 | 23 | set(CMAKE_CXX_STANDARD 11) 24 | add_definitions(-DUNICODE -D_UNICODE) 25 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 26 | add_definitions("-Wall -g -O0") 27 | else () 28 | add_definitions("-Wall") 29 | endif () 30 | 31 | # NCNN 32 | if (OCR_VULKAN STREQUAL "GPU") 33 | include(${CMAKE_CURRENT_SOURCE_DIR}/ncnn-vulkan-static/NcnnWrapperConfig.cmake) 34 | else () 35 | include(${CMAKE_CURRENT_SOURCE_DIR}/ncnn-static/NcnnWrapperConfig.cmake) 36 | endif () 37 | find_package(ncnn REQUIRED) 38 | if (ncnn_FOUND) 39 | message(STATUS "ncnn Found!") 40 | else () 41 | message(FATAL_ERROR "ncnn Not Found!") 42 | endif (ncnn_FOUND) 43 | 44 | # OpenCV 45 | set(BUILD_SHARED_LIBS false) 46 | include(${CMAKE_CURRENT_SOURCE_DIR}/opencv-static/OpenCVWrapperConfig.cmake) 47 | find_package(OpenCV REQUIRED) 48 | if (OpenCV_FOUND) 49 | message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}") 50 | message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") 51 | else () 52 | message(FATAL_ERROR "opencv Not Found!") 53 | endif (OpenCV_FOUND) 54 | 55 | # project include 56 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 57 | 58 | # source 59 | file(GLOB OCR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) 60 | set(OCR_COMPILE_CODE ${OCR_SRC}) 61 | 62 | # JNI 63 | if (OCR_OUTPUT STREQUAL "JNI") 64 | find_package(JNI REQUIRED) 65 | if (JNI_FOUND) 66 | message("JNI FOUND") 67 | message(STATUS "JNI_LIBS: ${JNI_LIBS}") 68 | message(STATUS "JNI_INCLUDE_DIRS: ${JNI_INCLUDE_DIRS}") 69 | include_directories(${JNI_INCLUDE_DIRS}) 70 | else () 71 | message(FATAL_ERROR "JNI Not Found!") 72 | endif () 73 | endif () 74 | 75 | if (OCR_OUTPUT STREQUAL "JNI") # JNI 76 | add_library(OcrLiteNcnn SHARED ${OCR_COMPILE_CODE}) 77 | target_compile_definitions(OcrLiteNcnn PRIVATE __JNI__) 78 | target_link_libraries(OcrLiteNcnn ncnn ${OpenCV_LIBS} ${JNI_LIBS}) 79 | elseif (OCR_OUTPUT STREQUAL "CLIB") # CLIB 80 | add_library(OcrLiteNcnn SHARED ${OCR_COMPILE_CODE}) 81 | target_compile_definitions(OcrLiteNcnn PRIVATE __CLIB__) 82 | target_link_libraries(OcrLiteNcnn ncnn ${OpenCV_LIBS}) 83 | elseif (OCR_OUTPUT STREQUAL "BIN") # BIN 84 | add_executable(OcrLiteNcnn ${OCR_COMPILE_CODE}) 85 | target_compile_definitions(OcrLiteNcnn PRIVATE __EXEC__) 86 | target_link_libraries(OcrLiteNcnn ncnn ${OpenCV_LIBS}) 87 | endif () 88 | 89 | install(TARGETS OcrLiteNcnn EXPORT OcrLiteNcnn) 90 | if (OCR_OUTPUT STREQUAL "CLIB") # CLIB 91 | file(GLOB OCR_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) 92 | install(FILES ${OCR_INCLUDE} DESTINATION include) 93 | endif () 94 | 95 | if (OCR_VULKAN STREQUAL "GPU") 96 | target_compile_definitions(OcrLiteNcnn PRIVATE __VULKAN__) 97 | endif () 98 | 99 | # benchmark 100 | if (OCR_BENCHMARK AND (OCR_OUTPUT STREQUAL "BIN")) 101 | add_executable(benchmark benchmark/benchmark.cpp 102 | src/AngleNet.cpp 103 | src/clipper.cpp 104 | src/CrnnNet.cpp 105 | src/DbNet.cpp 106 | src/getopt.cpp 107 | src/OcrLite.cpp 108 | src/OcrUtils.cpp) 109 | target_link_libraries(benchmark ncnn ${OpenCV_LIBS}) 110 | target_compile_definitions(benchmark PRIVATE __EXEC__) 111 | 112 | if (OCR_VULKAN STREQUAL "GPU") 113 | target_compile_definitions(benchmark PRIVATE __VULKAN__) 114 | endif () 115 | install(TARGETS benchmark EXPORT benchmark 116 | ARCHIVE DESTINATION staticlib 117 | LIBRARY DESTINATION sharedlib 118 | RUNTIME DESTINATION bin) 119 | endif () 120 | 121 | # Windows Link CRT 122 | if (OCR_BUILD_CRT STREQUAL "True") 123 | include(${CMAKE_CURRENT_SOURCE_DIR}/OcrCRTLinkage.cmake) 124 | endif () -------------------------------------------------------------------------------- /src/CrnnNet.cpp: -------------------------------------------------------------------------------- 1 | #include "CrnnNet.h" 2 | #include "OcrUtils.h" 3 | #include 4 | #include 5 | 6 | void CrnnNet::setGpuIndex(int gpuIndex) { 7 | #ifdef __VULKAN__ 8 | if (gpuIndex >= 0) { 9 | net.opt.use_vulkan_compute = true; 10 | net.set_vulkan_device(gpuIndex); 11 | printf("CrnnNet try to use Gpu%d\n", gpuIndex); 12 | } else { 13 | net.opt.use_vulkan_compute = false; 14 | printf("CrnnNet use Cpu\n"); 15 | } 16 | #endif 17 | } 18 | 19 | CrnnNet::~CrnnNet() { 20 | net.clear(); 21 | } 22 | 23 | void CrnnNet::setNumThread(int numOfThread) { 24 | numThread = numOfThread; 25 | } 26 | 27 | bool CrnnNet::initModel(const std::string &pathStr, const std::string &keysPath) { 28 | int ret_param = net.load_param((pathStr + ".param").c_str()); 29 | int ret_bin = net.load_model((pathStr + ".bin").c_str()); 30 | if (ret_param != 0 || ret_bin != 0) { 31 | printf("CrnnNet load param(%d), model(%d)\n", ret_param, ret_bin); 32 | return false; 33 | } 34 | //load keys 35 | std::ifstream in(keysPath.c_str()); 36 | std::string line; 37 | if (in) { 38 | while (getline(in, line)) {// line中不包括每行的换行符 39 | keys.push_back(line); 40 | } 41 | } else { 42 | printf("The keys.txt file was not found\n"); 43 | return false; 44 | } 45 | if (keys.size() != 5531) { 46 | fprintf(stderr, "missing keys\n"); 47 | return false; 48 | } 49 | //printf("total keys size(%lu)\n", keys.size()); 50 | return true; 51 | } 52 | 53 | template 54 | inline static size_t argmax(ForwardIterator first, ForwardIterator last) { 55 | return std::distance(first, std::max_element(first, last)); 56 | } 57 | 58 | TextLine CrnnNet::scoreToTextLine(const std::vector &outputData, int h, int w) { 59 | int keySize = keys.size(); 60 | std::string strRes; 61 | std::vector scores; 62 | int lastIndex = 0; 63 | int maxIndex; 64 | float maxValue; 65 | 66 | for (int i = 0; i < h; i++) { 67 | maxIndex = 0; 68 | maxValue = -1000.f; 69 | //do softmax 70 | std::vector exps(w); 71 | for (int j = 0; j < w; j++) { 72 | float expSingle = exp(outputData[i * w + j]); 73 | exps.at(j) = expSingle; 74 | } 75 | float partition = accumulate(exps.begin(), exps.end(), 0.0);//row sum 76 | maxIndex = int(argmax(exps.begin(), exps.end())); 77 | maxValue = float(*std::max_element(exps.begin(), exps.end())) / partition; 78 | if (maxIndex > 0 && maxIndex < keySize && (!(i > 0 && maxIndex == lastIndex))) { 79 | scores.emplace_back(maxValue); 80 | strRes.append(keys[maxIndex - 1]); 81 | } 82 | lastIndex = maxIndex; 83 | } 84 | return {strRes, scores}; 85 | } 86 | 87 | TextLine CrnnNet::getTextLine(const cv::Mat &src) { 88 | float scale = (float) dstHeight / (float) src.rows; 89 | int dstWidth = int((float) src.cols * scale); 90 | 91 | cv::Mat srcResize; 92 | resize(src, srcResize, cv::Size(dstWidth, dstHeight)); 93 | 94 | ncnn::Mat input = ncnn::Mat::from_pixels( 95 | srcResize.data, ncnn::Mat::PIXEL_RGB, 96 | srcResize.cols, srcResize.rows); 97 | 98 | input.substract_mean_normalize(meanValues, normValues); 99 | 100 | ncnn::Extractor extractor = net.create_extractor(); 101 | extractor.set_num_threads(numThread); 102 | extractor.input("input", input); 103 | 104 | ncnn::Mat out; 105 | extractor.extract("out", out); 106 | 107 | float *floatArray = (float *) out.data; 108 | std::vector outputData(floatArray, floatArray + out.h * out.w); 109 | return scoreToTextLine(outputData, out.h, out.w); 110 | } 111 | 112 | std::vector CrnnNet::getTextLines(std::vector &partImg, const char *path, const char *imgName) { 113 | int size = partImg.size(); 114 | std::vector textLines(size); 115 | for (int i = 0; i < size; ++i) { 116 | //OutPut DebugImg 117 | if (isOutputDebugImg) { 118 | std::string debugImgFile = getDebugImgFilePath(path, imgName, i, "-debug-"); 119 | saveImg(partImg[i], debugImgFile.c_str()); 120 | } 121 | 122 | //getTextLine 123 | double startCrnnTime = getCurrentTime(); 124 | TextLine textLine = getTextLine(partImg[i]); 125 | double endCrnnTime = getCurrentTime(); 126 | textLine.time = endCrnnTime - startCrnnTime; 127 | textLines[i] = textLine; 128 | } 129 | return textLines; 130 | } -------------------------------------------------------------------------------- /src/OcrResultUtils.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __JNI__ 2 | #include 3 | #include "OcrResultUtils.h" 4 | 5 | OcrResultUtils::OcrResultUtils(JNIEnv *env, OcrResult &ocrResult) { 6 | jniEnv = env; 7 | 8 | jclass jOcrResultClass = env->FindClass("com/benjaminwan/ocrlibrary/OcrResult"); 9 | 10 | if (jOcrResultClass == NULL) { 11 | printf("OcrResult class is null\n"); 12 | } 13 | 14 | jmethodID jOcrResultConstructor = env->GetMethodID(jOcrResultClass, "", 15 | "(DLjava/util/ArrayList;DLjava/lang/String;)V"); 16 | 17 | jobject textBlocks = getTextBlocks(ocrResult.textBlocks); 18 | jdouble dbNetTime = (jdouble) ocrResult.dbNetTime; 19 | jdouble detectTime = (jdouble) ocrResult.detectTime; 20 | jstring jStrRest = jniEnv->NewStringUTF(ocrResult.strRes.c_str()); 21 | 22 | jOcrResult = env->NewObject(jOcrResultClass, jOcrResultConstructor, dbNetTime, 23 | textBlocks, detectTime, jStrRest); 24 | } 25 | 26 | OcrResultUtils::~OcrResultUtils() { 27 | jniEnv = NULL; 28 | } 29 | 30 | jobject OcrResultUtils::getJObject() { 31 | return jOcrResult; 32 | } 33 | 34 | jclass OcrResultUtils::newJListClass() { 35 | jclass clazz = jniEnv->FindClass("java/util/ArrayList"); 36 | if (clazz == NULL) { 37 | printf("ArrayList class is null\n"); 38 | return NULL; 39 | } 40 | return clazz; 41 | } 42 | 43 | jmethodID OcrResultUtils::getListConstructor(jclass clazz) { 44 | jmethodID constructor = jniEnv->GetMethodID(clazz, "", "()V"); 45 | return constructor; 46 | } 47 | 48 | jobject OcrResultUtils::newJPoint(cv::Point &point) { 49 | jclass clazz = jniEnv->FindClass("com/benjaminwan/ocrlibrary/Point"); 50 | if (clazz == NULL) { 51 | printf("Point class is null\n"); 52 | return NULL; 53 | } 54 | jmethodID constructor = jniEnv->GetMethodID(clazz, "", "(II)V"); 55 | jobject obj = jniEnv->NewObject(clazz, constructor, point.x, point.y); 56 | return obj; 57 | } 58 | 59 | jobject OcrResultUtils::newJBoxPoint(std::vector &boxPoint) { 60 | jclass jListClass = newJListClass(); 61 | jmethodID jListConstructor = getListConstructor(jListClass); 62 | jobject jList = jniEnv->NewObject(jListClass, jListConstructor); 63 | jmethodID jListAdd = jniEnv->GetMethodID(jListClass, "add", "(Ljava/lang/Object;)Z"); 64 | 65 | for (auto point : boxPoint) { 66 | jobject jPoint = newJPoint(point); 67 | jniEnv->CallBooleanMethod(jList, jListAdd, jPoint); 68 | } 69 | return jList; 70 | } 71 | 72 | jobject OcrResultUtils::getTextBlock(TextBlock &textBlock) { 73 | jobject jBoxPint = newJBoxPoint(textBlock.boxPoint); 74 | jfloat jBoxScore = (jfloat) textBlock.boxScore; 75 | jfloat jAngleScore = (jfloat) textBlock.angleScore; 76 | jdouble jAngleTime = (jdouble) textBlock.angleTime; 77 | jstring jText = jniEnv->NewStringUTF(textBlock.text.c_str()); 78 | jobject jCharScores = newJScoreArray(textBlock.charScores); 79 | jdouble jCrnnTime = (jdouble) textBlock.crnnTime; 80 | jdouble jBlockTime = (jdouble) textBlock.blockTime; 81 | jclass clazz = jniEnv->FindClass("com/benjaminwan/ocrlibrary/TextBlock"); 82 | if (clazz == NULL) { 83 | printf("TextBlock class is null\n"); 84 | return NULL; 85 | } 86 | jmethodID constructor = jniEnv->GetMethodID(clazz, "", 87 | "(Ljava/util/ArrayList;FIFDLjava/lang/String;[FDD)V"); 88 | jobject obj = jniEnv->NewObject(clazz, constructor, jBoxPint, jBoxScore, textBlock.angleIndex, 89 | jAngleScore, jAngleTime, jText, jCharScores, jCrnnTime, 90 | jBlockTime); 91 | return obj; 92 | } 93 | 94 | jobject OcrResultUtils::getTextBlocks(std::vector &textBlocks) { 95 | jclass jListClass = newJListClass(); 96 | jmethodID jListConstructor = getListConstructor(jListClass); 97 | jobject jList = jniEnv->NewObject(jListClass, jListConstructor); 98 | jmethodID jListAdd = jniEnv->GetMethodID(jListClass, "add", "(Ljava/lang/Object;)Z"); 99 | 100 | for (int i = 0; i < textBlocks.size(); ++i) { 101 | auto textBlock = textBlocks[i]; 102 | jobject jTextBlock = getTextBlock(textBlock); 103 | jniEnv->CallBooleanMethod(jList, jListAdd, jTextBlock); 104 | } 105 | return jList; 106 | } 107 | 108 | jfloatArray OcrResultUtils::newJScoreArray(std::vector &scores) { 109 | jfloatArray jScores = jniEnv->NewFloatArray(scores.size()); 110 | jniEnv->SetFloatArrayRegion(jScores, 0, scores.size(), (jfloat *) scores.data()); 111 | return jScores; 112 | } 113 | 114 | #endif -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # 编译说明 2 | 3 | ### 依赖的第三方库下载 4 | 5 | 1. 下载opencv,[下载地址](https://github.com/RapidAI/OpenCVBuilder/releases) 6 | 7 | * OpenCV静态库:opencv-(版本号)-平台.7z, 8 | * 把压缩包解压到项目根目录,windows平台需要注意目录层次,解压后目录结构如下 9 | * 程序版本v1.6.0依赖opencv 4.5.4 10 | * 程序版本v1.7.0依赖opencv 4.6.0 11 | * windows平台分为mt和md版,mt代表静态链接CRT,md代表动态链接CRT 12 | * v1.8.0范例工程默认使用mt版,之前的版本则为md版 13 | ``` 14 | OcrLiteNcnn/opencv-static 15 | ├── OpenCVWrapperConfig.cmake 16 | ├── linux 17 | ├── macos 18 | ├── windows-x64 19 | └── windows-x86 20 | ``` 21 | 22 | 2. 下载ncnn,[下载地址](https://github.com/benjaminwan/NcnnBuilder/releases) 23 | 24 | * ncnn静态库含vulkan:ncnn-(版本号)-vulkan-平台.7z 25 | * ncnn静态库:ncnn-(版本号)-平台.7z 26 | * 注意:本demo从v1.5.1开始,去除crnn的lstm外循环改用框架的实现,需要配合ncnn>=20210124,请注意选择ncnn版本。 27 | * 程序版本v1.6.0需要ncnn 20210720 28 | * 程序版本v1.7.0依赖ncnn 20220420,此版本模型格式支持有变化,原有的db和angle模型需重新用onnx2ncnn工具转换 29 | * 程序版本v1.8.0依赖ncnn 20220729 30 | * 把压缩包解压到项目根目录,windows平台需要注意目录层次,解压后目录结构如下 31 | * windows平台分为mt和md版,mt代表静态链接CRT,md代表动态链接CRT 32 | * v1.8.0范例工程默认使用mt版,之前的版本则为md版 33 | ``` 34 | OcrLiteNcnn/ncnn-static 35 | ├── NcnnWrapperConfig.cmake 36 | ├── linux 37 | ├── macos 38 | ├── windows-x64 39 | └── windows-x86 40 | 41 | OcrLiteNcnn/ncnn-vulkan-static 42 | ├── NcnnWrapperConfig.cmake 43 | ├── linux 44 | ├── macos 45 | ├── windows-x64 46 | └── windows-x86 47 | ``` 48 | 49 | 3. Vulkan SDK,[下载地址](https://vulkan.lunarg.com/sdk/home) 50 | 51 | * 如果想编译ncnn带vulkan支持的版本,则必须先安装Vulkan SDK。 52 | * 一般下载最新版即可,当前最新版1.3.236.0,安装时选中所有组件。 53 | * Windows:直接双击安装。 54 | * macOS:加载dmg后,直接双击安装。 55 | * Linux: 解压tar.gz文件到某个路径,并配置export VULKAN_SDK=解压路径/1.3.236.0/x86_64, export PATH=解压路径/1.3.236.0/x86_64/bin:$PATH 56 | 57 | ### 编译环境 58 | 59 | 1. Windows 10 x64 60 | 2. macOS 10.15 61 | 3. Linux Ubuntu 1804 x64 62 | 63 | **注意:以下说明仅适用于本机编译。如果需要交叉编译为arm等其它平台(参考android),则需要先交叉编译所有第三方依赖库(ncnn、opencv),然后再把依赖库整合替换到本项目里。** 64 | 65 | ### Windows编译说明 66 | 67 | #### Windows nmake编译 68 | 69 | 1. 安装VS2017或VS2019,安装时,至少选中'使用C++的桌面开发' 70 | 2. cmake>=3.12请自行下载&配置,[下载地址](https://cmake.org/download/) 71 | 3. 开始菜单打开"x64 Native Tools Command Prompt for VS 2019"或"适用于 VS2017 的 x64 本机工具",并转到本项目根目录 72 | 4. 运行```build.bat```并按照提示输入选项,最后选择'编译成可执行文件' 73 | 5. 编译完成后运行```run-test.bat```进行测试(注意修改脚本内的目标图片路径) 74 | 6. 编译JNI动态运行库(可选,可用于java调用) 75 | 76 | * 下载jdk-8u221-windows-x64.exe,安装选项默认(确保“源代码”项选中),安装完成后,打开“系统”属性->高级->环境变量 77 | * 新建“系统变量”,变量名```JAVA_HOME``` ,变量值```C:\Program Files\Java\jdk1.8.0_221`` 78 | * 新建“系统变量”,变量名```CLASSPATH``` ,变量值```.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;`` 79 | * 编辑“系统变量”Path,Win7在变量值头部添加```%JAVA_HOME%\bin;``` ,win10直接添加一行```%JAVA_HOME%\bin``` 80 | * 开始菜单打开"x64 Native Tools Command Prompt for VS 2019"或"适用于 VS2017 的 x64 本机工具",并转到本项目根目录 81 | * 运行```build.bat```并按照提示输入选项,最后选择'编译成JNI动态库' 82 | 83 | #### Windows Visual Studio编译说明 84 | 85 | 1. VS2017/VS2019,cmake……等安装配置参考上述步骤。 86 | 2. 运行generate-vs-project.bat,输入数字选择要生成的visual studio项目解决方案版本。 87 | 3. 根据你的编译环境,进入build-xxxx-x86或x64文件夹,打开OcrLiteNcnn.sln。 88 | 4. 在顶部工具栏选择Release,在右边的"解决方案"窗口,右键选中"ALL_BUILD"->生成。要选择Debug,则您必须自行编译Debug版的opencv或ncnn。 89 | 90 | #### Windows部署说明 91 | 92 | 1. 编译选项选择第三方库为动态库时,部署的时候记得把dll复制到可执行文件目录。 93 | 2. 部署时如果提示缺少"VCRUNTIME140_1.dll",下载安装适用于 Visual Studio 2015、2017 和 2019 的 Microsoft Visual C++ 可再发行软件包, 94 | [下载地址](https://support.microsoft.com/zh-cn/help/2977003/the-latest-supported-visual-c-downloads) 95 | 96 | ### Mac编译说明 97 | 98 | 1. macOS Catalina 10.15.x,安装Xcode 12.1,并安装Xcode Command Line Tools, 终端运行```xcode-select –install``` 99 | 2. 自行下载安装HomeBrew,cmake >=3.17[下载地址](https://cmake.org/download/) 100 | 3. libomp: ```brew install libomp``` 101 | 4. 终端打开项目根目录,```./build.sh```并按照提示输入选项,最后选择'编译成可执行文件' 102 | 5. 测试:```./run-test.sh```(注意修改脚本内的目标图片路径) 103 | 6. 编译JNI动态运行库(可选,可用于java调用) 104 | 105 | * 下载jdk-8u221-macosx-x64.dmg,安装。 106 | * 编辑用户目录下的隐藏文件```.zshrc``` ,添加```export JAVA_HOME=$(/usr/libexec/java_home)``` 107 | * 运行```build.sh```并按照提示输入选项,最后选择'编译成JNI动态库' 108 | 109 | #### macOS部署说明 110 | 111 | opencv或onnxruntime使用动态库时,参考下列方法: 112 | 113 | * 把动态库所在路径加入DYLD_LIBRARY_PATH搜索路径 114 | * 把动态库复制或链接到到/usr/lib 115 | 116 | ### Linux编译说明 117 | 118 | 1. Ubuntu18.04 LTS 其它发行版(请自行编译依赖库opencv和ncnn,或自行适配官方发布的动态库) 119 | 2. ```sudo apt-get install build-essential``` 120 | 3. g++>=5,cmake>=3.17[下载地址](https://cmake.org/download/) 121 | 4. 终端打开项目根目录,```./build.sh```并按照提示输入选项,最后选择'编译成可执行文件' 122 | 5. 测试:```./run-test.sh```(注意修改脚本内的目标图片路径) 123 | 6. 编译JNI动态运行库(可选,可用于java调用) 124 | 125 | * 下载jdk-8u221并安装配置 126 | * 运行```build.sh```并按照提示输入选项,最后选择'编译成JNI动态库' 127 | * **注意:编译JNI时,g++版本要求>=6** 128 | 129 | #### Linux部署说明 130 | 131 | opencv或onnxruntime使用动态库时,参考下列方法: 132 | 133 | * 把动态库所在路径加入LD_LIBRARY_PATH搜索路径 134 | * 把动态库复制或链接到到/usr/lib 135 | 136 | ### 编译参数说明 137 | 138 | build.sh编译参数 139 | 140 | 1. ```OCR_OUTPUT="BIN"或"JNI"或"CLIB"```: BIN时编译为可执行文件,JNI时编译为java jni 动态库,CLIB时编译为 C动态库; 141 | 2. ```OCR_VULKAN="CPU"或"GPU"```: CPU时则选择不带vulkan的版本编译,GPU时选择ncnn(带vulkan)静态库进行编译; -------------------------------------------------------------------------------- /src/OcrLiteJni.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __JNI__ 2 | 3 | #include "version.h" 4 | #include 5 | #include "OcrLite.h" 6 | #include "OcrResultUtils.h" 7 | #include "OcrUtils.h" 8 | 9 | static OcrLite *ocrLite; 10 | 11 | JNIEXPORT jint JNICALL 12 | JNI_OnLoad(JavaVM *vm, void *reserved) { 13 | ocrLite = new OcrLite(); 14 | return JNI_VERSION_1_4; 15 | } 16 | 17 | JNIEXPORT void JNICALL 18 | JNI_OnUnload(JavaVM *vm, void *reserved) { 19 | //printf("JNI_OnUnload\n"); 20 | delete ocrLite; 21 | } 22 | 23 | #ifdef _WIN32 24 | char *jstringToChar(JNIEnv *env, jstring jstr) { 25 | char *rtn = NULL; 26 | jclass clsstring = env->FindClass("java/lang/String"); 27 | jstring strencode = env->NewStringUTF("gbk"); 28 | jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); 29 | jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode); 30 | jsize alen = env->GetArrayLength(barr); 31 | jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE); 32 | if (alen > 0) { 33 | rtn = (char *) malloc(alen + 1); 34 | memcpy(rtn, ba, alen); 35 | rtn[alen] = 0; 36 | } 37 | env->ReleaseByteArrayElements(barr, ba, 0); 38 | return rtn; 39 | } 40 | #else 41 | 42 | char *jstringToChar(JNIEnv *env, jstring input) { 43 | char *str = NULL; 44 | jclass clsstring = env->FindClass("java/lang/String"); 45 | jstring strencode = env->NewStringUTF("utf-8"); 46 | jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); 47 | jbyteArray barr = (jbyteArray) env->CallObjectMethod(input, mid, strencode); 48 | jsize alen = env->GetArrayLength(barr); 49 | jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE); 50 | if (alen > 0) { 51 | str = (char *) malloc(alen + 1); 52 | memcpy(str, ba, alen); 53 | str[alen] = 0; 54 | } 55 | env->ReleaseByteArrayElements(barr, ba, 0); 56 | return str; 57 | } 58 | 59 | #endif 60 | 61 | extern "C" JNIEXPORT jstring JNICALL 62 | Java_com_benjaminwan_ocrlibrary_OcrEngine_getVersion(JNIEnv *env, jobject thiz) { 63 | jstring ver = env->NewStringUTF(VERSION); 64 | return ver; 65 | } 66 | 67 | extern "C" JNIEXPORT jboolean JNICALL 68 | Java_com_benjaminwan_ocrlibrary_OcrEngine_setNumThread(JNIEnv *env, jobject thiz, jint numThread) { 69 | ocrLite->setNumThread(numThread); 70 | printf("numThread=%d\n", numThread); 71 | return JNI_TRUE; 72 | } 73 | 74 | extern "C" JNIEXPORT void JNICALL 75 | Java_com_benjaminwan_ocrlibrary_OcrEngine_initLogger(JNIEnv *env, jobject thiz, jboolean isConsole, 76 | jboolean isPartImg, jboolean isResultImg) { 77 | ocrLite->initLogger(isConsole,//isOutputConsole 78 | isPartImg,//isOutputPartImg 79 | isResultImg);//isOutputResultImg 80 | } 81 | 82 | extern "C" JNIEXPORT void JNICALL 83 | Java_com_benjaminwan_ocrlibrary_OcrEngine_enableResultText(JNIEnv *env, jobject thiz, jstring input) { 84 | std::string imgPath = jstringToChar(env, input); 85 | std::string imgDir = imgPath.substr(0, imgPath.find_last_of('/') + 1); 86 | std::string imgName = imgPath.substr(imgPath.find_last_of('/') + 1); 87 | ocrLite->enableResultTxt(imgDir.c_str(), imgName.c_str()); 88 | } 89 | 90 | extern "C" JNIEXPORT jboolean JNICALL 91 | Java_com_benjaminwan_ocrlibrary_OcrEngine_initModels(JNIEnv *env, jobject thiz, jstring path, 92 | jstring det, jstring cls, jstring rec, jstring keys) { 93 | std::string modelsDir = jstringToChar(env, path); 94 | std::string detName = jstringToChar(env, det); 95 | std::string clsName = jstringToChar(env, cls); 96 | std::string recName = jstringToChar(env, rec); 97 | std::string keysName = jstringToChar(env, keys); 98 | printf("modelsDir=%s\ndet=%s\ncls=%s\nrec=%s\nkeys=%s\n", modelsDir.c_str(), detName.c_str(), clsName.c_str(), 99 | recName.c_str(), keysName.c_str()); 100 | std::string modelDetPath, modelClsPath, modelRecPath, keysPath; 101 | if (detName.empty()) { 102 | modelDetPath = modelsDir + "/" + "dbnet_op"; 103 | } else { 104 | modelDetPath = modelsDir + "/" + detName; 105 | } 106 | if (clsName.empty()) { 107 | modelClsPath = modelsDir + "/" + "angle_op"; 108 | } else { 109 | modelClsPath = modelsDir + "/" + clsName; 110 | } 111 | if (recName.empty()) { 112 | modelRecPath = modelsDir + "/" + "crnn_lite_op"; 113 | } else { 114 | modelRecPath = modelsDir + "/" + recName; 115 | } 116 | if (keysName.empty()) { 117 | keysPath = modelsDir + "/" + "keys.txt"; 118 | } else { 119 | keysPath = modelsDir + "/" + keysName; 120 | } 121 | bool hasModelDetParam = isFileExists(modelDetPath + ".param"); 122 | if (!hasModelDetParam) { 123 | fprintf(stderr, "Model dbnet file not found: %s.param\n", modelDetPath.c_str()); 124 | return -1; 125 | } 126 | bool hasModelDetBin = isFileExists(modelDetPath + ".bin"); 127 | if (!hasModelDetBin) { 128 | fprintf(stderr, "Model dbnet file not found: %s.bin\n", modelDetPath.c_str()); 129 | return -1; 130 | } 131 | bool hasModelClsParam = isFileExists(modelClsPath + ".param"); 132 | if (!hasModelClsParam) { 133 | fprintf(stderr, "Model angle file not found: %s.param\n", modelClsPath.c_str()); 134 | return -1; 135 | } 136 | bool hasModelClsBin = isFileExists(modelClsPath + ".bin"); 137 | if (!hasModelClsBin) { 138 | fprintf(stderr, "Model angle file not found: %s.bin\n", modelClsPath.c_str()); 139 | return -1; 140 | } 141 | bool hasModelRecParam = isFileExists(modelRecPath + ".param"); 142 | if (!hasModelRecParam) { 143 | fprintf(stderr, "Model crnn file not found: %s.param\n", modelRecPath.c_str()); 144 | return -1; 145 | } 146 | bool hasModelRecBin = isFileExists(modelRecPath + ".bin"); 147 | if (!hasModelRecBin) { 148 | fprintf(stderr, "Model crnn file not found: %s.bin\n", modelRecPath.c_str()); 149 | return -1; 150 | } 151 | bool hasKeysFile = isFileExists(keysPath); 152 | if (!hasKeysFile) { 153 | fprintf(stderr, "keys file not found: %s\n", keysPath.c_str()); 154 | return -1; 155 | } 156 | ocrLite->initModels(modelDetPath, modelClsPath, modelRecPath, keysPath); 157 | return true; 158 | } 159 | 160 | extern "C" JNIEXPORT void JNICALL 161 | Java_com_benjaminwan_ocrlibrary_OcrEngine_setGpuIndex(JNIEnv *env, jobject thiz, jint gpuIndex) { 162 | ocrLite->setGpuIndex(gpuIndex); 163 | } 164 | 165 | extern "C" JNIEXPORT jobject JNICALL 166 | Java_com_benjaminwan_ocrlibrary_OcrEngine_detect(JNIEnv *env, jobject thiz, jstring input, jint padding, 167 | jint maxSideLen, 168 | jfloat boxScoreThresh, jfloat boxThresh, jfloat unClipRatio, 169 | jboolean doAngle, jboolean mostAngle 170 | ) { 171 | std::string imgPath = jstringToChar(env, input); 172 | bool hasTargetImgFile = isFileExists(imgPath); 173 | if (!hasTargetImgFile) { 174 | fprintf(stderr, "Target image not found: %s\n", imgPath.c_str()); 175 | OcrResult result{}; 176 | return OcrResultUtils(env, result).getJObject(); 177 | } 178 | std::string imgDir = imgPath.substr(0, imgPath.find_last_of('/') + 1); 179 | std::string imgName = imgPath.substr(imgPath.find_last_of('/') + 1); 180 | printf("imgDir=%s, imgName=%s\n", imgDir.c_str(), imgName.c_str()); 181 | OcrResult result = ocrLite->detect(imgDir.c_str(), imgName.c_str(), padding, maxSideLen, 182 | boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle); 183 | return OcrResultUtils(env, result).getJObject(); 184 | } 185 | #endif -------------------------------------------------------------------------------- /src/getopt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * getopt - POSIX like getopt for Windows console Application 3 | * 4 | * win-c - Windows Console Library 5 | * Copyright (c) 2015 Koji Takami 6 | * Released under the MIT license 7 | * https://github.com/takamin/win-c/blob/master/LICENSE 8 | */ 9 | #ifndef __CLIB__ 10 | 11 | #include 12 | #include 13 | #include "getopt.h" 14 | 15 | char *optarg = 0; 16 | int optind = 1; 17 | int opterr = 1; 18 | int optopt = 0; 19 | 20 | int postpone_count = 0; 21 | int nextchar = 0; 22 | 23 | static void postpone(int argc, char *const argv[], int index) { 24 | char **nc_argv = (char **) argv; 25 | char *p = nc_argv[index]; 26 | int j = index; 27 | for (; j < argc - 1; j++) { 28 | nc_argv[j] = nc_argv[j + 1]; 29 | } 30 | nc_argv[argc - 1] = p; 31 | } 32 | 33 | static int postpone_noopt(int argc, char *const argv[], int index) { 34 | int i = index; 35 | for (; i < argc; i++) { 36 | if (*(argv[i]) == '-') { 37 | postpone(argc, argv, index); 38 | return 1; 39 | } 40 | } 41 | return 0; 42 | } 43 | 44 | static int _getopt_(int argc, char *const argv[], 45 | const char *optstring, 46 | const struct option *longopts, int *longindex) { 47 | while (1) { 48 | int c; 49 | const char *optptr = 0; 50 | if (optind >= argc - postpone_count) { 51 | c = 0; 52 | optarg = 0; 53 | break; 54 | } 55 | c = *(argv[optind] + nextchar); 56 | if (c == '\0') { 57 | nextchar = 0; 58 | ++optind; 59 | continue; 60 | } 61 | if (nextchar == 0) { 62 | if (optstring[0] != '+' && optstring[0] != '-') { 63 | while (c != '-') { 64 | /* postpone non-opt parameter */ 65 | if (!postpone_noopt(argc, argv, optind)) { 66 | break; /* all args are non-opt param */ 67 | } 68 | ++postpone_count; 69 | c = *argv[optind]; 70 | } 71 | } 72 | if (c != '-') { 73 | if (optstring[0] == '-') { 74 | optarg = argv[optind]; 75 | nextchar = 0; 76 | ++optind; 77 | return 1; 78 | } 79 | break; 80 | } else { 81 | if (strcmp(argv[optind], "--") == 0) { 82 | optind++; 83 | break; 84 | } 85 | ++nextchar; 86 | if (longopts != 0 && *(argv[optind] + 1) == '-') { 87 | char const *spec_long = argv[optind] + 2; 88 | char const *pos_eq = strchr(spec_long, '='); 89 | int spec_len = (pos_eq == NULL ? strlen(spec_long) : pos_eq - spec_long); 90 | int index_search = 0; 91 | int index_found = -1; 92 | const struct option *optdef = 0; 93 | while (longopts->name != 0) { 94 | if (strncmp(spec_long, longopts->name, spec_len) == 0) { 95 | if (optdef != 0) { 96 | if (opterr) { 97 | fprintf(stderr, "ambiguous option: %s\n", spec_long); 98 | } 99 | return '?'; 100 | } 101 | optdef = longopts; 102 | index_found = index_search; 103 | } 104 | longopts++; 105 | index_search++; 106 | } 107 | if (optdef == 0) { 108 | if (opterr) { 109 | fprintf(stderr, "no such a option: %s\n", spec_long); 110 | } 111 | return '?'; 112 | } 113 | switch (optdef->has_arg) { 114 | case no_argument: 115 | optarg = 0; 116 | if (pos_eq != 0) { 117 | if (opterr) { 118 | fprintf(stderr, "no argument for %s\n", optdef->name); 119 | } 120 | return '?'; 121 | } 122 | break; 123 | case required_argument: 124 | if (pos_eq == NULL) { 125 | ++optind; 126 | optarg = argv[optind]; 127 | } else { 128 | optarg = (char *) pos_eq + 1; 129 | } 130 | break; 131 | } 132 | ++optind; 133 | nextchar = 0; 134 | if (longindex != 0) { 135 | *longindex = index_found; 136 | } 137 | if (optdef->flag != 0) { 138 | *optdef->flag = optdef->val; 139 | return 0; 140 | } 141 | return optdef->val; 142 | } 143 | continue; 144 | } 145 | } 146 | optptr = strchr(optstring, c); 147 | if (optptr == NULL) { 148 | optopt = c; 149 | if (opterr) { 150 | fprintf(stderr, 151 | "%s: invalid option -- %c\n", 152 | argv[0], c); 153 | } 154 | ++nextchar; 155 | return '?'; 156 | } 157 | if (*(optptr + 1) != ':') { 158 | nextchar++; 159 | if (*(argv[optind] + nextchar) == '\0') { 160 | ++optind; 161 | nextchar = 0; 162 | } 163 | optarg = 0; 164 | } else { 165 | nextchar++; 166 | if (*(argv[optind] + nextchar) != '\0') { 167 | optarg = argv[optind] + nextchar; 168 | } else { 169 | ++optind; 170 | if (optind < argc - postpone_count) { 171 | optarg = argv[optind]; 172 | } else { 173 | optopt = c; 174 | if (opterr) { 175 | fprintf(stderr, 176 | "%s: option requires an argument -- %c\n", 177 | argv[0], c); 178 | } 179 | if (optstring[0] == ':' || 180 | (optstring[0] == '-' || optstring[0] == '+') && 181 | optstring[1] == ':') { 182 | c = ':'; 183 | } else { 184 | c = '?'; 185 | } 186 | } 187 | } 188 | ++optind; 189 | nextchar = 0; 190 | } 191 | return c; 192 | } 193 | 194 | /* end of option analysis */ 195 | 196 | /* fix the order of non-opt params to original */ 197 | while ((argc - optind - postpone_count) > 0) { 198 | postpone(argc, argv, optind); 199 | ++postpone_count; 200 | } 201 | 202 | nextchar = 0; 203 | postpone_count = 0; 204 | return -1; 205 | } 206 | 207 | int getopt(int argc, char *const argv[], 208 | const char *optstring) { 209 | return _getopt_(argc, argv, optstring, 0, 0); 210 | } 211 | 212 | int getopt_long(int argc, char *const argv[], 213 | const char *optstring, 214 | const struct option *longopts, int *longindex) { 215 | return _getopt_(argc, argv, optstring, longopts, longindex); 216 | } 217 | /******************************************************** 218 | int getopt_long_only(int argc, char* const argv[], 219 | const char* optstring, 220 | const struct option* longopts, int* longindex) 221 | { 222 | return -1; 223 | } 224 | ********************************************************/ 225 | 226 | #endif // __CLIB__ 227 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #ifndef __JNI__ 2 | #ifndef __CLIB__ 3 | #include 4 | #include "main.h" 5 | #include "version.h" 6 | #include "OcrLite.h" 7 | #include "OcrUtils.h" 8 | 9 | void printHelp(FILE *out, char *argv0) { 10 | fprintf(out, " ------- Usage -------\n"); 11 | fprintf(out, "%s %s", argv0, usageMsg); 12 | fprintf(out, " ------- Required Parameters -------\n"); 13 | fprintf(out, "%s", requiredMsg); 14 | fprintf(out, " ------- Optional Parameters -------\n"); 15 | fprintf(out, "%s", optionalMsg); 16 | fprintf(out, " ------- Other Parameters -------\n"); 17 | fprintf(out, "%s", otherMsg); 18 | fprintf(out, " ------- Examples -------\n"); 19 | fprintf(out, example1Msg, argv0); 20 | fprintf(out, example2Msg, argv0); 21 | } 22 | 23 | int main(int argc, char **argv) { 24 | if (argc <= 1) { 25 | printHelp(stderr, argv[0]); 26 | return -1; 27 | } 28 | std::string modelsDir, modelDetPath, modelClsPath, modelRecPath, keysPath; 29 | std::string imgPath, imgDir, imgName; 30 | int numThread = 4; 31 | int padding = 50; 32 | int maxSideLen = 1024; 33 | float boxScoreThresh = 0.6f; 34 | float boxThresh = 0.3f; 35 | float unClipRatio = 2.0f; 36 | bool doAngle = true; 37 | int flagDoAngle = 1; 38 | bool mostAngle = true; 39 | int flagMostAngle = 1; 40 | int flagGpu = -1; 41 | 42 | int opt; 43 | int optionIndex = 0; 44 | while ((opt = getopt_long(argc, argv, "d:1:2:3:4:i:t:p:s:b:o:u:a:A:G:v:h", long_options, &optionIndex)) != -1) { 45 | //printf("option(-%c)=%s\n", opt, optarg); 46 | switch (opt) { 47 | case 'd': 48 | modelsDir = optarg; 49 | printf("modelsPath=%s\n", modelsDir.c_str()); 50 | break; 51 | case '1': 52 | modelDetPath = modelsDir + "/" + optarg; 53 | printf("model dbnet path=%s\n", modelDetPath.c_str()); 54 | break; 55 | case '2': 56 | modelClsPath = modelsDir + "/" + optarg; 57 | printf("model angle path=%s\n", modelClsPath.c_str()); 58 | break; 59 | case '3': 60 | modelRecPath = modelsDir + "/" + optarg; 61 | printf("model crnn path=%s\n", modelRecPath.c_str()); 62 | break; 63 | case '4': 64 | keysPath = modelsDir + "/" + optarg; 65 | printf("keys path=%s\n", keysPath.c_str()); 66 | break; 67 | case 'i': 68 | imgPath.assign(optarg); 69 | imgDir.assign(imgPath.substr(0, imgPath.find_last_of('/') + 1)); 70 | imgName.assign(imgPath.substr(imgPath.find_last_of('/') + 1)); 71 | printf("imgDir=%s, imgName=%s\n", imgDir.c_str(), imgName.c_str()); 72 | break; 73 | case 't': 74 | numThread = (int) strtol(optarg, NULL, 10); 75 | //printf("numThread=%d\n", numThread); 76 | break; 77 | case 'p': 78 | padding = (int) strtol(optarg, NULL, 10); 79 | //printf("padding=%d\n", padding); 80 | break; 81 | case 's': 82 | maxSideLen = (int) strtol(optarg, NULL, 10); 83 | //printf("maxSideLen=%d\n", maxSideLen); 84 | break; 85 | case 'b': 86 | boxScoreThresh = strtof(optarg, NULL); 87 | //printf("boxScoreThresh=%f\n", boxScoreThresh); 88 | break; 89 | case 'o': 90 | boxThresh = strtof(optarg, NULL); 91 | //printf("boxThresh=%f\n", boxThresh); 92 | break; 93 | case 'u': 94 | unClipRatio = strtof(optarg, NULL); 95 | //printf("unClipRatio=%f\n", unClipRatio); 96 | break; 97 | case 'a': 98 | flagDoAngle = (int) strtol(optarg, NULL, 10); 99 | if (flagDoAngle == 0) { 100 | doAngle = false; 101 | } else { 102 | doAngle = true; 103 | } 104 | //printf("doAngle=%d\n", doAngle); 105 | break; 106 | case 'A': 107 | flagMostAngle = (int) strtol(optarg, NULL, 10); 108 | if (flagMostAngle == 0) { 109 | mostAngle = false; 110 | } else { 111 | mostAngle = true; 112 | } 113 | //printf("mostAngle=%d\n", mostAngle); 114 | break; 115 | case 'v': 116 | printf("%s\n", VERSION); 117 | printGpuInfo(); 118 | return 0; 119 | case 'h': 120 | printHelp(stdout, argv[0]); 121 | return 0; 122 | case 'G': 123 | flagGpu = (int) strtol(optarg, NULL, 10); 124 | break; 125 | default: 126 | printf("other option %c :%s\n", opt, optarg); 127 | } 128 | } 129 | if (modelDetPath.empty()) { 130 | modelDetPath = modelsDir + "/" + "dbnet_op"; 131 | } 132 | if (modelClsPath.empty()) { 133 | modelClsPath = modelsDir + "/" + "angle_op"; 134 | } 135 | if (modelRecPath.empty()) { 136 | modelRecPath = modelsDir + "/" + "crnn_lite_op"; 137 | } 138 | if (keysPath.empty()) { 139 | keysPath = modelsDir + "/" + "keys.txt"; 140 | } 141 | bool hasTargetImgFile = isFileExists(imgPath); 142 | if (!hasTargetImgFile) { 143 | fprintf(stderr, "Target image not found: %s\n", imgPath.c_str()); 144 | return -1; 145 | } 146 | bool hasModelDetParam = isFileExists(modelDetPath + ".param"); 147 | if (!hasModelDetParam) { 148 | fprintf(stderr, "Model dbnet file not found: %s.param\n", modelDetPath.c_str()); 149 | return -1; 150 | } 151 | bool hasModelDetBin = isFileExists(modelDetPath + ".bin"); 152 | if (!hasModelDetBin) { 153 | fprintf(stderr, "Model dbnet file not found: %s.bin\n", modelDetPath.c_str()); 154 | return -1; 155 | } 156 | bool hasModelClsParam = isFileExists(modelClsPath + ".param"); 157 | if (!hasModelClsParam) { 158 | fprintf(stderr, "Model angle file not found: %s.param\n", modelClsPath.c_str()); 159 | return -1; 160 | } 161 | bool hasModelClsBin = isFileExists(modelClsPath + ".bin"); 162 | if (!hasModelClsBin) { 163 | fprintf(stderr, "Model angle file not found: %s.bin\n", modelClsPath.c_str()); 164 | return -1; 165 | } 166 | bool hasModelRecParam = isFileExists(modelRecPath + ".param"); 167 | if (!hasModelRecParam) { 168 | fprintf(stderr, "Model crnn file not found: %s.param\n", modelRecPath.c_str()); 169 | return -1; 170 | } 171 | bool hasModelRecBin = isFileExists(modelRecPath + ".bin"); 172 | if (!hasModelRecBin) { 173 | fprintf(stderr, "Model crnn file not found: %s.bin\n", modelRecPath.c_str()); 174 | return -1; 175 | } 176 | bool hasKeysFile = isFileExists(keysPath); 177 | if (!hasKeysFile) { 178 | fprintf(stderr, "keys file not found: %s\n", keysPath.c_str()); 179 | return -1; 180 | } 181 | OcrLite ocrLite; 182 | ocrLite.setNumThread(numThread); 183 | ocrLite.initLogger( 184 | true,//isOutputConsole 185 | false,//isOutputPartImg 186 | true);//isOutputResultImg 187 | 188 | ocrLite.enableResultTxt(imgDir.c_str(), imgName.c_str()); 189 | ocrLite.setGpuIndex(flagGpu); 190 | ocrLite.Logger("=====Input Params=====\n"); 191 | ocrLite.Logger( 192 | "numThread(%d),padding(%d),maxSideLen(%d),boxScoreThresh(%f),boxThresh(%f),unClipRatio(%f),doAngle(%d),mostAngle(%d),GPU(%d)\n", 193 | numThread, padding, maxSideLen, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle, 194 | flagGpu); 195 | 196 | bool initModelsRet = ocrLite.initModels(modelDetPath, modelClsPath, modelRecPath, keysPath); 197 | if (!initModelsRet) return -1; 198 | 199 | OcrResult result = ocrLite.detect(imgDir.c_str(), imgName.c_str(), padding, maxSideLen, 200 | boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle); 201 | ocrLite.Logger("%s\n", result.strRes.c_str()); 202 | return 0; 203 | } 204 | 205 | #endif 206 | #endif -------------------------------------------------------------------------------- /benchmark/benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "main.h" 3 | #include "version.h" 4 | #include "OcrLite.h" 5 | #include "OcrUtils.h" 6 | 7 | void printHelp(FILE *out, char *argv0) { 8 | fprintf(out, " ------- Usage -------\n"); 9 | fprintf(out, "%s %s", argv0, usageMsg); 10 | fprintf(out, " ------- Required Parameters -------\n"); 11 | fprintf(out, "%s", requiredMsg); 12 | fprintf(out, " ------- Optional Parameters -------\n"); 13 | fprintf(out, "%s", optionalMsg); 14 | fprintf(out, " ------- Other Parameters -------\n"); 15 | fprintf(out, "%s", otherMsg); 16 | fprintf(out, " ------- Examples -------\n"); 17 | fprintf(out, example1Msg, argv0); 18 | fprintf(out, example2Msg, argv0); 19 | } 20 | 21 | int main(int argc, char **argv) { 22 | if (argc <= 1) { 23 | printHelp(stderr, argv[0]); 24 | return -1; 25 | } 26 | std::string modelsDir, modelDetPath, modelClsPath, modelRecPath, keysPath; 27 | std::string imgPath, imgDir, imgName; 28 | int numThread = 4; 29 | int loopCount = 1; 30 | int padding = 50; 31 | int maxSideLen = 1024; 32 | float boxScoreThresh = 0.6f; 33 | float boxThresh = 0.3f; 34 | float unClipRatio = 2.0f; 35 | bool doAngle = true; 36 | int flagDoAngle = 1; 37 | bool mostAngle = true; 38 | int flagMostAngle = 1; 39 | int flagGpu = -1; 40 | 41 | int opt; 42 | int optionIndex = 0; 43 | while ((opt = getopt_long(argc, argv, "d:1:2:3:4:i:t:p:s:b:o:u:a:A:G:v:h:l", long_options, &optionIndex)) != -1) { 44 | //printf("option(-%c)=%s\n", opt, optarg); 45 | switch (opt) { 46 | case 'd': 47 | modelsDir = optarg; 48 | printf("modelsPath=%s\n", modelsDir.c_str()); 49 | break; 50 | case '1': 51 | modelDetPath = modelsDir + "/" + optarg; 52 | printf("model dbnet path=%s\n", modelDetPath.c_str()); 53 | break; 54 | case '2': 55 | modelClsPath = modelsDir + "/" + optarg; 56 | printf("model angle path=%s\n", modelClsPath.c_str()); 57 | break; 58 | case '3': 59 | modelRecPath = modelsDir + "/" + optarg; 60 | printf("model crnn path=%s\n", modelRecPath.c_str()); 61 | break; 62 | case '4': 63 | keysPath = modelsDir + "/" + optarg; 64 | printf("keys path=%s\n", keysPath.c_str()); 65 | break; 66 | case 'i': 67 | imgPath.assign(optarg); 68 | imgDir.assign(imgPath.substr(0, imgPath.find_last_of('/') + 1)); 69 | imgName.assign(imgPath.substr(imgPath.find_last_of('/') + 1)); 70 | printf("imgDir=%s, imgName=%s\n", imgDir.c_str(), imgName.c_str()); 71 | break; 72 | case 't': 73 | numThread = (int) strtol(optarg, NULL, 10); 74 | //printf("numThread=%d\n", numThread); 75 | break; 76 | case 'p': 77 | padding = (int) strtol(optarg, NULL, 10); 78 | //printf("padding=%d\n", padding); 79 | break; 80 | case 's': 81 | maxSideLen = (int) strtol(optarg, NULL, 10); 82 | //printf("maxSideLen=%d\n", maxSideLen); 83 | break; 84 | case 'b': 85 | boxScoreThresh = strtof(optarg, NULL); 86 | //printf("boxScoreThresh=%f\n", boxScoreThresh); 87 | break; 88 | case 'o': 89 | boxThresh = strtof(optarg, NULL); 90 | //printf("boxThresh=%f\n", boxThresh); 91 | break; 92 | case 'u': 93 | unClipRatio = strtof(optarg, NULL); 94 | //printf("unClipRatio=%f\n", unClipRatio); 95 | break; 96 | case 'a': 97 | flagDoAngle = (int) strtol(optarg, NULL, 10); 98 | if (flagDoAngle == 0) { 99 | doAngle = false; 100 | } else { 101 | doAngle = true; 102 | } 103 | //printf("doAngle=%d\n", doAngle); 104 | break; 105 | case 'A': 106 | flagMostAngle = (int) strtol(optarg, NULL, 10); 107 | if (flagMostAngle == 0) { 108 | mostAngle = false; 109 | } else { 110 | mostAngle = true; 111 | } 112 | //printf("mostAngle=%d\n", mostAngle); 113 | break; 114 | case 'v': 115 | printf("%s\n", VERSION); 116 | printGpuInfo(); 117 | return 0; 118 | case 'h': 119 | printHelp(stdout, argv[0]); 120 | return 0; 121 | case 'G': 122 | flagGpu = (int) strtol(optarg, NULL, 10); 123 | printGpuInfo(); 124 | break; 125 | case 'l': 126 | loopCount = (int) strtol(optarg, NULL, 10); 127 | //printf("loopCount=%d\n", loopCount); 128 | break; 129 | default: 130 | printf("other option %c :%s\n", opt, optarg); 131 | } 132 | } 133 | if (modelDetPath.empty()) { 134 | modelDetPath = modelsDir + "/" + "dbnet_op"; 135 | } 136 | if (modelClsPath.empty()) { 137 | modelClsPath = modelsDir + "/" + "angle_op"; 138 | } 139 | if (modelRecPath.empty()) { 140 | modelRecPath = modelsDir + "/" + "crnn_lite_op"; 141 | } 142 | if (keysPath.empty()) { 143 | keysPath = modelsDir + "/" + "keys.txt"; 144 | } 145 | bool hasTargetImgFile = isFileExists(imgPath); 146 | if (!hasTargetImgFile) { 147 | fprintf(stderr, "Target image not found: %s\n", imgPath.c_str()); 148 | return -1; 149 | } 150 | bool hasModelDetParam = isFileExists(modelDetPath + ".param"); 151 | if (!hasModelDetParam) { 152 | fprintf(stderr, "Model dbnet file not found: %s.param\n", modelDetPath.c_str()); 153 | return -1; 154 | } 155 | bool hasModelDetBin = isFileExists(modelDetPath + ".bin"); 156 | if (!hasModelDetBin) { 157 | fprintf(stderr, "Model dbnet file not found: %s.bin\n", modelDetPath.c_str()); 158 | return -1; 159 | } 160 | bool hasModelClsParam = isFileExists(modelClsPath + ".param"); 161 | if (!hasModelClsParam) { 162 | fprintf(stderr, "Model angle file not found: %s.param\n", modelClsPath.c_str()); 163 | return -1; 164 | } 165 | bool hasModelClsBin = isFileExists(modelClsPath + ".bin"); 166 | if (!hasModelClsBin) { 167 | fprintf(stderr, "Model angle file not found: %s.bin\n", modelClsPath.c_str()); 168 | return -1; 169 | } 170 | bool hasModelRecFile = isFileExists(modelRecPath + ".param"); 171 | if (!hasModelRecFile) { 172 | fprintf(stderr, "Model crnn file not found: %s.param\n", modelRecPath.c_str()); 173 | return -1; 174 | } 175 | bool hasModelRecBin = isFileExists(modelRecPath + ".bin"); 176 | if (!hasModelRecBin) { 177 | fprintf(stderr, "Model crnn file not found: %s.bin\n", modelRecPath.c_str()); 178 | return -1; 179 | } 180 | bool hasKeysFile = isFileExists(keysPath); 181 | if (!hasKeysFile) { 182 | fprintf(stderr, "keys file not found: %s\n", keysPath.c_str()); 183 | return -1; 184 | } 185 | OcrLite ocrLite; 186 | ocrLite.setNumThread(numThread); 187 | ocrLite.initLogger( 188 | false,//isOutputConsole 189 | false,//isOutputPartImg 190 | false);//isOutputResultImg 191 | 192 | //ocrLite.enableResultTxt(imgDir.c_str(), imgName.c_str()); 193 | ocrLite.setGpuIndex(flagGpu); 194 | printf("=====Input Params=====\n"); 195 | printf( 196 | "numThread(%d),padding(%d),maxSideLen(%d),boxScoreThresh(%f),boxThresh(%f),unClipRatio(%f),doAngle(%d),mostAngle(%d),GPU(%d)\n", 197 | numThread, padding, maxSideLen, boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle, 198 | flagGpu); 199 | bool initModelsRet = ocrLite.initModels(modelDetPath, modelClsPath, modelRecPath, keysPath); 200 | if (!initModelsRet) return -1; 201 | printf("=====warmup=====\n"); 202 | OcrResult result = ocrLite.detect(imgDir.c_str(), imgName.c_str(), padding, maxSideLen, 203 | boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle); 204 | printf("dbNetTime(%f) detectTime(%f)\n", result.dbNetTime, result.detectTime); 205 | double dbTime = 0.0f; 206 | double detectTime = 0.0f; 207 | for (int i = 0; i < loopCount; ++i) { 208 | printf("=====loop:%d=====\n", i + 1); 209 | OcrResult ocrResult = ocrLite.detect(imgDir.c_str(), imgName.c_str(), 210 | padding, maxSideLen, 211 | boxScoreThresh, boxThresh, 212 | unClipRatio, doAngle, mostAngle); 213 | printf("dbNetTime(%f) detectTime(%f)\n", ocrResult.dbNetTime, ocrResult.detectTime); 214 | dbTime += ocrResult.dbNetTime; 215 | detectTime += ocrResult.detectTime; 216 | } 217 | printf("=====result=====\n"); 218 | printf("average dbNetTime=%fms, average detectTime=%fms\n", dbTime / loopCount, 219 | detectTime / loopCount); 220 | return 0; 221 | } 222 | -------------------------------------------------------------------------------- /src/OcrLite.cpp: -------------------------------------------------------------------------------- 1 | #include "OcrLite.h" 2 | #include "OcrUtils.h" 3 | #include //windows&linux 4 | 5 | OcrLite::OcrLite() {} 6 | 7 | OcrLite::~OcrLite() { 8 | if (isOutputResultTxt) { 9 | fclose(resultTxt); 10 | } 11 | } 12 | 13 | void OcrLite::setNumThread(int numOfThread) { 14 | dbNet.setNumThread(numOfThread); 15 | angleNet.setNumThread(numOfThread); 16 | crnnNet.setNumThread(numOfThread); 17 | } 18 | 19 | void OcrLite::initLogger(bool isConsole, bool isPartImg, bool isResultImg) { 20 | isOutputConsole = isConsole; 21 | isOutputPartImg = isPartImg; 22 | isOutputResultImg = isResultImg; 23 | } 24 | 25 | void OcrLite::enableResultTxt(const char *path, const char *imgName) { 26 | isOutputResultTxt = true; 27 | std::string resultTxtPath = getResultTxtFilePath(path, imgName); 28 | printf("resultTxtPath(%s)\n", resultTxtPath.c_str()); 29 | resultTxt = fopen(resultTxtPath.c_str(), "w"); 30 | } 31 | 32 | void OcrLite::setGpuIndex(int gpuIndex) { 33 | dbNet.setGpuIndex(gpuIndex); 34 | angleNet.setGpuIndex(-1); 35 | crnnNet.setGpuIndex(-1); 36 | } 37 | 38 | bool OcrLite::initModels(const std::string &detPath, const std::string &clsPath, 39 | const std::string &recPath, const std::string &keysPath) { 40 | Logger("=====Init Models=====\n"); 41 | Logger("--- Init DbNet ---\n"); 42 | bool retDbNet = dbNet.initModel(detPath); 43 | 44 | Logger("--- Init AngleNet ---\n"); 45 | bool retAngleNet = angleNet.initModel(clsPath); 46 | 47 | Logger("--- Init CrnnNet ---\n"); 48 | bool retCrnnNet = crnnNet.initModel(recPath, keysPath); 49 | 50 | if (!retDbNet || !retAngleNet || !retCrnnNet) { 51 | Logger("Init Models Failed! %d %d %d\n", retDbNet, retAngleNet, retCrnnNet); 52 | return false; 53 | } 54 | return true; 55 | } 56 | 57 | void OcrLite::Logger(const char *format, ...) { 58 | if (!(isOutputConsole || isOutputResultTxt)) return; 59 | char *buffer = (char *) malloc(8192); 60 | va_list args; 61 | va_start(args, format); 62 | vsprintf(buffer, format, args); 63 | va_end(args); 64 | if (isOutputConsole) printf("%s", buffer); 65 | if (isOutputResultTxt) fprintf(resultTxt, "%s", buffer); 66 | free(buffer); 67 | } 68 | 69 | cv::Mat makePadding(cv::Mat &src, const int padding) { 70 | if (padding <= 0) return src; 71 | cv::Scalar paddingScalar = {255, 255, 255}; 72 | cv::Mat paddingSrc; 73 | cv::copyMakeBorder(src, paddingSrc, padding, padding, padding, padding, cv::BORDER_ISOLATED, paddingScalar); 74 | return paddingSrc; 75 | } 76 | 77 | OcrResult OcrLite::detect(const char *path, const char *imgName, 78 | const int padding, const int maxSideLen, 79 | float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle) { 80 | std::string imgFile = getSrcImgFilePath(path, imgName); 81 | 82 | cv::Mat bgrSrc = imread(imgFile, cv::IMREAD_COLOR);//default : BGR 83 | cv::Mat originSrc; 84 | cvtColor(bgrSrc, originSrc, cv::COLOR_BGR2RGB);// convert to RGB 85 | int originMaxSide = (std::max)(originSrc.cols, originSrc.rows); 86 | int resize; 87 | if (maxSideLen <= 0 || maxSideLen > originMaxSide) { 88 | resize = originMaxSide; 89 | } else { 90 | resize = maxSideLen; 91 | } 92 | resize += 2*padding; 93 | cv::Rect paddingRect(padding, padding, originSrc.cols, originSrc.rows); 94 | cv::Mat paddingSrc = makePadding(originSrc, padding); 95 | ScaleParam scale = getScaleParam(paddingSrc, resize); 96 | OcrResult result; 97 | result = detect(path, imgName, paddingSrc, paddingRect, scale, 98 | boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle); 99 | return result; 100 | } 101 | 102 | OcrResult OcrLite::detect(const cv::Mat& mat, int padding, int maxSideLen, float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle) 103 | { 104 | cv::Mat originSrc; 105 | cvtColor(mat, originSrc, cv::COLOR_BGR2RGB);// convert to RGB 106 | int originMaxSide = (std::max)(originSrc.cols, originSrc.rows); 107 | int resize; 108 | if (maxSideLen <= 0 || maxSideLen > originMaxSide) { 109 | resize = originMaxSide; 110 | } 111 | else { 112 | resize = maxSideLen; 113 | } 114 | resize += 2 * padding; 115 | cv::Rect paddingRect(padding, padding, originSrc.cols, originSrc.rows); 116 | cv::Mat paddingSrc = makePadding(originSrc, padding); 117 | ScaleParam scale = getScaleParam(paddingSrc, resize); 118 | OcrResult result; 119 | result = detect(NULL, NULL, paddingSrc, paddingRect, scale, 120 | boxScoreThresh, boxThresh, unClipRatio, doAngle, mostAngle); 121 | return result; 122 | } 123 | 124 | std::vector OcrLite::getPartImages(cv::Mat &src, std::vector &textBoxes, 125 | const char *path, const char *imgName) { 126 | std::vector partImages; 127 | for (int i = 0; i < textBoxes.size(); ++i) { 128 | cv::Mat partImg = getRotateCropImage(src, textBoxes[i].boxPoint); 129 | partImages.emplace_back(partImg); 130 | //OutPut DebugImg 131 | if (isOutputPartImg) { 132 | std::string debugImgFile = getDebugImgFilePath(path, imgName, i, "-part-"); 133 | saveImg(partImg, debugImgFile.c_str()); 134 | } 135 | } 136 | return partImages; 137 | } 138 | 139 | OcrResult OcrLite::detect(const char *path, const char *imgName, 140 | cv::Mat &src, cv::Rect &originRect, ScaleParam &scale, 141 | float boxScoreThresh, float boxThresh, float unClipRatio, bool doAngle, bool mostAngle) { 142 | 143 | cv::Mat textBoxPaddingImg = src.clone(); 144 | int thickness = getThickness(src); 145 | 146 | Logger("=====Start detect=====\n"); 147 | Logger("ScaleParam(sw:%d,sh:%d,dw:%d,dh:%d,%f,%f)\n", scale.srcWidth, scale.srcHeight, 148 | scale.dstWidth, scale.dstHeight, 149 | scale.ratioWidth, scale.ratioHeight); 150 | 151 | Logger("---------- step: dbNet getTextBoxes ----------\n"); 152 | double startTime = getCurrentTime(); 153 | std::vector textBoxes = dbNet.getTextBoxes(src, scale, boxScoreThresh, boxThresh, unClipRatio); 154 | double endDbNetTime = getCurrentTime(); 155 | double dbNetTime = endDbNetTime - startTime; 156 | Logger("dbNetTime(%fms)\n", dbNetTime); 157 | 158 | for (int i = 0; i < textBoxes.size(); ++i) { 159 | Logger("TextBox[%d](+padding)[score(%f),[x: %d, y: %d], [x: %d, y: %d], [x: %d, y: %d], [x: %d, y: %d]]\n", i, 160 | textBoxes[i].score, 161 | textBoxes[i].boxPoint[0].x, textBoxes[i].boxPoint[0].y, 162 | textBoxes[i].boxPoint[1].x, textBoxes[i].boxPoint[1].y, 163 | textBoxes[i].boxPoint[2].x, textBoxes[i].boxPoint[2].y, 164 | textBoxes[i].boxPoint[3].x, textBoxes[i].boxPoint[3].y); 165 | } 166 | 167 | Logger("---------- step: drawTextBoxes ----------\n"); 168 | drawTextBoxes(textBoxPaddingImg, textBoxes, thickness); 169 | 170 | //---------- getPartImages ---------- 171 | std::vector partImages = getPartImages(src, textBoxes, path, imgName); 172 | 173 | Logger("---------- step: angleNet getAngles ----------\n"); 174 | std::vector angles; 175 | angles = angleNet.getAngles(partImages, path, imgName, doAngle, mostAngle); 176 | 177 | //Log Angles 178 | for (int i = 0; i < angles.size(); ++i) { 179 | Logger("angle[%d][index(%d), score(%f), time(%fms)]\n", i, angles[i].index, angles[i].score, angles[i].time); 180 | } 181 | 182 | //Rotate partImgs 183 | for (int i = 0; i < partImages.size(); ++i) { 184 | if (angles[i].index == 0) { 185 | partImages.at(i) = matRotateClockWise180(partImages[i]); 186 | } 187 | } 188 | 189 | Logger("---------- step: crnnNet getTextLine ----------\n"); 190 | std::vector textLines = crnnNet.getTextLines(partImages, path, imgName); 191 | //Log TextLines 192 | for (int i = 0; i < textLines.size(); ++i) { 193 | Logger("textLine[%d](%s)\n", i, textLines[i].text.c_str()); 194 | std::ostringstream txtScores; 195 | for (int s = 0; s < textLines[i].charScores.size(); ++s) { 196 | if (s == 0) { 197 | txtScores << textLines[i].charScores[s]; 198 | } else { 199 | txtScores << " ," << textLines[i].charScores[s]; 200 | } 201 | } 202 | Logger("textScores[%d]{%s}\n", i, std::string(txtScores.str()).c_str()); 203 | Logger("crnnTime[%d](%fms)\n", i, textLines[i].time); 204 | } 205 | 206 | std::vector textBlocks; 207 | for (int i = 0; i < textLines.size(); ++i) { 208 | std::vector boxPoint = std::vector(4); 209 | int padding = originRect.x;//padding conversion 210 | boxPoint[0] = cv::Point(textBoxes[i].boxPoint[0].x - padding, textBoxes[i].boxPoint[0].y - padding); 211 | boxPoint[1] = cv::Point(textBoxes[i].boxPoint[1].x - padding, textBoxes[i].boxPoint[1].y - padding); 212 | boxPoint[2] = cv::Point(textBoxes[i].boxPoint[2].x - padding, textBoxes[i].boxPoint[2].y - padding); 213 | boxPoint[3] = cv::Point(textBoxes[i].boxPoint[3].x - padding, textBoxes[i].boxPoint[3].y - padding); 214 | TextBlock textBlock{boxPoint, textBoxes[i].score, angles[i].index, angles[i].score, 215 | angles[i].time, textLines[i].text, textLines[i].charScores, textLines[i].time, 216 | angles[i].time + textLines[i].time}; 217 | textBlocks.emplace_back(textBlock); 218 | } 219 | 220 | double endTime = getCurrentTime(); 221 | double fullTime = endTime - startTime; 222 | Logger("=====End detect=====\n"); 223 | Logger("FullDetectTime(%fms)\n", fullTime); 224 | 225 | //cropped to original size 226 | cv::Mat rgbBoxImg, textBoxImg; 227 | 228 | if (originRect.x > 0 && originRect.y > 0) { 229 | textBoxPaddingImg(originRect).copyTo(rgbBoxImg); 230 | } else { 231 | rgbBoxImg = textBoxPaddingImg; 232 | } 233 | cvtColor(rgbBoxImg, textBoxImg, cv::COLOR_RGB2BGR);//convert to BGR for Output Result Img 234 | 235 | //Save result.jpg 236 | if (isOutputResultImg) { 237 | std::string resultImgFile = getResultImgFilePath(path, imgName); 238 | imwrite(resultImgFile, textBoxImg); 239 | } 240 | 241 | std::string strRes; 242 | for (int i = 0; i < textBlocks.size(); ++i) { 243 | strRes.append(textBlocks[i].text); 244 | strRes.append("\n"); 245 | } 246 | 247 | return OcrResult{dbNetTime, textBlocks, textBoxImg, fullTime, strRes}; 248 | } 249 | -------------------------------------------------------------------------------- /src/OcrUtils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "OcrUtils.h" 4 | #include "clipper.hpp" 5 | #include "ncnn/net.h" 6 | 7 | double getCurrentTime() { 8 | return (static_cast(cv::getTickCount())) / cv::getTickFrequency() * 1000;//单位毫秒 9 | } 10 | 11 | //onnxruntime init windows 12 | std::wstring strToWstr(std::string str) { 13 | if (str.length() == 0) 14 | return L""; 15 | std::wstring wstr; 16 | wstr.assign(str.begin(), str.end()); 17 | return wstr; 18 | } 19 | 20 | ScaleParam getScaleParam(cv::Mat &src, const float scale) { 21 | int srcWidth = src.cols; 22 | int srcHeight = src.rows; 23 | int dstWidth = int((float) srcWidth * scale); 24 | int dstHeight = int((float) srcHeight * scale); 25 | if (dstWidth % 32 != 0) { 26 | dstWidth = (dstWidth / 32 - 1) * 32; 27 | dstWidth = (std::max)(dstWidth, 32); 28 | } 29 | if (dstHeight % 32 != 0) { 30 | dstHeight = (dstHeight / 32 - 1) * 32; 31 | dstHeight = (std::max)(dstHeight, 32); 32 | } 33 | float scaleWidth = (float) dstWidth / (float) srcWidth; 34 | float scaleHeight = (float) dstHeight / (float) srcHeight; 35 | return {srcWidth, srcHeight, dstWidth, dstHeight, scaleWidth, scaleHeight}; 36 | } 37 | 38 | ScaleParam getScaleParam(cv::Mat &src, const int targetSize) { 39 | int srcWidth, srcHeight, dstWidth, dstHeight; 40 | srcWidth = dstWidth = src.cols; 41 | srcHeight = dstHeight = src.rows; 42 | 43 | float ratio = 1.f; 44 | if (srcWidth > srcHeight) { 45 | ratio = float(targetSize) / float(srcWidth); 46 | } else { 47 | ratio = float(targetSize) / float(srcHeight); 48 | } 49 | dstWidth = int(float(srcWidth) * ratio); 50 | dstHeight = int(float(srcHeight) * ratio); 51 | if (dstWidth % 32 != 0) { 52 | dstWidth = (dstWidth / 32) * 32; 53 | dstWidth = (std::max)(dstWidth, 32); 54 | } 55 | if (dstHeight % 32 != 0) { 56 | dstHeight = (dstHeight / 32) * 32; 57 | dstHeight = (std::max)(dstHeight, 32); 58 | } 59 | float ratioWidth = (float) dstWidth / (float) srcWidth; 60 | float ratioHeight = (float) dstHeight / (float) srcHeight; 61 | return {srcWidth, srcHeight, dstWidth, dstHeight, ratioWidth, ratioHeight}; 62 | } 63 | 64 | std::vector getBox(const cv::RotatedRect &rect) { 65 | cv::Point2f vertices[4]; 66 | rect.points(vertices); 67 | //std::vector ret(4); 68 | std::vector ret2(vertices, vertices + sizeof(vertices) / sizeof(vertices[0])); 69 | //memcpy(vertices, &ret[0], ret.size() * sizeof(ret[0])); 70 | return ret2; 71 | } 72 | 73 | int getThickness(cv::Mat &boxImg) { 74 | int minSize = boxImg.cols > boxImg.rows ? boxImg.rows : boxImg.cols; 75 | int thickness = minSize / 1000 + 2; 76 | return thickness; 77 | } 78 | 79 | void drawTextBox(cv::Mat &boxImg, cv::RotatedRect &rect, int thickness) { 80 | cv::Point2f vertices[4]; 81 | rect.points(vertices); 82 | for (int i = 0; i < 4; i++) 83 | cv::line(boxImg, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 0, 255), thickness); 84 | //cv::polylines(srcmat, textpoint, true, cv::Scalar(0, 255, 0), 2); 85 | } 86 | 87 | void drawTextBox(cv::Mat &boxImg, const std::vector &box, int thickness) { 88 | auto color = cv::Scalar(255, 0, 0);// R(255) G(0) B(0) 89 | cv::line(boxImg, box[0], box[1], color, thickness); 90 | cv::line(boxImg, box[1], box[2], color, thickness); 91 | cv::line(boxImg, box[2], box[3], color, thickness); 92 | cv::line(boxImg, box[3], box[0], color, thickness); 93 | } 94 | 95 | void drawTextBoxes(cv::Mat &boxImg, std::vector &textBoxes, int thickness) { 96 | for (int i = 0; i < textBoxes.size(); ++i) { 97 | drawTextBox(boxImg, textBoxes[i].boxPoint, thickness); 98 | } 99 | } 100 | 101 | cv::Mat matRotateClockWise180(cv::Mat src) { 102 | flip(src, src, 0); 103 | flip(src, src, 1); 104 | return src; 105 | } 106 | 107 | cv::Mat matRotateClockWise90(cv::Mat src) { 108 | transpose(src, src); 109 | flip(src, src, 1); 110 | return src; 111 | } 112 | 113 | cv::Mat getRotateCropImage(const cv::Mat &src, std::vector box) { 114 | cv::Mat image; 115 | src.copyTo(image); 116 | std::vector points = box; 117 | 118 | int collectX[4] = {box[0].x, box[1].x, box[2].x, box[3].x}; 119 | int collectY[4] = {box[0].y, box[1].y, box[2].y, box[3].y}; 120 | int left = int(*std::min_element(collectX, collectX + 4)); 121 | int right = int(*std::max_element(collectX, collectX + 4)); 122 | int top = int(*std::min_element(collectY, collectY + 4)); 123 | int bottom = int(*std::max_element(collectY, collectY + 4)); 124 | 125 | cv::Mat imgCrop; 126 | image(cv::Rect(left, top, right - left, bottom - top)).copyTo(imgCrop); 127 | 128 | for (int i = 0; i < points.size(); i++) { 129 | points[i].x -= left; 130 | points[i].y -= top; 131 | } 132 | 133 | int imgCropWidth = int(sqrt(pow(points[0].x - points[1].x, 2) + 134 | pow(points[0].y - points[1].y, 2))); 135 | int imgCropHeight = int(sqrt(pow(points[0].x - points[3].x, 2) + 136 | pow(points[0].y - points[3].y, 2))); 137 | 138 | cv::Point2f ptsDst[4]; 139 | ptsDst[0] = cv::Point2f(0., 0.); 140 | ptsDst[1] = cv::Point2f(imgCropWidth, 0.); 141 | ptsDst[2] = cv::Point2f(imgCropWidth, imgCropHeight); 142 | ptsDst[3] = cv::Point2f(0.f, imgCropHeight); 143 | 144 | cv::Point2f ptsSrc[4]; 145 | ptsSrc[0] = cv::Point2f(points[0].x, points[0].y); 146 | ptsSrc[1] = cv::Point2f(points[1].x, points[1].y); 147 | ptsSrc[2] = cv::Point2f(points[2].x, points[2].y); 148 | ptsSrc[3] = cv::Point2f(points[3].x, points[3].y); 149 | 150 | cv::Mat M = cv::getPerspectiveTransform(ptsSrc, ptsDst); 151 | 152 | cv::Mat partImg; 153 | cv::warpPerspective(imgCrop, partImg, M, 154 | cv::Size(imgCropWidth, imgCropHeight), 155 | cv::BORDER_REPLICATE); 156 | 157 | if (float(partImg.rows) >= float(partImg.cols) * 1.5) { 158 | cv::Mat srcCopy = cv::Mat(partImg.rows, partImg.cols, partImg.depth()); 159 | cv::transpose(partImg, srcCopy); 160 | cv::flip(srcCopy, srcCopy, 0); 161 | return srcCopy; 162 | } else { 163 | return partImg; 164 | } 165 | } 166 | 167 | cv::Mat adjustTargetImg(cv::Mat &src, int dstWidth, int dstHeight) { 168 | cv::Mat srcResize; 169 | float scale = (float) dstHeight / (float) src.rows; 170 | int angleWidth = int((float) src.cols * scale); 171 | cv::resize(src, srcResize, cv::Size(angleWidth, dstHeight)); 172 | cv::Mat srcFit = cv::Mat(dstHeight, dstWidth, CV_8UC3, cv::Scalar(255, 255, 255)); 173 | if (angleWidth < dstWidth) { 174 | cv::Rect rect(0, 0, srcResize.cols, srcResize.rows); 175 | srcResize.copyTo(srcFit(rect)); 176 | } else { 177 | cv::Rect rect(0, 0, dstWidth, dstHeight); 178 | srcResize(rect).copyTo(srcFit); 179 | } 180 | return srcFit; 181 | } 182 | 183 | bool cvPointCompare(const cv::Point &a, const cv::Point &b) { 184 | return a.x < b.x; 185 | } 186 | 187 | std::vector getMinBoxes(const std::vector &inVec, float &minSideLen, float &allEdgeSize) { 188 | std::vector minBoxVec; 189 | cv::RotatedRect textRect = cv::minAreaRect(inVec); 190 | cv::Mat boxPoints2f; 191 | cv::boxPoints(textRect, boxPoints2f); 192 | 193 | float *p1 = (float *) boxPoints2f.data; 194 | std::vector tmpVec; 195 | for (int i = 0; i < 4; ++i, p1 += 2) { 196 | tmpVec.emplace_back(int(p1[0]), int(p1[1])); 197 | } 198 | 199 | std::sort(tmpVec.begin(), tmpVec.end(), cvPointCompare); 200 | 201 | minBoxVec.clear(); 202 | 203 | int index1, index2, index3, index4; 204 | if (tmpVec[1].y > tmpVec[0].y) { 205 | index1 = 0; 206 | index4 = 1; 207 | } else { 208 | index1 = 1; 209 | index4 = 0; 210 | } 211 | 212 | if (tmpVec[3].y > tmpVec[2].y) { 213 | index2 = 2; 214 | index3 = 3; 215 | } else { 216 | index2 = 3; 217 | index3 = 2; 218 | } 219 | 220 | minBoxVec.clear(); 221 | 222 | minBoxVec.push_back(tmpVec[index1]); 223 | minBoxVec.push_back(tmpVec[index2]); 224 | minBoxVec.push_back(tmpVec[index3]); 225 | minBoxVec.push_back(tmpVec[index4]); 226 | 227 | minSideLen = (std::min)(textRect.size.width, textRect.size.height); 228 | allEdgeSize = 2.f * (textRect.size.width + textRect.size.height); 229 | 230 | return minBoxVec; 231 | } 232 | 233 | float boxScoreFast(const cv::Mat &inMat, const std::vector &inBox) { 234 | std::vector box = inBox; 235 | int width = inMat.cols; 236 | int height = inMat.rows; 237 | int maxX = -1, minX = 1000000, maxY = -1, minY = 1000000; 238 | for (int i = 0; i < box.size(); ++i) { 239 | if (maxX < box[i].x) 240 | maxX = box[i].x; 241 | if (minX > box[i].x) 242 | minX = box[i].x; 243 | if (maxY < box[i].y) 244 | maxY = box[i].y; 245 | if (minY > box[i].y) 246 | minY = box[i].y; 247 | } 248 | maxX = (std::min)((std::max)(maxX, 0), width - 1); 249 | minX = (std::max)((std::min)(minX, width - 1), 0); 250 | maxY = (std::min)((std::max)(maxY, 0), height - 1); 251 | minY = (std::max)((std::min)(minY, height - 1), 0); 252 | 253 | for (int i = 0; i < box.size(); ++i) { 254 | box[i].x = box[i].x - minX; 255 | box[i].y = box[i].y - minY; 256 | } 257 | 258 | std::vector> maskBox; 259 | maskBox.push_back(box); 260 | cv::Mat maskMat(maxY - minY + 1, maxX - minX + 1, CV_8UC1, cv::Scalar(0, 0, 0)); 261 | cv::fillPoly(maskMat, maskBox, cv::Scalar(1, 1, 1), 1); 262 | 263 | // cv::Mat normat; 264 | // cv::normalize(maskMat, normat, 255, 0, cv::NORM_MINMAX); 265 | // 266 | // cv::Mat maskbinmat; 267 | // normat.convertTo(maskbinmat, CV_8UC1, 1); 268 | // imwrite("subbin.jpg", maskbinmat); 269 | 270 | //std::cout << inMat << std::endl; 271 | 272 | return cv::mean(inMat(cv::Rect(cv::Point(minX, minY), cv::Point(maxX + 1, maxY + 1))).clone(), 273 | maskMat).val[0]; 274 | } 275 | 276 | // use clipper 277 | std::vector unClip(const std::vector &inBox, float perimeter, float unClipRatio) { 278 | std::vector outBox; 279 | ClipperLib::Path poly; 280 | 281 | for (int i = 0; i < inBox.size(); ++i) { 282 | poly.push_back(ClipperLib::IntPoint(inBox[i].x, inBox[i].y)); 283 | } 284 | 285 | double distance = unClipRatio * ClipperLib::Area(poly) / (double) perimeter; 286 | 287 | ClipperLib::ClipperOffset clipperOffset; 288 | clipperOffset.AddPath(poly, ClipperLib::JoinType::jtRound, ClipperLib::EndType::etClosedPolygon); 289 | ClipperLib::Paths polys; 290 | polys.push_back(poly); 291 | clipperOffset.Execute(polys, distance); 292 | 293 | outBox.clear(); 294 | std::vector rsVec; 295 | for (int i = 0; i < polys.size(); ++i) { 296 | ClipperLib::Path tmpPoly = polys[i]; 297 | for (int j = 0; j < tmpPoly.size(); ++j) { 298 | outBox.emplace_back(tmpPoly[j].X, tmpPoly[j].Y); 299 | } 300 | } 301 | return outBox; 302 | } 303 | 304 | std::vector getAngleIndexes(std::vector &angles) { 305 | std::vector angleIndexes; 306 | angleIndexes.reserve(angles.size()); 307 | for (int i = 0; i < angles.size(); ++i) { 308 | angleIndexes.push_back(angles[i].index); 309 | } 310 | return angleIndexes; 311 | } 312 | 313 | void saveImg(cv::Mat &img, const char *imgPath) { 314 | cv::imwrite(imgPath, img); 315 | } 316 | 317 | std::string getSrcImgFilePath(const char *path, const char *imgName) { 318 | std::string filePath; 319 | filePath.append(path).append(imgName); 320 | return filePath; 321 | } 322 | 323 | std::string getResultTxtFilePath(const char *path, const char *imgName) { 324 | std::string filePath; 325 | filePath.append(path).append(imgName).append("-result.txt"); 326 | return filePath; 327 | } 328 | 329 | std::string getResultImgFilePath(const char *path, const char *imgName) { 330 | std::string filePath; 331 | filePath.append(path).append(imgName).append("-result.jpg"); 332 | return filePath; 333 | } 334 | 335 | std::string getDebugImgFilePath(const char *path, const char *imgName, int i, const char *tag) { 336 | std::string filePath; 337 | filePath.append(path).append(imgName).append(tag).append(std::to_string(i)).append(".jpg"); 338 | return filePath; 339 | } 340 | 341 | void printGpuInfo() { 342 | #ifdef __VULKAN__ 343 | auto gpuCount = ncnn::get_gpu_count(); 344 | if (gpuCount != 0) { 345 | printf("This device has %d GPUs\n", gpuCount); 346 | } else { 347 | printf("This device does not have a GPU\n"); 348 | } 349 | #endif 350 | } -------------------------------------------------------------------------------- /include/clipper.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * * 3 | * Author : Angus Johnson * 4 | * Version : 6.4.2 * 5 | * Date : 27 February 2017 * 6 | * Website : http://www.angusj.com * 7 | * Copyright : Angus Johnson 2010-2017 * 8 | * * 9 | * License: * 10 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 11 | * http://www.boost.org/LICENSE_1_0.txt * 12 | * * 13 | * Attributions: * 14 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 15 | * "A generic solution to polygon clipping" * 16 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 17 | * http://portal.acm.org/citation.cfm?id=129906 * 18 | * * 19 | * Computer graphics and geometric modeling: implementation and algorithms * 20 | * By Max K. Agoston * 21 | * Springer; 1 edition (January 4, 2005) * 22 | * http://books.google.com/books?q=vatti+clipping+agoston * 23 | * * 24 | * See also: * 25 | * "Polygon Offsetting by Computing Winding Numbers" * 26 | * Paper no. DETC2005-85513 pp. 565-575 * 27 | * ASME 2005 International Design Engineering Technical Conferences * 28 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 29 | * September 24-28, 2005 , Long Beach, California, USA * 30 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 31 | * * 32 | *******************************************************************************/ 33 | 34 | #ifndef clipper_hpp 35 | #define clipper_hpp 36 | 37 | #define CLIPPER_VERSION "6.4.2" 38 | 39 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 40 | //improve performance but coordinate values are limited to the range +/- 46340 41 | //#define use_int32 42 | 43 | //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. 44 | //#define use_xyz 45 | 46 | //use_lines: Enables line clipping. Adds a very minor cost to performance. 47 | #define use_lines 48 | 49 | //use_deprecated: Enables temporary support for the obsolete functions 50 | //#define use_deprecated 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | namespace ClipperLib { 63 | 64 | enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 65 | enum PolyType { ptSubject, ptClip }; 66 | //By far the most widely used winding rules for polygon filling are 67 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 68 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 69 | //see http://glprogramming.com/red/chapter11.html 70 | enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 71 | 72 | #ifdef use_int32 73 | typedef int cInt; 74 | static cInt const loRange = 0x7FFF; 75 | static cInt const hiRange = 0x7FFF; 76 | #else 77 | typedef signed long long cInt; 78 | static cInt const loRange = 0x3FFFFFFF; 79 | static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; 80 | typedef signed long long long64; //used by Int128 class 81 | typedef unsigned long long ulong64; 82 | 83 | #endif 84 | 85 | struct IntPoint { 86 | cInt X; 87 | cInt Y; 88 | #ifdef use_xyz 89 | cInt Z; 90 | IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 91 | #else 92 | IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 93 | #endif 94 | 95 | friend inline bool operator== (const IntPoint& a, const IntPoint& b) 96 | { 97 | return a.X == b.X && a.Y == b.Y; 98 | } 99 | friend inline bool operator!= (const IntPoint& a, const IntPoint& b) 100 | { 101 | return a.X != b.X || a.Y != b.Y; 102 | } 103 | }; 104 | //------------------------------------------------------------------------------ 105 | 106 | typedef std::vector< IntPoint > Path; 107 | typedef std::vector< Path > Paths; 108 | 109 | inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} 110 | inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} 111 | 112 | std::ostream& operator <<(std::ostream &s, const IntPoint &p); 113 | std::ostream& operator <<(std::ostream &s, const Path &p); 114 | std::ostream& operator <<(std::ostream &s, const Paths &p); 115 | 116 | struct DoublePoint 117 | { 118 | double X; 119 | double Y; 120 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 121 | DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} 122 | }; 123 | //------------------------------------------------------------------------------ 124 | 125 | #ifdef use_xyz 126 | typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); 127 | #endif 128 | 129 | enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; 130 | enum JoinType {jtSquare, jtRound, jtMiter}; 131 | enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; 132 | 133 | class PolyNode; 134 | typedef std::vector< PolyNode* > PolyNodes; 135 | 136 | class PolyNode 137 | { 138 | public: 139 | PolyNode(); 140 | virtual ~PolyNode(){}; 141 | Path Contour; 142 | PolyNodes Childs; 143 | PolyNode* Parent; 144 | PolyNode* GetNext() const; 145 | bool IsHole() const; 146 | bool IsOpen() const; 147 | int ChildCount() const; 148 | private: 149 | //PolyNode& operator =(PolyNode& other); 150 | unsigned Index; //node index in Parent.Childs 151 | bool m_IsOpen; 152 | JoinType m_jointype; 153 | EndType m_endtype; 154 | PolyNode* GetNextSiblingUp() const; 155 | void AddChild(PolyNode& child); 156 | friend class Clipper; //to access Index 157 | friend class ClipperOffset; 158 | }; 159 | 160 | class PolyTree: public PolyNode 161 | { 162 | public: 163 | ~PolyTree(){ Clear(); }; 164 | PolyNode* GetFirst() const; 165 | void Clear(); 166 | int Total() const; 167 | private: 168 | //PolyTree& operator =(PolyTree& other); 169 | PolyNodes AllNodes; 170 | friend class Clipper; //to access AllNodes 171 | }; 172 | 173 | bool Orientation(const Path &poly); 174 | double Area(const Path &poly); 175 | int PointInPolygon(const IntPoint &pt, const Path &path); 176 | 177 | void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 178 | void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 179 | void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); 180 | 181 | void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); 182 | void CleanPolygon(Path& poly, double distance = 1.415); 183 | void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); 184 | void CleanPolygons(Paths& polys, double distance = 1.415); 185 | 186 | void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); 187 | void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); 188 | void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); 189 | 190 | void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); 191 | void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); 192 | void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); 193 | 194 | void ReversePath(Path& p); 195 | void ReversePaths(Paths& p); 196 | 197 | struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; 198 | 199 | //enums that are used internally ... 200 | enum EdgeSide { esLeft = 1, esRight = 2}; 201 | 202 | //forward declarations (for stuff used internally) ... 203 | struct TEdge; 204 | struct IntersectNode; 205 | struct LocalMinimum; 206 | struct OutPt; 207 | struct OutRec; 208 | struct Join; 209 | 210 | typedef std::vector < OutRec* > PolyOutList; 211 | typedef std::vector < TEdge* > EdgeList; 212 | typedef std::vector < Join* > JoinList; 213 | typedef std::vector < IntersectNode* > IntersectList; 214 | 215 | //------------------------------------------------------------------------------ 216 | 217 | //ClipperBase is the ancestor to the Clipper class. It should not be 218 | //instantiated directly. This class simply abstracts the conversion of sets of 219 | //polygon coordinates into edge objects that are stored in a LocalMinima list. 220 | class ClipperBase 221 | { 222 | public: 223 | ClipperBase(); 224 | virtual ~ClipperBase(); 225 | virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); 226 | bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); 227 | virtual void Clear(); 228 | IntRect GetBounds(); 229 | bool PreserveCollinear() {return m_PreserveCollinear;}; 230 | void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; 231 | protected: 232 | void DisposeLocalMinimaList(); 233 | TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); 234 | virtual void Reset(); 235 | TEdge* ProcessBound(TEdge* E, bool IsClockwise); 236 | void InsertScanbeam(const cInt Y); 237 | bool PopScanbeam(cInt &Y); 238 | bool LocalMinimaPending(); 239 | bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); 240 | OutRec* CreateOutRec(); 241 | void DisposeAllOutRecs(); 242 | void DisposeOutRec(PolyOutList::size_type index); 243 | void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); 244 | void DeleteFromAEL(TEdge *e); 245 | void UpdateEdgeIntoAEL(TEdge *&e); 246 | 247 | typedef std::vector MinimaList; 248 | MinimaList::iterator m_CurrentLM; 249 | MinimaList m_MinimaList; 250 | 251 | bool m_UseFullRange; 252 | EdgeList m_edges; 253 | bool m_PreserveCollinear; 254 | bool m_HasOpenPaths; 255 | PolyOutList m_PolyOuts; 256 | TEdge *m_ActiveEdges; 257 | 258 | typedef std::priority_queue ScanbeamList; 259 | ScanbeamList m_Scanbeam; 260 | }; 261 | //------------------------------------------------------------------------------ 262 | 263 | class Clipper : public virtual ClipperBase 264 | { 265 | public: 266 | Clipper(int initOptions = 0); 267 | bool Execute(ClipType clipType, 268 | Paths &solution, 269 | PolyFillType fillType = pftEvenOdd); 270 | bool Execute(ClipType clipType, 271 | Paths &solution, 272 | PolyFillType subjFillType, 273 | PolyFillType clipFillType); 274 | bool Execute(ClipType clipType, 275 | PolyTree &polytree, 276 | PolyFillType fillType = pftEvenOdd); 277 | bool Execute(ClipType clipType, 278 | PolyTree &polytree, 279 | PolyFillType subjFillType, 280 | PolyFillType clipFillType); 281 | bool ReverseSolution() { return m_ReverseOutput; }; 282 | void ReverseSolution(bool value) {m_ReverseOutput = value;}; 283 | bool StrictlySimple() {return m_StrictSimple;}; 284 | void StrictlySimple(bool value) {m_StrictSimple = value;}; 285 | //set the callback function for z value filling on intersections (otherwise Z is 0) 286 | #ifdef use_xyz 287 | void ZFillFunction(ZFillCallback zFillFunc); 288 | #endif 289 | protected: 290 | virtual bool ExecuteInternal(); 291 | private: 292 | JoinList m_Joins; 293 | JoinList m_GhostJoins; 294 | IntersectList m_IntersectList; 295 | ClipType m_ClipType; 296 | typedef std::list MaximaList; 297 | MaximaList m_Maxima; 298 | TEdge *m_SortedEdges; 299 | bool m_ExecuteLocked; 300 | PolyFillType m_ClipFillType; 301 | PolyFillType m_SubjFillType; 302 | bool m_ReverseOutput; 303 | bool m_UsingPolyTree; 304 | bool m_StrictSimple; 305 | #ifdef use_xyz 306 | ZFillCallback m_ZFill; //custom callback 307 | #endif 308 | void SetWindingCount(TEdge& edge); 309 | bool IsEvenOddFillType(const TEdge& edge) const; 310 | bool IsEvenOddAltFillType(const TEdge& edge) const; 311 | void InsertLocalMinimaIntoAEL(const cInt botY); 312 | void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); 313 | void AddEdgeToSEL(TEdge *edge); 314 | bool PopEdgeFromSEL(TEdge *&edge); 315 | void CopyAELToSEL(); 316 | void DeleteFromSEL(TEdge *e); 317 | void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); 318 | bool IsContributing(const TEdge& edge) const; 319 | bool IsTopHorz(const cInt XPos); 320 | void DoMaxima(TEdge *e); 321 | void ProcessHorizontals(); 322 | void ProcessHorizontal(TEdge *horzEdge); 323 | void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 324 | OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 325 | OutRec* GetOutRec(int idx); 326 | void AppendPolygon(TEdge *e1, TEdge *e2); 327 | void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); 328 | OutPt* AddOutPt(TEdge *e, const IntPoint &pt); 329 | OutPt* GetLastOutPt(TEdge *e); 330 | bool ProcessIntersections(const cInt topY); 331 | void BuildIntersectList(const cInt topY); 332 | void ProcessIntersectList(); 333 | void ProcessEdgesAtTopOfScanbeam(const cInt topY); 334 | void BuildResult(Paths& polys); 335 | void BuildResult2(PolyTree& polytree); 336 | void SetHoleState(TEdge *e, OutRec *outrec); 337 | void DisposeIntersectNodes(); 338 | bool FixupIntersectionOrder(); 339 | void FixupOutPolygon(OutRec &outrec); 340 | void FixupOutPolyline(OutRec &outrec); 341 | bool IsHole(TEdge *e); 342 | bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); 343 | void FixHoleLinkage(OutRec &outrec); 344 | void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); 345 | void ClearJoins(); 346 | void ClearGhostJoins(); 347 | void AddGhostJoin(OutPt *op, const IntPoint offPt); 348 | bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); 349 | void JoinCommonEdges(); 350 | void DoSimplePolygons(); 351 | void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); 352 | void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); 353 | void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); 354 | #ifdef use_xyz 355 | void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); 356 | #endif 357 | }; 358 | //------------------------------------------------------------------------------ 359 | 360 | class ClipperOffset 361 | { 362 | public: 363 | ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); 364 | ~ClipperOffset(); 365 | void AddPath(const Path& path, JoinType joinType, EndType endType); 366 | void AddPaths(const Paths& paths, JoinType joinType, EndType endType); 367 | void Execute(Paths& solution, double delta); 368 | void Execute(PolyTree& solution, double delta); 369 | void Clear(); 370 | double MiterLimit; 371 | double ArcTolerance; 372 | private: 373 | Paths m_destPolys; 374 | Path m_srcPoly; 375 | Path m_destPoly; 376 | std::vector m_normals; 377 | double m_delta, m_sinA, m_sin, m_cos; 378 | double m_miterLim, m_StepsPerRad; 379 | IntPoint m_lowest; 380 | PolyNode m_polyNodes; 381 | 382 | void FixOrientations(); 383 | void DoOffset(double delta); 384 | void OffsetPoint(int j, int& k, JoinType jointype); 385 | void DoSquare(int j, int k); 386 | void DoMiter(int j, int k, double r); 387 | void DoRound(int j, int k); 388 | }; 389 | //------------------------------------------------------------------------------ 390 | 391 | class clipperException : public std::exception 392 | { 393 | public: 394 | clipperException(const char* description): m_descr(description) {} 395 | virtual ~clipperException() throw() {} 396 | virtual const char* what() const throw() {return m_descr.c_str();} 397 | private: 398 | std::string m_descr; 399 | }; 400 | //------------------------------------------------------------------------------ 401 | 402 | } //ClipperLib namespace 403 | 404 | #endif //clipper_hpp 405 | 406 | 407 | -------------------------------------------------------------------------------- /.github/workflows/builder.yml: -------------------------------------------------------------------------------- 1 | name: Builder 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | jobs: 10 | ubuntu1804: 11 | runs-on: ubuntu-18.04 12 | 13 | env: 14 | NCNN_VERSION: 20221128 15 | CPU_PKG_NAME: ncnn-20221128-ubuntu1804 16 | GPU_PKG_NAME: ncnn-20221128-ubuntu1804-vulkan 17 | CV_VERSION: 4.6.0 18 | CV_PKG_NAME: opencv-4.6.0-ubuntu1804 19 | 20 | steps: 21 | # 检出代码 22 | - uses: actions/checkout@v3 23 | 24 | # 安装vulkan 25 | - name: vulkansdk 26 | run: | 27 | wget https://sdk.lunarg.com/sdk/download/1.3.236.0/linux/vulkansdk-linux-x86_64-1.3.236.0.tar.gz?Human=true -O vulkansdk-linux-x86_64-1.3.236.0.tar.gz 28 | tar -xf vulkansdk-linux-x86_64-1.3.236.0.tar.gz 29 | rm -rf 1.3.236.0/source 1.3.236.0/samples 30 | find 1.3.236.0 -type f | grep -v -E 'vulkan|glslang' | xargs rm 31 | 32 | # 下载ncnn-static 33 | - name: download ncnn-static 34 | run: | 35 | cd ncnn-static 36 | wget https://github.com/benjaminwan/NcnnBuilder/releases/download/${{ env.NCNN_VERSION }}/${{ env.CPU_PKG_NAME }}.7z -O ${{ env.CPU_PKG_NAME }}.7z 37 | 7z x ${{ env.CPU_PKG_NAME }}.7z -aoa 38 | 39 | # 下载ncnn-vulkan-static 40 | - name: download ncnn-vulkan-static 41 | run: | 42 | cd ncnn-vulkan-static 43 | wget https://github.com/benjaminwan/NcnnBuilder/releases/download/${{ env.NCNN_VERSION }}/${{ env.GPU_PKG_NAME }}.7z -O ${{ env.GPU_PKG_NAME }}.7z 44 | 7z x ${{ env.GPU_PKG_NAME }}.7z -aoa 45 | 46 | # 下载opencv-static 47 | - name: download opencv-static 48 | run: | 49 | cd opencv-static 50 | wget https://github.com/RapidAI/OpenCVBuilder/releases/download/${{ env.CV_VERSION }}/${{ env.CV_PKG_NAME }}.7z -O ${{ env.CV_PKG_NAME }}.7z 51 | 7z x ${{ env.CV_PKG_NAME }}.7z -aoa 52 | 53 | # 编译 54 | - name: build 55 | run: | 56 | export VULKAN_SDK=`pwd`/1.3.236.0/x86_64 57 | export PATH=`pwd`/1.3.236.0/x86_64/bin:$PATH 58 | chmod a+x build-default.sh &&./build-default.sh 59 | 60 | # 使用7z压缩 61 | - name: 7zip 62 | run: | 63 | mkdir linux-bin 64 | cp run-benchmark.sh linux-bin/run-benchmark.sh 65 | cp run-test.sh linux-bin/run-test.sh 66 | cp -r images linux-bin/images 67 | mv Linux-BIN-CPU/install/bin linux-bin/Linux-BIN-CPU 68 | mv Linux-BIN-GPU/install/bin linux-bin/Linux-BIN-GPU 69 | 7z a linux-bin.7z linux-bin 70 | mkdir linux-jni 71 | mv Linux-JNI-CPU/install linux-jni/Linux-JNI-CPU 72 | mv Linux-JNI-GPU/install linux-jni/Linux-JNI-GPU 73 | 7z a linux-jni.7z linux-jni 74 | mkdir linux-clib 75 | mv Linux-CLIB-CPU/install linux-clib/Linux-CLIB-CPU 76 | mv Linux-CLIB-GPU/install linux-clib/Linux-CLIB-GPU 77 | 7z a linux-clib.7z linux-clib 78 | 79 | # 上传artifact 80 | - name: upload 81 | uses: actions/upload-artifact@v3 82 | with: 83 | name: linux-bin 84 | path: linux-bin.7z 85 | 86 | - name: upload 87 | uses: actions/upload-artifact@v3 88 | with: 89 | name: linux-jni 90 | path: linux-jni.7z 91 | 92 | - name: upload 93 | uses: actions/upload-artifact@v3 94 | with: 95 | name: linux-clib 96 | path: linux-clib.7z 97 | 98 | macos1015: 99 | runs-on: macos-latest 100 | 101 | env: 102 | NCNN_VERSION: 20221128 103 | CPU_PKG_NAME: ncnn-20221128-macos1015 104 | GPU_PKG_NAME: ncnn-20221128-macos1015-vulkan 105 | CV_VERSION: 4.6.0 106 | CV_PKG_NAME: opencv-4.6.0-macos1015 107 | 108 | steps: 109 | # 检出代码 110 | - uses: actions/checkout@v3 111 | 112 | # 安装openmp 113 | - name: install openmp 114 | run: | 115 | brew install libomp 116 | 117 | # 安装vulkan 118 | - name: vulkansdk 119 | run: | 120 | wget https://sdk.lunarg.com/sdk/download/1.3.236.0/mac/vulkansdk-macos-1.3.236.0.dmg?Human=true -O vulkansdk-macos-1.3.236.0.dmg 121 | hdiutil attach vulkansdk-macos-1.3.236.0.dmg 122 | sudo /Volumes/vulkansdk-macos-1.3.236.0/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $GITHUB_WORKSPACE/vulkansdk-macos-1.3.236.0 --accept-licenses --default-answer --confirm-command install 123 | hdiutil detach /Volumes/vulkansdk-macos-1.3.236.0 124 | 125 | # 下载ncnn-static 126 | - name: download ncnn-static 127 | run: | 128 | cd ncnn-static 129 | wget https://github.com/benjaminwan/NcnnBuilder/releases/download/${{ env.NCNN_VERSION }}/${{ env.CPU_PKG_NAME }}.7z -O ${{ env.CPU_PKG_NAME }}.7z 130 | 7z x ${{ env.CPU_PKG_NAME }}.7z -aoa 131 | 132 | # 下载ncnn-vulkan-static 133 | - name: download ncnn-vulkan-static 134 | run: | 135 | cd ncnn-vulkan-static 136 | wget https://github.com/benjaminwan/NcnnBuilder/releases/download/${{ env.NCNN_VERSION }}/${{ env.GPU_PKG_NAME }}.7z -O ${{ env.GPU_PKG_NAME }}.7z 137 | 7z x ${{ env.GPU_PKG_NAME }}.7z -aoa 138 | 139 | # 下载opencv-static 140 | - name: download opencv-static 141 | run: | 142 | cd opencv-static 143 | wget https://github.com/RapidAI/OpenCVBuilder/releases/download/${{ env.CV_VERSION }}/${{ env.CV_PKG_NAME }}.7z -O ${{ env.CV_PKG_NAME }}.7z 144 | 7z x ${{ env.CV_PKG_NAME }}.7z -aoa 145 | 146 | # 编译 147 | - name: build 148 | run: | 149 | export VULKAN_SDK=$GITHUB_WORKSPACE/vulkansdk-macos-1.3.236.0/macOS 150 | chmod a+x build-default.sh &&./build-default.sh 151 | 152 | # 使用7z压缩 153 | - name: 7zip 154 | run: | 155 | mkdir macos-bin 156 | cp run-benchmark.sh macos-bin/run-benchmark.sh 157 | cp run-test.sh macos-bin/run-test.sh 158 | cp -r images macos-bin/images 159 | mv Darwin-BIN-CPU/install/bin macos-bin/Darwin-BIN-CPU 160 | mv Darwin-BIN-GPU/install/bin macos-bin/Darwin-BIN-GPU 161 | 7z a macos-bin.7z macos-bin 162 | mkdir macos-jni 163 | mv Darwin-JNI-CPU/install macos-jni/Darwin-JNI-CPU 164 | mv Darwin-JNI-GPU/install macos-jni/Darwin-JNI-GPU 165 | 7z a macos-jni.7z macos-jni 166 | mkdir macos-clib 167 | mv Darwin-CLIB-CPU/install macos-clib/Darwin-CLIB-CPU 168 | mv Darwin-CLIB-GPU/install macos-clib/Darwin-CLIB-GPU 169 | 7z a macos-clib.7z macos-clib 170 | 171 | # 上传artifact 172 | - name: upload 173 | uses: actions/upload-artifact@v3 174 | with: 175 | name: macos-bin 176 | path: macos-bin.7z 177 | 178 | - name: upload 179 | uses: actions/upload-artifact@v3 180 | with: 181 | name: macos-jni 182 | path: macos-jni.7z 183 | 184 | - name: upload 185 | uses: actions/upload-artifact@v3 186 | with: 187 | name: macos-clib 188 | path: macos-clib.7z 189 | 190 | windows-vs2019: 191 | runs-on: windows-2019 192 | 193 | env: 194 | NCNN_VERSION: 20221128 195 | CPU_PKG_NAME: ncnn-20221128-windows-vs2019-mt 196 | GPU_PKG_NAME: ncnn-20221128-windows-vs2019-vulkan-mt 197 | CV_VERSION: 4.6.0 198 | CV_PKG_NAME: opencv-4.6.0-windows-vs2019-mt 199 | 200 | steps: 201 | # 检出代码 202 | - uses: actions/checkout@v3 203 | 204 | # 安装vulkan 205 | - name: vulkansdk 206 | run: | 207 | Invoke-WebRequest -Uri https://sdk.lunarg.com/sdk/download/1.3.236.0/windows/VulkanSDK-1.3.236.0-Installer.exe?Human=true -OutFile VulkanSDK.exe 208 | .\VulkanSDK.exe --accept-licenses --default-answer --confirm-command install "com.lunarg.vulkan.debug32" "com.lunarg.vulkan.sdl2" "com.lunarg.vulkan.32bit" "com.lunarg.vulkan" "com.lunarg.vulkan.glm" "com.lunarg.vulkan.vma" "com.lunarg.vulkan.core" "com.lunarg.vulkan.32bit" "com.lunarg.vulkan.volk" "com.lunarg.vulkan.debug" 209 | 210 | # 下载ncnn-static 211 | - name: download ncnn-static 212 | run: | 213 | cd ncnn-static 214 | Invoke-WebRequest -Uri https://github.com/benjaminwan/NcnnBuilder/releases/download/${{ env.NCNN_VERSION }}/${{ env.CPU_PKG_NAME }}.7z -OutFile ${{ env.CPU_PKG_NAME }}.7z 215 | 7z x ${{ env.CPU_PKG_NAME }}.7z -aoa 216 | 217 | # 下载ncnn-vulkan-static 218 | - name: download ncnn-vulkan-static 219 | run: | 220 | cd ncnn-vulkan-static 221 | Invoke-WebRequest -Uri https://github.com/benjaminwan/NcnnBuilder/releases/download/${{ env.NCNN_VERSION }}/${{ env.GPU_PKG_NAME }}.7z -OutFile ${{ env.GPU_PKG_NAME }}.7z 222 | 7z x ${{ env.GPU_PKG_NAME }}.7z -aoa 223 | 224 | # 下载opencv-static 225 | - name: download opencv-static 226 | run: | 227 | cd opencv-static 228 | Invoke-WebRequest -Uri https://github.com/RapidAI/OpenCVBuilder/releases/download/${{ env.CV_VERSION }}/${{ env.CV_PKG_NAME }}.7z -OutFile ${{ env.CV_PKG_NAME }}.7z 229 | 7z x ${{ env.CV_PKG_NAME }}.7z -aoa 230 | 231 | # 编译 232 | - name: build 233 | run: | 234 | $env:VULKAN_SDK="C:/VulkanSDK/1.3.236.0" 235 | $env:Path+=";C:/VulkanSDK/1.3.236.0/Bin" 236 | ./build-default.bat 237 | 238 | # 使用7z压缩 239 | - name: 7zip 240 | run: | 241 | mkdir windows-bin 242 | cp run-benchmark.bat windows-bin/run-benchmark.bat 243 | cp run-test.bat windows-bin/run-test.bat 244 | cp -r images windows-bin/images 245 | mv win-BIN-CPU-x64/install/bin windows-bin/win-BIN-CPU-x64 246 | mv win-BIN-CPU-Win32/install/bin windows-bin/win-BIN-CPU-Win32 247 | mv win-BIN-GPU-x64/install/bin windows-bin/win-BIN-GPU-x64 248 | mv win-BIN-GPU-Win32/install/bin windows-bin/win-BIN-GPU-Win32 249 | 7z a windows-bin.7z windows-bin 250 | mkdir windows-jni 251 | mv win-JNI-CPU-x64/install windows-jni/win-JNI-CPU-x64 252 | mv win-JNI-CPU-Win32/install windows-jni/win-JNI-CPU-Win32 253 | mv win-JNI-GPU-x64/install windows-jni/win-JNI-GPU-x64 254 | mv win-JNI-GPU-Win32/install windows-jni/win-JNI-GPU-Win32 255 | 7z a windows-jni.7z windows-jni 256 | mkdir windows-clib 257 | mv win-CLIB-CPU-x64/install windows-clib/win-CLIB-CPU-x64 258 | mv win-CLIB-CPU-Win32/install windows-clib/win-CLIB-CPU-Win32 259 | mv win-CLIB-GPU-x64/install windows-clib/win-CLIB-GPU-x64 260 | mv win-CLIB-GPU-Win32/install windows-clib/win-CLIB-GPU-Win32 261 | 7z a windows-clib.7z windows-clib 262 | 263 | # 上传artifact 264 | - name: upload 265 | uses: actions/upload-artifact@v3 266 | with: 267 | name: windows-bin 268 | path: windows-bin.7z 269 | 270 | - name: upload 271 | uses: actions/upload-artifact@v3 272 | with: 273 | name: windows-jni 274 | path: windows-jni.7z 275 | 276 | - name: upload 277 | uses: actions/upload-artifact@v3 278 | with: 279 | name: windows-clib 280 | path: windows-clib.7z 281 | 282 | release: 283 | needs: [ ubuntu1804, macos1015, windows-vs2019 ] 284 | 285 | runs-on: ubuntu-latest 286 | 287 | steps: 288 | # 检出代码 289 | - uses: actions/checkout@v3 290 | 291 | # 获取所有的git log和tag 292 | - name: Unshallow 293 | run: git fetch --prune --unshallow 294 | 295 | # 获取git log 从 previousTag 到 lastTag 296 | - name: Get git log 297 | id: git-log 298 | run: | 299 | previousTag=$(git describe --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1`) 300 | lastTag=$(git describe --abbrev=0 --tags) 301 | echo "previousTag:$previousTag ~ lastTag:$lastTag" 302 | log=$(git log $previousTag..$lastTag --pretty=format:'- %cd %an: %s\n' --date=format:'%Y-%m-%d %H:%M:%S') 303 | echo "$log" 304 | echo "log_state="$log"" >> $GITHUB_ENV 305 | 306 | # 创建Changelog文件 triggered by git tag push 307 | - name: Generate Changelog 308 | if: startsWith(github.ref, 'refs/tags/') 309 | run: | 310 | echo -e '${{ env.log_state }}' > Release.txt 311 | 312 | # Cat Changelog triggered by git tag push 313 | - name: Cat Changelog 314 | if: startsWith(github.ref, 'refs/tags/') 315 | run: | 316 | cat Release.txt 317 | 318 | # 下载artifact 319 | - name: download 320 | uses: actions/download-artifact@v3 321 | with: 322 | path: artifacts 323 | 324 | # 查看artifact 325 | - name: list artifact 326 | run: | 327 | tree artifacts 328 | 329 | # 创建release 330 | - name: create-release 331 | id: create-release 332 | uses: actions/create-release@v1 333 | env: 334 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 335 | with: 336 | tag_name: ${{ github.ref }} 337 | release_name: ${{ github.ref }} 338 | body_path: Release.txt 339 | draft: false 340 | prerelease: false 341 | 342 | - name: upload-linux-bin 343 | uses: actions/upload-release-asset@v1 344 | env: 345 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 346 | PKG_NAME: linux-bin 347 | with: 348 | upload_url: ${{ steps.create-release.outputs.upload_url }} 349 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 350 | asset_name: ${{ env.PKG_NAME }}.7z 351 | asset_content_type: application/x-7z-compressed 352 | 353 | - name: upload-linux-jni 354 | uses: actions/upload-release-asset@v1 355 | env: 356 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 357 | PKG_NAME: linux-jni 358 | with: 359 | upload_url: ${{ steps.create-release.outputs.upload_url }} 360 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 361 | asset_name: ${{ env.PKG_NAME }}.7z 362 | asset_content_type: application/x-7z-compressed 363 | 364 | - name: upload-linux-clib 365 | uses: actions/upload-release-asset@v1 366 | env: 367 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 368 | PKG_NAME: linux-clib 369 | with: 370 | upload_url: ${{ steps.create-release.outputs.upload_url }} 371 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 372 | asset_name: ${{ env.PKG_NAME }}.7z 373 | asset_content_type: application/x-7z-compressed 374 | 375 | - name: upload-macos-bin 376 | uses: actions/upload-release-asset@v1 377 | env: 378 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 379 | PKG_NAME: macos-bin 380 | with: 381 | upload_url: ${{ steps.create-release.outputs.upload_url }} 382 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 383 | asset_name: ${{ env.PKG_NAME }}.7z 384 | asset_content_type: application/x-7z-compressed 385 | 386 | - name: upload-macos-jni 387 | uses: actions/upload-release-asset@v1 388 | env: 389 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 390 | PKG_NAME: macos-jni 391 | with: 392 | upload_url: ${{ steps.create-release.outputs.upload_url }} 393 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 394 | asset_name: ${{ env.PKG_NAME }}.7z 395 | asset_content_type: application/x-7z-compressed 396 | 397 | - name: upload-macos-clib 398 | uses: actions/upload-release-asset@v1 399 | env: 400 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 401 | PKG_NAME: macos-clib 402 | with: 403 | upload_url: ${{ steps.create-release.outputs.upload_url }} 404 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 405 | asset_name: ${{ env.PKG_NAME }}.7z 406 | asset_content_type: application/x-7z-compressed 407 | 408 | - name: upload-windows-bin 409 | uses: actions/upload-release-asset@v1 410 | env: 411 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 412 | PKG_NAME: windows-bin 413 | with: 414 | upload_url: ${{ steps.create-release.outputs.upload_url }} 415 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 416 | asset_name: ${{ env.PKG_NAME }}.7z 417 | asset_content_type: application/x-7z-compressed 418 | 419 | - name: upload-windows-jni 420 | uses: actions/upload-release-asset@v1 421 | env: 422 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 423 | PKG_NAME: windows-jni 424 | with: 425 | upload_url: ${{ steps.create-release.outputs.upload_url }} 426 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 427 | asset_name: ${{ env.PKG_NAME }}.7z 428 | asset_content_type: application/x-7z-compressed 429 | 430 | - name: upload-windows-clib 431 | uses: actions/upload-release-asset@v1 432 | env: 433 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 434 | PKG_NAME: windows-clib 435 | with: 436 | upload_url: ${{ steps.create-release.outputs.upload_url }} 437 | asset_path: artifacts/${{ env.PKG_NAME }}/${{ env.PKG_NAME }}.7z 438 | asset_name: ${{ env.PKG_NAME }}.7z 439 | asset_content_type: application/x-7z-compressed 440 | 441 | # 获取指定时区的时间 442 | # https://github.com/marketplace/actions/get-time-action 443 | - name: Get Time 444 | id: time 445 | uses: JantHsueh/get-time-action@master 446 | with: 447 | timeZone: 8 448 | 449 | # 向钉钉发送消息 450 | # https://github.com/marketplace/actions/web-request-action 451 | - name: dingtalk 452 | uses: satak/webrequest-action@master 453 | with: 454 | url: ${{ secrets.DINGTALK_WEBHOOK }} 455 | method: POST 456 | payload: '{"msgtype": "text", "text": {"content": "版本更新: ${{ github.repository }}-版本号: ${{ github.ref }} \n 编译时间: ${{ steps.time.outputs.time }} \n 距上个正式版的更新记录: \n${{ env.log_state }}"}}' 457 | headers: '{"Content-Type": "application/json"}' -------------------------------------------------------------------------------- /valgrind-memcheck.txt: -------------------------------------------------------------------------------- 1 | ==4751== Memcheck, a memory error detector 2 | ==4751== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. 3 | ==4751== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info 4 | ==4751== Command: ./OcrLiteNcnn --models ../models --image ../../test_imgs/1.jpg --numThread 1 --padding 50 --imgResize 0 --boxScoreThresh 0.6 --boxThresh 0.3 --minArea 3 --unClipRatio 2.0 --doAngle 1 --mostAngle 1 5 | ==4751== Parent PID: 4750 6 | ==4751== 7 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 8 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 9 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 10 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 11 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 12 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 13 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 14 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 15 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 16 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 17 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 18 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 19 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 20 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 21 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 22 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 23 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 24 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 25 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 26 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 27 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 28 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 29 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 30 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 31 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 32 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 33 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 34 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 35 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 36 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 37 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 38 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 39 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 40 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 41 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 42 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 43 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 44 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 45 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 46 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 47 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 48 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 49 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 50 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 51 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 52 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 53 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 54 | --4751-- Warning: DWARF2 CFI reader: unhandled DW_OP_ opcode 0x13 55 | ==4751== 56 | ==4751== HEAP SUMMARY: 57 | ==4751== in use at exit: 93,420 bytes in 137 blocks 58 | ==4751== total heap usage: 17,147 allocs, 17,010 frees, 903,553,421 bytes allocated 59 | ==4751== 60 | ==4751== 80 bytes in 1 blocks are possibly lost in loss record 57 of 105 61 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 62 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 63 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 64 | ==4751== by 0x508CA5F: cv::BmpDecoder::BmpDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 65 | ==4751== by 0x508105A: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 66 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 67 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 68 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 69 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 70 | ==4751== by 0x4D41AC: main (main.cpp:141) 71 | ==4751== 72 | ==4751== 80 bytes in 1 blocks are possibly lost in loss record 58 of 105 73 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 74 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 75 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 76 | ==4751== by 0x5091230: cv::JpegDecoder::JpegDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 77 | ==4751== by 0x5081301: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 78 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 79 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 80 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 81 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 82 | ==4751== by 0x4D41AC: main (main.cpp:141) 83 | ==4751== 84 | ==4751== 84 bytes in 1 blocks are possibly lost in loss record 59 of 105 85 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 86 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 87 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 88 | ==4751== by 0x508FECB: cv::HdrDecoder::HdrDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 89 | ==4751== by 0x50811AF: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 90 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 91 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 92 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 93 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 94 | ==4751== by 0x4D41AC: main (main.cpp:141) 95 | ==4751== 96 | ==4751== 84 bytes in 1 blocks are possibly lost in loss record 60 of 105 97 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 98 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 99 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 100 | ==4751== by 0x509D769: cv::SunRasterDecoder::SunRasterDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 101 | ==4751== by 0x50815A5: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 102 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 103 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 104 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 105 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 106 | ==4751== by 0x4D41AC: main (main.cpp:141) 107 | ==4751== 108 | ==4751== 84 bytes in 1 blocks are possibly lost in loss record 61 of 105 109 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 110 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 111 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 112 | ==4751== by 0x508D1E5: cv::ExrDecoder::ExrDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 113 | ==4751== by 0x508207E: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 114 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 115 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 116 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 117 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 118 | ==4751== by 0x4D41AC: main (main.cpp:141) 119 | ==4751== 120 | ==4751== 88 bytes in 1 blocks are possibly lost in loss record 62 of 105 121 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 122 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 123 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 124 | ==4751== by 0x508FEEF: cv::HdrDecoder::HdrDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 125 | ==4751== by 0x50811AF: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 126 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 127 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 128 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 129 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 130 | ==4751== by 0x4D41AC: main (main.cpp:141) 131 | ==4751== 132 | ==4751== 88 bytes in 1 blocks are possibly lost in loss record 63 of 105 133 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 134 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 135 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 136 | ==4751== by 0x5098C30: cv::PngDecoder::PngDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 137 | ==4751== by 0x5081D9E: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 138 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 139 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 140 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 141 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 142 | ==4751== by 0x4D41AC: main (main.cpp:141) 143 | ==4751== 144 | ==4751== 92 bytes in 1 blocks are possibly lost in loss record 64 of 105 145 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 146 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 147 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 148 | ==4751== by 0x509233A: cv::Jpeg2KDecoder::Jpeg2KDecoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 149 | ==4751== by 0x5081F0E: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 150 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 151 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 152 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 153 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 154 | ==4751== by 0x4D41AC: main (main.cpp:141) 155 | ==4751== 156 | ==4751== 96 bytes in 1 blocks are possibly lost in loss record 65 of 105 157 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 158 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 159 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 160 | ==4751== by 0x50ADFB0: cv::WebPEncoder::WebPEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 161 | ==4751== by 0x50814FC: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 162 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 163 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 164 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 165 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 166 | ==4751== by 0x4D41AC: main (main.cpp:141) 167 | ==4751== 168 | ==4751== 100 bytes in 1 blocks are possibly lost in loss record 66 of 105 169 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 170 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 171 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 172 | ==4751== by 0x5093DE0: cv::Jpeg2KEncoder::Jpeg2KEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 173 | ==4751== by 0x5081FC6: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 174 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 175 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 176 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 177 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 178 | ==4751== by 0x4D41AC: main (main.cpp:141) 179 | ==4751== 180 | ==4751== 104 bytes in 1 blocks are possibly lost in loss record 67 of 105 181 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 182 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 183 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 184 | ==4751== by 0x5090000: cv::HdrEncoder::HdrEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 185 | ==4751== by 0x5081258: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 186 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 187 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 188 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 189 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 190 | ==4751== by 0x4D41AC: main (main.cpp:141) 191 | ==4751== 192 | ==4751== 104 bytes in 1 blocks are possibly lost in loss record 68 of 105 193 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 194 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 195 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 196 | ==4751== by 0x509E110: cv::TiffEncoder::TiffEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 197 | ==4751== by 0x5081CE6: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 198 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 199 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 200 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 201 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 202 | ==4751== by 0x4D41AC: main (main.cpp:141) 203 | ==4751== 204 | ==4751== 104 bytes in 1 blocks are possibly lost in loss record 69 of 105 205 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 206 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 207 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 208 | ==4751== by 0x508D930: cv::ExrEncoder::ExrEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 209 | ==4751== by 0x5082136: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 210 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 211 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 212 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 213 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 214 | ==4751== by 0x4D41AC: main (main.cpp:141) 215 | ==4751== 216 | ==4751== 108 bytes in 1 blocks are possibly lost in loss record 70 of 105 217 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 218 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 219 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 220 | ==4751== by 0x508CAF0: cv::BmpEncoder::BmpEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 221 | ==4751== by 0x5081102: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 222 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 223 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 224 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 225 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 226 | ==4751== by 0x4D41AC: main (main.cpp:141) 227 | ==4751== 228 | ==4751== 108 bytes in 1 blocks are possibly lost in loss record 71 of 105 229 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 230 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 231 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 232 | ==4751== by 0x50912B0: cv::JpegEncoder::JpegEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 233 | ==4751== by 0x50813AA: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 234 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 235 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 236 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 237 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 238 | ==4751== by 0x4D41AC: main (main.cpp:141) 239 | ==4751== 240 | ==4751== 108 bytes in 1 blocks are possibly lost in loss record 72 of 105 241 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 242 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 243 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 244 | ==4751== by 0x509D7F0: cv::SunRasterEncoder::SunRasterEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 245 | ==4751== by 0x508165D: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 246 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 247 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 248 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 249 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 250 | ==4751== by 0x4D41AC: main (main.cpp:141) 251 | ==4751== 252 | ==4751== 112 bytes in 1 blocks are possibly lost in loss record 73 of 105 253 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 254 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 255 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 256 | ==4751== by 0x5097640: cv::PAMEncoder::PAMEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 257 | ==4751== by 0x5081B76: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 258 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 259 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 260 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 261 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 262 | ==4751== by 0x4D41AC: main (main.cpp:141) 263 | ==4751== 264 | ==4751== 116 bytes in 1 blocks are possibly lost in loss record 74 of 105 265 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 266 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 267 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 268 | ==4751== by 0x509BB98: cv::PxMEncoder::PxMEncoder(cv::PxMMode) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 269 | ==4751== by 0x50817CF: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 270 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 271 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 272 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 273 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 274 | ==4751== by 0x4D41AC: main (main.cpp:141) 275 | ==4751== 276 | ==4751== 116 bytes in 1 blocks are possibly lost in loss record 75 of 105 277 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 278 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 279 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 280 | ==4751== by 0x509BB08: cv::PxMEncoder::PxMEncoder(cv::PxMMode) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 281 | ==4751== by 0x5081949: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 282 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 283 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 284 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 285 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 286 | ==4751== by 0x4D41AC: main (main.cpp:141) 287 | ==4751== 288 | ==4751== 116 bytes in 1 blocks are possibly lost in loss record 76 of 105 289 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 290 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 291 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 292 | ==4751== by 0x509BA88: cv::PxMEncoder::PxMEncoder(cv::PxMMode) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 293 | ==4751== by 0x5081A06: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 294 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 295 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 296 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 297 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 298 | ==4751== by 0x4D41AC: main (main.cpp:141) 299 | ==4751== 300 | ==4751== 116 bytes in 1 blocks are possibly lost in loss record 77 of 105 301 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 302 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 303 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 304 | ==4751== by 0x5098CE0: cv::PngEncoder::PngEncoder() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 305 | ==4751== by 0x5081E56: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 306 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 307 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 308 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 309 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 310 | ==4751== by 0x4D41AC: main (main.cpp:141) 311 | ==4751== 312 | ==4751== 120 bytes in 1 blocks are possibly lost in loss record 78 of 105 313 | ==4751== at 0x483577F: malloc (vg_replace_malloc.c:299) 314 | ==4751== by 0x49AD880: cv::fastMalloc(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 315 | ==4751== by 0x4BC340C: cv::String::allocate(unsigned long) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 316 | ==4751== by 0x509BBF8: cv::PxMEncoder::PxMEncoder(cv::PxMMode) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 317 | ==4751== by 0x508188C: cv::ImageCodecInitializer::ImageCodecInitializer() (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 318 | ==4751== by 0x5084218: cv::findDecoder(cv::String const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 319 | ==4751== by 0x508436B: cv::imread_(cv::String const&, int, int, cv::Mat*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 320 | ==4751== by 0x5084A4D: cv::imread(cv::String const&, int) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/opencv-shared/linux/lib/libopencv_world.so.3.4.13) 321 | ==4751== by 0x4A6046: OcrLite::detect(char const*, char const*, int, int, float, float, float, float, bool, bool) (OcrLite.cpp:80) 322 | ==4751== by 0x4D41AC: main (main.cpp:141) 323 | ==4751== 324 | ==4751== 3,200 bytes in 10 blocks are possibly lost in loss record 104 of 105 325 | ==4751== at 0x4837B65: calloc (vg_replace_malloc.c:752) 326 | ==4751== by 0x40116E1: allocate_dtv (dl-tls.c:286) 327 | ==4751== by 0x401204D: _dl_allocate_tls (dl-tls.c:532) 328 | ==4751== by 0x7A22B95: allocate_stack (allocatestack.c:621) 329 | ==4751== by 0x7A22B95: pthread_create@@GLIBC_2.2.5 (pthread_create.c:669) 330 | ==4751== by 0x79FFD61: ??? (in /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0) 331 | ==4751== by 0x79F6E09: GOMP_parallel (in /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0) 332 | ==4751== by 0x696723: ncnn::Packing_x86_avx2::forward(ncnn::Mat const&, ncnn::Mat&, ncnn::Option const&) const (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/build/OcrLiteNcnn) 333 | ==4751== by 0x4D6401: ncnn::convert_packing(ncnn::Mat const&, ncnn::Mat&, int, ncnn::Option const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/build/OcrLiteNcnn) 334 | ==4751== by 0x653F56: ncnn::ConvolutionDepthWise_x86_avx2::create_pipeline(ncnn::Option const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/build/OcrLiteNcnn) 335 | ==4751== by 0x4E6BA7: ncnn::Net::load_model(ncnn::DataReader const&) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/build/OcrLiteNcnn) 336 | ==4751== by 0x4E6DCD: ncnn::Net::load_model(char const*) (in /media/psf/Home/ChOcrLitProjects/OcrLiteNcnn/build/OcrLiteNcnn) 337 | ==4751== by 0x4A307B: DbNet::initModel(std::__cxx11::basic_string, std::allocator >&) (DbNet.cpp:27) 338 | ==4751== 339 | ==4751== LEAK SUMMARY: 340 | ==4751== definitely lost: 0 bytes in 0 blocks 341 | ==4751== indirectly lost: 0 bytes in 0 blocks 342 | ==4751== possibly lost: 5,408 bytes in 32 blocks 343 | ==4751== still reachable: 88,012 bytes in 105 blocks 344 | ==4751== suppressed: 0 bytes in 0 blocks 345 | ==4751== Reachable blocks (those to which a pointer was found) are not shown. 346 | ==4751== To see them, rerun with: --leak-check=full --show-leak-kinds=all 347 | ==4751== 348 | ==4751== For counts of detected and suppressed errors, rerun with: -v 349 | ==4751== ERROR SUMMARY: 23 errors from 23 contexts (suppressed: 0 from 0) 350 | --------------------------------------------------------------------------------