chore: import upstream snapshot with attribution
Build/Publish Develop Docs / deploy (push) Failing after 1s
PaddleOCR Code Style Check / check-code-style (push) Failing after 1s
PaddleOCR PR Tests GPU / detect-changes (push) Failing after 1s
PaddleOCR PR Tests / detect-changes (push) Failing after 1s
PaddleOCR PR Tests GPU / test-pr-gpu (push) Has been cancelled
PaddleOCR PR Tests / test-pr (push) Has been cancelled
PaddleOCR PR Tests GPU / test-pr-gpu-impl (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.13) (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.8) (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.9) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 11:59:26 +08:00
commit e904b667c6
2490 changed files with 596352 additions and 0 deletions
+277
View File
@@ -0,0 +1,277 @@
cmake_minimum_required(VERSION 3.14)
project(ppocr CXX)
set(DEMO_NAME "ppocr")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
option(WITH_MKL "Compile demo with MKL/OpenBlas support, default use MKL." ON)
option(WITH_GPU "Compile demo with GPU/CPU, default use CPU." OFF)
option(WITH_STATIC_LIB "Compile demo with static/shared library, default use static." ON)
option(USE_FREETYPE "Enable FreeType support" OFF)
SET(PADDLE_LIB "" CACHE PATH "Location of libraries")
SET(OPENCV_DIR "" CACHE PATH "Location of libraries")
SET(CUDA_LIB "" CACHE PATH "Location of libraries")
SET(CUDNN_LIB "" CACHE PATH "Location of libraries")
macro(safe_set_static_flag)
foreach(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(${${flag_var}} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endif()
endforeach(flag_var)
endmacro()
if (WITH_MKL)
ADD_DEFINITIONS(-DUSE_MKL)
endif()
if(NOT DEFINED PADDLE_LIB)
message(FATAL_ERROR "please set PADDLE_LIB with -DPADDLE_LIB=/path/paddle/lib")
endif()
if(NOT DEFINED OPENCV_DIR)
message(FATAL_ERROR "please set OPENCV_DIR with -DOPENCV_DIR=/path/opencv")
endif()
if (WIN32)
include_directories("${PADDLE_LIB}/paddle/include")
link_directories("${PADDLE_LIB}/paddle/lib")
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(OpenCV_DIR "${OPENCV_DIR}/x64/vc16/lib")
find_package(OpenCV REQUIRED )
if(USE_FREETYPE)
if(NOT "opencv_freetype" IN_LIST OpenCV_LIBS)
message(FATAL_ERROR "OpenCV was not compiled with the freetype module (opencv_freetype) !")
endif()
add_definitions(-DUSE_FREETYPE)
endif()
else ()
set(OpenCV_DIR "${OPENCV_DIR}/lib64/cmake/opencv4")
find_package(OpenCV REQUIRED)
if(USE_FREETYPE)
if(NOT "opencv_freetype" IN_LIST OpenCV_LIBS)
message(FATAL_ERROR "OpenCV was not compiled with the freetype module (opencv_freetype) !")
endif()
add_definitions(-DUSE_FREETYPE)
endif()
include_directories("${PADDLE_LIB}/paddle/include")
link_directories("${PADDLE_LIB}/paddle/lib")
endif ()
include_directories(${OpenCV_INCLUDE_DIRS})
if (WIN32)
add_definitions("/DGOOGLE_GLOG_DLL_DECL=")
if(WITH_MKL)
set(FLAG_OPENMP "/openmp")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /bigobj /MTd ${FLAG_OPENMP}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /bigobj /MT ${FLAG_OPENMP}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj /MTd ${FLAG_OPENMP}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /bigobj /MT ${FLAG_OPENMP}")
if (WITH_STATIC_LIB)
safe_set_static_flag()
add_definitions(-DSTATIC_LIB)
add_definitions(-DYAML_CPP_STATIC_DEFINE)
endif()
message("cmake c debug flags " ${CMAKE_C_FLAGS_DEBUG})
message("cmake c release flags " ${CMAKE_C_FLAGS_RELEASE})
message("cmake cxx debug flags " ${CMAKE_CXX_FLAGS_DEBUG})
message("cmake cxx release flags " ${CMAKE_CXX_FLAGS_RELEASE})
else()
if(WITH_MKL)
set(FLAG_OPENMP "-fopenmp")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 ${FLAG_OPENMP} -std=c++11")
set(CMAKE_STATIC_LIBRARY_PREFIX "")
message("cmake cxx flags" ${CMAKE_CXX_FLAGS})
endif()
if (WITH_GPU)
if (NOT DEFINED CUDA_LIB OR ${CUDA_LIB} STREQUAL "")
message(FATAL_ERROR "please set CUDA_LIB with -DCUDA_LIB=/path/cuda-8.0/lib64")
endif()
if (NOT WIN32)
if (NOT DEFINED CUDNN_LIB)
message(FATAL_ERROR "please set CUDNN_LIB with -DCUDNN_LIB=/path/cudnn_v7.4/cuda/lib64")
endif()
add_definitions(-DWITH_GPU)
endif(NOT WIN32)
endif()
include_directories("${PADDLE_LIB}/third_party/install/protobuf/include")
include_directories("${PADDLE_LIB}/third_party/install/glog/include")
include_directories("${PADDLE_LIB}/third_party/install/gflags/include")
include_directories("${PADDLE_LIB}/third_party/install/xxhash/include")
include_directories("${PADDLE_LIB}/third_party/install/zlib/include")
include_directories("${PADDLE_LIB}/third_party/install/onnxruntime/include")
include_directories("${PADDLE_LIB}/third_party/install/paddle2onnx/include")
include_directories("${PADDLE_LIB}/third_party/install/yaml-cpp/include")
include_directories("${PADDLE_LIB}/third_party/install/openvino/include")
include_directories("${PADDLE_LIB}/third_party/install/tbb/include")
include_directories("${PADDLE_LIB}/third_party/boost")
include_directories("${PADDLE_LIB}/third_party/eigen3")
include_directories("${PADDLE_LIB}/paddle/include/")
include_directories("${CMAKE_SOURCE_DIR}/")
link_directories("${PADDLE_LIB}/third_party/install/zlib/lib")
link_directories("${PADDLE_LIB}/third_party/install/protobuf/lib")
link_directories("${PADDLE_LIB}/third_party/install/glog/lib")
link_directories("${PADDLE_LIB}/third_party/install/gflags/lib")
link_directories("${PADDLE_LIB}/third_party/install/xxhash/lib")
link_directories("${PADDLE_LIB}/third_party/install/onnxruntime/lib")
link_directories("${PADDLE_LIB}/third_party/install/paddle2onnx/lib")
link_directories("${PADDLE_LIB}/third_party/install/yaml-cpp/lib")
link_directories("${PADDLE_LIB}/third_party/install/openvino/intel64")
link_directories("${PADDLE_LIB}/third_party/install/tbb/lib")
link_directories("${PADDLE_LIB}/paddle/lib")
if(WITH_MKL)
include_directories("${PADDLE_LIB}/third_party/install/mklml/include")
if (WIN32)
set(MATH_LIB ${PADDLE_LIB}/third_party/install/mklml/lib/mklml.lib
${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5md.lib)
else ()
set(MATH_LIB ${PADDLE_LIB}/third_party/install/mklml/lib/libmklml_intel${CMAKE_SHARED_LIBRARY_SUFFIX}
${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5${CMAKE_SHARED_LIBRARY_SUFFIX})
execute_process(COMMAND cp -r ${PADDLE_LIB}/third_party/install/mklml/lib/libmklml_intel${CMAKE_SHARED_LIBRARY_SUFFIX} /usr/lib)
endif ()
set(MKLDNN_PATH "${PADDLE_LIB}/third_party/install/onednn")
if(EXISTS ${MKLDNN_PATH})
include_directories("${MKLDNN_PATH}/include")
if (WIN32)
set(MKLDNN_LIB ${MKLDNN_PATH}/lib/mkldnn.lib)
else ()
set(MKLDNN_LIB ${MKLDNN_PATH}/lib/libdnnl.so.3)
endif ()
endif()
else()
if (WIN32)
set(MATH_LIB ${PADDLE_LIB}/third_party/install/openblas/lib/openblas${CMAKE_STATIC_LIBRARY_SUFFIX})
else ()
set(MATH_LIB ${PADDLE_LIB}/third_party/install/openblas/lib/libopenblas${CMAKE_STATIC_LIBRARY_SUFFIX})
endif ()
endif()
# Note: libpaddle_inference_api.so/a must put before libpaddle_inference.so/a
if(WITH_STATIC_LIB)
if(WIN32)
set(DEPS
${PADDLE_LIB}/paddle/lib/paddle_inference${CMAKE_STATIC_LIBRARY_SUFFIX})
else()
set(DEPS
${PADDLE_LIB}/paddle/lib/libpaddle_inference${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
else()
if(WIN32)
set(DEPS
${PADDLE_LIB}/paddle/lib/paddle_inference${CMAKE_SHARED_LIBRARY_SUFFIX})
else()
set(DEPS
${PADDLE_LIB}/paddle/lib/libpaddle_inference${CMAKE_SHARED_LIBRARY_SUFFIX})
endif()
endif(WITH_STATIC_LIB)
if (NOT WIN32)
set(DEPS ${DEPS}
${MATH_LIB} ${MKLDNN_LIB}
glog gflags protobuf z xxhash
)
if(EXISTS "${PADDLE_LIB}/third_party/install/snappystream/lib")
set(DEPS ${DEPS} snappystream)
endif()
if (EXISTS "${PADDLE_LIB}/third_party/install/snappy/lib")
set(DEPS ${DEPS} snappy)
endif()
else()
set(DEPS ${DEPS}
${MATH_LIB} ${MKLDNN_LIB}
glog gflags_static libprotobuf xxhash)
set(DEPS ${DEPS} libcmt shlwapi)
if (EXISTS "${PADDLE_LIB}/third_party/install/snappy/lib")
set(DEPS ${DEPS} snappy)
endif()
if(EXISTS "${PADDLE_LIB}/third_party/install/snappystream/lib")
set(DEPS ${DEPS} snappystream)
endif()
endif(NOT WIN32)
if (EXISTS "${PADDLE_LIB}/third_party/install/yaml-cpp/lib")
set(DEPS ${DEPS} yaml-cpp)
endif()
if(WITH_GPU)
if(NOT WIN32)
set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX})
set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX})
else()
set(DEPS ${DEPS} ${CUDA_LIB}/cudart${CMAKE_STATIC_LIBRARY_SUFFIX} )
message($DEPS)
set(DEPS ${DEPS} ${CUDA_LIB}/cublas${CMAKE_STATIC_LIBRARY_SUFFIX} )
set(DEPS ${DEPS} ${CUDNN_LIB}/cudnn${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
endif()
if (NOT WIN32)
set(EXTERNAL_LIB "-ldl -lrt -lgomp -lz -lm -lpthread")
set(DEPS ${DEPS} ${EXTERNAL_LIB})
endif()
set(THIRD_PARTY_PATH ${CMAKE_CURRENT_LIST_DIR}/third_party)
function(download_and_decompress url filename decompress_dir)
if(NOT EXISTS "${filename}" AND NOT EXISTS "${decompress_dir}")
message("Downloading file from ${url} to ${filename} ...")
file(DOWNLOAD ${url} "${filename}.tmp" SHOW_PROGRESS)
file(RENAME "${filename}.tmp" ${filename})
endif()
if(NOT EXISTS ${decompress_dir})
file(MAKE_DIRECTORY ${decompress_dir})
message("Decompress file ${filename} ...")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${filename} WORKING_DIRECTORY ${decompress_dir})
endif()
endfunction()
set(PACKAGE_LIST abseil-cpp clipper_ver6.4.2 nlohmann)
foreach(PKG ${PACKAGE_LIST})
set(PKG_URL "https://paddle-model-ecology.bj.bcebos.com/paddlex/cpp/libs/${PKG}.tgz")
set(PKG_TGZ_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PKG}.tgz")
set(PKG_DST_PATH "${THIRD_PARTY_PATH}/${PKG}")
download_and_decompress(${PKG_URL} ${PKG_TGZ_PATH} ${PKG_DST_PATH})
endforeach()
add_subdirectory(third_party/abseil-cpp)
add_subdirectory(third_party/clipper_ver6.4.2/cpp)
include_directories(${POLYCLIPPING_INCLUDE_DIR})
set(DEPS ${DEPS} ${OpenCV_LIBS})
set(DEPS ${DEPS} absl::statusor)
set(DEPS ${DEPS} polyclipping)
if(UNIX)
find_package(Iconv REQUIRED)
endif()
file(GLOB_RECURSE SRC_LIST "./src/*.cc")
set(SRCS cli.cc )
add_executable(${DEMO_NAME} ${SRCS} ${SRC_LIST} )
target_link_libraries(${DEMO_NAME} ${DEPS} )
if (WIN32 AND WITH_MKL)
add_custom_command(TARGET ${DEMO_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/mklml.dll ./mklml.dll
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5md.dll ./libiomp5md.dll
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/onednn/lib/mkldnn.dll ./mkldnn.dll
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_LIB}/third_party/install/onednn/lib/mkldnn.dll ./release/mkldnn.dll
)
endif()
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 TensorRTPro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+327
View File
@@ -0,0 +1,327 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/api/models/doc_img_orientation_classification.h"
#include "src/api/models/text_detection.h"
#include "src/api/models/text_image_unwarping.h"
#include "src/api/models/text_recognition.h"
#include "src/api/models/textline_orientation_classification.h"
#include "src/api/pipelines/doc_preprocessor.h"
#include "src/api/pipelines/ocr.h"
#include "src/utils/args.h"
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
static const std::unordered_set<std::string> SUPPORT_MODE_PIPELINE = {
"ocr",
"doc_preprocessor",
};
static const std::unordered_set<std::string> SUPPORT_MODE_MODEL = {
"text_image_unwarping", "doc_img_orientation_classification",
"textline_orientation_classification", "text_detection",
"text_recognition"};
void PrintErrorInfo(const std::string &msg, const std::string &main_mode = "") {
auto join_modes =
[](const std::unordered_set<std::string> &modes) -> std::string {
std::string result;
for (const auto &mode : modes) {
result += mode + ", ";
}
if (!result.empty()) {
result.pop_back();
result.pop_back();
}
return result;
};
std::string pipeline_modes = join_modes(SUPPORT_MODE_PIPELINE);
std::string model_modes = join_modes(SUPPORT_MODE_MODEL);
INFOE("%s%s", msg.c_str(),
main_mode.empty() ? "" : (": \"" + main_mode + "\"").c_str());
INFO("==========================================");
INFO("Supported pipeline : [%s]", pipeline_modes.c_str());
INFO("Supported model : [%s]", model_modes.c_str());
INFO("==========================================");
}
std::tuple<PaddleOCRParams, DocPreprocessorParams,
DocImgOrientationClassificationParams, TextImageUnwarpingParams,
TextDetectionParams, TextLineOrientationClassificationParams,
TextRecognitionParams>
GetPipelineMoudleParams() {
PaddleOCRParams ocr_params;
DocPreprocessorParams doc_pre_params;
DocImgOrientationClassificationParams doc_orient_params;
TextImageUnwarpingParams unwarp_params;
TextDetectionParams det_params;
TextLineOrientationClassificationParams teline_orient_params;
TextRecognitionParams rec_params;
if (!FLAGS_doc_orientation_classify_model_name.empty()) {
ocr_params.doc_orientation_classify_model_name =
FLAGS_doc_orientation_classify_model_name;
doc_pre_params.doc_orientation_classify_model_name =
FLAGS_doc_orientation_classify_model_name;
doc_orient_params.model_name = FLAGS_doc_orientation_classify_model_name;
}
if (!FLAGS_doc_orientation_classify_model_dir.empty()) {
ocr_params.doc_orientation_classify_model_dir =
FLAGS_doc_orientation_classify_model_dir;
doc_pre_params.doc_orientation_classify_model_dir =
FLAGS_doc_orientation_classify_model_dir;
doc_orient_params.model_dir = FLAGS_doc_orientation_classify_model_dir;
}
if (!FLAGS_doc_unwarping_model_name.empty()) {
ocr_params.doc_unwarping_model_name = FLAGS_doc_unwarping_model_name;
doc_pre_params.doc_unwarping_model_name = FLAGS_doc_unwarping_model_name;
unwarp_params.model_name = FLAGS_doc_unwarping_model_name;
}
if (!FLAGS_doc_unwarping_model_dir.empty()) {
ocr_params.doc_unwarping_model_dir = FLAGS_doc_unwarping_model_dir;
doc_pre_params.doc_unwarping_model_dir = FLAGS_doc_unwarping_model_dir;
unwarp_params.model_dir = FLAGS_doc_unwarping_model_dir;
}
if (!FLAGS_text_detection_model_name.empty()) {
ocr_params.text_detection_model_name = FLAGS_text_detection_model_name;
det_params.model_name = FLAGS_text_detection_model_name;
}
if (!FLAGS_text_detection_model_dir.empty()) {
ocr_params.text_detection_model_dir = FLAGS_text_detection_model_dir;
det_params.model_dir = FLAGS_text_detection_model_dir;
}
if (!FLAGS_textline_orientation_model_name.empty()) {
ocr_params.textline_orientation_model_name =
FLAGS_textline_orientation_model_name;
teline_orient_params.model_name = FLAGS_textline_orientation_model_name;
}
if (!FLAGS_textline_orientation_model_dir.empty()) {
ocr_params.textline_orientation_model_dir =
FLAGS_textline_orientation_model_dir;
teline_orient_params.model_dir = FLAGS_textline_orientation_model_dir;
}
if (!FLAGS_textline_orientation_batch_size.empty()) {
ocr_params.textline_orientation_batch_size =
std::stoi(FLAGS_textline_orientation_batch_size);
}
if (!FLAGS_text_recognition_model_name.empty()) {
ocr_params.text_recognition_model_name = FLAGS_text_recognition_model_name;
rec_params.model_name = FLAGS_text_recognition_model_name;
}
if (!FLAGS_text_recognition_model_dir.empty()) {
ocr_params.text_recognition_model_dir = FLAGS_text_recognition_model_dir;
rec_params.model_dir = FLAGS_text_recognition_model_dir;
}
if (!FLAGS_text_recognition_batch_size.empty()) {
ocr_params.text_recognition_batch_size =
std::stoi(FLAGS_text_recognition_batch_size);
rec_params.batch_size = std::stoi(FLAGS_text_recognition_batch_size);
rec_params.input_shape =
YamlConfig::SmartParseVector(FLAGS_text_rec_input_shape).vec_int;
}
if (!FLAGS_use_doc_orientation_classify.empty()) {
ocr_params.use_doc_orientation_classify =
Utility::StringToBool(FLAGS_use_doc_orientation_classify);
doc_pre_params.use_doc_orientation_classify =
Utility::StringToBool(FLAGS_use_doc_orientation_classify);
}
if (!FLAGS_use_doc_unwarping.empty()) {
ocr_params.use_doc_unwarping =
Utility::StringToBool(FLAGS_use_doc_unwarping);
doc_pre_params.use_doc_unwarping =
Utility::StringToBool(FLAGS_use_doc_unwarping);
}
if (!FLAGS_use_textline_orientation.empty()) {
ocr_params.use_textline_orientation =
Utility::StringToBool(FLAGS_use_textline_orientation);
}
if (!FLAGS_text_det_limit_side_len.empty()) {
ocr_params.text_det_limit_side_len =
std::stoi(FLAGS_text_det_limit_side_len);
}
if (!FLAGS_text_det_limit_type.empty()) {
ocr_params.text_det_limit_type = FLAGS_text_det_limit_type;
det_params.limit_type = FLAGS_text_det_limit_type;
}
if (!FLAGS_text_det_thresh.empty()) {
ocr_params.text_det_thresh = std::stof(FLAGS_text_det_thresh);
det_params.thresh = std::stof(FLAGS_text_det_thresh);
}
if (!FLAGS_text_det_box_thresh.empty()) {
ocr_params.text_det_box_thresh = std::stof(FLAGS_text_det_box_thresh);
det_params.box_thresh = std::stof(FLAGS_text_det_box_thresh);
}
if (!FLAGS_text_det_unclip_ratio.empty()) {
ocr_params.text_det_unclip_ratio = std::stof(FLAGS_text_det_unclip_ratio);
det_params.unclip_ratio = std::stof(FLAGS_text_det_unclip_ratio);
}
if (!FLAGS_text_det_input_shape.empty()) {
ocr_params.text_det_input_shape =
YamlConfig::SmartParseVector(FLAGS_text_det_input_shape).vec_int;
det_params.input_shape =
YamlConfig::SmartParseVector(FLAGS_text_det_input_shape).vec_int;
}
if (!FLAGS_text_rec_score_thresh.empty()) {
ocr_params.text_rec_score_thresh = std::stof(FLAGS_text_rec_score_thresh);
}
if (!FLAGS_text_rec_input_shape.empty()) {
ocr_params.text_rec_input_shape =
YamlConfig::SmartParseVector(FLAGS_text_rec_input_shape).vec_int;
}
if (!FLAGS_lang.empty()) {
ocr_params.lang = FLAGS_lang;
}
if (!FLAGS_ocr_version.empty()) {
ocr_params.ocr_version = FLAGS_ocr_version;
}
if (!FLAGS_vis_font_dir.empty()) {
ocr_params.vis_font_dir = FLAGS_vis_font_dir;
rec_params.vis_font_dir = FLAGS_vis_font_dir;
}
if (!FLAGS_device.empty()) {
ocr_params.device = FLAGS_device;
doc_pre_params.device = FLAGS_device;
doc_orient_params.device = FLAGS_device;
unwarp_params.device = FLAGS_device;
teline_orient_params.device = FLAGS_device;
det_params.device = FLAGS_device;
rec_params.device = FLAGS_device;
}
if (!FLAGS_precision.empty()) {
ocr_params.precision = FLAGS_precision;
doc_pre_params.precision = FLAGS_precision;
doc_orient_params.precision = FLAGS_precision;
unwarp_params.precision = FLAGS_precision;
teline_orient_params.precision = FLAGS_precision;
det_params.precision = FLAGS_precision;
rec_params.precision = FLAGS_precision;
}
if (!FLAGS_enable_mkldnn.empty()) {
ocr_params.enable_mkldnn = Utility::StringToBool(FLAGS_enable_mkldnn);
doc_pre_params.enable_mkldnn = Utility::StringToBool(FLAGS_enable_mkldnn);
doc_orient_params.enable_mkldnn =
Utility::StringToBool(FLAGS_enable_mkldnn);
unwarp_params.enable_mkldnn = Utility::StringToBool(FLAGS_enable_mkldnn);
teline_orient_params.enable_mkldnn =
Utility::StringToBool(FLAGS_enable_mkldnn);
det_params.enable_mkldnn = Utility::StringToBool(FLAGS_enable_mkldnn);
rec_params.enable_mkldnn = Utility::StringToBool(FLAGS_enable_mkldnn);
}
if (!FLAGS_mkldnn_cache_capacity.empty()) {
ocr_params.mkldnn_cache_capacity = std::stoi(FLAGS_mkldnn_cache_capacity);
doc_pre_params.mkldnn_cache_capacity =
std::stoi(FLAGS_mkldnn_cache_capacity);
doc_orient_params.mkldnn_cache_capacity =
std::stoi(FLAGS_mkldnn_cache_capacity);
unwarp_params.mkldnn_cache_capacity =
std::stoi(FLAGS_mkldnn_cache_capacity);
teline_orient_params.mkldnn_cache_capacity =
std::stoi(FLAGS_mkldnn_cache_capacity);
det_params.mkldnn_cache_capacity = std::stoi(FLAGS_mkldnn_cache_capacity);
rec_params.mkldnn_cache_capacity = std::stoi(FLAGS_mkldnn_cache_capacity);
}
if (!FLAGS_cpu_threads.empty()) {
ocr_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
doc_pre_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
doc_orient_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
unwarp_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
teline_orient_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
det_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
rec_params.cpu_threads = std::stoi(FLAGS_cpu_threads);
}
if (!FLAGS_thread_num.empty()) {
ocr_params.thread_num = std::stoi(FLAGS_thread_num);
doc_pre_params.thread_num = std::stoi(FLAGS_thread_num);
}
if (!FLAGS_paddlex_config.empty()) {
ocr_params.paddlex_config = FLAGS_paddlex_config;
doc_pre_params.paddlex_config = FLAGS_paddlex_config;
}
return std::make_tuple(ocr_params, doc_pre_params, doc_orient_params,
unwarp_params, det_params, teline_orient_params,
rec_params);
}
int main(int argc, char *argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (FLAGS_input.empty()) {
INFOE("Require input, such as ./build/ppocr <pipeline_or_module> --input "
"your_image_path [--param1] [--param2] [...]");
exit(-1);
}
std::string main_mode = "";
if (argc > 1) {
main_mode = argv[1];
if (SUPPORT_MODE_PIPELINE.count(main_mode) == 0 &&
SUPPORT_MODE_MODEL.count(main_mode) == 0) {
PrintErrorInfo("ERROR: Unsupported pipeline or module", main_mode);
exit(-1);
}
} else {
PrintErrorInfo(
"Must provide pipeline or module name, such as ./build/ppocr "
"<pipeline_or_module> [--param1] [--param2] [...]");
exit(-1);
}
auto params = GetPipelineMoudleParams();
using PredFunc = std::function<std::vector<std::unique_ptr<BaseCVResult>>(
const std::string &)>;
std::unordered_map<std::string, PredFunc> pred_map = {
{"ocr",
[&params](const std::string &input) {
return PaddleOCR(std::get<0>(params)).Predict(input);
}},
{"doc_preprocessor",
[&params](const std::string &input) {
return DocPreprocessor(std::get<1>(params)).Predict(input);
}},
{"doc_img_orientation_classification",
[&params](const std::string &input) {
return DocImgOrientationClassification(std::get<2>(params))
.Predict(input);
}},
{"text_image_unwarping",
[&params](const std::string &input) {
return TextImageUnwarping(std::get<3>(params)).Predict(input);
}},
{"text_detection",
[&params](const std::string &input) {
return TextDetection(std::get<4>(params)).Predict(input);
}},
{"textline_orientation_classification",
[&params](const std::string &input) {
return TextLineOrientationClassification(std::get<5>(params))
.Predict(input);
}},
{"text_recognition",
[&params](const std::string &input) {
return TextRecognition(std::get<6>(params)).Predict(input);
}},
};
auto it = pred_map.find(main_mode);
auto outputs = it->second(FLAGS_input);
for (auto &output : outputs) {
output->Print();
output->SaveToImg(FLAGS_save_path);
output->SaveToJson(FLAGS_save_path);
}
return 0;
}
@@ -0,0 +1,62 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "doc_img_orientation_classification.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
DocImgOrientationClassification::DocImgOrientationClassification(
const DocImgOrientationClassificationParams &params)
: params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init DocImgOrientationClassification fail : %s",
status.ToString().c_str());
exit(-1);
}
CreateModel();
};
std::vector<std::unique_ptr<BaseCVResult>>
DocImgOrientationClassification::Predict(
const std::vector<std::string> &input) {
return model_infer_->Predict(input);
}
void DocImgOrientationClassification::CreateModel() {
model_infer_ = std::unique_ptr<BasePredictor>(
new ClasPredictor(ToDocImgOrientationClassificationModelParams(params_)));
}
absl::Status DocImgOrientationClassification::CheckParams() {
if (!params_.model_dir.has_value()) {
return absl::NotFoundError("Require doc orientation classify model dir.");
}
return absl::OkStatus();
}
ClasPredictorParams
DocImgOrientationClassification::ToDocImgOrientationClassificationModelParams(
const DocImgOrientationClassificationParams &from) {
ClasPredictorParams to;
COPY_PARAMS(model_name)
COPY_PARAMS(model_dir)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
return to;
}
@@ -0,0 +1,51 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/modules/image_classification/predictor.h"
struct DocImgOrientationClassificationParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
};
class DocImgOrientationClassification {
public:
DocImgOrientationClassification(
const DocImgOrientationClassificationParams &params =
DocImgOrientationClassificationParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreateModel();
absl::Status CheckParams();
static ClasPredictorParams ToDocImgOrientationClassificationModelParams(
const DocImgOrientationClassificationParams &from);
private:
DocImgOrientationClassificationParams params_;
std::unique_ptr<BasePredictor> model_infer_;
};
@@ -0,0 +1,64 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "text_detection.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
TextDetection::TextDetection(const TextDetectionParams &params)
: params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init TextDetection fail : %s", status.ToString().c_str());
exit(-1);
}
CreateModel();
};
std::vector<std::unique_ptr<BaseCVResult>>
TextDetection::Predict(const std::vector<std::string> &input) {
return model_infer_->Predict(input);
}
void TextDetection::CreateModel() {
model_infer_ = std::unique_ptr<BasePredictor>(
new TextDetPredictor(ToTextDetectionModelParams(params_)));
}
absl::Status TextDetection::CheckParams() {
if (!params_.model_dir.has_value()) {
return absl::NotFoundError("Require text detection model dir.");
}
return absl::OkStatus();
}
TextDetPredictorParams
TextDetection::ToTextDetectionModelParams(const TextDetectionParams &from) {
TextDetPredictorParams to;
COPY_PARAMS(model_name)
COPY_PARAMS(model_dir)
COPY_PARAMS(limit_side_len)
COPY_PARAMS(limit_type)
COPY_PARAMS(thresh)
COPY_PARAMS(box_thresh)
COPY_PARAMS(unclip_ratio)
COPY_PARAMS(input_shape)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
return to;
}
@@ -0,0 +1,56 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/modules/text_detection/predictor.h"
struct TextDetectionParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
absl::optional<int> limit_side_len = absl::nullopt;
absl::optional<std::string> limit_type = absl::nullopt;
absl::optional<int> max_side_limit = absl::nullopt;
absl::optional<float> thresh = absl::nullopt;
absl::optional<float> box_thresh = absl::nullopt;
absl::optional<float> unclip_ratio = absl::nullopt;
absl::optional<std::vector<int>> input_shape = absl::nullopt;
};
class TextDetection {
public:
TextDetection(const TextDetectionParams &params = TextDetectionParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreateModel();
absl::Status CheckParams();
static TextDetPredictorParams
ToTextDetectionModelParams(const TextDetectionParams &from);
private:
TextDetectionParams params_;
std::unique_ptr<BasePredictor> model_infer_;
};
@@ -0,0 +1,58 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "text_image_unwarping.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
TextImageUnwarping::TextImageUnwarping(const TextImageUnwarpingParams &params)
: params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init TextImageUnwarping fail : %s", status.ToString().c_str());
exit(-1);
}
CreateModel();
};
std::vector<std::unique_ptr<BaseCVResult>>
TextImageUnwarping::Predict(const std::vector<std::string> &input) {
return model_infer_->Predict(input);
}
void TextImageUnwarping::CreateModel() {
model_infer_ = std::unique_ptr<BasePredictor>(
new WarpPredictor(ToTextImageUnwarpingModelParams(params_)));
}
absl::Status TextImageUnwarping::CheckParams() {
if (!params_.model_dir.has_value()) {
return absl::NotFoundError("Require doc unwarping model dir.");
}
return absl::OkStatus();
}
WarpPredictorParams TextImageUnwarping::ToTextImageUnwarpingModelParams(
const TextImageUnwarpingParams &from) {
WarpPredictorParams to;
COPY_PARAMS(model_name)
COPY_PARAMS(model_dir)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
return to;
}
@@ -0,0 +1,51 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/modules/image_unwarping/predictor.h"
struct TextImageUnwarpingParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
bool enable_mkldnn = true;
std::string precision = "fp32";
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
};
class TextImageUnwarping {
public:
TextImageUnwarping(
const TextImageUnwarpingParams &params = TextImageUnwarpingParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreateModel();
absl::Status CheckParams();
static WarpPredictorParams
ToTextImageUnwarpingModelParams(const TextImageUnwarpingParams &from);
private:
TextImageUnwarpingParams params_;
std::unique_ptr<BasePredictor> model_infer_;
};
@@ -0,0 +1,61 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "text_recognition.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
TextRecognition::TextRecognition(const TextRecognitionParams &params)
: params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init TextRecognition fail : %s", status.ToString().c_str());
exit(-1);
}
CreateModel();
};
std::vector<std::unique_ptr<BaseCVResult>>
TextRecognition::Predict(const std::vector<std::string> &input) {
return model_infer_->Predict(input);
}
void TextRecognition::CreateModel() {
model_infer_ = std::unique_ptr<BasePredictor>(
new TextRecPredictor(ToTextRecognitionModelParams(params_)));
}
absl::Status TextRecognition::CheckParams() {
if (!params_.model_dir.has_value()) {
return absl::NotFoundError("Require text recognition model_dir.");
}
return absl::OkStatus();
}
TextRecPredictorParams TextRecognition::ToTextRecognitionModelParams(
const TextRecognitionParams &from) {
TextRecPredictorParams to;
COPY_PARAMS(model_name)
COPY_PARAMS(model_dir)
COPY_PARAMS(batch_size)
COPY_PARAMS(input_shape)
COPY_PARAMS(vis_font_dir)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
return to;
}
@@ -0,0 +1,54 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/modules/text_recognition/predictor.h"
struct TextRecognitionParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> lang = absl::nullopt;
absl::optional<std::string> ocr_version = absl::nullopt;
absl::optional<std::string> vis_font_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
absl::optional<std::vector<int>> input_shape = absl::nullopt;
};
class TextRecognition {
public:
TextRecognition(
const TextRecognitionParams &params = TextRecognitionParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreateModel();
absl::Status CheckParams();
static TextRecPredictorParams
ToTextRecognitionModelParams(const TextRecognitionParams &from);
private:
TextRecognitionParams params_;
std::unique_ptr<BasePredictor> model_infer_;
};
@@ -0,0 +1,63 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "textline_orientation_classification.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
TextLineOrientationClassification::TextLineOrientationClassification(
const TextLineOrientationClassificationParams &params)
: params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init TextLineOrientationClassification fail : %s",
status.ToString().c_str());
exit(-1);
}
CreateModel();
};
std::vector<std::unique_ptr<BaseCVResult>>
TextLineOrientationClassification::Predict(
const std::vector<std::string> &input) {
return model_infer_->Predict(input);
}
void TextLineOrientationClassification::CreateModel() {
model_infer_ = std::unique_ptr<BasePredictor>(new ClasPredictor(
ToTextLineOrientationClassificationModelParams(params_)));
}
absl::Status TextLineOrientationClassification::CheckParams() {
if (!params_.model_dir.has_value()) {
return absl::NotFoundError(
"Require textLine orientation classification model dir.");
}
return absl::OkStatus();
}
ClasPredictorParams TextLineOrientationClassification::
ToTextLineOrientationClassificationModelParams(
const TextLineOrientationClassificationParams &from) {
ClasPredictorParams to;
COPY_PARAMS(model_name)
COPY_PARAMS(model_dir)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
return to;
}
@@ -0,0 +1,51 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/modules/image_classification/predictor.h"
struct TextLineOrientationClassificationParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
};
class TextLineOrientationClassification {
public:
TextLineOrientationClassification(
const TextLineOrientationClassificationParams &params =
TextLineOrientationClassificationParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreateModel();
absl::Status CheckParams();
static ClasPredictorParams ToTextLineOrientationClassificationModelParams(
const TextLineOrientationClassificationParams &from);
private:
TextLineOrientationClassificationParams params_;
std::unique_ptr<BasePredictor> model_infer_;
};
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <memory>
#include "src/base/base_pipeline.h"
class PaddleXPipelineWrapper {
public:
virtual ~PaddleXPipelineWrapper() = default;
PaddleXPipelineWrapper() = delete;
virtual std::unique_ptr<BasePipeline> CreatePipeline() = 0;
};
@@ -0,0 +1,71 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "doc_preprocessor.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
DocPreprocessor::DocPreprocessor(const DocPreprocessorParams &params)
: params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init DocPreprocessor fail : %s", status.ToString().c_str());
exit(-1);
}
CreatePipeline();
};
std::vector<std::unique_ptr<BaseCVResult>>
DocPreprocessor::Predict(const std::vector<std::string> &input) {
return pipeline_infer_->Predict(input);
}
void DocPreprocessor::CreatePipeline() {
pipeline_infer_ = std::unique_ptr<BasePipeline>(
new DocPreprocessorPipeline(ToDocPreprocessorPipelineParams(params_)));
}
absl::Status DocPreprocessor::CheckParams() {
if (!params_.doc_orientation_classify_model_dir.has_value() &&
!(params_.use_doc_orientation_classify.has_value() &&
!params_.use_doc_orientation_classify.value())) {
return absl::NotFoundError("Require doc orientation classify model dir.");
}
if (!params_.doc_unwarping_model_dir.has_value() &&
!(params_.use_doc_unwarping.has_value() &&
!params_.use_doc_unwarping.value())) {
return absl::NotFoundError("Require doc unwarping model dir.");
}
return absl::OkStatus();
}
DocPreprocessorPipelineParams DocPreprocessor::ToDocPreprocessorPipelineParams(
const DocPreprocessorParams &from) {
DocPreprocessorPipelineParams to;
COPY_PARAMS(doc_orientation_classify_model_name)
COPY_PARAMS(doc_orientation_classify_model_dir)
COPY_PARAMS(doc_unwarping_model_name)
COPY_PARAMS(doc_unwarping_model_dir)
COPY_PARAMS(use_doc_orientation_classify)
COPY_PARAMS(use_doc_unwarping)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
COPY_PARAMS(thread_num)
COPY_PARAMS(paddlex_config)
return to;
}
@@ -0,0 +1,57 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/pipelines/doc_preprocessor/pipeline.h"
struct DocPreprocessorParams {
absl::optional<std::string> doc_orientation_classify_model_name =
absl::nullopt;
absl::optional<std::string> doc_orientation_classify_model_dir =
absl::nullopt;
absl::optional<std::string> doc_unwarping_model_name = absl::nullopt;
absl::optional<std::string> doc_unwarping_model_dir = absl::nullopt;
absl::optional<bool> use_doc_orientation_classify = absl::nullopt;
absl::optional<bool> use_doc_unwarping = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
std::string precision = "fp32";
int cpu_threads = 8;
int thread_num = 1;
absl::optional<Utility::PaddleXConfigVariant> paddlex_config = absl::nullopt;
};
class DocPreprocessor {
public:
DocPreprocessor(
const DocPreprocessorParams &params = DocPreprocessorParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreatePipeline();
absl::Status CheckParams();
static DocPreprocessorPipelineParams
ToDocPreprocessorPipelineParams(const DocPreprocessorParams &from);
private:
DocPreprocessorParams params_;
std::unique_ptr<BasePipeline> pipeline_infer_;
};
+100
View File
@@ -0,0 +1,100 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ocr.h"
#include "src/utils/args.h"
#include "src/utils/yaml_config.h"
#define COPY_PARAMS(field) to.field = from.field;
PaddleOCR::PaddleOCR(const PaddleOCRParams &params) : params_(params) {
auto status = CheckParams();
if (!status.ok()) {
INFOE("Init paddleOCR fail : %s", status.ToString().c_str());
exit(-1);
}
CreatePipeline();
};
std::vector<std::unique_ptr<BaseCVResult>>
PaddleOCR::Predict(const std::vector<std::string> &input) {
return pipeline_infer_->Predict(input);
}
void PaddleOCR::CreatePipeline() {
pipeline_infer_ = std::unique_ptr<BasePipeline>(
new OCRPipeline(ToOCRPipelineParams(params_)));
}
absl::Status PaddleOCR::CheckParams() {
if (!params_.doc_orientation_classify_model_dir.has_value() &&
!(params_.use_doc_orientation_classify.has_value() &&
!params_.use_doc_orientation_classify.value())) {
return absl::NotFoundError("Require doc orientation classify model dir.");
}
if (!params_.doc_unwarping_model_dir.has_value() &&
!(params_.use_doc_unwarping.has_value() &&
!params_.use_doc_unwarping.value())) {
return absl::NotFoundError("Require doc unwarping model dir.");
}
if (!params_.textline_orientation_model_dir.has_value() &&
!(params_.use_textline_orientation.has_value() &&
!params_.use_textline_orientation.value())) {
return absl::NotFoundError("Require textline orientation model_dir.");
}
if (!params_.text_detection_model_dir.has_value()) {
return absl::NotFoundError("Require text detection model dir.");
}
if (!params_.text_recognition_model_dir.has_value()) {
return absl::NotFoundError("Require text recognition model_dir.");
}
return absl::OkStatus();
}
OCRPipelineParams PaddleOCR::ToOCRPipelineParams(const PaddleOCRParams &from) {
OCRPipelineParams to;
COPY_PARAMS(doc_orientation_classify_model_name)
COPY_PARAMS(doc_orientation_classify_model_dir)
COPY_PARAMS(doc_unwarping_model_name)
COPY_PARAMS(doc_unwarping_model_dir)
COPY_PARAMS(text_detection_model_name)
COPY_PARAMS(text_detection_model_dir)
COPY_PARAMS(textline_orientation_model_name)
COPY_PARAMS(textline_orientation_model_dir)
COPY_PARAMS(textline_orientation_batch_size)
COPY_PARAMS(text_recognition_model_name)
COPY_PARAMS(text_recognition_model_dir)
COPY_PARAMS(text_recognition_batch_size)
COPY_PARAMS(use_doc_orientation_classify)
COPY_PARAMS(use_doc_unwarping)
COPY_PARAMS(use_textline_orientation)
COPY_PARAMS(text_det_limit_side_len)
COPY_PARAMS(text_det_limit_type)
COPY_PARAMS(text_det_thresh)
COPY_PARAMS(text_det_box_thresh)
COPY_PARAMS(text_det_unclip_ratio)
COPY_PARAMS(text_det_input_shape)
COPY_PARAMS(text_rec_score_thresh)
COPY_PARAMS(text_rec_input_shape)
COPY_PARAMS(lang)
COPY_PARAMS(ocr_version)
COPY_PARAMS(vis_font_dir)
COPY_PARAMS(device)
COPY_PARAMS(enable_mkldnn)
COPY_PARAMS(mkldnn_cache_capacity)
COPY_PARAMS(precision)
COPY_PARAMS(cpu_threads)
COPY_PARAMS(thread_num)
COPY_PARAMS(paddlex_config)
return to;
}
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "src/pipelines/ocr/pipeline.h"
struct PaddleOCRParams {
absl::optional<std::string> doc_orientation_classify_model_name =
absl::nullopt;
absl::optional<std::string> doc_orientation_classify_model_dir =
absl::nullopt;
absl::optional<std::string> doc_unwarping_model_name = absl::nullopt;
absl::optional<std::string> doc_unwarping_model_dir = absl::nullopt;
absl::optional<std::string> text_detection_model_name = absl::nullopt;
absl::optional<std::string> text_detection_model_dir = absl::nullopt;
absl::optional<std::string> textline_orientation_model_name = absl::nullopt;
absl::optional<std::string> textline_orientation_model_dir = absl::nullopt;
absl::optional<int> textline_orientation_batch_size = absl::nullopt;
absl::optional<std::string> text_recognition_model_name = absl::nullopt;
absl::optional<std::string> text_recognition_model_dir = absl::nullopt;
absl::optional<int> text_recognition_batch_size = absl::nullopt;
absl::optional<bool> use_doc_orientation_classify = absl::nullopt;
absl::optional<bool> use_doc_unwarping = absl::nullopt;
absl::optional<bool> use_textline_orientation = absl::nullopt;
absl::optional<int> text_det_limit_side_len = absl::nullopt;
absl::optional<std::string> text_det_limit_type = absl::nullopt;
absl::optional<float> text_det_thresh = absl::nullopt;
absl::optional<float> text_det_box_thresh = absl::nullopt;
absl::optional<float> text_det_unclip_ratio = absl::nullopt;
absl::optional<std::vector<int>> text_det_input_shape = absl::nullopt;
absl::optional<float> text_rec_score_thresh = absl::nullopt;
absl::optional<std::vector<int>> text_rec_input_shape = absl::nullopt;
absl::optional<std::string> lang = absl::nullopt;
absl::optional<std::string> ocr_version = absl::nullopt;
absl::optional<std::string> vis_font_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
std::string precision = "fp32";
int cpu_threads = 8;
int thread_num = 1;
absl::optional<Utility::PaddleXConfigVariant> paddlex_config = absl::nullopt;
};
class PaddleOCR {
public:
PaddleOCR(const PaddleOCRParams &params = PaddleOCRParams());
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input);
void CreatePipeline();
absl::Status CheckParams();
static OCRPipelineParams ToOCRPipelineParams(const PaddleOCRParams &from);
private:
PaddleOCRParams params_;
std::unique_ptr<BasePipeline> pipeline_infer_;
};
@@ -0,0 +1,106 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "base_batch_sampler.h"
#include "src/utils/ilogger.h"
#include "src/utils/utility.h"
int BaseBatchSampler::BatchSize() const { return batch_size_; }
absl::Status BaseBatchSampler::SetBatchSize(int batch_size) {
if (batch_size <= 0) {
return absl::InvalidArgumentError("Batch size must be greater than 0");
}
batch_size_ = batch_size;
return absl::OkStatus();
}
absl::StatusOr<std::vector<std::vector<std::string>>>
BaseBatchSampler::SampleFromVectorToStringVector(
const std::vector<std::string> &inputs) {
std::vector<std::vector<std::string>> result;
std::vector<std::string> current_batch;
for (size_t i = 0; i < inputs.size(); ++i) {
const std::string &input = inputs[i];
if (Utility::IsDirectory(input)) {
absl::StatusOr<std::vector<std::string>> files_result =
GetFilesList(input);
if (!files_result.ok()) {
return files_result.status();
}
absl::StatusOr<std::vector<std::vector<std::string>>> sub_result =
SampleFromVectorToStringVector(files_result.value());
if (!sub_result.ok()) {
return sub_result.status();
}
const std::vector<std::vector<std::string>> &sub_batches =
sub_result.value();
for (size_t j = 0; j < sub_batches.size(); ++j) {
result.push_back(sub_batches[j]);
}
} else if (Utility::IsImageFile(input)) {
if (!Utility::FileExists(input).ok()) {
return absl::NotFoundError("File not found: " + input);
}
current_batch.push_back(input);
if (static_cast<int>(current_batch.size()) == batch_size_) {
result.push_back(current_batch);
current_batch.clear();
}
} else {
return absl::InvalidArgumentError("Unsupported file type: " + input);
}
}
if (!current_batch.empty()) {
result.push_back(current_batch); // last batch
}
return result;
}
absl::StatusOr<std::vector<std::vector<std::string>>>
BaseBatchSampler::SampleFromStringToStringVector(const std::string &input) {
std::vector<std::string> inputs = {input};
return SampleFromVectorToStringVector(inputs);
}
absl::StatusOr<std::vector<std::string>>
BaseBatchSampler::GetFilesList(const std::string &path) {
if (!Utility::FileExists(path).ok()) {
return absl::NotFoundError("Path not found: " + path);
}
std::vector<std::string> file_list;
if (!Utility::IsDirectory(path)) {
if (Utility::IsImageFile(path)) {
file_list.push_back(path);
}
} else {
Utility::GetFilesRecursive(path, file_list);
}
if (file_list.empty()) {
return absl::NotFoundError("No image files found in path: " + path);
}
std::sort(file_list.begin(), file_list.end());
return file_list;
}
@@ -0,0 +1,89 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <string>
#include <type_traits>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
class BaseBatchSampler {
public:
explicit BaseBatchSampler(int batch_size) : batch_size_(batch_size) {}
virtual ~BaseBatchSampler() = default;
int BatchSize() const;
absl::Status SetBatchSize(int batch_size);
template <typename T>
absl::StatusOr<std::vector<std::vector<cv::Mat>>> Apply(const T &input);
template <typename T>
absl::StatusOr<std::vector<std::vector<cv::Mat>>> Sample(const T &input) {
return absl::InvalidArgumentError(
"Sample failed! Unsupported type for Sample");
}
virtual absl::StatusOr<std::vector<std::vector<cv::Mat>>>
SampleFromString(const std::string &input) = 0;
virtual absl::StatusOr<std::vector<std::vector<cv::Mat>>>
SampleFromVector(const std::vector<std::string> &inputs) = 0;
std::vector<std::string> InputPath() { return input_path_; };
virtual absl::StatusOr<std::vector<std::vector<cv::Mat>>>
SampleFromMatVector(const std::vector<cv::Mat> &inputs) = 0;
absl::StatusOr<std::vector<std::vector<std::string>>>
SampleFromStringToStringVector(const std::string &input);
absl::StatusOr<std::vector<std::vector<std::string>>>
SampleFromVectorToStringVector(const std::vector<std::string> &input);
absl::StatusOr<std::vector<std::string>>
GetFilesList(const std::string &path);
protected:
int batch_size_ = 1;
std::vector<std::string> input_path_;
};
template <typename T>
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
BaseBatchSampler::Apply(const T &input) {
return Sample(input);
}
template <>
inline absl::StatusOr<std::vector<std::vector<cv::Mat>>>
BaseBatchSampler::Sample<std::string>(const std::string &input) {
return SampleFromString(input);
}
template <>
inline absl::StatusOr<std::vector<std::vector<cv::Mat>>>
BaseBatchSampler::Sample<std::vector<std::string>>(
const std::vector<std::string> &input) {
return SampleFromVector(input);
}
template <>
inline absl::StatusOr<std::vector<std::vector<cv::Mat>>>
BaseBatchSampler::Sample<std::vector<cv::Mat>>(
const std::vector<cv::Mat> &input) {
return SampleFromMatVector(input);
}
@@ -0,0 +1,46 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <chrono>
#include <iomanip>
#include <opencv2/opencv.hpp>
#include <string>
#include <unordered_map>
#include "absl/status/statusor.h"
class ImageWriter {};
class BaseCVResult {
public:
BaseCVResult(const std::string &backend);
BaseCVResult() = default;
virtual ~BaseCVResult() = default;
std::string Str() const;
std::unordered_map<std::string, cv::Mat> Img() const;
// absl::Status Print() const;
absl::Status SaveToImg() const;
virtual void SaveToImg(const std::string &save_path) = 0;
virtual void Print() const = 0;
virtual void SaveToJson(const std::string &save_path) const = 0;
protected:
std::unordered_map<std::string, std::string> res_;
ImageWriter img_writer_;
std::string ToStr() const;
// virtual std::unordered_map<std::string, cv::Mat> ToImg() const = 0;
};
@@ -0,0 +1,15 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "base_pipeline.h"
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <iostream>
#include <memory>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "base_cv_result.h"
#include "base_predictor.h"
class BasePipeline {
public:
BasePipeline() = default;
virtual ~BasePipeline() = default;
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
}
virtual std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input) = 0;
template <typename T, typename... Args>
std::unique_ptr<BasePredictor> CreateModule(Args &&...args);
template <typename T, typename... Args>
std::unique_ptr<BasePipeline> CreatePipeline(Args &&...args);
};
template <typename T, typename... Args>
std::unique_ptr<BasePredictor> BasePipeline::CreateModule(Args &&...args) {
std::unique_ptr<BasePredictor> base_predictor =
std::unique_ptr<T>(new T(std::forward<Args>(args)...));
return base_predictor;
}
template <typename T, typename... Args>
std::unique_ptr<BasePipeline> BasePipeline::CreatePipeline(Args &&...args) {
std::unique_ptr<BasePipeline> base_pipeline =
std::unique_ptr<T>(new T(std::forward<Args>(args)...));
return base_pipeline;
}
+176
View File
@@ -0,0 +1,176 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "base_predictor.h"
#include <yaml-cpp/yaml.h>
#include <iostream>
#include "base_batch_sampler.h"
#include "src/common/image_batch_sampler.h"
#include "src/utils/ilogger.h"
#include "src/utils/pp_option.h"
#include "src/utils/utility.h"
BasePredictor::BasePredictor(const absl::optional<std::string> &model_dir,
const absl::optional<std::string> &model_name,
const absl::optional<std::string> &device,
const std::string &precision,
const bool enable_mkldnn,
int mkldnn_cache_capacityint, int cpu_threads,
int batch_size, const std::string sampler_type)
: model_dir_(model_dir), batch_size_(batch_size),
sampler_type_(sampler_type) {
if (model_dir_.has_value()) {
config_ = YamlConfig(model_dir_.value());
} else {
INFOE("Model dir is empty.");
exit(-1);
}
auto status_build = BuildBatchSampler();
if (!status_build.ok()) {
INFOE("Build sampler fail: %s", status_build.ToString().c_str());
exit(-1);
}
auto model_name_config = config_.GetString(std::string("Global.model_name"));
if (!model_name_config.ok()) {
INFOE(model_name_config.status().ToString().c_str());
exit(-1);
}
model_name_ = model_name_config.value();
if (model_name.has_value()) {
if (model_name_ != model_name.value()) {
INFOE(
"Model name mismatch, please input the correct model dir. model dir "
"is %s, but model name is %s",
model_dir_.value().c_str(), model_name.value().c_str());
exit(-1);
}
}
model_name_ = model_name.value_or(model_name_);
pp_option_ptr_.reset(new PaddlePredictorOption());
auto device_result = device.value_or(DEVICE);
size_t pos = device_result.find(':');
std::string device_type = "";
int device_id = 0;
if (pos != std::string::npos) {
device_type = device_result.substr(0, pos);
device_id = std::stoi(device_result.substr(pos + 1));
} else {
device_type = device_result;
device_id = 0;
}
auto status_device_type = pp_option_ptr_->SetDeviceType(device_type);
if (!status_device_type.ok()) {
INFOE("Failed to set device : %s", status_device_type.ToString().c_str());
exit(-1);
;
}
auto status_device_id = pp_option_ptr_->SetDeviceId(device_id);
if (!status_device_id.ok()) {
INFOE("Failed to set device id: %s", status_device_id.ToString().c_str());
exit(-1);
;
}
if (enable_mkldnn && device_type == "cpu") {
if (precision == "fp16") {
INFOW("When MKLDNN is enabled, FP16 precision is not supported.The "
"computation will proceed with FP32 instead.");
}
if (Utility::IsMkldnnAvailable()) {
auto status_mkldnn = pp_option_ptr_->SetRunMode("mkldnn");
if (!status_mkldnn.ok()) {
INFOE("Failed to set run mode: %s", status_mkldnn.ToString().c_str());
exit(-1);
;
}
} else {
INFOW("Mkldnn is not available, using paddle instead!");
auto status_paddle = pp_option_ptr_->SetRunMode("paddle");
if (!status_paddle.ok()) {
INFOE("Failed to set run mode: %s", status_paddle.ToString().c_str());
exit(-1);
}
}
} else if (precision == "fp16") {
if (precision == "fp16") {
auto status_paddle_fp16 = pp_option_ptr_->SetRunMode("paddle_fp16");
if (!status_paddle_fp16.ok()) {
INFOE("Failed to set run mode: %s",
status_paddle_fp16.ToString().c_str());
exit(-1);
;
}
}
} else {
auto status_paddle = pp_option_ptr_->SetRunMode("paddle");
if (!status_paddle.ok()) {
INFOE("Failed to set run mode: %s", status_paddle.ToString().c_str());
exit(-1);
}
}
auto status_mkldnn_cache_capacityint =
pp_option_ptr_->SetMkldnnCacheCapacity(mkldnn_cache_capacityint);
if (!status_mkldnn_cache_capacityint.ok()) {
INFOE("Set status_mkldnn_cache_capacityint fail : %s",
status_mkldnn_cache_capacityint.ToString().c_str());
exit(-1);
}
auto status_cpu_threads = pp_option_ptr_->SetCpuThreads(cpu_threads);
if (!status_cpu_threads.ok()) {
INFOE("Set cpu threads fail : %s", status_cpu_threads.ToString().c_str());
exit(-1);
}
if (print_flag) {
INFO(pp_option_ptr_->DebugString().c_str());
print_flag = false;
}
INFO("Create model: %s.", model_name_.c_str());
}
std::vector<std::unique_ptr<BaseCVResult>>
BasePredictor::Predict(const std::string &input) {
std::vector<std::string> inputs = {input};
return Predict(inputs);
}
const PaddlePredictorOption &BasePredictor::PPOption() {
return *pp_option_ptr_;
}
void BasePredictor::SetBatchSize(int batch_size) { batch_size_ = batch_size; }
std::unique_ptr<PaddleInfer> BasePredictor::CreateStaticInfer() {
return std::unique_ptr<PaddleInfer>(new PaddleInfer(
model_name_, model_dir_.value(), MODEL_FILE_PREFIX, PPOption()));
}
absl::Status BasePredictor::BuildBatchSampler() {
if (SAMPLER_TYPE.count(sampler_type_) == 0) {
return absl::InvalidArgumentError("Unsupported sampler type !");
} else if (sampler_type_ == "image") {
batch_sampler_ptr_ =
std::unique_ptr<BaseBatchSampler>(new ImageBatchSampler(batch_size_));
}
return absl::OkStatus();
}
const std::unordered_set<std::string> BasePredictor::SAMPLER_TYPE = {
"image",
};
bool BasePredictor::print_flag = true;
+107
View File
@@ -0,0 +1,107 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include "base_batch_sampler.h"
#include "base_cv_result.h"
#include "src/common/static_infer.h"
#include "src/utils/func_register.h"
#include "src/utils/pp_option.h"
#include "src/utils/yaml_config.h"
class BasePredictor {
public:
BasePredictor(const absl::optional<std::string> &model_dir = absl::nullopt,
const absl::optional<std::string> &model_name = absl::nullopt,
const absl::optional<std::string> &device = absl::nullopt,
const std::string &precision = "fp32",
const bool enable_mkldnn = true,
int mkldnn_cache_capacityint = 10, int cpu_threads = 8,
int batch_size = 1, const std::string sample_type = "");
virtual ~BasePredictor() = default;
std::vector<std::unique_ptr<BaseCVResult>> Predict(const std::string &input);
template <typename T>
std::vector<std::unique_ptr<BaseCVResult>> Predict(const T &input);
std::unique_ptr<PaddleInfer> CreateStaticInfer();
const PaddlePredictorOption &PPOption();
absl::StatusOr<std::string> ModelName() { return model_name_; };
std::string ConfigPath() { return config_.ConfigYamlPath(); };
void SetBatchSize(int batch_size);
virtual std::vector<std::unique_ptr<BaseCVResult>>
Process(std::vector<cv::Mat> &batch_data) = 0;
virtual void ResetResult() = 0;
absl::Status BuildBatchSampler();
void SetInputPath(const std::vector<std::string> &input_path) {
input_path_ = input_path;
};
template <typename T, typename... Args>
void Register(const std::string &key, Args &&...args);
static constexpr const char *MODEL_FILE_PREFIX = "inference";
static const std::unordered_set<std::string> SAMPLER_TYPE;
static bool print_flag;
protected:
absl::optional<std::string> model_dir_;
YamlConfig config_;
int batch_size_;
std::unique_ptr<BaseBatchSampler> batch_sampler_ptr_;
std::unique_ptr<PaddlePredictorOption> pp_option_ptr_;
std::vector<std::string> input_path_;
std::string model_name_;
std::string sampler_type_;
std::unordered_map<std::string, std::unique_ptr<BaseProcessor>> pre_op_;
};
template <typename T, typename... Args>
void BasePredictor::Register(const std::string &key, Args &&...args) {
auto instance = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
pre_op_[key] = std::move(instance);
};
template <typename T>
std::vector<std::unique_ptr<BaseCVResult>>
BasePredictor::Predict(const T &input) {
std::vector<std::unique_ptr<BaseCVResult>> result;
ResetResult();
auto batches = batch_sampler_ptr_->Apply(input);
if (!batches.ok()) {
INFOE("Get sample fail : %s", batches.status().ToString().c_str());
exit(-1);
}
input_path_ = batch_sampler_ptr_->InputPath();
for (auto &batch_data : batches.value()) {
auto predictions = Process(batch_data);
for (auto &prediction : predictions) {
result.emplace_back(std::move(prediction));
}
}
return result;
}
@@ -0,0 +1,118 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "image_batch_sampler.h"
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <cctype>
#include <iostream>
#include "src/utils/ilogger.h"
#include "src/utils/utility.h"
const std::set<std::string> ImageBatchSampler::kImgSuffixes = {"jpg", "png",
"jpeg", "bmp"};
ImageBatchSampler::ImageBatchSampler(int batch_size)
: BaseBatchSampler(batch_size) {}
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
ImageBatchSampler::SampleFromString(const std::string &input) {
std::vector<std::string> inputs = {input};
return SampleFromVector(inputs);
}
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
ImageBatchSampler::SampleFromVector(const std::vector<std::string> &inputs) {
std::vector<std::vector<cv::Mat>> results;
std::vector<cv::Mat> current_batch;
input_path_.clear();
for (size_t i = 0; i < inputs.size(); ++i) {
const std::string &input = inputs[i];
if (Utility::IsDirectory(input)) {
absl::StatusOr<std::vector<std::string>> files_result =
GetFilesList(input);
if (!files_result.ok()) {
return files_result.status();
}
input_path_.insert(input_path_.end(), files_result.value().begin(),
files_result.value().end());
absl::StatusOr<std::vector<std::vector<cv::Mat>>> sub_result =
SampleFromVector(files_result.value());
if (!sub_result.ok()) {
return sub_result.status();
}
const std::vector<std::vector<cv::Mat>> &sub_batches = sub_result.value();
for (size_t j = 0; j < sub_batches.size(); ++j) {
results.push_back(sub_batches[j]);
}
} else if (Utility::IsImageFile(input)) {
if (!Utility::FileExists(input).ok()) {
return absl::NotFoundError("File not found: " + input);
}
input_path_.push_back(input);
absl::StatusOr<cv::Mat> image_result = Utility::MyLoadImage(input);
if (!image_result.ok()) {
return image_result.status();
}
current_batch.push_back(image_result.value());
if (static_cast<int>(current_batch.size()) == batch_size_) {
results.push_back(current_batch);
current_batch.clear();
}
} else {
return absl::InvalidArgumentError("Unsupported file type: " + input);
}
}
if (!current_batch.empty()) {
results.push_back(current_batch); // last batch
}
return results;
}
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
ImageBatchSampler::SampleFromMatVector(const std::vector<cv::Mat> &inputs) {
std::vector<std::vector<cv::Mat>> results;
std::vector<cv::Mat> current_batch;
for (size_t i = 0; i < inputs.size(); ++i) {
const cv::Mat &image = inputs[i];
if (image.empty()) {
return absl::InvalidArgumentError("Input image at index " +
std::to_string(i) + " is empty.");
}
current_batch.push_back(image);
if (static_cast<int>(current_batch.size()) == batch_size_) {
results.push_back(current_batch);
current_batch.clear();
}
}
if (!current_batch.empty()) {
results.push_back(current_batch);
}
return results;
}
@@ -0,0 +1,42 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <set>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "src/base/base_batch_sampler.h"
class ImageBatchSampler : public BaseBatchSampler {
public:
explicit ImageBatchSampler(int batch_size = 1);
virtual ~ImageBatchSampler() {} //这里还没调研怎么实现 ???
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
SampleFromString(const std::string &input) override;
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
SampleFromVector(const std::vector<std::string> &inputs) override;
absl::StatusOr<std::vector<std::vector<cv::Mat>>>
SampleFromMatVector(const std::vector<cv::Mat> &inputs) override;
private:
static const std::set<std::string> kImgSuffixes;
};
+229
View File
@@ -0,0 +1,229 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "src/base/base_pipeline.h"
#include "thread_pool.h"
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
class AutoParallelSimpleInferencePipeline : public BasePipeline {
private:
struct InferenceInstance {
std::shared_ptr<BasePipeline> pipeline;
std::queue<PipelineInput> task_queue;
std::queue<std::promise<PipelineResult>> promise_queue;
std::mutex queue_mutex;
std::atomic<bool> is_busy{false};
int instance_id;
};
public:
AutoParallelSimpleInferencePipeline(const PipelineParams &params);
absl::Status Init();
std::future<PipelineResult> PredictAsync(const PipelineInput &input);
absl::Status PredictThread(const PipelineInput &input);
absl::StatusOr<PipelineResult> GetResult();
virtual ~AutoParallelSimpleInferencePipeline();
private:
void ProcessInstanceTasks(int instance_id);
PipelineParams params_;
int thread_num_;
std::atomic<int> round_robin_index_{0};
std::unique_ptr<PaddlePool::ThreadPool> pool_;
std::vector<std::unique_ptr<InferenceInstance>> instances_;
std::queue<std::future<PipelineResult>> legacy_results_;
std::mutex legacy_results_mutex_;
};
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
AutoParallelSimpleInferencePipeline<Pipeline, PipelineParams, PipelineInput,
PipelineResult>::
AutoParallelSimpleInferencePipeline(const PipelineParams &params)
: BasePipeline(), params_(params), thread_num_(params.thread_num) {
if (thread_num_ > 1) {
auto status = Init();
if (!status.ok()) {
INFOE("Pipeline pool init error : %s", status.ToString().c_str());
exit(-1);
}
}
}
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
absl::Status
AutoParallelSimpleInferencePipeline<Pipeline, PipelineParams, PipelineInput,
PipelineResult>::Init() {
try {
pool_ = std::unique_ptr<PaddlePool::ThreadPool>(
new PaddlePool::ThreadPool(thread_num_));
for (int i = 0; i < thread_num_; i++) {
auto instance =
std::unique_ptr<InferenceInstance>(new InferenceInstance());
instance->instance_id = i;
instance->pipeline = std::shared_ptr<BasePipeline>(new Pipeline(params_));
instances_.push_back(std::move(instance));
}
} catch (const std::bad_alloc &e) {
return absl::ResourceExhaustedError(std::string("Out of memory: ") +
e.what());
} catch (const std::exception &e) {
return absl::InternalError(std::string("Init failed: ") + e.what());
}
return absl::OkStatus();
}
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
std::future<PipelineResult> AutoParallelSimpleInferencePipeline<
Pipeline, PipelineParams, PipelineInput,
PipelineResult>::PredictAsync(const PipelineInput &input) {
int instance_id = round_robin_index_.fetch_add(1) % thread_num_;
auto &instance = instances_[instance_id];
std::promise<PipelineResult> promise;
auto future = promise.get_future();
{
std::lock_guard<std::mutex> lock(instance->queue_mutex);
instance->task_queue.push(input);
instance->promise_queue.push(std::move(promise));
}
bool expected = false;
if (instance->is_busy.compare_exchange_strong(
expected, true)) { // one instance just process one input
pool_->submit([this, instance_id]() { ProcessInstanceTasks(instance_id); });
}
return future;
}
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
void AutoParallelSimpleInferencePipeline<
Pipeline, PipelineParams, PipelineInput,
PipelineResult>::ProcessInstanceTasks(int instance_id) {
auto &instance = instances_[instance_id];
while (true) {
std::vector<std::string> input;
std::promise<PipelineResult> promise;
{
std::lock_guard<std::mutex> lock(instance->queue_mutex);
if (instance->task_queue.empty()) {
instance->is_busy = false;
if (!instance->task_queue.empty()) {
bool expected = false;
if (instance->is_busy.compare_exchange_strong(expected, true)) {
continue;
}
}
return;
}
input = std::move(instance->task_queue.front());
instance->task_queue.pop();
promise = std::move(instance->promise_queue.front());
instance->promise_queue.pop();
}
try {
PipelineResult result = instance->pipeline->Predict(input);
promise.set_value(std::move(result));
} catch (const std::exception &e) {
promise.set_exception(std::current_exception());
}
}
}
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
absl::Status AutoParallelSimpleInferencePipeline<
Pipeline, PipelineParams, PipelineInput,
PipelineResult>::PredictThread(const PipelineInput &input) {
try {
auto future = PredictAsync(input);
std::lock_guard<std::mutex> lock(legacy_results_mutex_);
legacy_results_.push(std::move(future));
return absl::OkStatus();
} catch (const std::exception &e) {
return absl::InternalError(std::string("Failed to submit inference: ") +
e.what());
}
}
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
absl::StatusOr<PipelineResult>
AutoParallelSimpleInferencePipeline<Pipeline, PipelineParams, PipelineInput,
PipelineResult>::GetResult() {
std::lock_guard<std::mutex> lock(legacy_results_mutex_);
if (legacy_results_.empty())
return absl::NotFoundError("No inference result available");
try {
auto future = std::move(legacy_results_.front());
legacy_results_.pop();
PipelineResult result = future.get();
return result;
} catch (const std::exception &e) {
return absl::InternalError(std::string("Failed to get inference result: ") +
e.what());
}
}
template <typename Pipeline, typename PipelineParams, typename PipelineInput,
typename PipelineResult>
AutoParallelSimpleInferencePipeline<
Pipeline, PipelineParams, PipelineInput,
PipelineResult>::~AutoParallelSimpleInferencePipeline() {
while (!legacy_results_.empty()) {
try {
legacy_results_.front().get();
} catch (...) {
}
legacy_results_.pop();
}
for (auto &instance : instances_) {
while (instance->is_busy.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
+826
View File
@@ -0,0 +1,826 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "processors.h"
#include <algorithm>
#include <cmath>
#include <numeric>
#include <stdexcept>
#include <unordered_map>
#include "src/utils/ilogger.h"
#include "src/utils/utility.h"
absl::StatusOr<int> Resize::GetInterp(const std::string &interp) {
static const std::unordered_map<std::string, int> interp_map = {
{"NEAREST", cv::INTER_NEAREST},
{"LINEAR", cv::INTER_LINEAR},
{"BICUBIC", cv::INTER_CUBIC},
{"AREA", cv::INTER_AREA},
{"LANCZOS4", cv::INTER_LANCZOS4}};
auto it = interp_map.find(interp);
if (it == interp_map.end())
return -1;
return it->second;
}
std::pair<std::vector<int>, double>
Resize::RescaleSize(const std::vector<int> &img_size) const {
int img_w = img_size[0], img_h = img_size[1];
int target_w = target_size_[0], target_h = target_size_[1];
double scale = std::min(static_cast<double>(std::max(target_w, target_h)) /
std::max(img_w, img_h),
static_cast<double>(std::min(target_w, target_h)) /
std::min(img_w, img_h));
std::vector<int> rescaled_size = {
static_cast<int>(std::round(img_w * scale)),
static_cast<int>(std::round(img_h * scale))};
return std::make_pair(rescaled_size, scale);
}
absl::Status Resize::CheckImageSize() const {
if (target_size_.size() != 2) {
return absl::InvalidArgumentError("Size must be a vector of two elements.");
}
if (target_size_[0] <= 0 || target_size_[1] <= 0) {
return absl::InvalidArgumentError("Width and height must be positive.");
}
return absl::OkStatus();
}
Resize::Resize(const std::vector<int> &target_size, bool keep_ratio,
int size_divisor, const std::string &interp)
: keep_ratio_(keep_ratio), size_divisor_(size_divisor) {
if (target_size.size() == 1) {
target_size_ = {target_size[0], target_size[0]};
} else {
target_size_ = target_size;
}
absl::Status status = CheckImageSize();
if (!status.ok()) {
INFOE("image check fail : %s", status.ToString().c_str());
exit(-1);
}
std::string interp_upper = interp;
std::transform(interp_upper.begin(), interp_upper.end(), interp_upper.begin(),
::toupper);
auto interp_value = GetInterp(interp_upper);
if (!interp_value.ok()) {
INFOE("Unknown type: %s", interp_value.status().ToString().c_str());
exit(-1);
}
interp_ = interp_value.value();
}
absl::StatusOr<std::vector<cv::Mat>> Resize::Apply(std::vector<cv::Mat> &input,
const void *param) const {
std::vector<cv::Mat> out_imgs;
for (const auto &img : input) {
auto out = ResizeOne(img);
if (!out.ok())
return out.status();
out_imgs.push_back(std::move(out.value()));
}
return out_imgs;
}
absl::StatusOr<cv::Mat> Resize::ResizeOne(const cv::Mat &img) const {
if (img.empty()) {
return absl::InvalidArgumentError("Input image is empty.");
}
std::vector<int> cur_target = target_size_;
auto size_test = img.size();
cv::Size orig_size = img.size();
int orig_w = orig_size.width, orig_h = orig_size.height;
if (keep_ratio_) {
std::vector<int> wh = {orig_w, orig_h};
auto rescale = RescaleSize(wh);
cur_target = rescale.first;
}
if (size_divisor_ > 0) {
for (auto &x : cur_target) {
x = static_cast<int>(std::ceil(static_cast<double>(x) / size_divisor_)) *
size_divisor_;
}
}
cv::Mat out;
cv::resize(img, out, cv::Size(cur_target[0], cur_target[1]), 0, 0, interp_);
return out;
}
ResizeByShort::ResizeByShort(int target_short_edge, int size_divisor,
const std::string &interp)
: target_short_edge_(target_short_edge), size_divisor_(size_divisor) {
std::string interp_upper = interp;
std::transform(interp_upper.begin(), interp_upper.end(), interp_upper.begin(),
::toupper);
auto interp_value = Resize::GetInterp(interp_upper);
if (!interp_value.ok()) {
INFOE("Unknown type: %s", interp_value.status().ToString().c_str());
exit(-1);
}
interp_ = interp_value.value();
}
absl::StatusOr<std::vector<cv::Mat>>
ResizeByShort::Apply(std::vector<cv::Mat> &input, const void *param) const {
std::vector<cv::Mat> out_imgs;
for (auto &image : input) {
auto out = ResizeOne(image);
if (!out.ok())
return out.status();
out_imgs.push_back(std::move(out.value()));
}
return out_imgs;
}
absl::StatusOr<cv::Mat> ResizeByShort::ResizeOne(const cv::Mat &img) const {
if (img.empty()) {
return absl::InvalidArgumentError("Input image is empty.");
}
int h = img.size[0];
int w = img.size[1];
int short_edge = std::min(h, w);
float scale = static_cast<double>(target_short_edge_) / short_edge;
int h_resize = static_cast<int>(std::round(h * scale));
int w_resize = static_cast<int>(std::round(w * scale));
if (size_divisor_ > 0) {
h_resize = static_cast<int>(std::ceil(h_resize / (float)size_divisor_)) *
size_divisor_;
w_resize = static_cast<int>(std::ceil(w_resize / (float)size_divisor_)) *
size_divisor_;
}
cv::Mat dst;
cv::resize(img, dst, cv::Size(w_resize, h_resize), 0, 0, interp_);
return dst;
}
ReadImage::ReadImage(const std::string &format) {
auto fmt = StringToFormat(format);
if (!fmt.ok()) {
INFOE(fmt.status().ToString().c_str());
exit(-1);
}
format_ = *fmt;
}
absl::StatusOr<std::vector<cv::Mat>>
ReadImage::Apply(std::vector<cv::Mat> &input, const void *param_ptr) const {
if (input.empty()) {
return absl::InvalidArgumentError("Input image vector is empty.");
}
std::vector<cv::Mat> output;
output.reserve(input.size());
for (size_t i = 0; i < input.size(); ++i) {
const cv::Mat &img = input[i];
if (img.empty()) {
return absl::InvalidArgumentError("Image at index " + std::to_string(i) +
" is empty.");
}
cv::Mat converted;
switch (format_) {
case Format::BGR:
if (img.channels() == 3) {
converted = img.clone();
} else if (img.channels() == 1) {
cv::cvtColor(img, converted, cv::COLOR_GRAY2BGR);
} else {
return absl::InvalidArgumentError("Image at index " +
std::to_string(i) +
" channel not supported for BGR.");
}
break;
case Format::RGB:
if (img.channels() == 3) {
cv::cvtColor(img, converted, cv::COLOR_BGR2RGB);
} else if (img.channels() == 1) {
cv::cvtColor(img, converted, cv::COLOR_GRAY2RGB);
} else {
return absl::InvalidArgumentError("Image at index " +
std::to_string(i) +
" channel not supported for RGB.");
}
break;
case Format::GRAY:
if (img.channels() == 3) {
cv::cvtColor(img, converted, cv::COLOR_BGR2GRAY);
} else if (img.channels() == 1) {
converted = img.clone();
} else {
return absl::InvalidArgumentError("Image at index " +
std::to_string(i) +
" channel not supported for GRAY.");
}
break;
default:
return absl::InvalidArgumentError("Unknown format.");
}
output.push_back(std::move(converted));
}
return output;
}
absl::StatusOr<ReadImage::Format>
ReadImage::StringToFormat(const std::string &format) {
if (format == "BGR")
return Format::BGR;
if (format == "RGB")
return Format::RGB;
if (format == "GRAY")
return Format::GRAY;
return absl::InvalidArgumentError("Unsupported format: " + format);
}
absl::StatusOr<std::vector<cv::Mat>>
ToCHWImage::operator()(const std::vector<cv::Mat> &imgs_batch) {
std::vector<std::vector<cv::Mat>> chw_imgs_batch;
std::vector<cv::Mat> chw_imgs;
for (const auto &img : imgs_batch) {
if (img.empty()) {
return absl::InvalidArgumentError("Input image is empty!");
}
if (img.channels() != 3) {
return absl::InvalidArgumentError(
"Input image must have 3 channels (HWC format)!");
}
cv::Mat chw_img(3, img.rows * img.cols, CV_32F);
float *ptr = chw_img.ptr<float>();
for (int h = 0; h < img.rows; ++h) {
for (int w = 0; w < img.cols; ++w) {
const cv::Vec3b &pixel = img.at<cv::Vec3b>(h, w);
ptr[0 * img.total() + h * img.cols + w] = pixel[0];
ptr[1 * img.total() + h * img.cols + w] = pixel[1];
ptr[2 * img.total() + h * img.cols + w] = pixel[2];
}
}
chw_imgs.push_back(chw_img);
}
return chw_imgs;
};
Normalize::Normalize(float scale, const std::vector<float> &mean,
const std::vector<float> &std)
: alpha_(CHANNEL), beta_(CHANNEL) {
assert(mean.size() == CHANNEL && std.size() == CHANNEL);
for (size_t i = 0; i < CHANNEL; ++i) {
alpha_[i] = scale / std.at(i);
beta_[i] = -mean.at(i) / std.at(i);
}
}
Normalize::Normalize(float scale, const float &mean, const float &std)
: alpha_(CHANNEL), beta_(CHANNEL) {
for (size_t i = 0; i < CHANNEL; ++i) {
alpha_[i] = scale / std;
beta_[i] = -mean / std;
}
}
absl::StatusOr<cv::Mat> Normalize::NormalizeOne(const cv::Mat &image) const {
if (image.empty()) {
return absl::InvalidArgumentError("Input image is empty.");
}
if (image.channels() != CHANNEL) {
return absl::InvalidArgumentError("Input image must have 3 dims");
}
if (image.depth() != CV_8U && image.depth() != CV_32F) {
return absl::InvalidArgumentError("Input image must be CV_8U or CV_32F.");
}
cv::Mat input;
if (image.depth() == CV_8U) {
image.convertTo(input, CV_32F);
} else {
input = image.clone(); // note origin type is CV_8U
}
if (input.channels() == CHANNEL) {
cv::Mat processed = input;
std::vector<cv::Mat> channels(input.channels());
cv::split(processed, channels);
for (int c = 0; c < input.channels(); ++c) {
channels[c] = channels[c] * alpha_[c] + beta_[c];
}
cv::merge(channels, processed);
return processed;
} else { // dims >= 3
assert(input.isContinuous());
int total = 1;
for (int i = 0; i < input.dims - 1; i++) {
total *= input.size[i];
}
float *data = input.ptr<float>();
for (int i = 0; i < total; i++) {
float *group = data + i * CHANNEL;
for (int j = 0; j < CHANNEL; j++) {
group[j] = group[j] * alpha_[j] + beta_[j];
}
}
return input;
}
}
absl::StatusOr<std::vector<cv::Mat>>
Normalize::Apply(std::vector<cv::Mat> &input, const void *param) const {
std::vector<cv::Mat> results_norm;
results_norm.reserve(input.size());
for (const auto &img : input) {
auto norm_single = NormalizeOne(img);
if (!norm_single.ok()) {
return norm_single.status();
}
results_norm.emplace_back(norm_single.value());
}
return results_norm;
}
NormalizeImage::NormalizeImage(float scale, const std::vector<float> &mean,
const std::vector<float> &std)
: alpha_(CHANNEL), beta_(CHANNEL) {
assert(mean.size() == CHANNEL && std.size() == CHANNEL);
for (size_t i = 0; i < CHANNEL; ++i) {
alpha_[i] = scale / std.at(i);
beta_[i] = -mean.at(i) / std.at(i);
}
}
absl::StatusOr<cv::Mat> NormalizeImage::Normalize(const cv::Mat &img) const {
if (img.empty()) {
return absl::InvalidArgumentError("Input image is empty.");
}
if (img.channels() != CHANNEL) {
return absl::InvalidArgumentError("Input image must have 3 channels.");
}
if (img.depth() != CV_8U && img.depth() != CV_32F) {
return absl::InvalidArgumentError("Input image must be CV_8U or CV_32F.");
}
cv::Mat input;
if (img.depth() == CV_8U) {
img.convertTo(input, CV_32F);
} else {
input = img.clone();
}
cv::Mat processed = input;
std::vector<cv::Mat> channels(CHANNEL);
cv::split(processed, channels);
for (int c = 0; c < CHANNEL; ++c) {
channels[c] = channels[c] * alpha_[c] + beta_[c];
}
cv::merge(channels, processed);
return processed;
}
absl::StatusOr<std::vector<cv::Mat>>
NormalizeImage::Apply(std::vector<cv::Mat> &imgs, const void *param) const {
std::vector<cv::Mat> results;
results.reserve(imgs.size());
for (const auto &img : imgs) {
auto normed = this->Normalize(img);
if (!normed.ok()) {
return normed.status();
}
results.push_back(std::move(normed).value());
}
return results;
}
// absl::StatusOr<std::vector<cv::Mat>> ToCHWImage::Apply(
// std::vector<cv::Mat>& input, const void* param) const {
// std::vector<cv::Mat> chw_imgs;
// for (const auto& img : input) {
// if (img.empty()) {
// return absl::InvalidArgumentError("Input image is empty!");
// }
// if (img.channels() != 3) {
// return absl::InvalidArgumentError(
// "Input image must have 3 channels (HWC format)!");
// }
// std::vector<int> shape_chw = {img.channels(), img.rows, img.cols}; //
// Define sizes for CHW cv::Mat chw_img(shape_chw.size(), shape_chw.data(),
// CV_32F); float* ptr = chw_img.ptr<float>(); for (int h = 0; h < img.rows;
// ++h) {
// for (int w = 0; w < img.cols; ++w) {
// const cv::Vec3f& pixel = img.at<cv::Vec3f>(h, w);
// ptr[0 * img.total() + h * img.cols + w] = pixel[0];
// ptr[1 * img.total() + h * img.cols + w] = pixel[1];
// ptr[2 * img.total() + h * img.cols + w] = pixel[2];
// }
// }
// chw_imgs.push_back(chw_img);
// }
// return chw_imgs;
// }
absl::StatusOr<std::vector<cv::Mat>>
ToCHWImage::Apply(std::vector<cv::Mat> &input, const void *param) const {
std::vector<cv::Mat> chw_imgs;
for (const auto &img : input) {
if (img.empty()) {
return absl::InvalidArgumentError("Input image is empty!");
}
if (img.channels() != 3) {
return absl::InvalidArgumentError(
"Input image must have 3 channels (HWC format)!");
}
std::vector<cv::Mat> vec_split = {};
cv::split(img, vec_split);
cv::Mat chw_img;
for (auto &split : vec_split)
split = split.reshape(1, 1);
cv::hconcat(vec_split, chw_img);
std::vector<int> shape = {img.channels(), img.size[0], img.size[1]};
chw_img = chw_img.reshape(1, shape);
chw_imgs.push_back(chw_img);
}
return chw_imgs;
}
absl::StatusOr<std::vector<cv::Mat>>
ToBatch::operator()(const std::vector<cv::Mat> &imgs) const {
if (imgs.empty()) {
return absl::InvalidArgumentError("Input image vector is empty.");
}
const int batch = imgs.size();
const int rows = imgs[0].rows;
const int cols = imgs[0].cols;
const int channels = imgs[0].channels();
for (size_t i = 0; i < imgs.size(); ++i) {
if (imgs[i].rows != rows || imgs[i].cols != cols ||
imgs[i].channels() != channels) {
return absl::InvalidArgumentError(
"All images must have the same size and number of channels.");
}
}
std::vector<int> sizes = {batch, rows, cols, channels};
cv::Mat out(4, sizes.data(), CV_32F);
for (int b = 0; b < batch; ++b) {
cv::Mat img_float;
if (imgs[b].depth() != CV_32F) {
imgs[b].convertTo(img_float, CV_32F);
} else {
img_float = imgs[b];
}
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
if (channels == 1) {
float v = img_float.at<float>(r, c);
int idx[4] = {b, r, c, 0};
out.at<float>(idx) = v;
} else if (channels == 3) {
cv::Vec3f v = img_float.at<cv::Vec3f>(r, c);
for (int ch = 0; ch < 3; ++ch) {
int idx[4] = {b, r, c, ch};
out.at<float>(idx) = v[ch];
}
} else {
const float *pix = img_float.ptr<float>(r, c);
for (int ch = 0; ch < channels; ++ch) {
int idx[4] = {b, r, c, ch};
out.at<float>(idx) = pix[ch];
}
}
}
}
}
std::vector<cv::Mat> result{out};
return result;
}
absl::StatusOr<std::vector<cv::Mat>> ToBatch::Apply(std::vector<cv::Mat> &input,
const void *param) const {
if (input.empty()) {
return absl::InvalidArgumentError("Input image vector is empty.");
}
std::vector<int> batch_shape = {(int)input.size()};
for (const auto &image : input) {
if (image.dims != input[0].dims) {
return absl::InvalidArgumentError("All images must have the same dims.");
} else {
for (int i = 0; i < input[0].dims; i++) {
if (image.size[i] != input[0].size[i]) {
return absl::InvalidArgumentError(
"All images must have the same size and number of channels.");
}
if (&image == &(*std::begin(input)))
batch_shape.emplace_back(input[0].size[i]);
}
}
}
cv::Mat batch_out;
for (auto &image : input)
image = image.reshape(1, 1);
cv::vconcat(input, batch_out);
batch_out = batch_out.reshape(1, batch_shape);
std::vector<cv::Mat> out = {batch_out};
return out;
}
absl::StatusOr<cv::Mat> ComponentsProcessor::RotateImage(const cv::Mat &image,
int angle) {
if (image.empty() || image.channels() != 3) {
return absl::InvalidArgumentError("image is invalid");
}
if (angle < 0 || angle >= 360) {
return absl::InvalidArgumentError("`angle` should be in range [0, 360)");
}
if (std::abs(angle) < 1e-7) {
return image.clone();
}
int h = image.rows;
int w = image.cols;
cv::Point2f center(w / 2.0f, h / 2.0f);
double scale = 1.0;
cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, scale);
double abs_cos = std::abs(rot_mat.at<double>(0, 0));
double abs_sin = std::abs(rot_mat.at<double>(0, 1));
int new_w = int(h * abs_sin + w * abs_cos);
int new_h = int(h * abs_cos + w * abs_sin);
rot_mat.at<double>(0, 2) += (new_w - w) / 2.0;
rot_mat.at<double>(1, 2) += (new_h - h) / 2.0;
cv::Mat rotated;
cv::warpAffine(image, rotated, rot_mat, cv::Size(new_w, new_h),
cv::INTER_CUBIC);
return rotated;
}
std::vector<std::vector<cv::Point2f>> ComponentsProcessor::SortQuadBoxes(
const std::vector<std::vector<cv::Point2f>> &dt_polys) {
std::vector<std::vector<cv::Point2f>> dt_boxes = dt_polys;
std::sort(
dt_boxes.begin(), dt_boxes.end(),
[](const std::vector<cv::Point2f> &a, const std::vector<cv::Point2f> &b) {
return (a[0].y < b[0].y) || (a[0].y == b[0].y && a[0].x < b[0].x);
});
for (size_t i = 0; i < dt_boxes.size() - 1; ++i) {
for (size_t j = i + 1; j > 0; --j) {
if (std::abs(dt_boxes[j][0].y - dt_boxes[j - 1][0].y) < 10 &&
dt_boxes[j][0].x < dt_boxes[j - 1][0].x) {
std::swap(dt_boxes[j], dt_boxes[j - 1]);
} else {
break;
}
}
}
return dt_boxes;
}
std::vector<std::vector<cv::Point2f>> ComponentsProcessor::SortPolyBoxes(
const std::vector<std::vector<cv::Point2f>> &dt_polys) {
size_t num_boxes = dt_polys.size();
if (num_boxes == 0)
return dt_polys;
std::vector<int> y_min_list(num_boxes);
for (size_t i = 0; i < num_boxes; ++i) {
int y_min = dt_polys[i][0].y;
for (size_t j = 1; j < dt_polys[i].size(); ++j) {
if (dt_polys[i][j].y < y_min) {
y_min = dt_polys[i][j].y;
}
}
y_min_list[i] = y_min;
}
std::vector<size_t> rank(num_boxes);
std::iota(rank.begin(), rank.end(), 0);
std::sort(rank.begin(), rank.end(),
[&](size_t a, size_t b) { return y_min_list[a] < y_min_list[b]; });
std::vector<std::vector<cv::Point2f>> dt_polys_rank(num_boxes);
for (size_t i = 0; i < num_boxes; ++i) {
dt_polys_rank[i] = dt_polys[rank[i]];
}
return dt_polys_rank;
}
std::vector<std::array<float, 4>> ComponentsProcessor::ConvertPointsToBoxes(
const std::vector<std::vector<cv::Point2f>> &dt_polys) {
std::vector<std::array<float, 4>> dt_boxes;
for (const auto &poly : dt_polys) {
if (poly.empty()) {
continue;
}
float left = std::numeric_limits<float>::max();
float right = std::numeric_limits<float>::lowest();
float top = std::numeric_limits<float>::max();
float bottom = std::numeric_limits<float>::lowest();
for (const auto &pt : poly) {
if (pt.x < left)
left = pt.x;
if (pt.x > right)
right = pt.x;
if (pt.y < top)
top = pt.y;
if (pt.y > bottom)
bottom = pt.y;
}
dt_boxes.push_back({left, top, right, bottom});
}
return dt_boxes;
}
CropByPolys::CropByPolys(const std::string &box_type) {
assert(box_type == "quad" || box_type == "poly");
if (box_type == "quad") {
box_type_ = DetBoxType::kQuad;
} else {
box_type_ = DetBoxType::kPoly;
}
}
absl::StatusOr<std::vector<cv::Mat>>
CropByPolys::operator()(const cv::Mat &img,
const std::vector<std::vector<cv::Point2f>> &dt_polys) {
if (img.empty())
return absl::InvalidArgumentError("Input image is empty.");
std::vector<cv::Mat> output_list;
try {
if (box_type_ == DetBoxType::kQuad) {
for (const auto &poly : dt_polys) {
auto out = GetMinAreaRectCrop(img, poly);
if (!out.ok())
return out.status();
output_list.push_back(*out);
}
} else if (box_type_ == DetBoxType::kPoly) {
for (const auto &poly : dt_polys) {
auto out = GetPolyRectCrop(img, poly);
if (!out.ok())
return out.status();
output_list.push_back(*out);
}
} else {
return absl::UnimplementedError("Unknown box type.");
}
} catch (const std::exception &e) {
return absl::InternalError(std::string("Exception: ") + e.what());
}
return output_list;
}
absl::StatusOr<cv::Mat>
CropByPolys::GetMinAreaRectCrop(const cv::Mat &img,
const std::vector<cv::Point2f> &points) const {
if (points.size() < 4)
return absl::InvalidArgumentError("Less than 4 points for min area rect.");
std::vector<cv::Point2f> box = GetMinAreaRectPoints(points);
return GetRotateCropImage(img, box);
}
absl::StatusOr<cv::Mat>
CropByPolys::GetRotateCropImage(const cv::Mat &img,
const std::vector<cv::Point2f> &box) const {
if (box.size() != 4)
return absl::InvalidArgumentError("Box must have 4 points.");
float widthTop = cv::norm(box[0] - box[1]);
float widthBottom = cv::norm(box[2] - box[3]);
float maxWidth = std::max(widthTop, widthBottom);
float heightLeft = cv::norm(box[0] - box[3]);
float heightRight = cv::norm(box[1] - box[2]);
float maxHeight = std::max(heightLeft, heightRight);
std::vector<cv::Point2f> dst = {
cv::Point2f(0, 0), cv::Point2f(maxWidth - 1, 0),
cv::Point2f(maxWidth - 1, maxHeight - 1), cv::Point2f(0, maxHeight - 1)};
cv::Mat M = cv::getPerspectiveTransform(box, dst);
cv::Mat out;
cv::warpPerspective(img, out, M, cv::Size((int)maxWidth, (int)maxHeight),
cv::INTER_CUBIC, cv::BORDER_REPLICATE);
if (out.rows != 0 && 1.0 * out.rows / out.cols >= 1.5)
cv::rotate(out, out, cv::ROTATE_90_COUNTERCLOCKWISE);
return out;
}
std::vector<cv::Point2f>
CropByPolys::GetMinAreaRectPoints(const std::vector<cv::Point2f> &poly) const {
auto pts = poly;
if (pts.size() < 4)
return {};
cv::RotatedRect minRect = cv::minAreaRect(pts);
std::vector<cv::Point2f> box(4);
minRect.points(box.data());
std::sort(box.begin(), box.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
});
size_t index_a = 0, index_d = 1;
if (box[1].y > box[0].y) {
index_a = 0;
index_d = 1;
} else {
index_a = 1;
index_d = 0;
}
size_t index_b = 2, index_c = 3;
if (box[3].y > box[2].y) {
index_b = 2;
index_c = 3;
} else {
index_b = 3;
index_c = 2;
}
return {box[index_a], box[index_b], box[index_c], box[index_d]};
}
absl::StatusOr<cv::Mat>
CropByPolys::GetPolyRectCrop(const cv::Mat &img,
const std::vector<cv::Point2f> &poly) const {
if (poly.size() < 4)
return absl::InvalidArgumentError(
"Less than 4 points for GetPolyRectCrop.");
// 对Poly和最小外接矩形做IoU判断
std::vector<cv::Point2f> minrect = GetMinAreaRectPoints(poly);
if (minrect.size() != 4)
return absl::InternalError("Failed to get minarea rect.");
double iou = IoU(poly, minrect);
// 若IoU>0.7则返回直接crop,否则可做更复杂处理,如透视矫正,可进一步实现自定义变形矫正
auto crop_result = GetRotateCropImage(img, minrect);
if (!crop_result.ok())
return crop_result.status();
// 测试下如果IoU很高就用直接的最小外接矩形crop,否则复杂矫正(本实现只用直接crop)
// 若需更强几何修复,可集成TPS、ThinPlateSpline或AutoRectifier
return *crop_result;
}
const double CropByPolys::SCALE = 10000.0;
ClipperLib::Path
CropByPolys::CvPolyToClipperPath(const std::vector<cv::Point2f> &poly) {
ClipperLib::Path path;
for (const auto &pt : poly)
path.emplace_back(static_cast<ClipperLib::cInt>(std::round(pt.x * SCALE)),
static_cast<ClipperLib::cInt>(std::round(pt.y * SCALE)));
return path;
}
double CropByPolys::IoU(const std::vector<cv::Point2f> &poly1,
const std::vector<cv::Point2f> &poly2) {
auto path1 = CvPolyToClipperPath(poly1);
auto path2 = CvPolyToClipperPath(poly2);
ClipperLib::Paths inter_solution, union_solution;
ClipperLib::Clipper c_inter, c_union;
c_inter.AddPath(path1, ClipperLib::ptSubject, true);
c_inter.AddPath(path2, ClipperLib::ptClip, true);
c_inter.Execute(ClipperLib::ctIntersection, inter_solution,
ClipperLib::pftNonZero, ClipperLib::pftNonZero);
double area_inter = 0.0;
for (const auto &p : inter_solution)
area_inter += std::fabs(ClipperLib::Area(p));
c_union.AddPath(path1, ClipperLib::ptSubject, true);
c_union.AddPath(path2, ClipperLib::ptClip, true);
c_union.Execute(ClipperLib::ctUnion, union_solution, ClipperLib::pftNonZero,
ClipperLib::pftNonZero);
double area_union = 0.0;
for (const auto &p : union_solution)
area_union += std::fabs(ClipperLib::Area(p));
area_inter /= (SCALE * SCALE);
area_union /= (SCALE * SCALE);
if (area_union < 1e-8)
return 0.0;
return area_inter / area_union;
}
+182
View File
@@ -0,0 +1,182 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "polyclipping/clipper.hpp"
#include "src/utils/func_register.h"
class Resize : public BaseProcessor {
public:
Resize(const std::vector<int> &target_size, bool keep_ratio = false,
int size_divisor = 0, const std::string &interp = "LINEAR");
absl::Status CheckImageSize() const;
std::pair<std::vector<int>, double>
RescaleSize(const std::vector<int> &img_size) const;
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
absl::StatusOr<cv::Mat> ResizeOne(const cv::Mat &img) const;
static absl::StatusOr<int> GetInterp(const std::string &interp);
private:
std::vector<int> target_size_;
bool keep_ratio_;
int size_divisor_;
int interp_;
};
class ResizeByShort : public BaseProcessor {
public:
ResizeByShort(int target_short_edge, int size_divisor = 0,
const std::string &interp = "LINEAR");
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
absl::StatusOr<cv::Mat> ResizeOne(const cv::Mat &img) const;
private:
int target_short_edge_;
int size_divisor_;
int interp_;
};
class ReadImage : public BaseProcessor {
public:
enum class Format { BGR, RGB, GRAY };
ReadImage(const std::string &format = "RGB");
ReadImage(const ReadImage &) = delete;
ReadImage &operator=(const ReadImage &) = delete;
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param_ptr = nullptr) const override;
private:
static absl::StatusOr<Format> StringToFormat(const std::string &format);
Format format_;
};
class Normalize : public BaseProcessor {
public:
Normalize(float scale = 1.0 / 255.0,
const std::vector<float> &mean = {0.5, 0.5, 0.5},
const std::vector<float> &std = {0.5, 0.5, 0.5});
Normalize(float scale = 1.0 / 255.0, const float &mean = 0.5,
const float &std = 0.5);
absl::StatusOr<cv::Mat> NormalizeOne(const cv::Mat &input) const;
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
static constexpr int CHANNEL = 3;
private:
std::vector<float> alpha_;
std::vector<float> beta_;
};
class NormalizeImage : public BaseProcessor {
public:
NormalizeImage(float scale = 1.0 / 255.0,
const std::vector<float> &mean = {0.485, 0.456, 0.406},
const std::vector<float> &std = {0.229, 0.224, 0.225});
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
private:
std::vector<float> alpha_;
std::vector<float> beta_;
absl::StatusOr<cv::Mat> Normalize(const cv::Mat &img) const;
NormalizeImage(const NormalizeImage &) = delete;
NormalizeImage &operator=(const NormalizeImage &) = delete;
static constexpr int CHANNEL = 3;
};
class ToCHWImage : public BaseProcessor {
public:
absl::StatusOr<std::vector<cv::Mat>>
operator()(const std::vector<cv::Mat> &imgs_batch);
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
};
class ToBatch : public BaseProcessor {
public:
absl::StatusOr<std::vector<cv::Mat>>
operator()(const std::vector<cv::Mat> &imgs) const;
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
};
class ComponentsProcessor {
public:
static absl::StatusOr<cv::Mat> RotateImage(const cv::Mat &image, int angle);
static std::vector<std::vector<cv::Point2f>>
SortQuadBoxes(const std::vector<std::vector<cv::Point2f>> &dt_polys);
static std::vector<std::vector<cv::Point2f>>
SortPolyBoxes(const std::vector<std::vector<cv::Point2f>> &dt_polys);
static std::vector<std::array<float, 4>>
ConvertPointsToBoxes(const std::vector<std::vector<cv::Point2f>> &dt_polys);
};
class CropByPolys {
public:
enum class DetBoxType { kQuad, kPoly };
CropByPolys(const std::string &box_type = "quad");
absl::StatusOr<std::vector<cv::Mat>>
operator()(const cv::Mat &img,
const std::vector<std::vector<cv::Point2f>> &dt_polys);
absl::StatusOr<cv::Mat>
GetMinAreaRectCrop(const cv::Mat &img,
const std::vector<cv::Point2f> &points) const;
absl::StatusOr<cv::Mat>
GetPolyRectCrop(const cv::Mat &img,
const std::vector<cv::Point2f> &poly) const;
absl::StatusOr<cv::Mat>
GetRotateCropImage(const cv::Mat &img,
const std::vector<cv::Point2f> &box) const;
std::vector<cv::Point2f>
GetMinAreaRectPoints(const std::vector<cv::Point2f> &poly) const;
static double IoU(const std::vector<cv::Point2f> &poly1,
const std::vector<cv::Point2f> &poly2);
static ClipperLib::Path
CvPolyToClipperPath(const std::vector<cv::Point2f> &poly);
static const double SCALE;
private:
DetBoxType box_type_;
};
+197
View File
@@ -0,0 +1,197 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "static_infer.h"
#include <fstream>
#include "src/utils/ilogger.h"
#include "src/utils/mkldnn_blocklist.h"
#include "src/utils/utility.h"
PaddleInfer::PaddleInfer(const std::string &model_name,
const std::string &model_dir,
const std::string &model_file_prefix,
const PaddlePredictorOption &option)
: model_name_(model_name), model_dir_(model_dir),
model_file_prefix_(model_file_prefix), option_(option) {
auto result = Create();
if (!result.ok()) {
INFOE("Create predictor failed: %s", result.status().ToString().c_str());
exit(-1);
}
predictor_ = std::move(result.value());
auto input_names = predictor_->GetInputNames();
for (const auto &name : input_names) {
auto handle = predictor_->GetInputHandle(name);
input_handles_.emplace_back(std::move(handle));
}
auto output_names = predictor_->GetOutputNames();
for (const auto &name : output_names) {
auto handle = predictor_->GetOutputHandle(name);
output_handles_.emplace_back(std::move(handle));
}
}
absl::StatusOr<std::shared_ptr<paddle_infer::Predictor>> PaddleInfer::Create() {
auto model_paths = Utility::GetModelPaths(model_dir_, model_file_prefix_);
if (!model_paths.ok()) {
return model_paths.status();
}
if (model_paths->find("paddle") == model_paths->end()) {
return absl::NotFoundError("No valid PaddlePaddle model found");
}
auto result_check = CheckRunMode();
if (!result_check.ok()) {
return result_check;
}
auto model_files = model_paths.value()["paddle"];
std::string model_file = model_files.first;
std::string params_file = model_files.second;
if (option_.DeviceType() == "cpu" && option_.DeviceId() >= 0) {
auto result_set = option_.SetDeviceId(0);
if (!result_set.ok()) {
return result_set;
}
INFO("`device_id` has been set to nullptr");
}
if (option_.DeviceType() == "gpu" && option_.DeviceId() < 0) {
auto result_device_id = option_.SetDeviceId(0);
if (!result_device_id.ok()) {
return result_device_id;
}
INFO("`device_id` has been set to 0");
}
paddle_infer::Config config;
config.SetModel(model_file, params_file);
if (option_.DeviceType() == "gpu") {
std::unordered_set<std::string> mixed_op_set = {"feed", "fetch"};
config.Exp_DisableMixedPrecisionOps(mixed_op_set);
paddle_infer::PrecisionType precision =
paddle_infer::PrecisionType::kFloat32;
if (option_.RunMode() == "paddle_fp16") {
precision = paddle_infer::PrecisionType::kHalf;
}
config.DisableMKLDNN();
config.EnableUseGpu(100, option_.DeviceId(), precision);
config.EnableNewIR(option_.EnableNewIR());
if (option_.EnableNewIR() && option_.EnableCinn()) {
config.EnableCINN();
}
config.EnableNewExecutor();
config.SetOptimizationLevel(3);
} else if (option_.DeviceType() == "cpu") {
config.DisableGpu();
if (option_.RunMode().find("mkldnn") != std::string::npos) {
config.EnableMKLDNN();
if (option_.RunMode().find("bf16") != std::string::npos) {
config.EnableMkldnnBfloat16();
}
config.SetMkldnnCacheCapacity(option_.MkldnnCacheCapacity());
} else {
config.DisableMKLDNN();
}
config.SetCpuMathLibraryNumThreads(option_.CpuThreads());
config.EnableNewIR(option_.EnableNewIR());
config.EnableNewExecutor();
config.SetOptimizationLevel(3);
} else {
return absl::InvalidArgumentError("Not supported device type: " +
option_.DeviceType());
}
config.EnableMemoryOptim();
for (const auto &del_p : option_.DeletePass()) {
config.DeletePass(del_p);
}
config.DisableGlogInfo();
auto predictor_shared = paddle_infer::CreatePredictor(config);
return predictor_shared;
};
absl::StatusOr<std::vector<cv::Mat>>
PaddleInfer::Apply(const std::vector<cv::Mat> &x) {
for (size_t i = 0; i < x.size(); ++i) {
auto &input_handle = input_handles_[i];
std::vector<int> input_shape(x[0].dims);
for (int i = 0; i < x[0].dims; i++) {
input_shape[i] = x[0].size[i];
}
input_handle->Reshape(input_shape);
input_handle->CopyFromCpu<float>((float *)x[i].data);
}
try {
predictor_->Run();
} catch (const std::exception &e) {
INFOE("static Infer fail: %s", e.what());
exit(-1);
}
std::vector<std::vector<float>> outputs;
std::vector<int> output_shape = {};
for (auto &output_handle : output_handles_) {
output_shape = output_handle->shape();
size_t numel = 1;
for (auto dim : output_shape)
numel *= dim;
std::vector<float> out_data(numel);
output_handle->CopyToCpu(out_data.data());
outputs.push_back(std::move(out_data));
}
auto size_v = outputs[0].size();
cv::Mat pred(output_shape.size(), output_shape.data(), CV_32F);
memcpy(pred.ptr<float>(), outputs[0].data(),
outputs[0].size() * sizeof(float));
std::vector<cv::Mat> pred_outputs = {pred};
return pred_outputs;
};
absl::Status PaddleInfer::CheckRunMode() {
if (option_.RunMode().rfind("mkldnn", 0) == 0 &&
Mkldnn::MKLDNN_BLOCKLIST.count(model_name_) > 0 &&
option_.DeviceType() == "cpu") {
INFOW("The model %s is not supported to run in MKLDNN mode! Using `paddle` "
"instead!",
model_name_.c_str());
auto result = option_.SetRunMode("paddle");
if (!result.ok()) {
return result;
}
}
if (model_name_ == "LaTeX_OCR_rec" && option_.DeviceType() == "cpu") {
if (Utility::IsMkldnnAvailable() && option_.RunMode() != "mkldnn") {
INFOE("Now, the `LaTeX_OCR_rec` model only support `mkldnn` mode when "
"running on Intel CPU devices. So using `mkldnn` instead.");
exit(-1);
auto result = option_.SetRunMode("mkldnn");
if (!result.ok()) {
return result;
}
}
}
return absl::OkStatus();
};
@@ -0,0 +1,49 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "paddle_inference_api.h"
#include "src/utils/ilogger.h"
#include "src/utils/pp_option.h"
class PaddleInfer {
public:
explicit PaddleInfer(const std::string &model_name,
const std::string &model_dir,
const std::string &model_file_prefix,
const PaddlePredictorOption &option);
~PaddleInfer() = default;
absl::StatusOr<std::vector<cv::Mat>>
Apply(const std::vector<cv::Mat> &x); //***********
private:
std::string model_dir_;
std::string model_file_prefix_;
std::string model_name_;
PaddlePredictorOption option_;
std::shared_ptr<paddle_infer::Predictor> predictor_;
std::vector<std::unique_ptr<paddle_infer::Tensor>> input_handles_;
std::vector<std::unique_ptr<paddle_infer::Tensor>> output_handles_;
absl::StatusOr<std::shared_ptr<paddle_infer::Predictor>> Create();
absl::Status CheckRunMode();
};
@@ -0,0 +1,88 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "thread_pool.h"
namespace PaddlePool {
constexpr size_t ThreadPool::WAIT_SECONDS;
ThreadPool::ThreadPool() : ThreadPool(Thread::hardware_concurrency()) {}
ThreadPool::ThreadPool(size_t maxThreads)
: quit_(false), currentThreads_(0), idleThreads_(0),
maxThreads_(maxThreads) {}
ThreadPool::~ThreadPool() {
{
MutexGuard guard(mutex_);
quit_ = true;
}
cv_.notify_all();
for (auto &elem : threads_) {
assert(elem.second.joinable());
elem.second.join();
}
}
size_t ThreadPool::threadsNum() const {
MutexGuard guard(mutex_);
return currentThreads_;
}
void ThreadPool::worker() {
while (true) {
Task task;
{
UniqueLock uniqueLock(mutex_);
++idleThreads_;
auto hasTimedout =
!cv_.wait_for(uniqueLock, std::chrono::seconds(WAIT_SECONDS),
[this]() { return quit_ || !tasks_.empty(); });
--idleThreads_;
if (tasks_.empty()) {
if (quit_) {
--currentThreads_;
return;
}
if (hasTimedout) {
--currentThreads_;
joinFinishedThreads();
finishedThreadIDs_.emplace(std::this_thread::get_id());
return;
}
}
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
}
void ThreadPool::joinFinishedThreads() {
while (!finishedThreadIDs_.empty()) {
auto id = std::move(finishedThreadIDs_.front());
finishedThreadIDs_.pop();
auto iter = threads_.find(id);
assert(iter != threads_.end());
assert(iter->second.joinable());
iter->second.join();
threads_.erase(iter);
}
}
} // namespace PaddlePool
+99
View File
@@ -0,0 +1,99 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cassert>
#include <condition_variable>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_map>
namespace PaddlePool {
class ThreadPool {
public:
using MutexGuard = std::lock_guard<std::mutex>;
using UniqueLock = std::unique_lock<std::mutex>;
using Thread = std::thread;
using ThreadID = std::thread::id;
using Task = std::function<void()>;
ThreadPool();
explicit ThreadPool(size_t maxThreads);
ThreadPool(const ThreadPool &) = delete;
ThreadPool &operator=(const ThreadPool &) = delete;
~ThreadPool();
template <typename Func, typename... Ts>
auto submit(Func &&func, Ts &&...params)
-> std::future<typename std::result_of<Func(Ts...)>::type>;
size_t threadsNum() const;
private:
static constexpr size_t WAIT_SECONDS = 2;
void worker();
void joinFinishedThreads();
bool quit_;
size_t currentThreads_;
size_t idleThreads_;
size_t maxThreads_;
mutable std::mutex mutex_;
std::condition_variable cv_;
std::queue<Task> tasks_;
std::queue<ThreadID> finishedThreadIDs_;
std::unordered_map<ThreadID, Thread> threads_;
};
} // namespace PaddlePool
namespace PaddlePool {
template <typename Func, typename... Ts>
auto ThreadPool::submit(Func &&func, Ts &&...params)
-> std::future<typename std::result_of<Func(Ts...)>::type> {
auto execute =
std::bind(std::forward<Func>(func), std::forward<Ts>(params)...);
using ReturnType = typename std::result_of<Func(Ts...)>::type;
using PackagedTask = std::packaged_task<ReturnType()>;
auto task = std::make_shared<PackagedTask>(std::move(execute));
auto result = task->get_future();
MutexGuard guard(mutex_);
assert(!quit_);
tasks_.emplace([task]() { (*task)(); });
if (idleThreads_ > 0) {
cv_.notify_one();
} else if (currentThreads_ < maxThreads_) {
Thread t(&ThreadPool::worker, this);
assert(threads_.find(t.get_id()) == threads_.end());
threads_[t.get_id()] = std::move(t);
++currentThreads_;
}
return result;
}
} // namespace PaddlePool
+45
View File
@@ -0,0 +1,45 @@
pipeline_name: OCR
text_type: general
use_doc_preprocessor: True
use_textline_orientation: True
SubPipelines:
DocPreprocessor:
pipeline_name: doc_preprocessor
use_doc_orientation_classify: True
use_doc_unwarping: True
SubModules:
DocOrientationClassify:
module_name: doc_text_orientation
model_name: PP-LCNet_x1_0_doc_ori
model_dir: null
DocUnwarping:
module_name: image_unwarping
model_name: UVDoc
model_dir: null
SubModules:
TextDetection:
module_name: text_detection
model_name: PP-OCRv6_medium_det
model_dir: null
limit_side_len: 64
limit_type: min
max_side_limit: 4000
thresh: 0.3
box_thresh: 0.6
unclip_ratio: 1.5
TextLineOrientation:
module_name: textline_orientation
model_name: PP-LCNet_x1_0_textline_ori
model_dir: null
batch_size: 6
TextRecognition:
module_name: text_recognition
model_name: PP-OCRv6_medium_rec
model_dir: null
batch_size: 6
score_thresh: 0.0
@@ -0,0 +1,15 @@
pipeline_name: doc_preprocessor
use_doc_orientation_classify: True
use_doc_unwarping: True
batch_size: 1
SubModules:
DocOrientationClassify:
module_name: doc_text_orientation
model_name: PP-LCNet_x1_0_doc_ori
model_dir: null
DocUnwarping:
module_name: image_unwarping
model_name: UVDoc
model_dir: null
@@ -0,0 +1,158 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "predictor.h"
#include "result.h"
#include "src/common/image_batch_sampler.h"
#include "src/utils/ilogger.h"
ClasPredictor::ClasPredictor(const ClasPredictorParams &params)
: BasePredictor(params.model_dir, params.model_name, params.device,
params.precision, params.enable_mkldnn,
params.mkldnn_cache_capacity, params.cpu_threads,
params.batch_size, "image"),
params_(params) {
auto status = Build();
if (!status.ok()) {
INFOE("Build fail: %s", status.ToString().c_str());
exit(-1);
}
};
absl::Status ClasPredictor::BuildResize() {
const auto &pre_params = config_.PreProcessOpInfo();
if (pre_params.find("ResizeImage.size") != pre_params.end()) {
Register<Resize>("Resize", YamlConfig::SmartParseVector(
pre_params.at("ResizeImage.size"))
.vec_int);
} else if (pre_params.find("ResizeImage.resize_short") != pre_params.end()) {
Register<ResizeByShort>(
"Resize", std::stoi(pre_params.at("ResizeImage.resize_short")));
} else {
return absl::NotFoundError("Resize must be provide param !");
}
return absl::OkStatus();
}
absl::Status ClasPredictor::Build() {
const auto &pre_params = config_.PreProcessOpInfo();
Register<ReadImage>("Read");
auto status = BuildResize();
if (!status.ok()) {
return absl::InternalError("build resize_op fail: " + status.ToString());
}
if (config_.FindKey("Crop").ok()) {
Register<Crop>("Crop",
YamlConfig::SmartParseVector(pre_params.at("CropImage.size"))
.vec_int); //*************
}
Register<NormalizeImage>(
"Normalize", std::stof(pre_params.at("NormalizeImage.scale")),
YamlConfig::SmartParseVector(pre_params.at("NormalizeImage.mean"))
.vec_float,
YamlConfig::SmartParseVector(pre_params.at("NormalizeImage.std"))
.vec_float);
Register<ToCHWImage>("ToCHW");
Register<ToBatch>("ToBatch");
infer_ptr_ = CreateStaticInfer();
const auto &post_params = config_.PostProcessOpInfo();
auto gsj = YamlConfig::SmartParseVector(
post_params.at("PostProcess.Topk.label_list"));
post_op_["Topk"] = std::unique_ptr<Topk>(
new Topk(YamlConfig::SmartParseVector(
post_params.at("PostProcess.Topk.label_list"))
.vec_string,
std::stoi(post_params.at("PostProcess.Topk.topk"))));
return absl::OkStatus();
};
std::vector<std::unique_ptr<BaseCVResult>>
ClasPredictor::Process(std::vector<cv::Mat> &batch_data) {
std::vector<cv::Mat> origin_image = {};
origin_image.reserve(batch_data.size());
for (const auto &mat : batch_data) {
origin_image.push_back(mat.clone());
}
auto batch_read = pre_op_.at("Read")->Apply(batch_data);
if (!batch_read.ok()) {
INFOE(batch_read.status().ToString().c_str());
exit(-1);
}
auto batch_resize = pre_op_.at("Resize")->Apply(batch_read.value());
if (!batch_resize.ok()) {
INFOE(batch_resize.status().ToString().c_str());
exit(-1);
}
if (config_.FindKey("Crop").ok()) {
batch_resize = pre_op_.at("Crop")->Apply(batch_resize.value()); // **
if (!batch_resize.ok()) {
INFOE(batch_resize.status().ToString().c_str());
exit(-1);
}
}
auto batch_normalize = pre_op_.at("Normalize")->Apply(batch_resize.value());
if (!batch_normalize.ok()) {
INFOE(batch_normalize.status().ToString().c_str());
exit(-1);
}
auto batch_tochw = pre_op_.at("ToCHW")->Apply(batch_normalize.value());
if (!batch_tochw.ok()) {
INFOE(batch_tochw.status().ToString().c_str());
exit(-1);
}
auto batch_tobatch = pre_op_.at("ToBatch")->Apply(batch_tochw.value());
if (!batch_tobatch.ok()) {
INFOE(batch_tobatch.status().ToString().c_str());
exit(-1);
}
auto batch_infer = infer_ptr_->Apply(batch_tobatch.value());
if (!batch_infer.ok()) {
INFOE(batch_infer.status().ToString().c_str());
exit(-1);
}
auto cls_result = post_op_.at("Topk")->Apply(batch_infer.value()[0]);
if (!cls_result.ok()) {
INFOE(cls_result.status().ToString().c_str());
exit(-1);
}
std::vector<std::unique_ptr<BaseCVResult>> base_cv_result_ptr_vec = {};
for (int i = 0; i < cls_result.value().size(); i++, input_index_++) {
ClasPredictorResult predictor_result;
if (!input_path_.empty()) {
if (input_index_ == input_path_.size())
input_index_ = 0;
predictor_result.input_path = input_path_[input_index_];
}
predictor_result.input_image = origin_image[i];
predictor_result.class_ids = cls_result.value()[i].class_ids;
predictor_result.scores = cls_result.value()[i].scores;
predictor_result.label_names = cls_result.value()[i].label_names;
predictor_result_vec_.push_back(predictor_result);
base_cv_result_ptr_vec.push_back(
std::unique_ptr<BaseCVResult>(new TopkResult(predictor_result)));
}
return base_cv_result_ptr_vec;
}
@@ -0,0 +1,67 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "processors.h"
#include "src/base/base_batch_sampler.h"
#include "src/base/base_cv_result.h"
#include "src/base/base_predictor.h"
#include "src/common/processors.h"
struct ClasPredictorParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
};
struct ClasPredictorResult {
std::string input_path = "";
cv::Mat input_image;
std::vector<int> class_ids;
std::vector<float> scores;
std::vector<std::string> label_names;
};
class ClasPredictor : public BasePredictor {
public:
explicit ClasPredictor(const ClasPredictorParams &params);
ClasPredictor() = delete;
absl::Status Build();
std::vector<std::unique_ptr<BaseCVResult>>
Process(std::vector<cv::Mat> &batch_data) override;
std::vector<ClasPredictorResult> PredictorResult() const {
return predictor_result_vec_;
};
void ResetResult() override { predictor_result_vec_.clear(); };
absl::Status BuildResize();
private:
ClasPredictorParams params_;
std::unordered_map<std::string, std::unique_ptr<Topk>> post_op_;
std::vector<ClasPredictorResult> predictor_result_vec_;
std::unique_ptr<PaddleInfer> infer_ptr_;
int input_index_ = 0;
};
@@ -0,0 +1,133 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "processors.h"
#include <algorithm>
#include <stdexcept>
#include "processors.h"
#include "src/utils/utility.h"
Crop::Crop(const std::vector<int> crop_size, const std::string &mode)
: mode_(mode) {
if (crop_size.size() == 1) {
crop_size_ = std::vector<int>(2, crop_size[0]);
} else {
crop_size_ = crop_size;
}
assert(mode_ == "Center" || mode_ == "TopLeft");
assert(crop_size_.size() == 2 && crop_size_[0] > 0 && crop_size_[1] > 0);
}
Crop::Crop(const int crop_size, const std::string &mode)
: crop_size_(2, crop_size), mode_(mode) {
assert(mode_ == "Center" || mode_ == "TopLeft");
assert(crop_size_.size() == 2 && crop_size_[0] > 0 && crop_size_[1] > 0);
}
absl::StatusOr<cv::Mat> Crop::CropImage(const cv::Mat &img) const {
int h = img.rows;
int w = img.cols;
int crop_width = crop_size_[0];
int crop_height = crop_size_[1];
if (w < crop_width || h < crop_height) {
return absl::InvalidArgumentError(
"Input image (" + std::to_string(w) + ", " + std::to_string(h) +
") smaller than target size (" + std::to_string(crop_width) + ", " +
std::to_string(crop_height) + ").");
}
int x1 = 0, y1 = 0;
if (mode_ == "Center") {
x1 = std::max(0, (w - crop_width) / 2);
y1 = std::max(0, (h - crop_height) / 2);
} else if (mode_ == "TopLeft") {
x1 = 0;
y1 = 0;
} else {
return absl::InvalidArgumentError("Unsupported crop mode.");
}
cv::Rect roi(x1, y1, crop_width, crop_height);
return img(roi).clone();
}
absl::StatusOr<std::vector<cv::Mat>> Crop::Apply(std::vector<cv::Mat> &imgs,
const void *param) const {
std::vector<cv::Mat> result;
result.reserve(imgs.size());
for (const auto &img : imgs) {
auto cropped = CropImage(img);
if (!cropped.ok())
return cropped.status();
result.push_back(cropped.value());
}
return result;
}
Topk::Topk(const std::vector<std::string> &class_names, const int topk)
: class_names_(class_names), topk_(topk) {}
absl::StatusOr<std::vector<Topk::TopkOutput>> Topk::Apply(const cv::Mat &preds,
const int topk) {
topk_ = topk;
auto preds_batch = Utility::SplitBatch(preds);
if (!preds_batch.ok()) {
return preds_batch.status();
}
std::vector<TopkOutput> topk_results = {};
topk_results.reserve(preds_batch.value().size());
for (const auto &pred : preds_batch.value()) {
auto topk_result = Process(pred);
if (!topk_result.ok()) {
return topk_result.status();
}
topk_results.push_back(topk_result.value());
}
return topk_results;
}
absl::StatusOr<Topk::TopkOutput> Topk::Process(const cv::Mat &pred) const {
if (pred.dims != 2 || pred.type() != CV_32F) {
return absl::InvalidArgumentError("Input scores must be 2-D float matrix.");
}
TopkOutput topk_result(pred.size[0]);
int num_classes = pred.size[1];
const float *row = pred.ptr<float>();
std::vector<std::pair<float, int>> score_idx;
for (int j = 0; j < num_classes; ++j) {
score_idx.emplace_back(row[j], j);
}
std::partial_sort(
score_idx.begin(), score_idx.begin() + topk_, score_idx.end(),
[](const std::pair<float, int> &a, const std::pair<float, int> &b) {
return a.first > b.first;
});
for (int t = 0; t < topk_; ++t) {
topk_result.class_ids.push_back(score_idx[t].second);
topk_result.scores.push_back(score_idx[t].first);
if (!class_names_.empty() &&
score_idx[t].second < (int)class_names_.size()) {
topk_result.label_names.push_back(class_names_[score_idx[t].second]);
} else {
topk_result.label_names.push_back(std::to_string(score_idx[t].second));
}
}
return topk_result;
}
@@ -0,0 +1,63 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "src/utils/func_register.h"
class Crop : public BaseProcessor {
public:
explicit Crop(const std::vector<int> crop_size,
const std::string &mode = "Center");
explicit Crop(const int crop_size, const std::string &mode = "Center");
absl::StatusOr<cv::Mat> CropImage(const cv::Mat &img) const;
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
private:
std::vector<int> crop_size_;
std::string mode_;
};
class Topk {
public:
struct TopkOutput {
TopkOutput(int batch) {
class_ids.reserve(batch);
scores.reserve(batch);
label_names.reserve(batch);
}
std::vector<int> class_ids;
std::vector<float> scores;
std::vector<std::string> label_names;
};
explicit Topk(
const std::vector<std::string> &class_names = std::vector<std::string>(),
int topk = 1);
absl::StatusOr<TopkOutput> Process(const cv::Mat &pred_data) const;
absl::StatusOr<std::vector<TopkOutput>> Apply(const cv::Mat &preds,
const int topk = 1);
private:
std::vector<std::string> class_names_;
int topk_;
};
@@ -0,0 +1,160 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "result.h"
#include <fstream>
#include <string>
#include "src/utils/utility.h"
#include "third_party/nlohmann/json.hpp"
using json = nlohmann::json;
void TopkResult::SaveToImg(const std::string &save_path) {
cv::Mat img = predictor_result_.input_image.clone();
std::ostringstream oss;
oss << predictor_result_.label_names[0] << " " << std::fixed
<< std::setprecision(2) << predictor_result_.scores[0];
std::string label_str = oss.str();
int imgWidth = img.cols;
int minFont = std::max(12, imgWidth * 2 / 100);
int maxFont = std::max(18, imgWidth * 5 / 100);
int baseline = 0;
int fontFace = 0;
double fontScale = getAdaptiveFontScale(
label_str, imgWidth, imgWidth, minFont, maxFont, 2, baseline, fontFace);
cv::Size textSize =
cv::getTextSize(label_str, fontFace, fontScale, 2, &baseline);
int rect_left = 3, rect_top = 3;
int rect_right = rect_left + textSize.width + 6;
int rect_bottom = rect_top + textSize.height + 6;
cv::Scalar bgColor(0, 0, 255);
cv::rectangle(img, cv::Point(rect_left, rect_top),
cv::Point(rect_right, rect_bottom), bgColor, cv::FILLED);
int text_x = rect_left + 3;
int text_y = rect_top + textSize.height + 2;
cv::Scalar fontColor(255, 255, 255);
cv::putText(img, label_str, cv::Point(text_x, text_y), fontFace, fontScale,
fontColor, 2, cv::LINE_AA);
absl::StatusOr<std::string> full_path;
if (predictor_result_.input_path.empty()) {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "output_" << std::put_time(std::localtime(&now_time), "%Y%m%d_%H%M%S")
<< ".jpg";
std::string timestamp_filename = ss.str();
INFOW("Input path is empty, will use %s instead!",
timestamp_filename.c_str());
predictor_result_.input_path = timestamp_filename;
full_path =
Utility::SmartCreateDirectoryForImage(save_path, timestamp_filename);
} else {
full_path = Utility::SmartCreateDirectoryForImage(
save_path, predictor_result_.input_path);
}
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
bool success = cv::imwrite(full_path.value(), img);
if (!success) {
INFOE("Failed to write the image %s ", full_path.value().c_str());
exit(-1);
}
}
void TopkResult::Print() const {
std::cout << "{\n \"res\": {" << std::endl;
std::cout << " \"input_path\": {" << predictor_result_.input_path << "},"
<< std::endl;
std::cout << " \"class_ids\": {" << predictor_result_.class_ids[0] << "},"
<< std::endl;
std::cout << " \"scores\": {" << predictor_result_.scores[0] << "},"
<< std::endl;
std::cout << " \"label_names\": {" << predictor_result_.label_names[0]
<< "}," << std::endl;
std::cout << "}" << std::endl;
}
void TopkResult::SaveToJson(const std::string &save_path) const {
nlohmann::ordered_json j;
j["input_path"] = predictor_result_.input_path;
j["page_index"] = nullptr; //********
json class_ids = json::array();
for (const auto &item : predictor_result_.class_ids) {
class_ids.push_back(item);
}
json scores = json::array();
for (const auto &item : predictor_result_.scores) {
scores.push_back(item);
}
json label_names = json::array();
for (const auto &item : predictor_result_.label_names) {
label_names.push_back(item);
}
j["class_ids"] = class_ids;
j["scores"] = scores;
j["label_names"] = label_names;
auto full_path = Utility::SmartCreateDirectoryForJson(
save_path, predictor_result_.input_path);
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
std::ofstream file(full_path.value());
if (file.is_open()) {
file << j.dump(4);
file.close();
} else {
INFOE("Could not open file for writing: %s", save_path.c_str());
exit(-1);
}
}
int TopkResult::getAdaptiveFontScale(const std::string &text, int imgWidth,
int maxWidth, int minFont, int maxFont,
int thickness, int &outBaseline,
int &outFontFace) {
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
double fontScale = 1.0;
int baseline = 0;
int bestFontSize = minFont;
for (int fontSize = maxFont; fontSize >= minFont; --fontSize) {
fontScale = fontSize / 20.0; // 20为基准比例,可调
int base;
cv::Size textSize =
cv::getTextSize(text, fontFace, fontScale, thickness, &base);
if (textSize.width <= maxWidth) {
bestFontSize = fontSize;
outBaseline = base;
outFontFace = fontFace;
return fontScale;
}
}
outBaseline = 0;
outFontFace = fontFace;
return minFont / 20.0;
}
@@ -0,0 +1,38 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include "predictor.h"
#include "src/base/base_cv_result.h"
class TopkResult : public BaseCVResult {
public:
TopkResult(ClasPredictorResult predictor_result)
: BaseCVResult(), predictor_result_(predictor_result){};
// std::unordered_map<std::string, cv::Mat> ToImg() const override;
void SaveToImg(const std::string &save_path) override;
void Print() const override;
void SaveToJson(const std::string &save_path) const override;
static int getAdaptiveFontScale(const std::string &text, int imgWidth,
int maxWidth, int minFont, int maxFont,
int thickness, int &outBaseline,
int &outFontFace);
private:
ClasPredictorResult predictor_result_;
};
@@ -0,0 +1,100 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "predictor.h"
#include "result.h"
#include "src/common/image_batch_sampler.h"
WarpPredictor::WarpPredictor(const WarpPredictorParams &params)
: BasePredictor(params.model_dir, params.model_name, params.device,
params.precision, params.enable_mkldnn,
params.mkldnn_cache_capacity, params.cpu_threads,
params.batch_size, "image"),
params_(params) {
auto status = Build();
if (!status.ok()) {
INFOE("Build fail: %s", status.ToString().c_str());
exit(-1);
}
};
absl::Status WarpPredictor::Build() {
const auto &pre_params = config_.PreProcessOpInfo();
Register<ReadImage>("Read", "BGR");
Register<Normalize>("Normalize", 1.0 / 255.0, 0.0, 1.0);
Register<ToCHWImage>("ToCHW");
Register<ToBatch>("ToBatch");
infer_ptr_ = CreateStaticInfer();
const auto &post_params = config_.PostProcessOpInfo();
post_op_["DocTr"] = std::unique_ptr<DocTrPostProcess>(new DocTrPostProcess());
return absl::OkStatus();
};
std::vector<std::unique_ptr<BaseCVResult>>
WarpPredictor::Process(std::vector<cv::Mat> &batch_data) {
std::vector<cv::Mat> origin_image = {};
origin_image.reserve(batch_data.size());
for (const auto &mat : batch_data) {
origin_image.push_back(mat.clone());
}
auto batch_read = pre_op_.at("Read")->Apply(batch_data);
if (!batch_read.ok()) {
INFOE(batch_read.status().ToString().c_str());
exit(-1);
}
auto batch_normalize = pre_op_.at("Normalize")->Apply(batch_read.value());
if (!batch_normalize.ok()) {
INFOE(batch_normalize.status().ToString().c_str());
exit(-1);
}
auto batch_tochw = pre_op_.at("ToCHW")->Apply(batch_normalize.value());
if (!batch_tochw.ok()) {
INFOE(batch_tochw.status().ToString().c_str());
exit(-1);
}
auto batch_tobatch = pre_op_.at("ToBatch")->Apply(batch_tochw.value());
if (!batch_tobatch.ok()) {
INFOE(batch_tobatch.status().ToString().c_str());
exit(-1);
}
auto batch_infer = infer_ptr_->Apply(batch_tobatch.value());
if (!batch_infer.ok()) {
INFOE(batch_infer.status().ToString().c_str());
exit(-1);
}
auto warp_result = post_op_.at("DocTr")->Apply(batch_infer.value()[0]);
if (!warp_result.ok()) {
INFOE(warp_result.status().ToString().c_str());
exit(-1);
}
std::vector<std::unique_ptr<BaseCVResult>> base_cv_result_ptr_vec = {};
for (int i = 0; i < warp_result.value().size(); i++, input_index_++) {
WarpPredictorResult predictor_result;
if (!input_path_.empty()) {
if (input_index_ == input_path_.size())
input_index_ = 0;
predictor_result.input_path = input_path_[input_index_];
}
predictor_result.input_image = origin_image[i];
predictor_result.doctr_img = warp_result.value()[i];
predictor_result_vec_.push_back(predictor_result);
base_cv_result_ptr_vec.push_back(
std::unique_ptr<BaseCVResult>(new DocTrResult(predictor_result)));
}
return base_cv_result_ptr_vec;
}
@@ -0,0 +1,61 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "processors.h"
#include "src/base/base_batch_sampler.h"
#include "src/base/base_cv_result.h"
#include "src/base/base_predictor.h"
#include "src/common/processors.h"
struct WarpPredictorParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
bool enable_mkldnn = true;
std::string precision = "fp32";
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
};
struct WarpPredictorResult {
std::string input_path = "";
cv::Mat input_image;
cv::Mat doctr_img;
};
class WarpPredictor : public BasePredictor {
public:
explicit WarpPredictor(const WarpPredictorParams &params);
absl::Status Build();
std::vector<std::unique_ptr<BaseCVResult>>
Process(std::vector<cv::Mat> &batch_data) override;
std::vector<WarpPredictorResult> PredictorResult() const {
return predictor_result_vec_;
};
void ResetResult() override { predictor_result_vec_.clear(); };
private:
std::unordered_map<std::string, std::unique_ptr<DocTrPostProcess>> post_op_;
std::vector<WarpPredictorResult> predictor_result_vec_;
std::unique_ptr<PaddleInfer> infer_ptr_;
WarpPredictorParams params_;
int input_index_ = 0;
};
@@ -0,0 +1,73 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "processors.h"
#include <sstream>
#include <stdexcept>
#include "src/utils/utility.h"
DocTrPostProcess::DocTrPostProcess(double scale) : scale_(scale) {}
absl::StatusOr<std::vector<cv::Mat>>
DocTrPostProcess::Apply(const cv::Mat &preds) const {
auto preds_batch = Utility::SplitBatch(preds);
if (!preds_batch.ok()) {
return preds_batch.status();
}
std::vector<cv::Mat> doc_out;
doc_out.reserve(preds_batch.value().size());
for (auto &pred_data : preds_batch.value()) {
auto result = Process(pred_data);
if (!result.ok()) {
return result.status();
}
doc_out.push_back(result.value());
}
return doc_out;
}
absl::StatusOr<cv::Mat> DocTrPostProcess::Process(cv::Mat &pred_data) const {
if (pred_data.dims != 4) {
return absl::InvalidArgumentError("must have 4D"); //********
}
std::vector<int> shape = {};
for (int i = 1; i < pred_data.dims; i++) {
shape.push_back(pred_data.size[i]);
}
pred_data = pred_data.reshape(1, shape);
std::vector<cv::Range> ranges(pred_data.size[0]);
std::vector<cv::Mat> mat_split(pred_data.size[0]);
for (int i = 0; i < pred_data.size[0]; i++) {
ranges[0] = cv::Range(i, i + 1);
for (int j = 1; j < pred_data.dims; j++) {
ranges[j] = cv::Range::all();
}
mat_split[i] = pred_data(&ranges[0]);
}
for (auto &item : mat_split) {
std::vector<int> shape_item = {};
for (int i = 1; i < item.dims; i++) {
shape_item.push_back(item.size[i]);
}
item = item.reshape(1, shape_item);
item = item * scale_;
}
cv::Mat out_hwc;
cv::merge(mat_split, out_hwc);
out_hwc.convertTo(out_hwc, CV_8U);
return out_hwc;
}
@@ -0,0 +1,35 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "src/utils/func_register.h"
class DocTrPostProcess {
public:
explicit DocTrPostProcess(double scale = 255.0f);
absl::StatusOr<std::vector<cv::Mat>> Apply(const cv::Mat &preds) const;
absl::StatusOr<cv::Mat> Process(cv::Mat &pred_data) const;
private:
double scale_;
};
@@ -0,0 +1,119 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "result.h"
#include <fstream>
#include <string>
#include "src/utils/utility.h"
#include "third_party/nlohmann/json.hpp"
using json = nlohmann::json;
void DocTrResult::SaveToImg(const std::string &save_path) {
absl::StatusOr<std::string> full_path;
if (predictor_result_.input_path.empty()) {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "output_" << std::put_time(std::localtime(&now_time), "%Y%m%d_%H%M%S")
<< ".jpg";
std::string timestamp_filename = ss.str();
INFOW("Input path is empty, will use %s instead!",
timestamp_filename.c_str());
predictor_result_.input_path = timestamp_filename;
full_path =
Utility::SmartCreateDirectoryForImage(save_path, timestamp_filename);
} else {
full_path = Utility::SmartCreateDirectoryForImage(
save_path, predictor_result_.input_path);
}
bool success = cv::imwrite(full_path.value(), predictor_result_.doctr_img);
if (!success) {
INFOE("Failed to write the image %s", full_path.value().c_str());
exit(-1);
}
}
void DocTrResult::Print() const {
std::cout << "{\n \"res\": {" << std::endl;
std::cout << " \"input_path\": {" << predictor_result_.input_path << "},"
<< std::endl;
std::cout << " \"doctr_img\": {"
<< "..."
<< "}" << std::endl;
std::cout << "}" << std::endl;
}
void DocTrResult::SaveToJson(const std::string &save_path) const {
nlohmann::ordered_json j;
j["input_path"] = predictor_result_.input_path;
j["page_index"] = nullptr; //********
nlohmann::json mat_array = nlohmann::json::array();
for (int i = 0; i < predictor_result_.doctr_img.rows; ++i) {
nlohmann::json row = nlohmann::json::array();
for (int j = 0; j < predictor_result_.doctr_img.cols; ++j) {
cv::Vec3b color = predictor_result_.doctr_img.at<cv::Vec3b>(i, j);
row.push_back({color[0], color[1], color[2]});
}
mat_array.push_back(row);
}
j["doctr_img"] = mat_array;
auto full_path = Utility::SmartCreateDirectoryForJson(
save_path, predictor_result_.input_path);
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
std::ofstream file(full_path.value());
if (file.is_open()) {
file << j.dump(4);
file.close();
} else {
INFOE("Could not open file for writing: %s", save_path.c_str());
exit(-1);
}
}
int DocTrResult::getAdaptiveFontScale(const std::string &text, int imgWidth,
int maxWidth, int minFont, int maxFont,
int thickness, int &outBaseline,
int &outFontFace) {
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
double fontScale = 1.0;
int baseline = 0;
int bestFontSize = minFont;
for (int fontSize = maxFont; fontSize >= minFont; --fontSize) {
fontScale = fontSize / 20.0;
int base;
cv::Size textSize =
cv::getTextSize(text, fontFace, fontScale, thickness, &base);
if (textSize.width <= maxWidth) {
bestFontSize = fontSize;
outBaseline = base;
outFontFace = fontFace;
return fontScale;
}
}
outBaseline = 0;
outFontFace = fontFace;
return minFont / 20.0;
}
@@ -0,0 +1,38 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include "predictor.h"
#include "src/base/base_cv_result.h"
class DocTrResult : public BaseCVResult {
public:
DocTrResult(WarpPredictorResult predictor_result)
: BaseCVResult(), predictor_result_(predictor_result){};
// std::unordered_map<std::string, cv::Mat> ToImg() const override;
void SaveToImg(const std::string &save_path) override;
void Print() const override;
void SaveToJson(const std::string &save_path) const override;
static int getAdaptiveFontScale(const std::string &text, int imgWidth,
int maxWidth, int minFont, int maxFont,
int thickness, int &outBaseline,
int &outFontFace);
private:
WarpPredictorResult predictor_result_;
};
@@ -0,0 +1,139 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "predictor.h"
#include "result.h"
#include "src/common/image_batch_sampler.h"
TextDetPredictor::TextDetPredictor(const TextDetPredictorParams &params)
: BasePredictor(params.model_dir, params.model_name, params.device,
params.precision, params.enable_mkldnn,
params.mkldnn_cache_capacity, params.cpu_threads,
params.batch_size, "image"),
params_(params) {
auto status = Build();
if (!status.ok()) {
INFOE("Build fail: %s", status.ToString().c_str());
exit(-1);
}
};
absl::Status TextDetPredictor::Build() {
const auto &pre_tfs = config_.PreProcessOpInfo();
// Register<ReadImage>("Read", pre_tfs.at("DecodeImage.img_mode"));
Register<ReadImage>("Read");
DetResizeForTestParam resize_param;
resize_param.input_shape = params_.input_shape;
resize_param.max_side_limit = params_.max_side_limit;
resize_param.limit_side_len = params_.limit_side_len;
resize_param.limit_type = params_.limit_type;
resize_param.max_side_limit = params_.max_side_limit;
resize_param.resize_long =
std::stoi(pre_tfs.at("DetResizeForTest.resize_long"));
Register<DetResizeForTest>("Resize", resize_param);
Register<NormalizeImage>("Normalize");
Register<ToCHWImage>("ToCHW");
Register<ToBatch>("ToBatch");
infer_ptr_ = CreateStaticInfer();
const auto &post_params = config_.PostProcessOpInfo();
DBPostProcessParams db_param;
db_param.thresh = params_.thresh.has_value()
? params_.thresh
: std::stof(post_params.at("PostProcess.thresh"));
db_param.box_thresh =
params_.box_thresh.has_value()
? params_.box_thresh
: std::stof(post_params.at("PostProcess.box_thresh"));
db_param.unclip_ratio =
params_.unclip_ratio.has_value()
? params_.unclip_ratio
: std::stof(post_params.at("PostProcess.unclip_ratio"));
db_param.max_candidates =
std::stoi(post_params.at("PostProcess.max_candidates"));
post_op_["DBPostProcess"] =
std::unique_ptr<DBPostProcess>(new DBPostProcess(db_param));
return absl::OkStatus();
};
std::vector<std::unique_ptr<BaseCVResult>>
TextDetPredictor::Process(std::vector<cv::Mat> &batch_data) {
std::vector<cv::Mat> origin_image = {};
origin_image.reserve(batch_data.size());
for (const auto &mat : batch_data) {
origin_image.push_back(mat.clone());
}
auto batch_raw_imgs = pre_op_.at("Read")->Apply(batch_data);
if (!batch_raw_imgs.ok()) {
INFOE(batch_raw_imgs.status().ToString().c_str());
exit(-1);
}
std::vector<int> origin_shape = {batch_raw_imgs.value()[0].rows,
batch_raw_imgs.value()[0].cols};
auto batch_imgs = pre_op_.at("Resize")->Apply(batch_raw_imgs.value());
if (!batch_imgs.ok()) {
INFOE(batch_imgs.status().ToString().c_str());
exit(-1);
}
auto batch_imgs_normalize =
pre_op_.at("Normalize")->Apply(batch_imgs.value());
if (!batch_imgs_normalize.ok()) {
INFOE(batch_imgs_normalize.status().ToString().c_str());
exit(-1);
}
auto batch_imgs_to_chw =
pre_op_.at("ToCHW")->Apply(batch_imgs_normalize.value());
if (!batch_imgs_to_chw.ok()) {
INFOE(batch_imgs_to_chw.status().ToString().c_str());
exit(-1);
}
auto batch_imgs_to_batch =
pre_op_.at("ToBatch")->Apply(batch_imgs_to_chw.value());
if (!batch_imgs_to_batch.ok()) {
INFOE(batch_imgs_to_batch.status().ToString().c_str());
exit(-1);
}
auto infer_result = infer_ptr_->Apply(batch_imgs_to_batch.value());
if (!infer_result.ok()) {
INFOE(infer_result.status().ToString().c_str());
exit(-1);
}
auto db_result = post_op_.at("DBPostProcess")
->Apply(infer_result.value()[0], origin_shape);
if (!db_result.ok()) {
INFOE(db_result.status().ToString().c_str());
exit(-1);
}
std::vector<std::unique_ptr<BaseCVResult>> base_cv_result_ptr_vec = {};
for (int i = 0; i < db_result.value().size(); i++, input_index_++) {
TextDetPredictorResult predictor_result;
if (!input_path_.empty()) {
if (input_index_ == input_path_.size())
input_index_ = 0;
predictor_result.input_path = input_path_[input_index_];
}
predictor_result.input_image = origin_image[i];
predictor_result.dt_polys = db_result.value()[i].first;
predictor_result.dt_scores = db_result.value()[i].second;
predictor_result_vec_.push_back(predictor_result);
base_cv_result_ptr_vec.push_back(
std::unique_ptr<BaseCVResult>(new TextDetResult(predictor_result)));
}
return base_cv_result_ptr_vec;
}
@@ -0,0 +1,69 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "processors.h"
#include "src/base/base_batch_sampler.h"
#include "src/base/base_cv_result.h"
#include "src/base/base_predictor.h"
#include "src/common/processors.h"
struct TextDetPredictorResult {
std::string input_path = "";
cv::Mat input_image;
std::vector<std::vector<cv::Point2f>> dt_polys = {};
std::vector<float> dt_scores = {};
};
struct TextDetPredictorParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
absl::optional<int> limit_side_len = absl::nullopt;
absl::optional<std::string> limit_type = absl::nullopt;
absl::optional<int> max_side_limit = absl::nullopt;
absl::optional<float> thresh = absl::nullopt;
absl::optional<float> box_thresh = absl::nullopt;
absl::optional<float> unclip_ratio = absl::nullopt;
absl::optional<std::vector<int>> input_shape = absl::nullopt;
};
class TextDetPredictor : public BasePredictor {
public:
TextDetPredictor(const TextDetPredictorParams &params);
std::vector<TextDetPredictorResult> PredictorResult() const {
return predictor_result_vec_;
};
void ResetResult() override { predictor_result_vec_.clear(); };
absl::Status Build();
std::vector<std::unique_ptr<BaseCVResult>>
Process(std::vector<cv::Mat> &batch_data) override;
private:
TextDetPredictorParams params_;
std::unordered_map<std::string, std::unique_ptr<DBPostProcess>> post_op_;
std::vector<TextDetPredictorResult> predictor_result_vec_;
std::unique_ptr<PaddleInfer> infer_ptr_;
int input_index_ = 0;
};
@@ -0,0 +1,651 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "processors.h"
#include <stdexcept>
#include "src/utils/utility.h"
DetResizeForTest::DetResizeForTest(const DetResizeForTestParam &params) {
if (params.input_shape.has_value()) {
input_shape_ = params.input_shape.value();
resize_type_ = 3;
} else if (params.image_shape.has_value()) {
image_shape_ = params.image_shape.value();
resize_type_ = 1;
if (params.keep_ratio.has_value()) {
keep_ratio_ = params.keep_ratio.value();
}
} else if (params.limit_side_len.has_value()) {
limit_side_len_ = params.limit_side_len.value();
limit_type_ = params.limit_type.value_or("min");
} else if (params.resize_long.has_value()) {
resize_type_ = 2;
resize_long_ = params.resize_long.value_or(960);
} else {
limit_side_len_ = 736;
limit_type_ = "min";
}
if (params.max_side_limit.has_value()) {
max_side_limit_ = params.max_side_limit.value();
}
}
absl::StatusOr<std::vector<cv::Mat>>
DetResizeForTest::Apply(std::vector<cv::Mat> &input,
const void *param_ptr) const {
if (input.empty()) {
return absl::InvalidArgumentError("Input image vector is empty.");
}
std::vector<cv::Mat> results;
if (param_ptr != nullptr) {
const DetResizeForTestParam *param =
static_cast<const DetResizeForTestParam *>(param_ptr);
for (const auto &img : input) {
auto res = Resize(
img,
param->limit_side_len.has_value() ? param->limit_side_len.value()
: limit_side_len_,
param->limit_type.has_value() ? param->limit_type.value()
: limit_type_,
param->max_side_limit.has_value() ? param->max_side_limit.value()
: max_side_limit_);
if (!res.ok())
return res.status();
results.push_back(res.value());
}
} else {
for (const auto &img : input) {
auto res = Resize(img, limit_side_len_, limit_type_, max_side_limit_);
if (!res.ok())
return res.status();
results.push_back(res.value());
}
}
return results;
}
absl::StatusOr<cv::Mat> DetResizeForTest::Resize(const cv::Mat &img,
int limit_side_len,
const std::string &limit_type,
int max_side_limit) const {
int src_h = img.rows;
int src_w = img.cols;
if (src_h + src_w < 64) {
cv::Mat padded = ImagePadding(img);
src_h = padded.rows;
src_w = padded.cols;
return Resize(padded, limit_side_len, limit_type, max_side_limit);
}
switch (resize_type_) {
case 0:
return ResizeImageType0(img, limit_side_len, limit_type, max_side_limit);
case 1:
return ResizeImageType1(img);
case 2:
return ResizeImageType2(img);
case 3:
return ResizeImageType3(img);
default:
return absl::InvalidArgumentError("Unknown resize_type: " +
std::to_string(resize_type_));
}
}
cv::Mat DetResizeForTest::ImagePadding(const cv::Mat &img, int value) const {
int h = img.rows, w = img.cols, c = img.channels();
int pad_h = std::max(32, h);
int pad_w = std::max(32, w);
cv::Mat im_pad = cv::Mat::zeros(pad_h, pad_w, img.type());
im_pad.setTo(cv::Scalar::all(value));
img.copyTo(im_pad(cv::Rect(0, 0, w, h)));
return im_pad;
}
absl::StatusOr<cv::Mat>
DetResizeForTest::ResizeImageType0(const cv::Mat &img, int limit_side_len,
const std::string &limit_type,
int max_side_limit) const {
int h = img.rows, w = img.cols;
float ratio = 1.f;
if (limit_type == "max") {
if (std::max(h, w) > limit_side_len)
ratio = float(limit_side_len) / std::max(h, w);
} else if (limit_type == "min") {
if (std::min(h, w) < limit_side_len)
ratio = float(limit_side_len) / std::min(h, w);
} else if (limit_type == "resize_long") {
ratio = float(limit_side_len) / std::max(h, w);
} else {
return absl::InvalidArgumentError("Not supported limit_type: " +
limit_type);
}
int resize_h = int(h * ratio);
int resize_w = int(w * ratio);
if (std::max(resize_h, resize_w) > max_side_limit) {
ratio = float(max_side_limit) / std::max(resize_h, resize_w);
resize_h = int(resize_h * ratio);
resize_w = int(resize_w * ratio);
}
resize_h = std::max(int(std::round(resize_h / 32.0) * 32), 32);
resize_w = std::max(int(std::round(resize_w / 32.0) * 32), 32);
if (resize_h == h && resize_w == w)
return img;
if (resize_h <= 0 || resize_w <= 0)
return absl::InvalidArgumentError("resize_w/h <= 0");
cv::Mat resized;
cv::resize(img, resized, cv::Size(resize_w, resize_h));
return resized;
}
absl::StatusOr<cv::Mat>
DetResizeForTest::ResizeImageType1(const cv::Mat &img) const {
int resize_h = image_shape_[0];
int resize_w = image_shape_[1];
int ori_h = img.rows, ori_w = img.cols;
if (keep_ratio_) {
resize_w = int(ori_w * (float(resize_h) / ori_h));
int N = int(std::ceil(resize_w / 32.0));
resize_w = N * 32;
}
if (resize_h == ori_h && resize_w == ori_w)
return img;
cv::Mat resized;
cv::resize(img, resized, cv::Size(resize_w, resize_h));
return resized;
}
absl::StatusOr<cv::Mat>
DetResizeForTest::ResizeImageType2(const cv::Mat &img) const {
int h = img.rows, w = img.cols;
int resize_h = h, resize_w = w;
float ratio;
if (resize_h > resize_w)
ratio = float(resize_long_) / resize_h;
else
ratio = float(resize_long_) / resize_w;
resize_h = int(resize_h * ratio);
resize_w = int(resize_w * ratio);
int max_stride = 128;
resize_h = ((resize_h + max_stride - 1) / max_stride) * max_stride;
resize_w = ((resize_w + max_stride - 1) / max_stride) * max_stride;
if (resize_h == h && resize_w == w)
return img;
cv::Mat resized;
cv::resize(img, resized, cv::Size(resize_w, resize_h));
return resized;
}
absl::StatusOr<cv::Mat>
DetResizeForTest::ResizeImageType3(const cv::Mat &img) const {
if (input_shape_.size() != INPUTSHAPE)
return absl::InvalidArgumentError("input_shape not set for type " +
std::to_string(INPUTSHAPE));
int resize_h = input_shape_[1];
int resize_w = input_shape_[2];
int ori_h = img.rows, ori_w = img.cols;
if (resize_h == ori_h && resize_w == ori_w)
return img;
cv::Mat resized;
cv::resize(img, resized, cv::Size(resize_w, resize_h));
return resized;
}
DBPostProcess::DBPostProcess(const DBPostProcessParams &params)
: thresh_(params.thresh.value_or(0.3)),
box_thresh_(params.box_thresh.value_or(0.7)),
unclip_ratio_(params.unclip_ratio.value_or(2.0)),
max_candidates_(params.max_candidates), min_size_(3),
use_dilation_(params.use_dilation), score_mode_(params.score_mode),
box_type_(params.box_type) {
assert(score_mode_ == "slow" || score_mode_ == "fast");
assert(box_type_ == "quad" || box_type_ == "poly");
}
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
DBPostProcess::operator()(const cv::Mat &preds,
const std::vector<int> &img_shapes,
absl::optional<float> thresh,
absl::optional<float> box_thresh,
absl::optional<float> unclip_ratio) {
std::vector<std::vector<cv::Point2f>> all_boxes;
std::vector<float> all_scores;
auto preds_batch = Utility::SplitBatch(preds);
if (!preds_batch.ok()) {
return preds_batch.status();
}
for (const auto &preds_data : *preds_batch) {
auto result = Process(preds_data, img_shapes, thresh.value_or(thresh_),
box_thresh.value_or(box_thresh_),
unclip_ratio.value_or(unclip_ratio_));
if (!result.ok()) {
return result.status();
}
auto boxes_result = *result;
auto boxes = boxes_result.first;
auto scores = boxes_result.second;
all_boxes.insert(all_boxes.end(), boxes.begin(), boxes.end());
all_scores.insert(all_scores.end(), scores.begin(), scores.end());
}
return std::make_pair(all_boxes, all_scores);
}
absl::StatusOr<std::vector<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>>
DBPostProcess::Apply(const cv::Mat &preds, const std::vector<int> &img_shapes,
absl::optional<float> thresh,
absl::optional<float> box_thresh,
absl::optional<float> unclip_ratio) {
std::vector<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
db_result = {};
auto preds_batch = Utility::SplitBatch(preds);
if (!preds_batch.ok()) {
return preds_batch.status();
}
for (const auto &pred : preds_batch.value()) {
auto result = Process(pred, img_shapes, thresh.value_or(thresh_),
box_thresh.value_or(box_thresh_),
unclip_ratio.value_or(unclip_ratio_));
if (!result.ok()) {
return result.status();
}
db_result.push_back(result.value());
}
return db_result;
}
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
DBPostProcess::Process(const cv::Mat &pred, const std::vector<int> &img_shape,
float thresh, float box_thresh, float unclip_ratio) {
cv::Mat pred_single = pred.clone();
std::vector<int> shape_pred = {pred_single.size[pred_single.dims - 2],
pred_single.size[pred_single.dims - 1]};
pred_single = pred_single.reshape(1, shape_pred);
cv::Mat segmentation = pred_single > thresh;
cv::Mat mask;
if (use_dilation_) {
cv::Mat kernel = (cv::Mat_<uchar>(2, 2) << 1, 1, 1, 1); //暂时未测试
cv::dilate(segmentation, mask, kernel);
} else {
mask = segmentation;
}
int src_h = img_shape[0];
int src_w = img_shape[1];
if (box_type_ == "poly") {
return PolygonsFromBitmap(pred_single, mask, src_w, src_h, box_thresh,
unclip_ratio);
} else if (box_type_ == "quad") {
return BoxesFromBitmap(pred_single, mask, src_w, src_h, box_thresh,
unclip_ratio);
}
return absl::InvalidArgumentError(
"box_type can only be one of ['quad', 'poly']");
}
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
DBPostProcess::PolygonsFromBitmap(const cv::Mat &pred, const cv::Mat &bitmap,
int dest_width, int dest_height,
float box_thresh, float unclip_ratio) {
std::vector<std::vector<cv::Point2f>> boxes;
std::vector<float> scores;
float width_scale = static_cast<float>(dest_width) / bitmap.cols;
float height_scale = static_cast<float>(dest_height) / bitmap.rows;
cv::Mat bitmap_uint8;
bitmap.convertTo(bitmap_uint8, CV_8UC1, 255.0);
std::vector<std::vector<cv::Point2f>> contours;
cv::findContours(bitmap_uint8, contours, cv::RETR_LIST,
cv::CHAIN_APPROX_SIMPLE);
int num_contours =
std::min(static_cast<int>(contours.size()), max_candidates_);
for (int i = 0; i < num_contours; ++i) {
const auto &contour = contours[i];
std::vector<cv::Point2f> approx;
double epsilon = 0.002 * cv::arcLength(contour, true);
cv::approxPolyDP(contour, approx, epsilon, true);
if (approx.size() < 4) {
continue;
}
float score = BoxScoreFast(pred, approx);
if (box_thresh > score) {
continue;
}
std::vector<cv::Point2f> box;
if (approx.size() > 2) {
auto unclip_result = Unclip(approx, unclip_ratio);
if (!unclip_result.ok()) {
continue;
}
box = *unclip_result;
if (box.size() > 1) {
continue;
}
} else {
continue;
}
if (!box.empty()) {
auto min_box_result = GetMiniBoxes(box);
auto min_box = min_box_result.first;
auto sside = min_box_result.second;
if (sside < min_size_ + 2) {
continue;
}
for (auto &point : box) {
point.x = std::max(
0, std::min(static_cast<int>(std::round(point.x * width_scale)),
dest_width - 1));
point.y = std::max(
0, std::min(static_cast<int>(std::round(point.y * height_scale)),
dest_height - 1));
}
boxes.push_back(box);
scores.push_back(score);
}
}
return std::make_pair(boxes, scores);
}
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
DBPostProcess::BoxesFromBitmap(const cv::Mat &pred, const cv::Mat &bitmap,
int dest_width, int dest_height,
float box_thresh, float unclip_ratio) {
std::vector<std::vector<cv::Point2f>> boxes;
std::vector<float> scores;
float width_scale = static_cast<float>(dest_width) / bitmap.cols;
float height_scale = static_cast<float>(dest_height) / bitmap.rows;
cv::Mat bitmap_uint8;
bitmap.convertTo(bitmap_uint8, CV_8UC1, 255.0);
std::vector<std::vector<cv::Point>> contours_;
cv::findContours(bitmap_uint8, contours_, cv::RETR_LIST,
cv::CHAIN_APPROX_SIMPLE);
std::vector<std::vector<cv::Point2f>> contours;
for (const auto &contour : contours_) { // 这里可以优化
std::vector<cv::Point2f> float_contour;
for (const auto &point : contour) {
float_contour.push_back(cv::Point2f(point.x, point.y));
}
contours.push_back(float_contour);
}
int num_contours =
std::min(static_cast<int>(contours.size()), max_candidates_);
for (int i = 0; i < num_contours; ++i) {
const auto &contour = contours[i];
auto contour_result = GetMiniBoxes(contour);
auto points = contour_result.first;
auto sside = contour_result.second;
if (sside < min_size_) {
continue;
}
float score = 0;
if (score_mode_ == "fast") {
score = BoxScoreFast(pred, points);
} else {
score = BoxScoreSlow(pred, contour);
}
if (box_thresh > score) {
continue;
}
auto unclip_result = Unclip(points, unclip_ratio);
if (!unclip_result.ok()) {
continue;
}
auto box = *unclip_result;
auto min_box_result = GetMiniBoxes(box);
auto min_box = min_box_result.first;
auto new_sside = min_box_result.second;
if (new_sside < min_size_ + 2) {
continue;
}
for (auto &point : min_box) {
point.x = std::max(
0, std::min(static_cast<int>(std::round(point.x * width_scale)),
dest_width - 1));
point.y = std::max(
0, std::min(static_cast<int>(std::round(point.y * height_scale)),
dest_height - 1));
}
boxes.push_back(min_box);
scores.push_back(score);
}
return std::make_pair(boxes, scores);
}
absl::StatusOr<std::vector<cv::Point2f>>
DBPostProcess::Unclip(const std::vector<cv::Point2f> &box, float unclip_ratio) {
float area = cv::contourArea(box);
float length = cv::arcLength(box, true);
float distance = area * unclip_ratio / length;
ClipperLib::Path path;
for (const auto &point : box) {
path << ClipperLib::IntPoint(point.x, point.y);
}
ClipperLib::ClipperOffset co;
co.AddPath(path, ClipperLib::jtRound, ClipperLib::etClosedPolygon);
ClipperLib::Paths solution;
co.Execute(solution, distance);
if (solution.empty()) {
return absl::InternalError("Failed to unclip polygon");
}
std::vector<cv::Point2f> result;
for (const auto &p : solution[0]) {
result.emplace_back(p.X, p.Y);
}
return result;
}
std::pair<std::vector<cv::Point2f>, float>
DBPostProcess::GetMiniBoxes(const std::vector<cv::Point2f> &contour) {
cv::RotatedRect box = cv::minAreaRect(contour);
std::vector<cv::Point2f> points(4);
box.points(points.data());
std::sort(
points.begin(), points.end(),
[](const cv::Point2f &a, const cv::Point2f &b) { return a.x < b.x; });
int index_1 = 0, index_2 = 1, index_3 = 2, index_4 = 3;
if (points[1].y > points[0].y) {
index_1 = 0;
index_4 = 1;
} else {
index_1 = 1;
index_4 = 0;
}
if (points[3].y > points[2].y) {
index_2 = 2;
index_3 = 3;
} else {
index_2 = 3;
index_3 = 2;
}
std::vector<cv::Point2f> box_points = {points[index_1], points[index_2],
points[index_3], points[index_4]};
float sside = std::min(box.size.width, box.size.height);
return std::make_pair(box_points, sside);
}
float DBPostProcess::BoxScoreFast(const cv::Mat &bitmap,
const std::vector<cv::Point2f> &contour) {
int h = bitmap.size[bitmap.dims - 2]; // must be CHW
int w = bitmap.size[bitmap.dims - 1];
std::vector<cv::Point2f> contour_copy = contour;
int xmin = std::max(
0, static_cast<int>(std::floor(
std::min_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.x < b.x;
})
->x)));
int xmax = std::max(
0, static_cast<int>(std::ceil(
std::max_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.x < b.x;
})
->x)));
int ymin = std::max(
0, static_cast<int>(std::floor(
std::min_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.y < b.y;
})
->y)));
int ymax = std::max(
0, static_cast<int>(std::ceil(
std::max_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.y < b.y;
})
->y)));
xmin = std::min(xmin, w - 1);
xmax = std::min(xmax, w - 1);
ymin = std::min(ymin, h - 1);
ymax = std::min(ymax, h - 1);
cv::Mat mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1);
std::vector<cv::Point> contour_copy_int;
for (auto &point : contour_copy) {
point.x -= xmin;
point.y -= ymin;
contour_copy_int.push_back(
cv::Point(static_cast<int>(point.x), static_cast<int>(point.y)));
}
std::vector<std::vector<cv::Point>> contours = {contour_copy_int};
cv::fillPoly(mask, contours, cv::Scalar(1));
cv::Mat roi = bitmap(cv::Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1));
cv::Scalar mean_val = cv::mean(roi, mask);
return mean_val[0];
}
float DBPostProcess::BoxScoreSlow(const cv::Mat &bitmap,
const std::vector<cv::Point2f> &contour) {
int h = bitmap.size[bitmap.dims - 2]; // must be CHW
int w = bitmap.size[bitmap.dims - 1];
std::vector<cv::Point2f> contour_copy = contour;
int xmin = std::max(
0, static_cast<int>(std::floor(
std::min_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.x < b.x;
})
->x)));
int xmax = std::max(
0, static_cast<int>(std::ceil(
std::max_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.x < b.x;
})
->x)));
int ymin = std::max(
0, static_cast<int>(std::floor(
std::min_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.y < b.y;
})
->y)));
int ymax = std::max(
0, static_cast<int>(std::ceil(
std::max_element(contour_copy.begin(), contour_copy.end(),
[](const cv::Point2f &a, const cv::Point2f &b) {
return a.y < b.y;
})
->y)));
xmin = std::min(xmin, w - 1);
xmax = std::min(xmax, w - 1);
ymin = std::min(ymin, h - 1);
ymax = std::min(ymax, h - 1);
cv::Mat mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8UC1);
std::vector<cv::Point> contour_copy_int;
for (auto &point : contour_copy) {
point.x -= xmin;
point.y -= ymin;
contour_copy_int.push_back(
cv::Point(static_cast<int>(point.x), static_cast<int>(point.y)));
}
std::vector<std::vector<cv::Point>> contours = {contour_copy_int};
cv::fillPoly(mask, contours, 1);
cv::Scalar mean = cv::mean(
bitmap(cv::Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1)), mask);
return static_cast<float>(mean[0]);
}
@@ -0,0 +1,135 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include "polyclipping/clipper.hpp"
#include "src/utils/func_register.h"
struct DetResizeForTestParam {
absl::optional<std::vector<int>> input_shape = absl::nullopt;
absl::optional<int> max_side_limit = absl::nullopt;
absl::optional<std::vector<int>> image_shape = absl::nullopt;
absl::optional<bool> keep_ratio = absl::nullopt;
absl::optional<int> limit_side_len = absl::nullopt;
absl::optional<std::string> limit_type = absl::nullopt;
absl::optional<int> resize_long = absl::nullopt;
};
class DetResizeForTest : public BaseProcessor {
public:
DetResizeForTest(const DetResizeForTestParam &params);
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param_ptr = nullptr) const override;
private:
int resize_type_ = 0;
bool keep_ratio_ = false;
int resize_long_;
std::vector<int> input_shape_;
std::vector<int> image_shape_;
int limit_side_len_;
std::string limit_type_;
int max_side_limit_ = 4000;
absl::StatusOr<cv::Mat> Resize(const cv::Mat &img, int limit_side_len,
const std::string &limit_type,
int max_side_limit) const;
cv::Mat ImagePadding(const cv::Mat &img, int value = 0) const;
absl::StatusOr<cv::Mat> ResizeImageType0(const cv::Mat &img,
int limit_side_len,
const std::string &limit_type,
int max_side_limit) const;
absl::StatusOr<cv::Mat> ResizeImageType1(const cv::Mat &img) const;
absl::StatusOr<cv::Mat> ResizeImageType2(const cv::Mat &img) const;
absl::StatusOr<cv::Mat> ResizeImageType3(const cv::Mat &img) const;
static constexpr int INPUTSHAPE = 3;
};
struct DBPostProcessParams {
absl::optional<float> thresh = absl::nullopt;
absl::optional<float> box_thresh = absl::nullopt;
absl::optional<float> unclip_ratio = absl::nullopt;
int max_candidates = 1000;
bool use_dilation = false;
std::string score_mode = "fast";
std::string box_type = "quad";
};
class DBPostProcess {
public:
DBPostProcess(const DBPostProcessParams &params);
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
operator()(const cv::Mat &preds, const std::vector<int> &img_shapes,
absl::optional<float> thresh = absl::nullopt,
absl::optional<float> box_thresh = absl::nullopt,
absl::optional<float> unclip_ratio = absl::nullopt);
absl::StatusOr<std::vector<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>>
Apply(const cv::Mat &preds, const std::vector<int> &img_shapes,
absl::optional<float> thresh = absl::nullopt,
absl::optional<float> box_thresh = absl::nullopt,
absl::optional<float> unclip_ratio = absl::nullopt);
private:
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
Process(const cv::Mat &pred, const std::vector<int> &img_shape, float thresh,
float box_thresh, float unclip_ratio);
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
PolygonsFromBitmap(const cv::Mat &pred, const cv::Mat &bitmap, int dest_width,
int dest_height, float box_thresh, float unclip_ratio);
absl::StatusOr<
std::pair<std::vector<std::vector<cv::Point2f>>, std::vector<float>>>
BoxesFromBitmap(const cv::Mat &pred, const cv::Mat &bitmap, int dest_width,
int dest_height, float box_thresh, float unclip_ratio);
absl::StatusOr<std::vector<cv::Point2f>>
Unclip(const std::vector<cv::Point2f> &box, float unclip_ratio);
std::pair<std::vector<cv::Point2f>, float>
GetMiniBoxes(const std::vector<cv::Point2f> &contour);
float BoxScoreFast(const cv::Mat &bitmap,
const std::vector<cv::Point2f> &contour);
float BoxScoreSlow(const cv::Mat &bitmap,
const std::vector<cv::Point2f> &contour);
private:
float thresh_;
float box_thresh_;
int max_candidates_;
float unclip_ratio_;
int min_size_;
bool use_dilation_;
std::string score_mode_;
std::string box_type_;
};
@@ -0,0 +1,135 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "result.h"
#include <fstream>
#include <string>
#include "src/utils/utility.h"
#include "third_party/nlohmann/json.hpp"
using json = nlohmann::json;
void TextDetResult::SaveToImg(const std::string &save_path) {
cv::Mat img = predictor_result_.input_image.clone();
const auto &dt_polys = predictor_result_.dt_polys;
for (const auto &poly : dt_polys) {
std::vector<cv::Point> pts;
for (const auto &pt : poly) {
pts.emplace_back(cv::Point(cvRound(pt.x), cvRound(pt.y)));
}
const cv::Point *pts_ptr = pts.data();
int npts = pts.size();
cv::polylines(img, &pts_ptr, &npts, 1, true, cv::Scalar(0, 0, 255), 2);
}
absl::StatusOr<std::string> full_path;
if (predictor_result_.input_path.empty()) {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "output_" << std::put_time(std::localtime(&now_time), "%Y%m%d_%H%M%S")
<< ".jpg";
std::string timestamp_filename = ss.str();
INFOW("Input path is empty, will use %s instead!",
timestamp_filename.c_str());
predictor_result_.input_path = timestamp_filename;
full_path =
Utility::SmartCreateDirectoryForImage(save_path, timestamp_filename);
} else {
full_path = Utility::SmartCreateDirectoryForImage(
save_path, predictor_result_.input_path);
}
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
bool success = cv::imwrite(full_path.value(), img);
if (!success) {
INFOE("Failed to write the image : %s", full_path.value().c_str());
exit(-1);
}
}
void TextDetResult::Print() const {
std::cout << "{\n \"res\": {" << std::endl;
std::cout << " \"input_path\": {" << predictor_result_.input_path
<< " }," << std::endl;
std::cout << " \"dt_polys\": [" << std::endl;
for (const auto &polygon : predictor_result_.dt_polys) {
std::cout << " [";
for (size_t i = 0; i < polygon.size(); ++i) {
std::cout << "[" << static_cast<int>(polygon[i].x) << ", "
<< static_cast<int>(polygon[i].y) << "]";
if (i < polygon.size() - 1)
std::cout << ", ";
}
std::cout << "]," << std::endl;
}
std::cout << " ]}," << std::endl;
std::cout << " \"dt_scores\": [" << std::endl;
for (auto it = predictor_result_.dt_scores.begin();
it != predictor_result_.dt_scores.end(); ++it) {
std::cout << *it;
if (it < predictor_result_.dt_scores.end() - 1)
std::cout << ", ";
}
std::cout << "]}" << std::endl;
std::cout << " ]" << std::endl;
std::cout << " }\n}" << std::endl;
}
void TextDetResult::SaveToJson(const std::string &save_path) const {
nlohmann::ordered_json j;
j["input_path"] = predictor_result_.input_path;
j["page_index"] = nullptr; //********
json polys_json = json::array();
for (const auto &polygon : predictor_result_.dt_polys) {
json poly_json = json::array();
for (const auto &point : polygon) {
poly_json.push_back(
{static_cast<int>(point.x), static_cast<int>(point.y)});
}
polys_json.push_back(poly_json);
}
j["dt_polys"] = polys_json;
j["dt_score"] = predictor_result_.dt_scores;
absl::StatusOr<std::string> full_path;
full_path = Utility::SmartCreateDirectoryForJson(
save_path, predictor_result_.input_path);
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
std::ofstream file(full_path.value());
if (file.is_open()) {
file << j.dump(4);
file.close();
} else {
INFOE("Could not open file for writing: %s", save_path.c_str());
exit(-1);
}
}
@@ -0,0 +1,34 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include "predictor.h"
#include "src/base/base_cv_result.h"
class TextDetResult : public BaseCVResult {
public:
TextDetResult(TextDetPredictorResult predictor_result)
: BaseCVResult(), predictor_result_(predictor_result){};
// std::unordered_map<std::string, cv::Mat> ToImg() const override;
void SaveToImg(const std::string &save_path) override;
void Print() const override;
void SaveToJson(const std::string &save_path) const override;
private:
TextDetPredictorResult predictor_result_;
};
@@ -0,0 +1,153 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "predictor.h"
#include <algorithm>
#include "result.h"
#include "src/common/image_batch_sampler.h"
TextRecPredictor::TextRecPredictor(const TextRecPredictorParams &params)
: BasePredictor(params.model_dir, params.model_name, params.device,
params.precision, params.enable_mkldnn,
params.mkldnn_cache_capacity, params.cpu_threads,
params.batch_size, "image"),
params_(params) {
auto status = CheckRecModelParams();
auto status_build = Build();
if (!status_build.ok()) {
INFOE("Build fail: %s", status_build.ToString().c_str());
exit(-1);
}
};
absl::Status TextRecPredictor::Build() {
const auto &pre_params = config_.PreProcessOpInfo();
Register<ReadImage>("Read", "BGR"); //******
Register<OCRReisizeNormImg>("ReisizeNorm", params_.input_shape);
Register<ToBatchUniform>("ToBatch");
infer_ptr_ = CreateStaticInfer();
const auto &post_params = config_.PostProcessOpInfo();
post_op_["CTCLabelDecode"] = std::unique_ptr<CTCLabelDecode>(
new CTCLabelDecode(YamlConfig::SmartParseVector(
post_params.at("PostProcess.character_dict"))
.vec_string));
return absl::OkStatus();
};
std::vector<std::unique_ptr<BaseCVResult>>
TextRecPredictor::Process(std::vector<cv::Mat> &batch_data) {
std::vector<cv::Mat> origin_image = {};
origin_image.reserve(batch_data.size());
for (const auto &mat : batch_data) {
origin_image.push_back(mat.clone());
}
auto batch_read = pre_op_.at("Read")->Apply(batch_data);
if (!batch_read.ok()) {
INFOE(batch_read.status().ToString().c_str());
exit(-1);
}
auto batch_resize_norm = pre_op_.at("ReisizeNorm")->Apply(batch_read.value());
if (!batch_resize_norm.ok()) {
INFOE(batch_resize_norm.status().ToString().c_str());
exit(-1);
}
auto batch_tobatch = pre_op_.at("ToBatch")->Apply(batch_resize_norm.value());
if (!batch_tobatch.ok()) {
INFOE(batch_tobatch.status().ToString().c_str());
exit(-1);
}
auto batch_infer = infer_ptr_->Apply(batch_tobatch.value());
if (!batch_infer.ok()) {
INFOE(batch_infer.status().ToString().c_str());
exit(-1);
}
auto ctc_result =
post_op_.at("CTCLabelDecode")->Apply(batch_infer.value()[0]);
if (!ctc_result.ok()) {
INFOE(ctc_result.status().ToString().c_str());
exit(-1);
}
std::vector<std::unique_ptr<BaseCVResult>> base_cv_result_ptr_vec = {};
for (int i = 0; i < ctc_result.value().size(); i++, input_index_++) {
TextRecPredictorResult predictor_result;
if (!input_path_.empty()) {
if (input_index_ == input_path_.size())
input_index_ = 0;
predictor_result.input_path = input_path_[input_index_];
}
predictor_result.input_image = origin_image[i];
predictor_result.rec_text = ctc_result.value()[i].first;
predictor_result.rec_score = ctc_result.value()[i].second;
predictor_result.vis_font = params_.vis_font_dir.value_or("");
predictor_result_vec_.push_back(predictor_result);
base_cv_result_ptr_vec.push_back(
std::unique_ptr<BaseCVResult>(new TextRecResult(predictor_result)));
}
return base_cv_result_ptr_vec;
}
absl::Status TextRecPredictor::CheckRecModelParams() {
auto result_models_check = Utility::GetOcrModelInfo(
params_.lang.value_or(""), params_.ocr_version.value_or(""));
if (!result_models_check.ok()) {
return absl::InvalidArgumentError("lang and ocr_version is invalid : " +
result_models_check.status().ToString());
}
auto result_model_name = ModelName();
if (!result_model_name.ok()) {
return absl::InternalError("Get model name fail : " +
result_model_name.status().ToString());
}
size_t pos_model_name = result_model_name.value().find('_');
size_t pos_model_check = std::get<1>(result_models_check.value()).find('_');
std::string prefix_model_name =
result_model_name.value().substr(0, pos_model_name);
std::string prefix_model_check =
std::get<1>(result_models_check.value()).substr(0, pos_model_check);
auto result =
Utility::GetOcrModelInfo(params_.lang.value_or(""), prefix_model_name);
if (!result.ok()) {
return absl::InternalError("Model and lang do not match : " +
result.status().ToString());
}
if (params_.ocr_version.has_value()) {
if (prefix_model_name != params_.ocr_version.value()) {
INFOW("Rec model ocr_version and ocr_verision params do not match");
}
}
#ifdef USE_FREETYPE
if (!params_.vis_font_dir.has_value()) {
return absl::InvalidArgumentError(
"Visualization font path is empty, please provide " +
std::get<2>(result_models_check.value()) + " path.");
} else {
size_t pos = params_.vis_font_dir.value().find_last_of("/\\");
std::string filename = params_.vis_font_dir.value().substr(pos + 1);
if (filename != std::get<2>(result_models_check.value())) {
return absl::NotFoundError("Expected visualization font is " +
std::get<2>(result_models_check.value()) +
", but get is " + filename);
}
}
#endif
return absl::OkStatus();
}
@@ -0,0 +1,69 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "processors.h"
#include "src/base/base_batch_sampler.h"
#include "src/base/base_cv_result.h"
#include "src/base/base_predictor.h"
#include "src/common/processors.h"
struct TextRecPredictorResult {
std::string input_path = "";
cv::Mat input_image;
std::string rec_text = "";
float rec_score = 0.0;
std::string vis_font = "";
};
struct TextRecPredictorParams {
absl::optional<std::string> model_name = absl::nullopt;
absl::optional<std::string> model_dir = absl::nullopt;
absl::optional<std::string> lang = absl::nullopt;
absl::optional<std::string> ocr_version = absl::nullopt;
absl::optional<std::string> vis_font_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
std::string precision = "fp32";
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
int cpu_threads = 8;
int batch_size = 1;
absl::optional<std::vector<int>> input_shape = absl::nullopt;
};
class TextRecPredictor : public BasePredictor {
public:
TextRecPredictor(const TextRecPredictorParams &params);
std::vector<TextRecPredictorResult> PredictorResult() const {
return predictor_result_vec_;
};
void ResetResult() override { predictor_result_vec_.clear(); };
absl::Status Build();
std::vector<std::unique_ptr<BaseCVResult>>
Process(std::vector<cv::Mat> &batch_data) override;
absl::Status CheckRecModelParams();
private:
std::unordered_map<std::string, std::unique_ptr<CTCLabelDecode>> post_op_;
std::vector<TextRecPredictorResult> predictor_result_vec_;
std::unique_ptr<PaddleInfer> infer_ptr_;
TextRecPredictorParams params_;
int input_index_ = 0;
};
@@ -0,0 +1,267 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "processors.h"
#include <numeric>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include "src/utils/utility.h"
absl::StatusOr<std::vector<cv::Mat>>
OCRReisizeNormImg::Apply(std::vector<cv::Mat> &input, const void *param) const {
std::vector<cv::Mat> output = {};
output.reserve(input.size());
if (input_shape_.empty()) {
for (auto &image : input) {
auto result = Resize(image);
if (!result.ok()) {
return result.status();
}
output.push_back(result.value());
}
} else {
for (auto &image : input) {
auto result = StaticResize(image);
if (!result.ok()) {
return result.status();
}
output.push_back(result.value());
}
}
return output;
}
absl::StatusOr<cv::Mat> OCRReisizeNormImg::Resize(cv::Mat &image) const {
float rec_wh_ratio = (float)rec_image_shape_[2] / (float)rec_image_shape_[1];
float image_wh_ratio = (float)image.size[1] / (float)image.size[0];
float max_wh_ratio = std::max(rec_wh_ratio, image_wh_ratio);
auto image_result = ResizeNormImg(image, max_wh_ratio);
if (!image_result.ok()) {
return image_result.status();
}
return image_result.value();
}
absl::StatusOr<cv::Mat> OCRReisizeNormImg::StaticResize(cv::Mat &image) const {
cv::Mat resize_image;
int img_c = input_shape_[0];
int img_h = input_shape_[1];
int img_w = input_shape_[2];
cv::resize(image, resize_image, cv::Size(img_w, img_h));
resize_image.convertTo(resize_image, CV_32F);
std::vector<cv::Mat> mat_split(resize_image.channels());
cv::split(resize_image, mat_split);
for (auto &item : mat_split) {
item /= 255;
item -= 0.5;
item /= 0.5;
item = item.reshape(1, 1);
}
cv::Mat resize_image_process;
cv::hconcat(mat_split, resize_image_process);
std::vector<int> resize_shape = {img_c, img_h, img_w};
resize_image_process = resize_image_process.reshape(1, resize_shape);
return resize_image_process;
}
absl::StatusOr<cv::Mat>
OCRReisizeNormImg::ResizeNormImg(cv::Mat &image, float max_wh_ratio) const {
assert(rec_image_shape_[0] == image.channels());
int rec_c = rec_image_shape_[0];
int rec_h = rec_image_shape_[1];
int rec_w = rec_image_shape_[2];
rec_w = rec_h * max_wh_ratio;
cv::Mat resize_image;
int resize_w = 0;
if (rec_w > MAX_IMG_W) {
rec_w = MAX_IMG_W;
resize_w = MAX_IMG_W;
cv::resize(image, resize_image, cv::Size(resize_w, rec_h));
} else {
float wh_ratio = (float)image.size[1] / (float)image.size[0];
if (std::ceil(rec_h * wh_ratio) > rec_w) {
resize_w = rec_w;
} else {
resize_w = std::ceil(rec_h * wh_ratio);
}
cv::resize(image, resize_image, cv::Size(resize_w, rec_h));
}
resize_image.convertTo(resize_image, CV_32F);
std::vector<cv::Mat> mat_split(resize_image.channels());
cv::split(resize_image, mat_split);
for (auto &item : mat_split) {
item /= 255;
item -= 0.5;
item /= 0.5;
item = item.reshape(1, 1);
}
cv::Mat resize_image_process;
cv::hconcat(mat_split, resize_image_process);
std::vector<int> resize_shape = {rec_c, rec_h, resize_w};
resize_image_process = resize_image_process.reshape(1, resize_shape);
std::vector<int> image_shape = {rec_c, rec_h, rec_w};
cv::Mat padding_im =
cv::Mat::zeros(image_shape.size(), &image_shape[0], CV_32F);
for (int c = 0; c < rec_c; ++c) {
for (int row = 0; row < rec_h; ++row) {
float *dst = padding_im.ptr<float>(c) + row * rec_w;
float *src = resize_image_process.ptr<float>(c) + row * resize_w;
std::copy(src, src + resize_w, dst);
}
}
return padding_im;
}
CTCLabelDecode::CTCLabelDecode(const std::vector<std::string> &character_list,
bool use_space_char)
: character_list_(character_list), use_space_char_(use_space_char) {
if (character_list_.empty()) {
const std::string normal = "0123456789abcdefghijklmnopqrstuvwxyz";
for (const auto &item : normal) {
character_list_.emplace_back(std::string(1, item));
}
}
if (use_space_char) {
character_list_.emplace_back(std::string(" "));
}
AddSpecialChar();
dict_.reserve(character_list_.size());
for (int i = 0; i < character_list_.size(); i++) {
dict_[i] = character_list_[i];
}
}
absl::StatusOr<std::vector<std::pair<std::string, float>>>
CTCLabelDecode::Apply(const cv::Mat &preds) const {
auto preds_batch = Utility::SplitBatch(preds);
std::vector<std::pair<std::string, float>> ctc_result = {};
ctc_result.reserve(preds_batch.value().size());
if (!preds_batch.ok()) {
return preds_batch.status();
}
for (const auto &pred : preds_batch.value()) {
auto result = Process(pred);
if (!result.ok()) {
return result.status();
}
ctc_result.push_back(result.value());
}
return ctc_result;
}
absl::StatusOr<std::pair<std::string, float>>
CTCLabelDecode::Process(const cv::Mat &pred_data) const {
std::vector<int> shape_squeeze = {};
for (int i = 1; i < pred_data.dims; i++) {
shape_squeeze.push_back(pred_data.size[i]);
}
cv::Mat pred_data_process;
pred_data_process = pred_data.reshape(1, shape_squeeze);
int seq_len = pred_data_process.size[0];
int num_classes = pred_data_process.size[1];
std::list<int> text_index;
std::list<float> text_prob;
for (int t = 0; t < seq_len; ++t) {
const float *row_ptr = pred_data_process.ptr<float>(t);
float max_val = row_ptr[0];
int max_idx = 0;
for (int c = 1; c < num_classes; ++c) {
if (row_ptr[c] > max_val) {
max_val = row_ptr[c];
max_idx = c;
}
}
text_index.push_back(max_idx);
text_prob.push_back(max_val);
}
auto decode_result = Decode(text_index, text_prob, true);
if (!decode_result.ok()) {
return decode_result.status();
}
return decode_result.value();
}
absl::StatusOr<std::pair<std::string, float>>
CTCLabelDecode::Decode(std::list<int> &text_index, std::list<float> &text_prob,
bool is_remove_duplicate) const {
std::vector<bool> selection(text_index.size(), true);
if (is_remove_duplicate && text_index.size() > 1) {
auto prev = text_index.begin();
auto curr = std::next(prev);
size_t idx = 1;
for (; curr != text_index.end(); ++curr, ++prev, ++idx) {
if (*curr == *prev)
selection[idx] = false;
}
}
for (const auto &ignore_item : IGNORE_TOKEN) {
size_t idx = 0;
for (auto item_list = text_index.begin(); item_list != text_index.end();
++item_list, idx++) {
if (*item_list == ignore_item) {
selection[idx] = false;
}
}
}
auto sel_it = selection.begin();
for (auto it = text_index.begin(); it != text_index.end();) {
if (!(*sel_it)) {
it = text_index.erase(it);
} else {
++it;
}
++sel_it;
}
auto sel_it_prob = selection.begin();
for (auto it = text_prob.begin(); it != text_prob.end();) {
if (!(*sel_it_prob)) {
it = text_prob.erase(it);
} else {
++it;
}
++sel_it_prob;
}
std::vector<std::string> char_list = {};
for (auto list_index = text_index.begin(); list_index != text_index.end();
++list_index) {
if ((*list_index) < character_list_.size()) {
char_list.push_back(character_list_[*list_index]);
} else {
char_list.push_back(" ");
}
}
std::list<float> conf_list = {};
if (!text_prob.empty()) {
conf_list = text_prob;
} else {
conf_list = std::list<float>(selection.size(), 0);
}
std::string text;
for (const auto &item_char : char_list) {
text += item_char;
}
float sum = std::accumulate(conf_list.begin(), conf_list.end(), 0.0f);
float mean = sum / conf_list.size();
return std::pair<std::string, float>(text, mean);
}
void CTCLabelDecode::AddSpecialChar() {
character_list_.insert(character_list_.begin(), "blank");
}
@@ -0,0 +1,125 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <algorithm>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "src/common/processors.h"
#include "src/utils/func_register.h"
class OCRReisizeNormImg : public BaseProcessor {
public:
OCRReisizeNormImg(
absl::optional<std::vector<int>> input_shape = absl::nullopt,
std::vector<int> rec_image_shape = {3, 48, 320})
: rec_image_shape_(rec_image_shape),
input_shape_(input_shape.value_or(std::vector<int>())){};
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override;
absl::StatusOr<cv::Mat> Resize(cv::Mat &image) const;
absl::StatusOr<cv::Mat> StaticResize(cv::Mat &image) const;
absl::StatusOr<cv::Mat> ResizeNormImg(cv::Mat &image,
float max_wh_ratio) const;
static constexpr int MAX_IMG_W = 3200;
private:
std::vector<int> rec_image_shape_;
std::vector<int> input_shape_;
};
class CTCLabelDecode {
public:
CTCLabelDecode(const std::vector<std::string> &character_list = {},
bool use_space_char = true);
absl::StatusOr<std::vector<std::pair<std::string, float>>>
Apply(const cv::Mat &preds) const;
absl::StatusOr<std::pair<std::string, float>>
Process(const cv::Mat &pred_data) const;
absl::StatusOr<std::pair<std::string, float>>
Decode(std::list<int> &text_index, std::list<float> &text_prob,
bool is_remove_duplicate = false) const;
void AddSpecialChar();
private:
std::vector<std::string> character_list_;
bool use_space_char_;
std::unordered_map<int, std::string> dict_;
const std::vector<int> IGNORE_TOKEN = {0};
};
class ToBatchUniform : public ToBatch {
public:
absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input,
const void *param = nullptr) const override {
if (input.empty()) {
return absl::InvalidArgumentError("Input image vector is empty.");
}
int numDims = input[0].dims;
int dtype = input[0].type();
int maxWidth = 0;
for (const auto &img : input) {
if (img.dims != numDims || img.type() != dtype) {
return absl::InvalidArgumentError(
"All images must have the same number of dimensions and data type");
}
for (int i = 0; i < numDims - 1; ++i) {
if (img.size[i] != input[0].size[i]) {
return absl::InvalidArgumentError(
"All images must have the same dimensions except width");
}
}
maxWidth = std::max(maxWidth, img.size[numDims - 1]);
}
std::vector<cv::Mat> paddedImages;
for (const auto &img : input) {
int currentWidth = img.size[numDims - 1];
if (currentWidth == maxWidth) {
paddedImages.push_back(img.clone());
continue;
}
std::vector<int> newSizes(numDims);
for (int i = 0; i < numDims - 1; ++i) {
newSizes[i] = img.size[i];
}
newSizes[numDims - 1] = maxWidth;
cv::Mat paddedImg(numDims, newSizes.data(), dtype, cv::Scalar::all(0));
std::vector<cv::Range> srcRanges(numDims, cv::Range::all());
std::vector<cv::Range> dstRanges(numDims, cv::Range::all());
dstRanges[numDims - 1] = cv::Range(0, currentWidth);
img.copyTo(paddedImg(dstRanges));
paddedImages.push_back(paddedImg);
}
return ToBatch::Apply(paddedImages);
}
};
@@ -0,0 +1,138 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "result.h"
#include <fstream>
#ifdef USE_FREETYPE
#include <opencv2/freetype.hpp>
#endif
#include <string>
#include "src/utils/utility.h"
#include "third_party/nlohmann/json.hpp"
using json = nlohmann::json;
#ifdef USE_FREETYPE
void TextRecResult::SaveToImg(const std::string &save_path) {
int image_width = predictor_result_.input_image.size[1];
int image_height = predictor_result_.input_image.size[0];
std::string text = predictor_result_.rec_text + "(" +
std::to_string(predictor_result_.rec_score) + ")";
int font = AdjustFontSize(image_width, text);
cv::Ptr<cv::freetype::FreeType2> ft2 = cv::freetype::createFreeType2();
ft2->loadFontData(predictor_result_.vis_font, 0);
int baseline = 0;
cv::Size text_size = ft2->getTextSize(text, font, -1, &baseline);
int row_height = text_size.height;
int new_image_height = image_height + static_cast<int>(row_height * 1.2);
cv::Mat new_image(new_image_height, image_width, CV_8UC3,
cv::Scalar(255, 255, 255));
predictor_result_.input_image.copyTo(
new_image(cv::Rect(0, 0, image_width, image_height)));
cv::Point org(0, image_height + row_height);
ft2->putText(new_image, text, org, font, cv::Scalar(0, 0, 0), -1, cv::LINE_AA,
true);
absl::StatusOr<std::string> full_path;
if (predictor_result_.input_path.empty()) {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "output_" << std::put_time(std::localtime(&now_time), "%Y%m%d_%H%M%S")
<< ".jpg";
std::string timestamp_filename = ss.str();
INFOW("Input path is empty, will use %s instead!",
timestamp_filename.c_str());
predictor_result_.input_path = timestamp_filename;
full_path =
Utility::SmartCreateDirectoryForImage(save_path, timestamp_filename);
} else {
full_path = Utility::SmartCreateDirectoryForImage(
save_path, predictor_result_.input_path);
}
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
bool success = cv::imwrite(full_path.value(), new_image);
if (!success) {
INFOE("Error: Failed to write the image :%s ", full_path.value().c_str());
exit(-1);
}
}
int TextRecResult::AdjustFontSize(int image_width,
const std::string &text) const {
cv::Ptr<cv::freetype::FreeType2> ft2 = cv::freetype::createFreeType2();
int font_size = static_cast<int>(image_width * 0.06);
ft2->loadFontData(predictor_result_.vis_font, 0);
cv::Size text_size;
int baseline = 0;
do {
text_size = ft2->getTextSize(text, font_size, -1, &baseline);
if (text_size.width <= image_width)
break;
font_size--;
} while (font_size > 0);
return font_size;
}
#else
void TextRecResult::SaveToImg(const std::string &save_path) {
INFOW(
"OpenCV was not compiled with the freetype module (opencv_freetype), rec "
"image will be not saved.");
}
#endif
void TextRecResult::Print() const {
std::cout << "{\n \"res\": {" << std::endl;
std::cout << " \"input_path\": {" << predictor_result_.input_path << " },"
<< std::endl;
std::cout << " \"rec_text\": {" << predictor_result_.rec_text << " }"
<< std::endl;
std::cout << " \"rec_score\": {" << predictor_result_.rec_score << " }"
<< std::endl;
std::cout << "}" << std::endl;
}
void TextRecResult::SaveToJson(const std::string &save_path) const {
nlohmann::ordered_json j;
j["input_path"] = predictor_result_.input_path;
j["page_index"] = nlohmann::json::value_t::null; //********
j["rec_text"] = predictor_result_.rec_text;
j["rec_score"] = predictor_result_.rec_score;
auto full_path = Utility::SmartCreateDirectoryForJson(
save_path, predictor_result_.input_path);
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
std::ofstream file(full_path.value());
if (file.is_open()) {
file << j.dump(4);
file.close();
} else {
INFOE("Could not open file for writing: %s", save_path.c_str());
exit(-1);
}
}
@@ -0,0 +1,35 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include "predictor.h"
#include "src/base/base_cv_result.h"
class TextRecResult : public BaseCVResult {
public:
TextRecResult(TextRecPredictorResult predictor_result)
: BaseCVResult(), predictor_result_(predictor_result){};
// std::unordered_map<std::string, cv::Mat> ToImg() const override;
void SaveToImg(const std::string &save_path) override;
void Print() const override;
void SaveToJson(const std::string &save_path) const override;
int AdjustFontSize(int image_width, const std::string &text) const;
private:
TextRecPredictorResult predictor_result_;
};
@@ -0,0 +1,367 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "pipeline.h"
#include "result.h"
#include "src/modules/image_classification/predictor.h"
#include "src/modules/image_unwarping/predictor.h"
_DocPreprocessorPipeline::_DocPreprocessorPipeline(
const DocPreprocessorPipelineParams &params)
: BasePipeline(), params_(params) {
if (params.paddlex_config.has_value()) {
if (params.paddlex_config.value().IsStr()) {
config_ = YamlConfig(params.paddlex_config.value().GetStr());
} else {
config_ = YamlConfig(params.paddlex_config.value().GetMap());
}
} else {
auto config_path = Utility::GetDefaultConfig("doc_preprocessor");
if (!config_path.ok()) {
INFOE("Could not find doc_preprocessors pipeline config file : %s",
config_path.status().ToString().c_str());
exit(-1);
}
config_ = YamlConfig(config_path.value());
}
OverrideConfig();
auto result_doc = config_.GetBool("use_doc_orientation_classify", true);
if (!result_doc.ok()) {
INFOE("use_doc_orientation_classify set fail : %s",
result_doc.status().ToString().c_str());
exit(-1);
}
use_doc_orientation_classify_ = result_doc.value();
auto result_batch = config_.GetInt("batch_size", 1);
if (!result_batch.ok()) {
INFOE("batch_size get fail: %s", result_batch.status().ToString().c_str());
exit(-1);
}
if (use_doc_orientation_classify_) {
ClasPredictorParams doc_ori_classify_params;
auto result_model_dir =
config_.GetString("DocOrientationClassify.model_dir");
if (!result_model_dir.ok()) {
INFOE("Could not find DocOrientationClassify model dir : %s",
result_model_dir.status().ToString().c_str());
exit(-1);
}
auto result_model_name =
config_.GetString("DocOrientationClassify.model_name");
if (!result_model_name.ok()) {
INFOE("Could not find DocOrientationClassify model name : %s",
result_model_name.status().ToString().c_str());
exit(-1);
}
doc_ori_classify_params.model_dir = result_model_dir.value();
doc_ori_classify_params.model_name = result_model_name.value();
doc_ori_classify_params.device = params_.device;
doc_ori_classify_params.precision = params_.precision;
doc_ori_classify_params.enable_mkldnn = params_.enable_mkldnn;
doc_ori_classify_params.mkldnn_cache_capacity =
params_.mkldnn_cache_capacity;
doc_ori_classify_params.cpu_threads = params_.cpu_threads;
doc_ori_classify_params.batch_size = result_batch.value();
doc_ori_classify_model_ =
CreateModule<ClasPredictor>(doc_ori_classify_params);
}
auto result_unwarping = config_.GetBool("use_doc_unwarping", true);
if (!result_unwarping.ok()) {
INFOE("use_doc_unwarping get fail:%s",
result_unwarping.status().ToString().c_str());
exit(-1);
}
use_doc_unwarping_ = result_unwarping.value();
if (use_doc_unwarping_) {
WarpPredictorParams doc_unwarping_params;
auto result_model_dir = config_.GetString("DocUnwarping.model_dir");
if (!result_model_dir.ok()) {
INFOE("Could not find DocUnwarping model dir : %s",
result_model_dir.status().ToString().c_str());
exit(-1);
}
auto result_model_name = config_.GetString("DocUnwarping.model_name");
if (!result_model_name.ok()) {
INFOE("Could not find DocUnwarping model name : %s",
result_model_name.status().ToString().c_str());
exit(-1);
}
doc_unwarping_params.model_dir = result_model_dir.value();
doc_unwarping_params.model_name = result_model_name.value();
doc_unwarping_params.device = params_.device;
doc_unwarping_params.precision = params_.precision;
doc_unwarping_params.enable_mkldnn = params_.enable_mkldnn;
doc_unwarping_params.mkldnn_cache_capacity = params_.mkldnn_cache_capacity;
doc_unwarping_params.cpu_threads = params_.cpu_threads;
doc_unwarping_params.batch_size = result_batch.value();
doc_unwarping_model_ = CreateModule<WarpPredictor>(doc_unwarping_params);
}
batch_sampler_ptr_ = std::unique_ptr<BaseBatchSampler>(
new ImageBatchSampler(result_batch.value()));
};
std::vector<std::unique_ptr<BaseCVResult>>
_DocPreprocessorPipeline::Predict(const std::vector<std::string> &input) {
auto model_setting = GetModelSettings();
auto status = CheckModelSettingsVaild(model_setting);
if (!status.ok()) {
INFOE("the input params for model settings are invalid!: %s",
status.ToString().c_str());
exit(-1);
}
auto batches = batch_sampler_ptr_->Apply(input);
if (!batches.ok()) {
INFOE("pipeline get sample fail : %s", batches.status().ToString().c_str());
exit(-1);
}
auto input_path = batch_sampler_ptr_->InputPath();
int index = 0;
std::vector<cv::Mat> origin_image = {};
std::vector<std::unique_ptr<BaseCVResult>> base_cv_result_ptr_vec = {};
std::vector<DocPreprocessorPipelineResult> pipeline_result_vec = {};
pipeline_result_vec_.clear();
for (auto &batch_data : batches.value()) {
origin_image.reserve(batch_data.size());
for (const auto &mat : batch_data) {
origin_image.push_back(mat.clone());
}
std::vector<int> angles = {};
std::vector<cv::Mat> rotate_images = {};
if (model_setting["use_doc_orientation_classify"]) {
doc_ori_classify_model_->Predict(batch_data);
ClasPredictor *derived =
static_cast<ClasPredictor *>(doc_ori_classify_model_.get());
std::vector<ClasPredictorResult> preds = derived->PredictorResult();
for (auto &pred : preds) {
auto result_angle = Utility::StringToInt(pred.label_names[0]);
if (!result_angle.ok()) {
INFOE("angle is invalid : %s",
result_angle.status().ToString().c_str());
exit(-1);
}
angles.push_back(result_angle.value());
auto result_rotate = ComponentsProcessor::RotateImage(
pred.input_image, result_angle.value());
if (!result_rotate.ok()) {
INFOE("RotateImage fail : %s",
result_rotate.status().ToString().c_str());
exit(-1);
}
rotate_images.push_back(result_rotate.value());
}
} else {
angles = std::vector<int>(batch_data.size(), -1);
rotate_images = batch_data;
}
std::vector<cv::Mat> output_imgs = {};
if (model_setting["use_doc_unwarping"]) {
doc_unwarping_model_->Predict(rotate_images);
WarpPredictor *derived =
static_cast<WarpPredictor *>(doc_unwarping_model_.get());
std::vector<WarpPredictorResult> preds = derived->PredictorResult();
for (auto &pred : preds) {
output_imgs.push_back(pred.doctr_img); //***"RGB" "BGR"
}
} else {
output_imgs = rotate_images;
}
pipeline_result_vec.clear();
for (int i = 0; i < output_imgs.size(); i++, index++) {
DocPreprocessorPipelineResult pipeline_result;
pipeline_result.input_path = input_path[index];
pipeline_result.input_image = origin_image[i];
pipeline_result.model_settings = model_setting;
pipeline_result.angle = angles[i];
pipeline_result.rotate_image = rotate_images[i];
pipeline_result.output_image = output_imgs[i];
pipeline_result_vec.push_back(pipeline_result);
}
origin_image.clear();
pipeline_result_vec_.insert(pipeline_result_vec_.end(),
pipeline_result_vec.begin(),
pipeline_result_vec.end());
for (auto &pipeline_result : pipeline_result_vec) {
std::unique_ptr<BaseCVResult> base_cv_result_ptr =
std::unique_ptr<BaseCVResult>(
new DocPreprocessorResult(pipeline_result));
base_cv_result_ptr_vec.emplace_back(std::move(base_cv_result_ptr));
}
}
return base_cv_result_ptr_vec;
};
std::unordered_map<std::string, bool>
_DocPreprocessorPipeline::GetModelSettings(
absl::optional<bool> use_doc_orientation_classify,
absl::optional<bool> use_doc_unwarping) const {
if (!use_doc_orientation_classify.has_value()) {
use_doc_orientation_classify = use_doc_orientation_classify_;
}
if (!use_doc_unwarping.has_value()) {
use_doc_unwarping = use_doc_unwarping_;
}
std::unordered_map<std::string, bool> model_settings = {};
model_settings["use_doc_orientation_classify"] =
use_doc_orientation_classify.value();
model_settings["use_doc_unwarping"] = use_doc_unwarping.value();
return model_settings;
};
absl::Status _DocPreprocessorPipeline::CheckModelSettingsVaild(
std::unordered_map<std::string, bool> model_settings) const {
if (model_settings["use_doc_orientation_classify"] &&
!use_doc_orientation_classify_) {
return absl::InvalidArgumentError(
"Set use_doc_orientation_classify, but the model for doc orientation "
"classify is not initialized.");
}
if (model_settings["use_doc_unwarping"] && !use_doc_unwarping_) {
return absl::InvalidArgumentError(
"Set use_doc_unwarping, but the model for doc unwarping is not "
"initialized.");
}
return absl::OkStatus();
}
std::vector<std::unique_ptr<BaseCVResult>>
DocPreprocessorPipeline::Predict(const std::vector<std::string> &input) {
if (thread_num_ == 1) {
return infer_->Predict(input);
}
batch_sampler_ptr_ =
std::unique_ptr<BaseBatchSampler>(new ImageBatchSampler(1));
auto nomeaning = batch_sampler_ptr_->Apply(input);
int input_num = nomeaning.value().size();
if (thread_num_ > input_num) {
INFOW("thread num exceed input num, will set %d", input_num);
thread_num_ = input_num;
}
int infer_batch_num = input_num / thread_num_;
auto status = batch_sampler_ptr_->SetBatchSize(infer_batch_num);
if (!status.ok()) {
INFOE("Set batch size fail : %s", status.ToString().c_str());
exit(-1);
}
auto infer_batch_data =
batch_sampler_ptr_->SampleFromVectorToStringVector(input);
if (!infer_batch_data.ok()) {
INFOE("Get infer batch data fail : %s",
infer_batch_data.status().ToString().c_str());
exit(-1);
}
std::vector<std::unique_ptr<BaseCVResult>> results = {};
results.reserve(input_num);
for (auto &infer_data : infer_batch_data.value()) {
auto status =
AutoParallelSimpleInferencePipeline::PredictThread(infer_data);
if (!status.ok()) {
INFOE("Infer fail : %s", status.ToString().c_str());
exit(-1);
}
}
for (int i = 0; i < infer_batch_data.value().size(); i++) {
auto infer_data_result = GetResult();
if (!infer_data_result.ok()) {
INFOE("Get infer result fail : %s",
infer_batch_data.status().ToString().c_str());
exit(-1);
}
results.insert(results.end(),
std::make_move_iterator(infer_data_result.value().begin()),
std::make_move_iterator(infer_data_result.value().end()));
}
return results;
}
void _DocPreprocessorPipeline::OverrideConfig() {
auto &data = config_.Data();
if (params_.doc_orientation_classify_model_name.has_value()) {
auto it = config_.FindKey("DocOrientationClassify.model_name");
if (!it.ok()) {
data["DocPreprocessor.SubModules.DocOrientationClassify."
"model_name"] = params_.doc_orientation_classify_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_orientation_classify_model_name.value();
}
}
if (params_.doc_orientation_classify_model_dir.has_value()) {
auto it = config_.FindKey("DocOrientationClassify.model_dir");
if (!it.ok()) {
data["DocPreprocessor.SubModules.DocOrientationClassify."
"model_dir"] = params_.doc_orientation_classify_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_orientation_classify_model_dir.value();
}
}
if (params_.doc_unwarping_model_name.has_value()) {
auto it = config_.FindKey("DocUnwarping.model_name");
if (!it.ok()) {
data["DocPreprocessor.SubModules.DocUnwarping.model_name"] =
params_.doc_unwarping_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_unwarping_model_name.value();
}
}
if (params_.doc_unwarping_model_dir.has_value()) {
auto it = config_.FindKey("DocUnwarping.model_dir");
if (!it.ok()) {
data["DocPreprocessor.SubModules.DocUnwarping.model_dir"] =
params_.doc_unwarping_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_unwarping_model_dir.value();
}
}
if (params_.use_doc_orientation_classify.has_value()) {
auto it = config_.FindKey("DocPreprocessor.use_doc_orientation_classify");
if (!it.ok()) {
data["DocPreprocessor.use_doc_orientation_classify"] =
params_.use_doc_orientation_classify.value() ? "true" : "false";
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] =
params_.use_doc_orientation_classify.value() ? "true" : "false";
}
}
if (params_.use_doc_unwarping.has_value()) {
auto it = config_.FindKey("DocPreprocessor.use_doc_unwarping");
if (!it.ok()) {
data["DocPreprocessor.use_doc_unwarping"] =
params_.use_doc_unwarping.value() ? "true" : "false";
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.use_doc_unwarping.value() ? "true" : "false";
}
}
}
@@ -0,0 +1,115 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include "src/base/base_pipeline.h"
#include "src/common/image_batch_sampler.h"
#include "src/common/parallel.h"
#include "src/common/processors.h"
#include "src/utils/ilogger.h"
#include "src/utils/utility.h"
struct DocPreprocessorPipelineResult {
std::string input_path = "";
cv::Mat input_image;
std::unordered_map<std::string, bool> model_settings;
int angle = 0;
cv::Mat rotate_image;
cv::Mat output_image;
cv::Mat image_all;
};
struct DocPreprocessorPipelineParams {
absl::optional<std::string> doc_orientation_classify_model_name =
absl::nullopt;
absl::optional<std::string> doc_orientation_classify_model_dir =
absl::nullopt;
absl::optional<std::string> doc_unwarping_model_name = absl::nullopt;
absl::optional<std::string> doc_unwarping_model_dir = absl::nullopt;
absl::optional<bool> use_doc_orientation_classify = absl::nullopt;
absl::optional<bool> use_doc_unwarping = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
std::string precision = "fp32";
int cpu_threads = 8;
int thread_num = 1;
absl::optional<Utility::PaddleXConfigVariant> paddlex_config = absl::nullopt;
};
class _DocPreprocessorPipeline : public BasePipeline {
public:
explicit _DocPreprocessorPipeline(
const DocPreprocessorPipelineParams &params);
virtual ~_DocPreprocessorPipeline() = default;
_DocPreprocessorPipeline() = delete;
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input) override;
std::unordered_map<std::string, bool> GetModelSettings(
absl::optional<bool> use_doc_orientation_classify = absl::nullopt,
absl::optional<bool> use_doc_unwarping = absl::nullopt) const;
absl::Status CheckModelSettingsVaild(
std::unordered_map<std::string, bool> model_settings) const;
std::vector<DocPreprocessorPipelineResult> PipelineResult() const {
return pipeline_result_vec_;
};
void OverrideConfig();
private:
bool use_doc_orientation_classify_;
bool use_doc_unwarping_;
std::unique_ptr<BasePredictor> doc_ori_classify_model_;
std::unique_ptr<BasePredictor> doc_unwarping_model_;
DocPreprocessorPipelineParams params_;
YamlConfig config_;
std::unique_ptr<BaseBatchSampler> batch_sampler_ptr_;
std::vector<DocPreprocessorPipelineResult> pipeline_result_vec_;
};
class DocPreprocessorPipeline
: public AutoParallelSimpleInferencePipeline<
_DocPreprocessorPipeline, DocPreprocessorPipelineParams,
std::vector<std::string>,
std::vector<std::unique_ptr<BaseCVResult>>> {
public:
DocPreprocessorPipeline(const DocPreprocessorPipelineParams &params)
: AutoParallelSimpleInferencePipeline(params),
thread_num_(params.thread_num) {
if (thread_num_ == 1) {
infer_ =
std::unique_ptr<BasePipeline>(new _DocPreprocessorPipeline(params));
}
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input) override;
private:
int thread_num_;
std::unique_ptr<BasePipeline> infer_;
std::unique_ptr<BaseBatchSampler> batch_sampler_ptr_;
};
@@ -0,0 +1,129 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "result.h"
#include <algorithm>
#include <fstream>
#include <string>
#include "src/utils/utility.h"
#include "third_party/nlohmann/json.hpp"
using json = nlohmann::json;
void DocPreprocessorResult::SaveToImg(const std::string &save_path) {
cv::Mat input_img = pipeline_result_.input_image.clone();
cv::Mat rot_img = pipeline_result_.rotate_image.clone();
cv::Mat output_img = pipeline_result_.output_image.clone();
bool use_doc_orientation_classify =
pipeline_result_.model_settings.at("use_doc_orientation_classify");
bool use_doc_unwarping =
pipeline_result_.model_settings.at("use_doc_unwarping");
int angle = pipeline_result_.angle;
int h1 = input_img.size[0], w1 = input_img.size[1];
int h2 = rot_img.size[0], w2 = rot_img.size[1];
int h3 = output_img.size[0], w3 = output_img.size[1];
int h = std::max(h1, std::max(h2, h3));
int total_w = w1 + w2 + w3;
int final_h = h + 25;
cv::Mat img_show(final_h, total_w, CV_8UC3, cv::Scalar(255, 255, 255));
input_img.copyTo(img_show(cv::Rect(0, 0, w1, h1)));
rot_img.copyTo(img_show(cv::Rect(w1, 0, w2, h2)));
output_img.copyTo(img_show(cv::Rect(w1 + w2, 0, w3, h3)));
pipeline_result_.image_all = img_show.clone();
std::vector<std::string> txt_list = {
"Original Image",
"Rotated Image (" +
std::string(use_doc_orientation_classify ? "True" : "False") + ", " +
std::to_string(angle) + ")",
"Unwarping Image (" + std::string(use_doc_unwarping ? "True" : "False") +
")"};
std::vector<int> region_w_list = {w1, w2, w3};
std::vector<int> beg_w_list = {0, w1, w1 + w2};
for (int tno = 0; tno < 3; ++tno) {
DrawText(img_show, txt_list[tno], beg_w_list[tno], h, region_w_list[tno]);
}
auto full_path = Utility::SmartCreateDirectoryForImage(
save_path, pipeline_result_.input_path);
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
bool success = cv::imwrite(full_path.value(), img_show);
if (!success) {
INFOE("Error: Failed to write the image : %s", full_path.value().c_str());
exit(-1);
}
}
void DocPreprocessorResult::Print() const {
std::cout << "{\n \"res\": {" << std::endl;
std::cout << " \"input_path\": {" << pipeline_result_.input_path << "},"
<< std::endl;
std::cout << " \"model_settings\": {"
<< "use_doc_orientation_classify: " +
std::string(pipeline_result_.model_settings.at(
"use_doc_orientation_classify")
? "True"
: "False")
<< ", use_doc_unwarping: " +
std::string(
pipeline_result_.model_settings.at("use_doc_unwarping")
? "True"
: "False")
<< "}," << std::endl;
std::cout << " \"angle\": {" << pipeline_result_.angle << "},"
<< std::endl;
std::cout << "}" << std::endl;
}
void DocPreprocessorResult::SaveToJson(const std::string &save_path) const {
nlohmann::ordered_json j;
j["input_path"] = pipeline_result_.input_path;
j["page_index"] = nullptr; //********
j["model_settings"] = pipeline_result_.model_settings;
j["angle"] = pipeline_result_.angle;
auto full_path = Utility::SmartCreateDirectoryForJson(
save_path, pipeline_result_.input_path);
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
std::ofstream file(full_path.value());
if (file.is_open()) {
file << j.dump(4);
file.close();
} else {
INFOE("Could not open file for writing : %s", save_path.c_str());
exit(-1);
}
}
void DocPreprocessorResult::DrawText(cv::Mat &img, const std::string &text,
int x, int y, int width) {
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
double fontScale = 0.7;
int thickness = 2;
int baseline = 0;
cv::Size textSize =
cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
putText(img, text, cv::Point(x + 10, y + textSize.height + 2), fontFace,
fontScale, cv::Scalar(0, 0, 0), thickness, cv::LINE_AA);
}
@@ -0,0 +1,33 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "pipeline.h"
#include "src/base/base_cv_result.h"
class DocPreprocessorResult : public BaseCVResult {
public:
DocPreprocessorResult(DocPreprocessorPipelineResult pipeline_result_)
: BaseCVResult(), pipeline_result_(pipeline_result_){};
void SaveToImg(const std::string &save_path) override;
void Print() const override;
void SaveToJson(const std::string &save_path) const override;
static void DrawText(cv::Mat &img, const std::string &text, int x, int y,
int width);
private:
DocPreprocessorPipelineResult pipeline_result_;
};
@@ -0,0 +1,767 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "pipeline.h"
#include "result.h"
#include "src/utils/args.h"
_OCRPipeline::_OCRPipeline(const OCRPipelineParams &params)
: BasePipeline(), params_(params) {
if (params.paddlex_config.has_value()) {
if (params.paddlex_config.value().IsStr()) {
config_ = YamlConfig(params.paddlex_config.value().GetStr());
} else {
config_ = YamlConfig(params.paddlex_config.value().GetMap());
}
} else {
auto config_path = Utility::GetDefaultConfig("OCR");
if (!config_path.ok()) {
INFOE("Could not find OCR pipeline config file: %s",
config_path.status().ToString().c_str());
exit(-1);
}
config_ = YamlConfig(config_path.value());
}
OverrideConfig();
auto result_use_doc_orientation_classify =
config_.GetBool("use_doc_orientation_classify", true);
if (!result_use_doc_orientation_classify.ok()) {
INFOE("use_doc_orientation_classify config error : %s",
result_use_doc_orientation_classify.status().ToString().c_str());
exit(-1);
}
auto result_use_use_doc_unwarping =
config_.GetBool("use_doc_unwarping", true);
if (!result_use_use_doc_unwarping.ok()) {
INFOE("use_doc_unwarping config error : %s",
result_use_use_doc_unwarping.status().ToString().c_str());
exit(-1);
}
if (result_use_doc_orientation_classify.value() ||
result_use_use_doc_unwarping.value()) {
use_doc_preprocessor_ = true;
} else {
use_doc_preprocessor_ = false;
}
if (use_doc_preprocessor_) {
auto result_doc_preprocessor_config = config_.GetSubModule("SubPipelines");
if (!result_doc_preprocessor_config.ok()) {
INFOE("Get doc preprocessors subpipelines config fail : ",
result_doc_preprocessor_config.status().ToString().c_str());
exit(-1);
}
DocPreprocessorPipelineParams params;
params.device = params_.device;
params.precision = params_.precision;
params.enable_mkldnn = params_.enable_mkldnn;
params.mkldnn_cache_capacity = params_.mkldnn_cache_capacity;
params.cpu_threads = params_.cpu_threads;
params.paddlex_config = result_doc_preprocessor_config.value();
doc_preprocessors_pipeline_ =
CreatePipeline<_DocPreprocessorPipeline>(params);
use_doc_orientation_classify_ =
config_.GetBool("DocPreprocessor.use_doc_orientation_classify", true)
.value();
use_doc_unwarping_ =
config_.GetBool("DocPreprocessor.use_doc_unwarping", true).value();
}
auto result_use_textline_orientation =
config_.GetBool("use_textline_orientation", true);
if (!result_use_textline_orientation.ok()) {
INFOE("use_textline_orientation config error : %s",
result_use_textline_orientation.status().ToString().c_str());
exit(-1);
}
use_textline_orientation_ = result_use_textline_orientation.value();
if (use_textline_orientation_) {
ClasPredictorParams params;
params.device = params_.device;
params.precision = params_.precision;
params.enable_mkldnn = params_.enable_mkldnn;
params.mkldnn_cache_capacity = params_.mkldnn_cache_capacity;
params.cpu_threads = params_.cpu_threads;
auto result_batch_size =
config_.GetInt("TextLineOrientation.batch_size", 1);
if (!result_batch_size.ok()) {
INFOE("Get TextLineOrientation batch size fail: %s",
result_batch_size.status().ToString().c_str());
exit(-1);
}
params.batch_size = result_batch_size.value();
auto result_model_name =
config_.GetString("TextLineOrientation.model_name");
if (!result_model_name.ok()) {
INFOE("Could not find TextLineOrientation model name : %s",
result_model_name.status().ToString().c_str());
exit(-1);
}
params.model_name = result_model_name.value();
auto result_model_dir = config_.GetString("TextLineOrientation.model_dir");
if (!result_model_dir.ok()) {
INFOE("Could not find TextLineOrientation model dir : %s",
result_model_dir.status().ToString().c_str());
exit(-1);
}
params.model_dir = result_model_dir.value();
textline_orientation_model_ = CreateModule<ClasPredictor>(params);
}
auto text_type = config_.GetString("text_type");
if (!text_type.ok()) {
INFOE("Get text type fail : %s", text_type.status().ToString().c_str());
exit(-1);
}
text_type_ = text_type.value();
TextDetPredictorParams params_det;
auto result_text_det_model_name =
config_.GetString("TextDetection.model_name");
if (!result_text_det_model_name.ok()) {
INFOE("Could not find TextDetection model name : %s",
result_text_det_model_name.status().ToString().c_str());
exit(-1);
}
params_det.model_name = result_text_det_model_name.value();
auto result_text_det_model_dir = config_.GetString("TextDetection.model_dir");
if (!result_text_det_model_dir.ok()) {
INFOE("Could not find TextDetection model dir : %s",
result_text_det_model_dir.status().ToString().c_str());
exit(-1);
}
params_det.model_dir = result_text_det_model_dir.value();
auto result_det_input_shape = config_.GetString("TextDetection.input_shape");
if (!result_det_input_shape.value().empty()) {
params_det.input_shape =
config_.SmartParseVector(result_det_input_shape.value()).vec_int;
}
params_det.device = params_.device;
params_det.precision = params_.precision;
params_det.enable_mkldnn = params_.enable_mkldnn;
params_det.mkldnn_cache_capacity = params_.mkldnn_cache_capacity;
params_det.cpu_threads = params_.cpu_threads;
params_det.batch_size = config_.GetInt("TextDetection.batch_size", 1).value();
if (text_type_ == "general") {
params_det.limit_side_len =
config_.GetInt("TextDetection.limit_side_len", 960).value();
params_det.limit_type =
config_.GetString("TextDetection.limit_type", "max").value();
params_det.max_side_limit =
config_.GetInt("TextDetection.max_side_limit", 4000).value();
params_det.thresh = config_.GetFloat("TextDetection.thresh", 0.3).value();
params_det.box_thresh =
config_.GetFloat("TextDetection.box_thresh", 0.6).value();
params_det.unclip_ratio =
config_.GetFloat("TextDetection.unclip_ratio", 2.0).value();
sort_boxes_ = ComponentsProcessor::SortQuadBoxes;
crop_by_polys_ = std::unique_ptr<CropByPolys>(new CropByPolys("quad"));
} else if (text_type_ == "seal") {
params_det.limit_side_len =
config_.GetInt("TextDetection.limit_side_len", 736).value();
params_det.limit_type =
config_.GetString("TextDetection.limit_type", "min").value();
params_det.max_side_limit =
config_.GetInt("TextDetection.max_side_limit", 4000).value();
params_det.thresh = config_.GetFloat("TextDetection.thresh", 0.2).value();
params_det.box_thresh =
config_.GetFloat("TextDetection.box_thresh", 0.6).value();
params_det.unclip_ratio =
config_.GetFloat("TextDetection.unclip_ratio", 0.5).value();
sort_boxes_ = ComponentsProcessor::SortPolyBoxes;
crop_by_polys_ = std::unique_ptr<CropByPolys>(new CropByPolys("poly"));
} else {
INFOE("Unsupported text type We %s", text_type.value().c_str());
exit(-1);
}
text_det_model_ = CreateModule<TextDetPredictor>(params_det);
text_det_params_.text_det_limit_side_len = params_det.limit_side_len.value();
text_det_params_.text_det_limit_type = params_det.limit_type.value();
text_det_params_.text_det_max_side_limit = params_det.max_side_limit.value();
text_det_params_.text_det_thresh = params_det.thresh.value();
text_det_params_.text_det_box_thresh = params_det.box_thresh.value();
text_det_params_.text_det_unclip_ratio = params_det.unclip_ratio.value();
TextRecPredictorParams params_rec;
auto result_text_rec_model_name =
config_.GetString("TextRecognition.model_name");
if (!result_text_rec_model_name.ok()) {
INFOE("Could not find TextRecognition model name : %s",
result_text_rec_model_name.status().ToString().c_str());
exit(-1);
}
params_rec.model_name = result_text_rec_model_name.value();
auto result_text_rec_model_dir =
config_.GetString("TextRecognition.model_dir");
if (!result_text_rec_model_dir.ok()) {
INFOE("Could not find TextRecognition model dir : %s",
result_text_rec_model_dir.status().ToString().c_str());
exit(-1);
}
auto result_rec_input_shape =
config_.GetString("TextRecognition.input_shape");
if (!result_rec_input_shape.value().empty()) {
params_rec.input_shape =
config_.SmartParseVector(result_rec_input_shape.value()).vec_int;
}
params_rec.model_dir = result_text_rec_model_dir.value();
params_rec.lang = params_.lang;
params_rec.ocr_version = params_.ocr_version;
params_rec.vis_font_dir = params_.vis_font_dir;
params_rec.device = params_.device;
params_rec.precision = params_.precision;
params_rec.enable_mkldnn = params_.enable_mkldnn;
params_rec.mkldnn_cache_capacity = params_.mkldnn_cache_capacity;
params_rec.cpu_threads = params_.cpu_threads;
params_rec.batch_size =
config_.GetInt("TextRecognition.batch_size", 1).value();
text_rec_model_ = CreateModule<TextRecPredictor>(params_rec);
text_rec_score_thresh_ =
config_.GetFloat("TextRecognition.score_thresh", 0.0).value();
batch_sampler_ptr_ = std::unique_ptr<BaseBatchSampler>(
new ImageBatchSampler(1)); //** pipeline batch_size
};
absl::StatusOr<std::vector<cv::Mat>>
_OCRPipeline::RotateImage(const std::vector<cv::Mat> &image_array_list,
const std::vector<int> &rotate_angle_list) {
if (image_array_list.size() != rotate_angle_list.size()) {
return absl::InvalidArgumentError(
"Length of image_array_list (" +
std::to_string(image_array_list.size()) +
") must match length of rotate_angle_list (" +
std::to_string(rotate_angle_list.size()) + ")");
}
std::vector<cv::Mat> rotated_images;
rotated_images.reserve(image_array_list.size());
for (std::size_t i = 0; i < image_array_list.size(); ++i) {
int angle_indicator = rotate_angle_list[i];
if (angle_indicator != 0 && angle_indicator != 1) {
return absl::InvalidArgumentError(
"rotate_angle must be 0 or 1, now it's: " +
std::to_string(angle_indicator));
}
int rotate_angle = angle_indicator * 180;
auto result_rotated_image =
ComponentsProcessor::RotateImage(image_array_list[i], rotate_angle);
if (!result_rotated_image.ok()) {
return result_rotated_image.status();
}
cv::Mat rotated_image = result_rotated_image.value();
rotated_images.push_back(rotated_image);
}
return rotated_images;
}
std::unordered_map<std::string, bool> _OCRPipeline::GetModelSettings() const {
std::unordered_map<std::string, bool> model_settings = {};
model_settings["use_doc_preprocessor"] = use_doc_preprocessor_;
model_settings["use_textline_orientation"] = use_textline_orientation_;
return model_settings;
}
std::vector<std::unique_ptr<BaseCVResult>>
_OCRPipeline::Predict(const std::vector<std::string> &input) {
auto model_settings = GetModelSettings();
auto batches = batch_sampler_ptr_->Apply(input);
auto batches_string =
batch_sampler_ptr_->SampleFromVectorToStringVector(input);
if (!batches.ok()) {
INFOE("pipeline get sample fail : %s", batches.status().ToString().c_str());
exit(-1);
}
if (!batches_string.ok()) {
INFOE("pipeline get sample fail : %s",
batches_string.status().ToString().c_str());
exit(-1);
}
auto input_path = batch_sampler_ptr_->InputPath();
int index = 0;
std::vector<cv::Mat> origin_image = {};
std::vector<std::unique_ptr<BaseCVResult>> base_results = {};
pipeline_result_vec_.clear();
for (int i = 0; i < batches.value().size(); i++) {
origin_image.reserve(batches.value()[i].size());
for (const auto &mat : batches.value()[i]) {
origin_image.push_back(mat.clone());
}
std::vector<DocPreprocessorPipelineResult>
doc_preprocessors_pipeline_results = {};
if (use_doc_preprocessor_) {
doc_preprocessors_pipeline_->Predict(batches_string.value()[i]);
doc_preprocessors_pipeline_results =
static_cast<_DocPreprocessorPipeline *>(
doc_preprocessors_pipeline_.get())
->PipelineResult();
} else {
DocPreprocessorPipelineResult result;
for (auto &image : batches.value()[i]) {
result.output_image = image.clone();
doc_preprocessors_pipeline_results.push_back(result);
}
}
std::vector<cv::Mat> doc_preprocessor_pipeline_images = {};
std::vector<cv::Mat> doc_preprocessor_pipeline_images_copy = {};
for (auto &item : doc_preprocessors_pipeline_results) {
doc_preprocessor_pipeline_images.push_back(item.output_image);
doc_preprocessor_pipeline_images_copy.push_back(
item.output_image.clone());
}
text_det_model_->Predict(doc_preprocessor_pipeline_images_copy);
std::vector<TextDetPredictorResult> det_results =
static_cast<TextDetPredictor *>(text_det_model_.get())
->PredictorResult();
std::vector<std::vector<std::vector<cv::Point2f>>> dt_polys_list = {};
for (auto &item : det_results) {
if (!item.dt_polys.empty()) {
auto sort_item = sort_boxes_(item.dt_polys);
dt_polys_list.push_back(sort_item);
} else {
dt_polys_list.push_back(std::vector<std::vector<cv::Point2f>>{});
}
}
std::vector<int> indices = {};
for (int j = 0; j < doc_preprocessor_pipeline_images.size(); j++) {
if (!dt_polys_list.empty() && !dt_polys_list[j].empty()) {
indices.push_back(j);
}
}
std::vector<OCRPipelineResult> results(
doc_preprocessor_pipeline_images.size());
for (int k = 0; k < results.size(); k++, index++) {
results[k].input_path = input_path[index];
results[k].doc_preprocessor_res = doc_preprocessors_pipeline_results[k];
results[k].dt_polys = dt_polys_list[k];
results[k].model_settings = model_settings;
results[k].text_det_params = text_det_params_;
results[k].text_type = text_type_;
results[k].text_rec_score_thresh = text_rec_score_thresh_;
}
if (!indices.empty()) {
std::vector<cv::Mat> all_subs_of_imgs = {};
std::vector<cv::Mat> all_subs_of_imgs_copy = {};
std::vector<int> chunk_indices(1, 0);
for (auto &idx : indices) {
auto result_all_subs_of_img = (*crop_by_polys_)(
doc_preprocessor_pipeline_images[idx], dt_polys_list[idx]);
if (!result_all_subs_of_img.ok()) {
INFOE("Split image fail : ",
result_all_subs_of_img.status().ToString().c_str());
exit(-1);
}
all_subs_of_imgs.insert(all_subs_of_imgs.end(),
result_all_subs_of_img.value().begin(),
result_all_subs_of_img.value().end());
chunk_indices.emplace_back(chunk_indices.back() +
result_all_subs_of_img.value().size());
}
for (auto &item : all_subs_of_imgs) {
all_subs_of_imgs_copy.push_back(item.clone());
}
std::vector<int> angles = {};
if (model_settings["use_textline_orientation"]) {
textline_orientation_model_->Predict(all_subs_of_imgs_copy);
auto textline_orientation_model_results =
static_cast<ClasPredictor *>(textline_orientation_model_.get())
->PredictorResult();
textline_orientation_model_results[0].input_image;
for (auto &result_angle : textline_orientation_model_results) {
angles.push_back(result_angle.class_ids[0]);
}
auto result_all_subs_of_imgs = RotateImage(all_subs_of_imgs, angles);
if (!result_all_subs_of_imgs.ok()) {
INFOE("Rotate images fail : %s",
result_all_subs_of_imgs.status().ToString().c_str());
exit(-1);
}
all_subs_of_imgs = result_all_subs_of_imgs.value();
} else {
angles = std::vector<int>(all_subs_of_imgs.size(), -1);
}
for (int l = 0; l < indices.size(); l++) {
for (int m = chunk_indices[l]; m < chunk_indices[l + 1]; m++) {
results[indices[l]].textline_orientation_angles.push_back(angles[m]);
}
}
for (int l = 0; l < indices.size(); l++) {
std::vector<cv::Mat> all_subs_of_img = {};
for (int m = chunk_indices[l]; m < chunk_indices[l + 1]; m++) {
all_subs_of_img.push_back(all_subs_of_imgs[m]);
}
std::vector<std::pair<std::pair<int, float>, TextRecPredictorResult>>
sub_img_info_list = {};
for (int m = 0; m < all_subs_of_img.size(); m++) {
int sub_img_id = m;
float sub_img_ratio = (float)all_subs_of_img[m].size[1] /
(float)all_subs_of_img[m].size[0];
TextRecPredictorResult result;
sub_img_info_list.push_back({{sub_img_id, sub_img_ratio}, result});
}
std::vector<std::pair<int, float>> sorted_subs_info = {};
for (auto &item : sub_img_info_list) {
sorted_subs_info.push_back(item.first);
}
std::sort(
sorted_subs_info.begin(), sorted_subs_info.end(),
[](const std::pair<int, float> &a, const std::pair<int, float> &b) {
return a.second < b.second;
});
std::vector<cv::Mat> sorted_subs_of_img = {};
for (auto &item : sorted_subs_info) {
sorted_subs_of_img.push_back(all_subs_of_img[item.first]);
}
text_rec_model_->Predict(sorted_subs_of_img);
auto text_rec_model_results =
static_cast<TextRecPredictor *>(text_rec_model_.get())
->PredictorResult();
for (int m = 0; m < text_rec_model_results.size(); m++) {
int sub_img_id = sorted_subs_info[m].first;
sub_img_info_list[sub_img_id].second = text_rec_model_results[m];
}
for (int sno = 0; sno < sub_img_info_list.size(); sno++) {
auto rec_res = sub_img_info_list[sno].second;
if (rec_res.rec_score >= text_rec_score_thresh_) {
results[l].rec_texts.push_back(rec_res.rec_text);
results[l].rec_scores.push_back(rec_res.rec_score);
results[l].rec_polys.push_back(dt_polys_list[l][sno]);
results[l].vis_fonts = rec_res.vis_font;
}
}
}
}
for (auto &res : results) {
if (text_type_ == "general") {
res.rec_boxes =
ComponentsProcessor::ConvertPointsToBoxes(res.rec_polys);
}
pipeline_result_vec_.push_back(res);
base_results.push_back(std::unique_ptr<BaseCVResult>(new OCRResult(res)));
}
}
return base_results;
}
std::vector<std::unique_ptr<BaseCVResult>>
OCRPipeline::Predict(const std::vector<std::string> &input) {
if (thread_num_ == 1) {
return infer_->Predict(input);
}
batch_sampler_ptr_ =
std::unique_ptr<BaseBatchSampler>(new ImageBatchSampler(1));
auto nomeaning = batch_sampler_ptr_->Apply(input);
int input_num = nomeaning.value().size();
if (thread_num_ > input_num) {
INFOW("thread num exceed input num, will set %d", input_num);
thread_num_ = input_num;
}
int infer_batch_num = input_num / thread_num_;
auto status = batch_sampler_ptr_->SetBatchSize(infer_batch_num);
if (!status.ok()) {
INFOE("Set batch size fail : %s", status.ToString().c_str());
exit(-1);
}
auto infer_batch_data =
batch_sampler_ptr_->SampleFromVectorToStringVector(input);
if (!infer_batch_data.ok()) {
INFOE("Get infer batch data fail : %s",
infer_batch_data.status().ToString().c_str());
exit(-1);
}
std::vector<std::unique_ptr<BaseCVResult>> results = {};
results.reserve(input_num);
for (auto &infer_data : infer_batch_data.value()) {
auto status =
AutoParallelSimpleInferencePipeline::PredictThread(infer_data);
if (!status.ok()) {
INFOE("Infer fail : %s", status.ToString().c_str());
exit(-1);
}
}
for (int i = 0; i < infer_batch_data.value().size(); i++) {
auto infer_data_result = GetResult();
if (!infer_data_result.ok()) {
INFOE("Get infer result fail : %s",
infer_batch_data.status().ToString().c_str());
exit(-1);
}
results.insert(results.end(),
std::make_move_iterator(infer_data_result.value().begin()),
std::make_move_iterator(infer_data_result.value().end()));
}
return results;
}
void _OCRPipeline::OverrideConfig() {
auto &data = config_.Data();
if (params_.doc_orientation_classify_model_name.has_value()) {
auto it = config_.FindKey("DocOrientationClassify.model_name");
if (!it.ok()) {
data["SubPipelines.DocPreprocessor.SubModules.DocOrientationClassify."
"model_name"] = params_.doc_orientation_classify_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_orientation_classify_model_name.value();
}
}
if (params_.doc_orientation_classify_model_dir.has_value()) {
auto it = config_.FindKey("DocOrientationClassify.model_dir");
if (!it.ok()) {
data["SubPipelines.DocPreprocessor.SubModules.DocOrientationClassify."
"model_dir"] = params_.doc_orientation_classify_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_orientation_classify_model_dir.value();
}
}
if (params_.doc_unwarping_model_name.has_value()) {
auto it = config_.FindKey("DocUnwarping.model_name");
if (!it.ok()) {
data["SubPipelines.DocPreprocessor.SubModules.DocUnwarping.model_name"] =
params_.doc_unwarping_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_unwarping_model_name.value();
}
}
if (params_.doc_unwarping_model_dir.has_value()) {
auto it = config_.FindKey("DocUnwarping.model_dir");
if (!it.ok()) {
data["SubPipelines.DocPreprocessor.SubModules.DocUnwarping.model_dir"] =
params_.doc_unwarping_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.doc_unwarping_model_dir.value();
}
}
if (params_.text_detection_model_name.has_value()) {
auto it = config_.FindKey("TextDetection.model_name");
if (!it.ok()) {
data["SubModules.TextDetection.model_name"] =
params_.text_detection_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.text_detection_model_name.value();
}
}
if (params_.text_detection_model_dir.has_value()) {
auto it = config_.FindKey("TextDetection.model_dir");
if (!it.ok()) {
data["SubModules.TextDetection.model_dir"] =
params_.text_detection_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.text_detection_model_dir.value();
}
}
if (params_.textline_orientation_model_name.has_value()) {
auto it = config_.FindKey("TextLineOrientation.model_name");
if (!it.ok()) {
data["SubModules.TextLineOrientation.model_name"] =
params_.textline_orientation_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.textline_orientation_model_name.value();
}
}
if (params_.textline_orientation_model_dir.has_value()) {
auto it = config_.FindKey("TextLineOrientation.model_dir");
if (!it.ok()) {
data["SubModules.TextLineOrientation.model_dir"] =
params_.textline_orientation_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.textline_orientation_model_dir.value();
}
}
if (params_.textline_orientation_batch_size.has_value()) {
auto it = config_.FindKey("TextLineOrientation.batch_size");
if (!it.ok()) {
data["SubModules.TextLineOrientation.batch_size"] =
std::to_string(params_.textline_orientation_batch_size.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] =
std::to_string(params_.textline_orientation_batch_size.value());
}
}
if (params_.text_recognition_model_name.has_value()) {
auto it = config_.FindKey("TextRecognition.model_name");
if (!it.ok()) {
data["SubModules.TextRecognition.model_name"] =
params_.text_recognition_model_name.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.text_recognition_model_name.value();
}
}
if (params_.text_recognition_model_dir.has_value()) {
auto it = config_.FindKey("TextRecognition.model_dir");
if (!it.ok()) {
data["SubModules.TextRecognition.model_dir"] =
params_.text_recognition_model_dir.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.text_recognition_model_dir.value();
}
}
if (params_.text_recognition_batch_size.has_value()) {
auto it = config_.FindKey("TextRecognition.batch_size");
if (!it.ok()) {
data["SubModules.TextRecognition.batch_size"] =
std::to_string(params_.text_recognition_batch_size.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = std::to_string(params_.text_recognition_batch_size.value());
}
}
if (params_.use_doc_orientation_classify.has_value()) {
auto it = config_.FindKey("DocPreprocessor.use_doc_orientation_classify");
if (!it.ok()) {
data["SubPipelines.DocPreprocessor.use_doc_orientation_classify"] =
params_.use_doc_orientation_classify.value() ? "true" : "false";
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] =
params_.use_doc_orientation_classify.value() ? "true" : "false";
}
}
if (params_.use_doc_unwarping.has_value()) {
auto it = config_.FindKey("DocPreprocessor.use_doc_unwarping");
if (!it.ok()) {
data["SubPipelines.DocPreprocessor.use_doc_unwarping"] =
params_.use_doc_unwarping.value() ? "true" : "false";
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.use_doc_unwarping.value() ? "true" : "false";
}
}
if (params_.use_textline_orientation.has_value()) {
auto it = config_.FindKey("use_textline_orientation");
if (!it.ok()) {
data["use_textline_orientation"] =
params_.use_textline_orientation.value() ? "true" : "false";
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.use_textline_orientation.value() ? "true" : "false";
}
}
if (params_.text_det_limit_side_len.has_value()) {
auto it = config_.FindKey("TextDetection.limit_side_len");
if (!it.ok()) {
data["SubModules.TextDetection.limit_side_len"] =
std::to_string(params_.text_det_limit_side_len.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = std::to_string(params_.text_det_limit_side_len.value());
}
}
if (params_.text_det_limit_type.has_value()) {
auto it = config_.FindKey("TextDetection.limit_type");
if (!it.ok()) {
data["SubModules.TextDetection.limit_type"] =
params_.text_det_limit_type.value();
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = params_.text_det_limit_type.value();
}
}
if (params_.text_det_thresh.has_value()) {
auto it = config_.FindKey("TextDetection.thresh");
if (!it.ok()) {
data["SubModules.TextDetection.thresh"] =
std::to_string(params_.text_det_thresh.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = std::to_string(params_.text_det_thresh.value());
}
}
if (params_.text_det_box_thresh.has_value()) {
auto it = config_.FindKey("TextDetection.box_thresh");
if (!it.ok()) {
data["SubModules.TextDetection.box_thresh"] =
std::to_string(params_.text_det_box_thresh.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = std::to_string(params_.text_det_box_thresh.value());
}
}
if (params_.text_det_unclip_ratio.has_value()) {
auto it = config_.FindKey("TextDetection.unclip_ratio");
if (!it.ok()) {
data["SubModules.TextDetection.unclip_ratio"] =
std::to_string(params_.text_det_unclip_ratio.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = std::to_string(params_.text_det_unclip_ratio.value());
}
}
if (params_.text_det_input_shape.has_value()) {
auto it = config_.FindKey("TextDetection.input_shape");
if (!it.ok()) {
data["SubModules.TextDetection.input_shape"] =
Utility::VecToString(params_.text_det_input_shape.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = Utility::VecToString(params_.text_det_input_shape.value());
}
}
if (params_.text_rec_score_thresh.has_value()) {
auto it = config_.FindKey("TextRecognition.score_thresh");
if (!it.ok()) {
data["SubModules.TextRecognition.score_thresh"] =
std::to_string(params_.text_rec_score_thresh.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = std::to_string(params_.text_rec_score_thresh.value());
}
}
if (params_.text_rec_input_shape.has_value()) {
auto it = config_.FindKey("TextRecognition.input_shape");
if (!it.ok()) {
data["SubModules.TextRecognition.input_shape"] =
Utility::VecToString(params_.text_rec_input_shape.value());
} else {
auto key = it.value().first;
data.erase(data.find(key));
data[key] = Utility::VecToString(params_.text_rec_input_shape.value());
}
}
}
@@ -0,0 +1,162 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include "src/base/base_pipeline.h"
#include "src/common/image_batch_sampler.h"
#include "src/common/processors.h"
#include "src/modules/image_classification/predictor.h"
#include "src/modules/text_detection/predictor.h"
#include "src/modules/text_recognition/predictor.h"
#include "src/pipelines/doc_preprocessor/pipeline.h"
#include "src/utils/ilogger.h"
#include "src/utils/utility.h"
struct TextDetParams {
int text_det_limit_side_len = -1;
std::string text_det_limit_type = "";
int text_det_max_side_limit = -1;
float text_det_thresh = -1;
float text_det_box_thresh = -1;
float text_det_unclip_ratio = -1;
};
struct OCRPipelineResult {
std::string input_path = "";
DocPreprocessorPipelineResult doc_preprocessor_res;
std::vector<std::vector<cv::Point2f>> dt_polys = {};
std::unordered_map<std::string, bool> model_settings = {};
TextDetParams text_det_params;
std::string text_type = "";
float text_rec_score_thresh = 0.0;
std::vector<std::string> rec_texts = {};
std::vector<float> rec_scores = {};
std::vector<int> textline_orientation_angles = {};
std::vector<std::vector<cv::Point2f>> rec_polys = {};
std::vector<std::array<float, 4>> rec_boxes = {};
std::string vis_fonts = "";
};
struct OCRPipelineParams {
absl::optional<std::string> doc_orientation_classify_model_name =
absl::nullopt;
absl::optional<std::string> doc_orientation_classify_model_dir =
absl::nullopt;
absl::optional<std::string> doc_unwarping_model_name = absl::nullopt;
absl::optional<std::string> doc_unwarping_model_dir = absl::nullopt;
absl::optional<std::string> text_detection_model_name = absl::nullopt;
absl::optional<std::string> text_detection_model_dir = absl::nullopt;
absl::optional<std::string> textline_orientation_model_name = absl::nullopt;
absl::optional<std::string> textline_orientation_model_dir = absl::nullopt;
absl::optional<int> textline_orientation_batch_size = absl::nullopt;
absl::optional<std::string> text_recognition_model_name = absl::nullopt;
absl::optional<std::string> text_recognition_model_dir = absl::nullopt;
absl::optional<int> text_recognition_batch_size = absl::nullopt;
absl::optional<bool> use_doc_orientation_classify = absl::nullopt;
absl::optional<bool> use_doc_unwarping = absl::nullopt;
absl::optional<bool> use_textline_orientation = absl::nullopt;
absl::optional<int> text_det_limit_side_len = absl::nullopt;
absl::optional<std::string> text_det_limit_type = absl::nullopt;
absl::optional<float> text_det_thresh = absl::nullopt;
absl::optional<float> text_det_box_thresh = absl::nullopt;
absl::optional<float> text_det_unclip_ratio = absl::nullopt;
absl::optional<std::vector<int>> text_det_input_shape = absl::nullopt;
absl::optional<float> text_rec_score_thresh = absl::nullopt;
absl::optional<std::vector<int>> text_rec_input_shape = absl::nullopt;
absl::optional<std::string> lang = absl::nullopt;
absl::optional<std::string> ocr_version = absl::nullopt;
absl::optional<std::string> vis_font_dir = absl::nullopt;
absl::optional<std::string> device = absl::nullopt;
bool enable_mkldnn = true;
int mkldnn_cache_capacity = 10;
std::string precision = "fp32";
int cpu_threads = 8;
int thread_num = 1;
absl::optional<Utility::PaddleXConfigVariant> paddlex_config = absl::nullopt;
};
class _OCRPipeline : public BasePipeline {
public:
explicit _OCRPipeline(const OCRPipelineParams &params);
virtual ~_OCRPipeline() = default;
_OCRPipeline() = delete;
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input) override;
std::vector<OCRPipelineResult> PipelineResult() const {
return pipeline_result_vec_;
};
static absl::StatusOr<std::vector<cv::Mat>>
RotateImage(const std::vector<cv::Mat> &image_array_list,
const std::vector<int> &rotate_angle_list);
std::unordered_map<std::string, bool> GetModelSettings() const;
TextDetParams GetTextDetParams() const { return text_det_params_; };
void OverrideConfig();
private:
OCRPipelineParams params_;
YamlConfig config_;
std::unique_ptr<BaseBatchSampler> batch_sampler_ptr_;
std::vector<OCRPipelineResult> pipeline_result_vec_;
bool use_doc_preprocessor_ = false;
bool use_doc_orientation_classify_ = false;
bool use_doc_unwarping_ = false;
std::unique_ptr<BasePipeline> doc_preprocessors_pipeline_;
bool use_textline_orientation_ = false;
std::unique_ptr<BasePredictor> textline_orientation_model_;
std::unique_ptr<BasePredictor> text_det_model_;
std::unique_ptr<BasePredictor> text_rec_model_;
std::unique_ptr<CropByPolys> crop_by_polys_;
std::function<std::vector<std::vector<cv::Point2f>>(
const std::vector<std::vector<cv::Point2f>> &)>
sort_boxes_;
float text_rec_score_thresh_ = 0.0;
std::string text_type_;
TextDetParams text_det_params_;
};
class OCRPipeline
: public AutoParallelSimpleInferencePipeline<
_OCRPipeline, OCRPipelineParams, std::vector<std::string>,
std::vector<std::unique_ptr<BaseCVResult>>> {
public:
OCRPipeline(const OCRPipelineParams &params)
: AutoParallelSimpleInferencePipeline(params),
thread_num_(params.thread_num) {
if (thread_num_ == 1) {
infer_ = std::unique_ptr<BasePipeline>(new _OCRPipeline(params));
}
};
std::vector<std::unique_ptr<BaseCVResult>>
Predict(const std::vector<std::string> &input) override;
private:
int thread_num_;
std::unique_ptr<BasePipeline> infer_;
std::unique_ptr<BaseBatchSampler> batch_sampler_ptr_;
};
@@ -0,0 +1,539 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "result.h"
#include <algorithm>
#include <codecvt>
#include <fstream>
#include <locale>
#include <random>
#include <string>
#include "src/utils/utility.h"
#include "third_party/nlohmann/json.hpp"
using json = nlohmann::json;
void OCRResult::SaveToImg(const std::string &save_path) {
cv::Mat image = pipeline_result_.doc_preprocessor_res.output_image;
auto texts = pipeline_result_.rec_texts;
std::vector<std::vector<cv::Point>> boxes;
std::vector<std::vector<cv::Point2f>> boxes_float =
pipeline_result_.rec_polys;
for (const auto &floatPolygon : pipeline_result_.rec_polys) {
std::vector<cv::Point> intPolygon;
for (const auto &point : floatPolygon) {
intPolygon.push_back(cv::Point(cvRound(point.x), cvRound(point.y)));
}
boxes.push_back(intPolygon);
}
if (image.empty()) {
INFOE("Input image is empty.");
exit(-1);
}
int h = image.rows;
int w = image.cols;
cv::Mat img_left = image.clone();
cv::Mat img_right(h, w, CV_8UC3, cv::Scalar(255, 255, 255));
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 255);
for (size_t i = 0; i < boxes.size(); ++i) {
auto &box = boxes[i];
const auto &box_float = boxes_float[i];
const auto &text = texts[i];
cv::Scalar color(dis(gen), dis(gen), dis(gen));
if (box.size() > 4) {
const std::vector<std::vector<cv::Point>> polygons{box};
cv::fillPoly(img_left, polygons, color);
cv::polylines(img_left, polygons, true, color, 8);
box = GetMinareaRect(box);
std::vector<int> ys;
for (const auto &pt : box)
ys.push_back(pt.y);
int min_y = *std::min_element(ys.begin(), ys.end());
int max_y = *std::max_element(ys.begin(), ys.end());
int height = static_cast<int>(0.5 * (max_y - min_y));
double mean_y = std::accumulate(ys.begin(), ys.end(), 0.0) / ys.size();
if (box.size() >= 4) {
box[0].y = static_cast<int>(mean_y);
box[1].y = static_cast<int>(mean_y);
box[2].y = static_cast<int>(mean_y + std::min(20, height));
box[3].y = static_cast<int>(mean_y + std::min(20, height));
}
} else {
cv::fillPoly(img_left, std::vector<std::vector<cv::Point>>{box}, color);
}
#ifdef USE_FREETYPE
cv::Mat img_right_text = DrawBoxTextFine(cv::Size(w, h), box_float, text,
pipeline_result_.vis_fonts);
cv::polylines(img_right_text, box, true, color, 1);
cv::bitwise_and(img_right, img_right_text, img_right);
#endif
}
cv::Mat blended;
cv::addWeighted(image, 0.5, img_left, 0.5, 0, blended);
#ifdef USE_FREETYPE
cv::Mat ocr_res_image(h, w * 2, CV_8UC3, cv::Scalar(255, 255, 255));
blended.copyTo(ocr_res_image(cv::Rect(0, 0, w, h)));
img_right.copyTo(ocr_res_image(cv::Rect(w, 0, w, h)));
#else
cv::Mat ocr_res_image = blended;
#endif
auto model_settings = pipeline_result_.model_settings;
std::unordered_map<std::string, cv::Mat> res_img_dict;
res_img_dict["ocr_res_img"] = ocr_res_image;
auto ocr_path = Utility::SmartCreateDirectoryForImage(
save_path, pipeline_result_.input_path, "_ocr_res_img");
if (!ocr_path.ok()) {
INFOE(ocr_path.status().ToString().c_str());
exit(-1);
}
auto doc_pre_path = Utility::SmartCreateDirectoryForImage(
save_path, pipeline_result_.input_path, "_doc_preprocessor_res");
if (!doc_pre_path.ok()) {
INFOE(doc_pre_path.status().ToString().c_str());
exit(-1);
}
cv::imwrite(ocr_path.value(), ocr_res_image);
if (model_settings["use_doc_preprocessor"]) {
int h1 = pipeline_result_.doc_preprocessor_res.input_image.size[0];
int w1 = pipeline_result_.doc_preprocessor_res.input_image.size[1];
int h2 = pipeline_result_.doc_preprocessor_res.rotate_image.size[0];
int w2 = pipeline_result_.doc_preprocessor_res.rotate_image.size[1];
int h3 = pipeline_result_.doc_preprocessor_res.output_image.size[0];
int w3 = pipeline_result_.doc_preprocessor_res.output_image.size[1];
int h_all = std::max(h1, std::max(h2, h3));
int total_w = w1 + w2 + w3;
cv::Mat doc_pre_res_image(h_all, total_w, CV_8UC3,
cv::Scalar(255, 255, 255));
pipeline_result_.doc_preprocessor_res.input_image.copyTo(
doc_pre_res_image(cv::Rect(0, 0, w1, h1)));
pipeline_result_.doc_preprocessor_res.rotate_image.copyTo(
doc_pre_res_image(cv::Rect(w1, 0, w2, h2)));
pipeline_result_.doc_preprocessor_res.output_image.copyTo(
doc_pre_res_image(cv::Rect(w1 + w2, 0, w3, h3)));
cv::imwrite(doc_pre_path.value(), doc_pre_res_image);
res_img_dict["doc_preprocessor_res"] = doc_pre_res_image;
}
}
#ifdef USE_FREETYPE
cv::Mat OCRResult::DrawBoxTextFine(const cv::Size &img_size,
const std::vector<cv::Point2f> &box,
const std::string &txt,
const std::string &vis_font) {
int box_height = cv::norm(box[0] - box[3]);
int box_width = cv::norm(box[0] - box[1]);
auto ft2 = cv::freetype::createFreeType2();
ft2->loadFontData(vis_font, 0);
bool vertical_mode = box_height > 2 * box_width && box_height > 30;
int n = std::max(int(txt.size()), 1);
int font_height = 10;
if (!txt.empty()) {
if (vertical_mode) {
font_height = CreateFontVertical(ft2, txt, box_height, box_width);
} else {
font_height = CreateFont(ft2, txt, box_height, box_width);
}
}
cv::Mat img_text(box_height, box_width, CV_8UC3, cv::Scalar(255, 255, 255));
int x = 0, y = 0;
if (!txt.empty()) {
if (vertical_mode) {
DrawVerticalText(ft2, img_text, txt, x, y, font_height,
cv::Scalar(0, 0, 0));
} else {
int baseline = 0;
cv::Size textsize = ft2->getTextSize(txt, font_height, -1, &baseline);
x = (box_width - textsize.width) / 2;
y = (box_height + textsize.height) / 2 - baseline;
ft2->putText(img_text, txt, cv::Point(x, y), font_height,
cv::Scalar(0, 0, 0), -1, cv::LINE_AA, true);
}
}
std::vector<cv::Point2f> src_pts = {{0, 0},
{float(box_width), 0},
{float(box_width), float(box_height)},
{0, float(box_height)}};
cv::Mat M = cv::getPerspectiveTransform(src_pts, box);
cv::Mat dst(img_size, CV_8UC3, cv::Scalar(255, 255, 255));
cv::warpPerspective(img_text, dst, M, img_size, cv::INTER_NEAREST,
cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255));
return dst;
}
cv::Size OCRResult::getActualCharSize(cv::Ptr<cv::freetype::FreeType2> &ft2,
const std::string &utf8_char,
int font_height) {
cv::Mat temp = cv::Mat::zeros(300, 300, CV_8UC1);
cv::Point pos(100, 150);
ft2->putText(temp, utf8_char, pos, font_height, cv::Scalar(255), -1,
cv::LINE_AA, false);
std::vector<cv::Point> nonZeroPoints;
cv::findNonZero(temp, nonZeroPoints);
if (nonZeroPoints.empty()) {
return cv::Size(0, 0);
}
cv::Rect boundingRect = cv::boundingRect(nonZeroPoints);
return cv::Size(boundingRect.width, boundingRect.height);
}
void OCRResult::DrawVerticalText(cv::Ptr<cv::freetype::FreeType2> &ft2,
cv::Mat &img, const std::string &text, int x,
int y, int font_height, cv::Scalar color,
float line_spacing) {
std::wstring wtext =
std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(text);
for (size_t i = 0; i < wtext.size(); ++i) {
std::wstring single_char(1, wtext[i]);
std::string utf8_char =
std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(
single_char);
ft2->putText(img, utf8_char, cv::Point(x, y), font_height, color, -1,
cv::LINE_AA, true);
int baseline = 0;
cv::Size size = ft2->getTextSize(utf8_char, font_height, -1, &baseline);
size.height += baseline;
y += size.height * 1.1 + line_spacing;
}
}
int OCRResult::CreateFont(cv::Ptr<cv::freetype::FreeType2> &ft2,
const std::string &text, int region_height,
int region_width) {
int font_height = std::max(int(region_height * 0.8), 10);
int baseline = 0;
cv::Size text_size = ft2->getTextSize(text, font_height, -1, &baseline);
if (text_size.width > region_width) {
font_height =
static_cast<int>(font_height * region_width / text_size.width);
text_size = ft2->getTextSize(text, font_height, -1, &baseline);
}
return font_height;
}
int OCRResult::CreateFontVertical(cv::Ptr<cv::freetype::FreeType2> &ft2,
const std::string &text, int region_height,
int region_width, float scale) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
std::wstring wtext = conv.from_bytes(text);
int n = static_cast<int>(wtext.length());
int baseFontSize = static_cast<int>(region_height / n * 0.8 * scale);
baseFontSize = std::max(baseFontSize, 10);
int maxCharWidth = 0;
for (size_t i = 0; i < wtext.length(); ++i) {
std::wstring singleChar(1, wtext[i]);
std::string utf8Char =
std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(singleChar);
cv::Size textSize = getActualCharSize(ft2, utf8Char, baseFontSize);
maxCharWidth = std::max(maxCharWidth, textSize.width);
}
int finalFontSize = baseFontSize;
if (maxCharWidth > region_width) {
finalFontSize =
static_cast<int>(baseFontSize * region_width / maxCharWidth);
finalFontSize = std::max(finalFontSize, 10);
}
return finalFontSize;
}
#endif
std::vector<cv::Point>
OCRResult::GetMinareaRect(const std::vector<cv::Point> &points) {
cv::RotatedRect bounding_box = cv::minAreaRect(points);
cv::Point2f boxPts[4];
bounding_box.points(boxPts);
std::vector<cv::Point2f> ptsVec(boxPts, boxPts + 4);
std::sort(
ptsVec.begin(), ptsVec.end(),
[](const cv::Point2f &a, const cv::Point2f &b) { return a.x < b.x; });
int index_a, index_b, index_c, index_d;
if (ptsVec[1].y > ptsVec[0].y) {
index_a = 0;
index_d = 1;
} else {
index_a = 1;
index_d = 0;
}
if (ptsVec[3].y > ptsVec[2].y) {
index_b = 2;
index_c = 3;
} else {
index_b = 3;
index_c = 2;
}
std::vector<cv::Point> box = {ptsVec[index_a], ptsVec[index_b],
ptsVec[index_c], ptsVec[index_d]};
for (auto &pt : box) {
pt.x = static_cast<int>(std::round(pt.x));
pt.y = static_cast<int>(std::round(pt.y));
}
return box;
}
void OCRResult::SaveToJson(const std::string &save_path) const {
nlohmann::ordered_json j;
j["input_path"] = pipeline_result_.input_path;
j["page_index"] = nullptr;
j["model_settings"] = pipeline_result_.model_settings;
auto it = pipeline_result_.model_settings.find("use_doc_preprocessor");
if (it != pipeline_result_.model_settings.end() && it->second) {
nlohmann::ordered_json j_doc_pre;
j_doc_pre["model_settings"] =
pipeline_result_.doc_preprocessor_res.model_settings;
j_doc_pre["angle"] = pipeline_result_.doc_preprocessor_res.angle;
j["doc_preprocessor_res"] = j_doc_pre;
}
json polys_json = json::array();
for (const auto &polygon : pipeline_result_.dt_polys) {
json poly_json = json::array();
for (const auto &point : polygon) {
poly_json.push_back(
{static_cast<int>(point.x), static_cast<int>(point.y)});
}
polys_json.push_back(poly_json);
}
j["dt_polys"] = polys_json;
nlohmann::ordered_json j_text_det_params;
j_text_det_params["limit_side_len"] =
pipeline_result_.text_det_params.text_det_limit_side_len;
j_text_det_params["limit_type"] =
pipeline_result_.text_det_params.text_det_limit_type;
j_text_det_params["thresh"] =
pipeline_result_.text_det_params.text_det_thresh;
j_text_det_params["max_side_limit"] =
pipeline_result_.text_det_params.text_det_max_side_limit;
j_text_det_params["box_thresh"] =
pipeline_result_.text_det_params.text_det_box_thresh;
j_text_det_params["unclip_ratio"] =
pipeline_result_.text_det_params.text_det_unclip_ratio;
j["text_det_params"] = j_text_det_params;
j["text_type"] = pipeline_result_.text_type;
if (!pipeline_result_.textline_orientation_angles.empty()) {
j["textline_orientation_angles"] =
pipeline_result_.textline_orientation_angles;
}
j["text_rec_score_thresh"] = pipeline_result_.text_rec_score_thresh;
j["rec_texts"] = pipeline_result_.rec_texts;
j["rec_scores"] = pipeline_result_.rec_scores;
json rec_polys_json = json::array();
for (const auto &polygon : pipeline_result_.rec_polys) {
json poly_json = json::array();
for (const auto &point : polygon) {
poly_json.push_back(
{static_cast<int>(point.x), static_cast<int>(point.y)});
}
rec_polys_json.push_back(poly_json);
}
j["rec_polys"] = rec_polys_json;
std::vector<std::array<int, 4>> int_vec;
int_vec.reserve(pipeline_result_.rec_boxes.size());
std::transform(pipeline_result_.rec_boxes.begin(),
pipeline_result_.rec_boxes.end(), std::back_inserter(int_vec),
[](const std::array<float, 4> &arr) {
std::array<int, 4> res;
for (size_t i = 0; i < 4; ++i) {
res[i] = static_cast<int>(arr[i]);
}
return res;
});
j["rec_boxes"] = int_vec;
absl::StatusOr<std::string> full_path;
if (pipeline_result_.input_path.empty()) {
INFOW("Input path is empty, will use output_res.json instead!");
full_path = Utility::SmartCreateDirectoryForJson(save_path, "output");
} else {
full_path = Utility::SmartCreateDirectoryForJson(
save_path, pipeline_result_.input_path);
}
if (!full_path.ok()) {
INFOE(full_path.status().ToString().c_str());
exit(-1);
}
std::ofstream file(full_path.value());
if (file.is_open()) {
file << j.dump(4);
file.close();
} else {
INFOE("Could not open file for writing: %s", save_path.c_str());
exit(-1);
}
}
void PrintDocPreprocessorPipelineResult(
const DocPreprocessorPipelineResult &doc) {
std::cout << "{\n";
std::cout << " \"model_settings\": {";
bool first = true;
for (const auto &kv : doc.model_settings) {
if (!first)
std::cout << ", ";
std::cout << "\"" << kv.first << "\": " << (kv.second ? "true" : "false");
first = false;
}
std::cout << "},\n";
std::cout << " \"angle\": " << doc.angle << "\n";
std::cout << " }";
}
void PrintPolys(const std::vector<std::vector<cv::Point2f>> &polys) {
std::cout << "[";
for (size_t i = 0; i < polys.size(); ++i) {
if (i != 0)
std::cout << ",\n ";
std::cout << "[";
for (size_t j = 0; j < polys[i].size(); ++j) {
if (j != 0)
std::cout << ", ";
std::cout << "[" << polys[i][j].x << ", " << polys[i][j].y << "]";
}
std::cout << "]";
}
std::cout << "]";
}
void PrintModelSettings(const std::unordered_map<std::string, bool> &ms) {
std::cout << "{";
bool first = true;
for (const auto &kv : ms) {
if (!first)
std::cout << ", ";
std::cout << "\"" << kv.first << "\": " << (kv.second ? "true" : "false");
first = false;
}
std::cout << "}";
}
void PrintArray(const std::vector<float> &arr) {
std::cout << "[";
for (size_t i = 0; i < arr.size(); ++i) {
if (i != 0)
std::cout << ", ";
std::cout << arr[i];
}
std::cout << "]";
}
void PrintStringArray(const std::vector<std::string> &arr) {
std::cout << "[";
for (size_t i = 0; i < arr.size(); ++i) {
if (i != 0)
std::cout << ", ";
std::cout << "\"" << arr[i] << "\"";
}
std::cout << "]";
}
void PrintIntArray(const std::vector<int> &arr) {
std::cout << "[";
for (size_t i = 0; i < arr.size(); ++i) {
if (i != 0)
std::cout << ", ";
std::cout << arr[i];
}
std::cout << "]";
}
void PrintRecBoxes(const std::vector<std::array<float, 4>> &arr) {
std::cout << "[";
for (size_t i = 0; i < arr.size(); ++i) {
if (i != 0)
std::cout << ", ";
std::cout << "[" << arr[i][0] << ", " << arr[i][1] << ", " << arr[i][2]
<< ", " << arr[i][3] << "]";
}
std::cout << "],";
}
void PrintTextDetParams(const TextDetParams &p) {
std::cout << "{";
std::cout << "\"limit_side_len\": " << p.text_det_limit_side_len << ", ";
std::cout << "\"limit_type\": \"" << p.text_det_limit_type << "\", ";
std::cout << "\"thresh\": " << p.text_det_thresh << ", ";
std::cout << "\"max_side_limit\": " << p.text_det_max_side_limit << ", ";
std::cout << "\"box_thresh\": " << p.text_det_box_thresh << ", ";
std::cout << "\"unclip_ratio\": " << p.text_det_unclip_ratio;
std::cout << "}";
}
void OCRResult::Print() const {
std::cout << "{\n";
std::cout << " \"input_path\": \"" << pipeline_result_.input_path << "\",\n";
if (pipeline_result_.model_settings.at("use_doc_preprocessor")) {
std::cout << " \"doc_preprocessor_res\": ";
PrintDocPreprocessorPipelineResult(pipeline_result_.doc_preprocessor_res);
std::cout << ",\n";
}
std::cout << " \"dt_polys\": ";
PrintPolys(pipeline_result_.dt_polys);
std::cout << ",\n";
std::cout << " \"model_settings\": ";
PrintModelSettings(pipeline_result_.model_settings);
std::cout << ",\n";
std::cout << " \"text_det_params\": ";
PrintTextDetParams(pipeline_result_.text_det_params);
std::cout << ",\n";
std::cout << " \"text_type\": \"" << pipeline_result_.text_type << "\",\n";
std::cout << " \"text_rec_score_thresh\": "
<< pipeline_result_.text_rec_score_thresh << ",\n";
std::cout << " \"rec_texts\": ";
PrintStringArray(pipeline_result_.rec_texts);
std::cout << ",\n";
std::cout << " \"rec_scores\": ";
PrintArray(pipeline_result_.rec_scores);
std::cout << ",\n";
std::cout << " \"textline_orientation_angles\": ";
PrintIntArray(pipeline_result_.textline_orientation_angles);
std::cout << ",\n";
std::cout << " \"rec_polys\": ";
PrintPolys(pipeline_result_.rec_polys);
std::cout << ",\n";
std::cout << " \"rec_boxes\": ";
PrintRecBoxes(pipeline_result_.rec_boxes);
std::cout << "\n}" << std::endl;
}
@@ -0,0 +1,58 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef USE_FREETYPE
#include <opencv2/freetype.hpp>
#endif
#include "pipeline.h"
#include "src/base/base_cv_result.h"
class OCRResult : public BaseCVResult {
public:
OCRResult(OCRPipelineResult pipeline_result_)
: BaseCVResult(), pipeline_result_(pipeline_result_){};
void SaveToImg(const std::string &save_path) override;
void Print() const override;
void SaveToJson(const std::string &save_path) const override;
#ifdef USE_FREETYPE
static cv::Mat DrawBoxTextFine(const cv::Size &img_ize,
const std::vector<cv::Point2f> &box,
const std::string &txt,
const std::string &vis_font);
static void DrawVerticalText(cv::Ptr<cv::freetype::FreeType2> &ft2,
cv::Mat &img, const std::string &text, int x,
int y, int font_height, cv::Scalar color,
float line_spacing = 2);
static int CreateFont(cv::Ptr<cv::freetype::FreeType2> &ft2,
const std::string &text, int region_height,
int region_width);
static int CreateFontVertical(cv::Ptr<cv::freetype::FreeType2> &ft2,
const std::string &text, int region_height,
int region_width, float scale = 1.2f);
static cv::Size getActualCharSize(cv::Ptr<cv::freetype::FreeType2> &ft2,
const std::string &utf8_char,
int font_height);
#endif
static std::vector<cv::Point>
GetMinareaRect(const std::vector<cv::Point> &points);
private:
OCRPipelineResult pipeline_result_;
};
+101
View File
@@ -0,0 +1,101 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "args.h"
DEFINE_string(input, "",
"Data to be predicted, required. Local path of an image file.");
DEFINE_string(save_path, "./output/", "Path to save inference result files.");
DEFINE_string(doc_orientation_classify_model_name, "PP-LCNet_x1_0_doc_ori",
"Name of the document image orientation classification model.");
DEFINE_string(
doc_orientation_classify_model_dir, "",
"Path to the document image orientation classification model directory.");
DEFINE_string(doc_unwarping_model_name, "UVDoc",
"Name of the text image unwarping model.");
DEFINE_string(doc_unwarping_model_dir, "",
"Path to the image unwarping model directory.");
DEFINE_string(text_detection_model_name, "PP-OCRv5_server_det",
"Name of the text detection model.");
DEFINE_string(text_detection_model_dir, "",
"Path to the text detection model directory.");
DEFINE_string(textline_orientation_model_name, "PP-LCNet_x1_0_textline_ori",
"Name of the text line orientation classification model.");
DEFINE_string(
textline_orientation_model_dir, "",
"Path to the text line orientation classification model directory.");
DEFINE_string(textline_orientation_batch_size, "6",
"Batch size for the text line orientation classification model.");
DEFINE_string(text_recognition_model_name, "PP-OCRv5_server_rec",
"Name of the text recognition model.");
DEFINE_string(text_recognition_model_dir, "",
"Path to the text recognition model directory.");
DEFINE_string(text_recognition_batch_size, "6",
"Batch size for the text recognition model.");
DEFINE_string(use_doc_orientation_classify, "true",
"Whether to use document image orientation classification.");
DEFINE_string(use_doc_unwarping, "true",
"Whether to use text image unwarping.");
DEFINE_string(use_textline_orientation, "true",
"Whether to use text line orientation classification.");
DEFINE_string(text_det_limit_side_len, "64",
"This sets a limit on the side length of the input image for the "
"text detection model.");
DEFINE_string(text_det_limit_type, "min",
"This determines how the side length limit is applied to the "
"input image before feeding it into the text detection model.");
DEFINE_string(text_det_thresh, "0.3",
"Detection pixel threshold for the text detection model. Pixels "
"with scores greater than this threshold in the output "
"probability map are considered text pixels.");
DEFINE_string(
text_det_box_thresh, "0.6",
"Detection box threshold for the text detection model. A detection result "
"is considered a text region if the average score of all pixels within the "
"border of the result is greater than this threshold.");
DEFINE_string(
text_det_unclip_ratio, "1.5",
"Text detection expansion coefficient, which expands the text region using "
"this method. The larger the value, the larger the expansion area.");
DEFINE_string(text_det_input_shape, "",
"Input shape of the text detection model.eg C,H,W");
DEFINE_string(text_rec_score_thresh, "0",
"Text recognition threshold. Text results with scores greater "
"than this threshold are retained.");
DEFINE_string(text_rec_input_shape, "",
"Input shape of the text recognition model.eg C,H,W");
DEFINE_string(lang, "", "Language in the input image for OCR processing.");
DEFINE_string(ocr_version, "", "PP-OCR version to use.");
#ifdef WITH_GPU
DEFINE_string(device, "gpu:0",
"Device for inference. Supports specifying a specific card "
"number: gpu:0.");
#else
DEFINE_string(device, "cpu",
"Device for inference. Supports specifying a specific card "
"number: gpu:0.");
#endif
DEFINE_string(vis_font_dir, "",
"When enable USE_FREETYPE, required. Path to the visualization "
"font, render the detected texts on images");
DEFINE_string(precision, "fp32",
"Computational precision, such as fp32, fp16.");
DEFINE_string(enable_mkldnn, "true", "enable_mkldnn");
DEFINE_string(mkldnn_cache_capacity, "10", "MKL-DNN cache capacity.");
DEFINE_string(cpu_threads, "8",
"Number of threads used for paddlepaddle inference on CPU.");
DEFINE_string(thread_num, "1",
"Number of threads used for pipeline instance inference on CPU.");
DEFINE_string(paddlex_config, "",
"Path to the PaddleX pipeline configuration file.");
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <gflags/gflags.h>
DECLARE_string(input);
DECLARE_string(save_path);
DECLARE_string(doc_orientation_classify_model_name);
DECLARE_string(doc_orientation_classify_model_dir);
DECLARE_string(doc_unwarping_model_name);
DECLARE_string(doc_unwarping_model_dir);
DECLARE_string(text_detection_model_name);
DECLARE_string(text_detection_model_dir);
DECLARE_string(textline_orientation_model_name);
DECLARE_string(textline_orientation_model_dir);
DECLARE_string(textline_orientation_batch_size);
DECLARE_string(text_recognition_model_name);
DECLARE_string(text_recognition_model_dir);
DECLARE_string(text_recognition_batch_size);
DECLARE_string(use_doc_orientation_classify);
DECLARE_string(use_doc_unwarping);
DECLARE_string(use_textline_orientation);
DECLARE_string(text_det_limit_side_len);
DECLARE_string(text_det_limit_type);
DECLARE_string(text_det_thresh);
DECLARE_string(text_det_box_thresh);
DECLARE_string(text_det_unclip_ratio);
DECLARE_string(text_det_input_shape);
DECLARE_string(text_rec_score_thresh);
DECLARE_string(text_rec_input_shape);
DECLARE_string(lang);
DECLARE_string(ocr_version);
DECLARE_string(device);
DECLARE_string(vis_font_dir);
DECLARE_string(precision);
DECLARE_string(enable_mkldnn);
DECLARE_string(mkldnn_cache_capacity);
DECLARE_string(cpu_threads);
DECLARE_string(thread_num);
DECLARE_string(paddlex_config);
@@ -0,0 +1,32 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <iostream>
#include <memory>
#include <opencv2/opencv.hpp>
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
class BaseProcessor {
public:
BaseProcessor() = default;
virtual ~BaseProcessor() = default;
virtual absl::StatusOr<std::vector<cv::Mat>>
Apply(std::vector<cv::Mat> &input, const void *param_ptr = nullptr) const = 0;
};
File diff suppressed because it is too large Load Diff
+151
View File
@@ -0,0 +1,151 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Based on https://github.com/shouxieai/tensorRT_Pro
// Copyright (c) 2022 TensorRTPro
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software && associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, &&/|| sell
// copies of the Software, && to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice && this permission notice shall be included in
// all copies || substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <time.h>
#include <string>
#include <tuple>
#include <vector>
#if defined(_WIN32)
#define U_OS_WINDOWS
#else
#define U_OS_LINUX
#endif
namespace iLogger {
using namespace std;
enum class LogLevel : int {
Debug = 5,
Verbose = 4,
Info = 3,
Warning = 2,
Error = 1,
Fatal = 0
};
#define INFOD(...) \
iLogger::__log_func(__FILE__, __LINE__, iLogger::LogLevel::Debug, __VA_ARGS__)
#define INFOV(...) \
iLogger::__log_func(__FILE__, __LINE__, iLogger::LogLevel::Verbose, \
__VA_ARGS__)
#define INFO(...) \
iLogger::__log_func(__FILE__, __LINE__, iLogger::LogLevel::Info, __VA_ARGS__)
#define INFOW(...) \
iLogger::__log_func(__FILE__, __LINE__, iLogger::LogLevel::Warning, \
__VA_ARGS__)
#define INFOE(...) \
iLogger::__log_func(__FILE__, __LINE__, iLogger::LogLevel::Error, __VA_ARGS__)
#define INFOF(...) \
iLogger::__log_func(__FILE__, __LINE__, iLogger::LogLevel::Fatal, __VA_ARGS__)
string date_now();
string time_now();
string gmtime_now();
string gmtime(time_t t);
time_t gmtime2ctime(const string &gmt);
void sleep(int ms);
bool isfile(const string &file);
bool mkdir(const string &path);
bool mkdirs(const string &path);
bool delete_file(const string &path);
bool rmtree(const string &directory, bool ignore_fail = false);
bool exists(const string &path);
string format(const char *fmt, ...);
FILE *fopen_mkdirs(const string &path, const string &mode);
string file_name(const string &path, bool include_suffix = true);
string directory(const string &path);
long long timestamp_now();
double timestamp_now_float();
time_t last_modify(const string &file);
vector<uint8_t> load_file(const string &file);
string load_text_file(const string &file);
size_t file_size(const string &file);
bool begin_with(const string &str, const string &with);
bool end_with(const string &str, const string &with);
vector<string> split_string(const string &str, const std::string &spstr);
string replace_string(const string &str, const string &token,
const string &value, int nreplace = -1,
int *out_num_replace = nullptr);
// h[0-1], s[0-1], v[0-1]
// return, 0-255, 0-255, 0-255
tuple<uint8_t, uint8_t, uint8_t> hsv2rgb(float h, float s, float v);
tuple<uint8_t, uint8_t, uint8_t> random_color(int id);
// abcdefg.pnga *.png > false
// abcdefg.png *.png > true
// abcdefg.png a?cdefg.png > true
bool pattern_match(const char *str, const char *matcher,
bool igrnoe_case = true);
vector<string> find_files(const string &directory, const string &filter = "*",
bool findDirectory = false,
bool includeSubDirectory = false);
string align_blank(const string &input, int align_size, char blank = ' ');
bool save_file(const string &file, const vector<uint8_t> &data,
bool mk_dirs = true);
bool save_file(const string &file, const string &data, bool mk_dirs = true);
bool save_file(const string &file, const void *data, size_t length,
bool mk_dirs = true);
// 捕获:SIGINT(2)、SIGQUIT(3)
int while_loop();
// 关于logger的api
const char *level_string(LogLevel level);
void set_logger_save_directory(const string &loggerDirectory);
void set_log_level(LogLevel level);
LogLevel get_log_level();
void __log_func(const char *file, int line, LogLevel level, const char *fmt,
...);
void destroy_logger();
string base64_decode(const string &base64);
string base64_encode(const void *data, size_t size);
inline int upbound(int n, int align = 32) {
return (n + align - 1) / align * align;
}
string join_dims(const vector<int64_t> &dims);
}; // namespace iLogger
@@ -0,0 +1,63 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mkldnn_blocklist.h"
namespace Mkldnn {
const std::unordered_set<std::string> MKLDNN_BLOCKLIST = {
"LaTeX_OCR_rec",
"PP-FormulaNet-L",
"PP-FormulaNet-S",
"UniMERNet",
"UVDoc",
"Cascade-MaskRCNN-ResNet50-FPN",
"Cascade-MaskRCNN-ResNet50-vd-SSLDv2-FPN",
"Mask-RT-DETR-M",
"Mask-RT-DETR-S",
"MaskRCNN-ResNeXt101-vd-FPN",
"MaskRCNN-ResNet101-FPN",
"MaskRCNN-ResNet101-vd-FPN",
"MaskRCNN-ResNet50-FPN",
"MaskRCNN-ResNet50-vd-FPN",
"MaskRCNN-ResNet50",
"SOLOv2",
"PP-TinyPose_128x96",
"PP-TinyPose_256x192",
"Cascade-FasterRCNN-ResNet50-FPN",
"Cascade-FasterRCNN-ResNet50-vd-SSLDv2-FPN",
"Co-DINO-Swin-L",
"Co-Deformable-DETR-Swin-T",
"FasterRCNN-ResNeXt101-vd-FPN",
"FasterRCNN-ResNet101-FPN",
"FasterRCNN-ResNet101",
"FasterRCNN-ResNet34-FPN",
"FasterRCNN-ResNet50-FPN",
"FasterRCNN-ResNet50-vd-FPN",
"FasterRCNN-ResNet50-vd-SSLDv2-FPN",
"FasterRCNN-ResNet50",
"FasterRCNN-Swin-Tiny-FPN",
"MaskFormer_small",
"MaskFormer_tiny",
"SLANeXt_wired",
"SLANeXt_wireless",
"SLANet",
"SLANet_plus",
"YOWO",
"SAM-H_box",
"SAM-H_point",
"PP-FormulaNet_plus-L",
"PP-FormulaNet_plus-M",
"PP-FormulaNet_plus-S"};
}
@@ -0,0 +1,23 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string>
#include <unordered_set>
namespace Mkldnn {
extern const std::unordered_set<std::string> MKLDNN_BLOCKLIST;
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/utils/pp_option.h"
#include <algorithm>
#include <sstream>
#include <stdexcept>
#include "absl/status/statusor.h"
const std::string &PaddlePredictorOption::RunMode() const { return run_mode_; }
const std::string &PaddlePredictorOption::DeviceType() const {
return device_type_;
}
int PaddlePredictorOption::DeviceId() const { return device_id_; }
int PaddlePredictorOption::CpuThreads() const { return cpu_threads_; }
const std::vector<std::string> &PaddlePredictorOption::DeletePass() const {
return delete_pass_;
}
bool PaddlePredictorOption::EnableNewIR() const { return enable_new_ir_; }
bool PaddlePredictorOption::EnableCinn() const { return enable_cinn_; }
int PaddlePredictorOption::MkldnnCacheCapacity() const {
return mkldnn_cache_capacity_;
}
const std::vector<std::string> &
PaddlePredictorOption::GetSupportRunMode() const {
return SUPPORT_RUN_MODE;
}
const std::vector<std::string> &
PaddlePredictorOption::GetSupportDevice() const {
return SUPPORT_DEVICE;
}
absl::Status PaddlePredictorOption::SetRunMode(const std::string &run_mode) {
if (std::find(SUPPORT_RUN_MODE.begin(), SUPPORT_RUN_MODE.end(), run_mode) ==
SUPPORT_RUN_MODE.end()) {
return absl::InvalidArgumentError("Unsupported run_mode: " + run_mode);
}
run_mode_ = run_mode;
return absl::OkStatus();
}
absl::Status
PaddlePredictorOption::SetDeviceType(const std::string &device_type) {
if (std::find(SUPPORT_DEVICE.begin(), SUPPORT_DEVICE.end(), device_type) ==
SUPPORT_DEVICE.end()) {
return absl::InvalidArgumentError(
"SetDeviceType failed! Unsupported device_type: " + device_type);
}
device_type_ = device_type;
if (device_type_ == "cpu") {
device_id_ = 0;
}
return absl::OkStatus();
}
absl::Status PaddlePredictorOption::SetDeviceId(int device_id) {
if (device_id < 0) {
return absl::InvalidArgumentError(
"SetDeviceId failed! device_id must be >= 0");
}
device_id_ = device_id;
return absl::OkStatus();
}
absl::Status PaddlePredictorOption::SetCpuThreads(int cpu_threads) {
if (cpu_threads < 1) {
throw std::invalid_argument(
"SetCpuThreads failed! cpu_threads must be >= 1");
}
cpu_threads_ = cpu_threads;
return absl::OkStatus();
}
absl::Status
PaddlePredictorOption::SetMkldnnCacheCapacity(int mkldnn_cache_capacity) {
if (mkldnn_cache_capacity < 1) {
throw std::invalid_argument(
"SetMkldnnCacheCapacity failed! mkldnn_cache_capacity must be >= 1");
}
mkldnn_cache_capacity_ = mkldnn_cache_capacity;
return absl::OkStatus();
}
void PaddlePredictorOption::SetDeletePass(
const std::vector<std::string> &delete_pass) {
delete_pass_ = delete_pass;
}
void PaddlePredictorOption::SetEnableNewIR(bool enable_new_ir) {
enable_new_ir_ = enable_new_ir;
}
void PaddlePredictorOption::SetEnableCinn(bool enable_cinn) {
enable_cinn_ = enable_cinn;
}
std::string PaddlePredictorOption::DebugString() const {
std::ostringstream oss;
oss << "run_mode: " << run_mode_ << ", "
<< "device_type: " << device_type_ << ", "
<< "device_id: " << device_id_ << ", "
<< "cpu_threads: " << cpu_threads_ << ", "
<< "delete_pass: [";
for (size_t i = 0; i < delete_pass_.size(); ++i) {
oss << delete_pass_[i];
if (i != delete_pass_.size() - 1)
oss << ", ";
}
oss << "], "
<< "enable_new_ir: " << (enable_new_ir_ ? "true" : "false") << ", "
<< "enable_cinn: " << (enable_cinn_ ? "true" : "false") << ", "
<< "mkldnn_cache_capacity: " << mkldnn_cache_capacity_;
return oss.str();
}
+65
View File
@@ -0,0 +1,65 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <set>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#ifdef WITH_GPU
static constexpr const char *DEVICE = "gpu:0";
#else
static constexpr const char *DEVICE = "cpu";
#endif
class PaddlePredictorOption {
public:
const std::vector<std::string> SUPPORT_RUN_MODE = {"paddle", "paddle_fp16",
"mkldnn", "mkldnn_bf16"};
const std::vector<std::string> SUPPORT_DEVICE = {"gpu", "cpu"};
const std::string &RunMode() const;
const std::string &DeviceType() const;
int DeviceId() const;
int CpuThreads() const;
const std::vector<std::string> &DeletePass() const;
bool EnableNewIR() const;
bool EnableCinn() const;
int MkldnnCacheCapacity() const;
const std::vector<std::string> &GetSupportRunMode() const;
const std::vector<std::string> &GetSupportDevice() const;
std::string DebugString() const;
absl::Status SetRunMode(const std::string &run_mode);
absl::Status SetDeviceType(const std::string &device_type);
absl::Status SetDeviceId(int device_id);
absl::Status SetCpuThreads(int cpu_threads);
absl::Status SetMkldnnCacheCapacity(int mkldnn_cache_capacity);
void SetDeletePass(const std::vector<std::string> &delete_pass);
void SetEnableNewIR(bool enable_new_ir);
void SetEnableCinn(bool enable_cinn);
private:
std::string run_mode_ = "paddle";
std::string device_type_ = DEVICE;
int device_id_ = 0;
int cpu_threads_ = 10;
std::vector<std::string> delete_pass_ = {};
bool enable_new_ir_ = true;
bool enable_cinn_ = false;
int mkldnn_cache_capacity_ = 10;
};
+612
View File
@@ -0,0 +1,612 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "utility.h"
#include <dirent.h>
#include <sys/stat.h>
#include <regex>
#include "ilogger.h"
absl::Status Utility::FileExists(const std::string &path) {
struct stat st;
if (stat(path.c_str(), &st) == 0) {
return absl::OkStatus();
} else {
return absl::NotFoundError("File is not exist:" + path);
}
}
absl::StatusOr<std::map<std::string, std::pair<std::string, std::string>>>
Utility::GetModelPaths(const std::string &model_dir,
const std::string &model_file_prefix) {
std::map<std::string, std::pair<std::string, std::string>> model_paths;
std::string model_path;
std::string json_path =
model_dir + PATH_SEPARATOR + model_file_prefix + ".json";
std::string pdmodel_path =
model_dir + PATH_SEPARATOR + model_file_prefix + ".pdmodel";
std::string params_path =
model_dir + PATH_SEPARATOR + model_file_prefix + ".pdiparams";
if (FileExists(json_path).ok()) {
model_path = json_path;
} else if (FileExists(pdmodel_path).ok()) {
model_path = pdmodel_path;
} else {
return absl::NotFoundError(FileExists(json_path).ToString() + " and " +
FileExists(pdmodel_path).ToString());
}
if (model_path.empty()) {
return absl::NotFoundError(
"No PaddlePaddle model file (.json or .pdmodel) found!");
}
if (FileExists(params_path).ok()) {
model_paths["paddle"] = std::make_pair(model_path, params_path);
} else {
return absl::NotFoundError(
"No PaddlePaddle params file (.pdiparams) found!");
}
return model_paths;
}
absl::StatusOr<std::string>
Utility::FindModelPath(const std::string &model_dir,
const std::string &model_name) {
char last_char = model_dir.back();
std::string model_path;
if (last_char == PATH_SEPARATOR)
model_path = model_dir + model_name;
else
model_path = model_dir + PATH_SEPARATOR + model_name;
auto status = FileExists(model_path);
if (!status.ok()) {
return status;
}
return model_path;
}
absl::StatusOr<std::string>
Utility::GetDefaultConfig(std::string pipeline_name) {
std::string current_path = __FILE__;
for (int i = 0; i < 2; i++) {
size_t pos = current_path.find_last_of(PATH_SEPARATOR);
if (pos == std::string::npos) {
return absl::NotFoundError("Could not find pipeline config yaml :" +
pipeline_name);
}
current_path = current_path.substr(0, pos);
}
std::string config_path_yaml = current_path + PATH_SEPARATOR + "configs" +
PATH_SEPARATOR + pipeline_name + ".yaml";
std::string config_path_yml = current_path + PATH_SEPARATOR + "configs" +
PATH_SEPARATOR + pipeline_name + ".yml";
if (FileExists(config_path_yaml).ok()) {
return config_path_yaml;
} else if (FileExists(config_path_yml).ok()) {
return config_path_yml;
}
return absl::NotFoundError("Could not find pipeline config yaml :" +
pipeline_name);
}
absl::StatusOr<std::string>
Utility::GetConfigPaths(const std::string &model_dir,
const std::string &model_file_prefix) {
std::string config_path = "";
std::string config_path_find =
model_dir + PATH_SEPARATOR + model_file_prefix + ".yml";
if (FileExists(config_path_find).ok()) {
config_path = config_path_find;
} else {
return FileExists(config_path_find);
}
return config_path;
};
bool Utility::IsMkldnnAvailable() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
char cpuBrand[0x40] = {0};
int cpuInfo[4] = {0};
__cpuid(cpuInfo, 0x80000002);
memcpy(cpuBrand, cpuInfo, sizeof(cpuInfo));
__cpuid(cpuInfo, 0x80000003);
memcpy(cpuBrand + 16, cpuInfo, sizeof(cpuInfo));
__cpuid(cpuInfo, 0x80000004);
memcpy(cpuBrand + 32, cpuInfo, sizeof(cpuInfo));
std::string brandStr(cpuBrand);
if (brandStr.find("Intel") != std::string::npos) {
return true;
}
return false;
#else
std::ifstream cpuinfo("/proc/cpuinfo");
std::string line;
while (std::getline(cpuinfo, line)) {
if (line.find("vendor_id") != std::string::npos) {
auto pos = line.find(":");
if (pos != std::string::npos) {
if (line.substr(pos + 2).find("Intel") != std::string::npos) {
return true;
}
}
}
}
return false;
#endif
};
void Utility::PrintShape(const cv::Mat &img) {
for (int i = 0; i < img.dims; i++) {
std::cout << img.size[i] << " ";
}
std::cout << std::endl;
}
absl::Status Utility::MyCreateDirectory(const std::string &path) {
#ifdef _WIN32
int ret = _mkdir(path.c_str());
#else
int ret = mkdir(path.c_str(), 0755);
#endif
if (ret == 0) {
return absl::OkStatus();
}
if (errno == EEXIST) {
return absl::OkStatus();
}
return absl::ErrnoToStatus(errno, "Failed to create directory: " + path);
}
absl::Status Utility::MyCreatePath(const std::string &path) {
std::vector<std::string> paths;
std::string tmp;
for (size_t i = 0; i < path.size(); ++i) {
tmp += path[i];
if (path[i] == PATH_SEPARATOR) {
paths.push_back(tmp);
}
}
if (!tmp.empty() && tmp.back() != PATH_SEPARATOR)
paths.push_back(tmp);
std::string current;
for (size_t i = 0; i < paths.size(); ++i) {
current += paths[i];
absl::Status status = MyCreateDirectory(current);
if (!status.ok()) {
return status;
}
}
return absl::OkStatus();
}
absl::Status Utility::MyCreateFile(const std::string &filepath) {
std::ifstream infile(filepath.c_str());
if (infile.good()) {
return absl::OkStatus();
}
std::ofstream outfile(filepath.c_str(), std::ios::out | std::ios::trunc);
if (!outfile.is_open()) {
return absl::InternalError("Failed to create file: " + filepath);
}
outfile.close();
return absl::OkStatus();
}
absl::StatusOr<std::vector<cv::Mat>> Utility::SplitBatch(const cv::Mat &batch) {
if (batch.dims < 1) {
return absl::InvalidArgumentError(
"Input batch must have at least 1 dimension.");
}
if (batch.type() != CV_32F) {
return absl::InvalidArgumentError(
"Input batch must have CV_32F element type.");
}
std::vector<cv::Mat> split_mats;
int batch_size = batch.size[0];
std::vector<cv::Range> myranges(batch.dims);
for (int i = 0; i < batch_size; ++i) {
myranges[0] = cv::Range(i, i + 1);
for (int d = 1; d < batch.dims; ++d)
myranges[d] = cv::Range::all();
cv::Mat sub_mat = batch(&myranges[0]);
split_mats.push_back(sub_mat);
}
return split_mats;
}
std::string Utility::GetFileExtension(const std::string &file_path) {
size_t pos = file_path.find_last_of('.');
if (pos == std::string::npos || pos == file_path.length() - 1) {
return "";
}
return file_path.substr(pos + 1);
}
std::string Utility::ToLower(const std::string &str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
bool Utility::IsDirectory(const std::string &path) {
struct stat path_stat;
if (stat(path.c_str(), &path_stat) != 0) {
return false;
}
return S_ISDIR(path_stat.st_mode);
}
void Utility::GetFilesRecursive(const std::string &dir_path,
std::vector<std::string> &file_list) {
DIR *dir = opendir(dir_path.c_str());
if (dir == NULL) {
return;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
std::string name = entry->d_name;
if (name == "." || name == "..") {
continue;
}
std::string full_path = "";
if (dir_path.back() == PATH_SEPARATOR) {
full_path = dir_path + name;
} else {
full_path = dir_path + PATH_SEPARATOR + name;
}
if (Utility::IsDirectory(full_path)) {
Utility::GetFilesRecursive(full_path, file_list);
} else if (IsImageFile(full_path)) {
file_list.push_back(full_path);
}
}
closedir(dir);
}
bool Utility::IsImageFile(const std::string &file_path) {
std::string extension = GetFileExtension(file_path);
std::string lower_ext = ToLower(extension);
return kImgSuffixes.find(lower_ext) != kImgSuffixes.end();
}
absl::StatusOr<cv::Mat> Utility::MyLoadImage(const std::string &file_path) {
cv::Mat image = cv::imread(file_path, cv::IMREAD_COLOR);
if (image.empty()) {
return absl::InvalidArgumentError("Failed to load image: " + file_path);
}
return image;
}
int Utility::MakeDir(const std::string &path) {
#ifdef _WIN32
return _mkdir(path.c_str());
#else
return mkdir(path.c_str(), 0755); // Linux/macOS 权限 755
#endif
}
absl::Status Utility::CreateDirectoryRecursive(const std::string &path) {
if (path.empty()) {
return absl::InvalidArgumentError("Path cannot be empty");
}
size_t pos = 0;
std::string dir = path;
#ifdef _WIN32
#define ACCESS _access
#define F_OK 0
#else
#define ACCESS access
#endif
while (pos < dir.size()) {
pos = dir.find_first_of(PATH_SEPARATOR, pos + 1);
std::string subdir = (pos == std::string::npos) ? dir : dir.substr(0, pos);
if (!subdir.empty() && ACCESS(subdir.c_str(), F_OK) != 0) {
if (MakeDir(subdir) != 0) {
return absl::InternalError("Failed to create directory: " + subdir);
}
}
if (pos == std::string::npos) {
break;
}
}
return absl::OkStatus();
}
absl::Status Utility::CreateDirectoryForFile(const std::string &filePath) {
size_t found = filePath.find_last_of(PATH_SEPARATOR);
if (found != std::string::npos) {
std::string dirPath = filePath.substr(0, found);
if (!CreateDirectoryRecursive(dirPath).ok()) {
return absl::InternalError("Failed to create file: " + filePath);
;
}
}
return absl::OkStatus();
}
absl::StatusOr<std::string>
Utility::SmartCreateDirectoryForImage(std::string save_path,
const std::string &input_path,
const std::string &suffix) {
size_t pos = save_path.find_last_of("/\\");
std::string lastPart = save_path.substr(pos + 1);
if (lastPart.find(".") == std::string::npos) {
save_path += PATH_SEPARATOR;
}
std::string full_path = save_path;
auto status = CreateDirectoryForFile(save_path);
if (!status.ok()) {
return status;
}
if (Utility::IsDirectory(save_path)) {
auto file_path = input_path;
size_t pos = file_path.find_last_of("/\\");
std::string file_name =
(pos == std::string::npos) ? file_path : file_path.substr(pos + 1);
size_t dot_pos = file_name.find_last_of('.');
if (dot_pos == std::string::npos) {
file_name = file_name + suffix;
} else {
file_name.insert(dot_pos, suffix);
}
if (save_path.back() != PATH_SEPARATOR) {
full_path += PATH_SEPARATOR;
}
full_path += file_name;
}
return full_path;
}
absl::StatusOr<std::string>
Utility::SmartCreateDirectoryForJson(const std::string &save_path,
const std::string &input_path,
const std::string &suffix) {
auto full_path = SmartCreateDirectoryForImage(save_path, input_path, suffix);
if (!full_path.ok()) {
return full_path.status();
}
size_t pos = full_path.value().rfind('.');
if (pos != std::string::npos) {
full_path.value().replace(pos, std::string::npos, ".json");
}
return full_path.value();
}
absl::StatusOr<int> Utility::StringToInt(std::string s) {
std::regex pattern("(\\d+)");
std::smatch match;
if (std::regex_search(s, match, pattern)) {
int value = std::stoi(match[1]);
return value;
} else {
return absl::NotFoundError("Could not find int !");
}
}
bool Utility::StringToBool(const std::string &str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
assert(result == "true" || result == "false");
if (result == "true") {
return true;
} else {
return false;
}
}
std::string Utility::VecToString(const std::vector<int> &input) {
std::string result;
for (auto it = input.begin(); it != input.end(); ++it) {
if (it != input.begin())
result += ",";
result += std::to_string(*it);
}
return result;
}
absl::StatusOr<std::tuple<std::string, std::string, std::string>>
Utility::GetOcrModelInfo(std::string lang, std::string ppocr_version) {
// Font constants
const static std::string PINGFANG_FONT = "PingFang-SC-Regular.ttf";
const static std::string SIMFANG_FONT = "simfang.ttf";
const static std::string LATIN_FONT = "latin.ttf";
const static std::string KOREAN_FONT = "korean.ttf";
const static std::string ARABIC_FONT = "arabic.ttf";
const static std::string CYRILLIC_FONT = "cyrillic.ttf";
const static std::string KANNADA_FONT = "kannada.ttf";
const static std::string TELUGU_FONT = "telugu.ttf";
const static std::string TAMIL_FONT = "tamil.ttf";
const static std::string DEVANAGARI_FONT = "devanagari.ttf";
// Supported PP-OCR versions
const static std::unordered_set<std::string> SUPPORT_PPOCR_VERSION = {
"PP-OCRv5", "PP-OCRv4", "PP-OCRv3"};
// Language sets
const static std::unordered_set<std::string> LATIN_LANGS = {
"af", "az", "bs", "cs", "cy", "da", "de", "es", "et",
"fr", "ga", "hr", "hu", "id", "is", "it", "ku", "la",
"lt", "lv", "mi", "ms", "mt", "nl", "no", "oc", "pi",
"pl", "pt", "ro", "rs_latin", "sk", "sl", "sq", "sv", "sw",
"tl", "tr", "uz", "vi", "french", "german"};
const static std::unordered_set<std::string> ARABIC_LANGS = {"ar", "fa", "ug",
"ur"};
const static std::unordered_set<std::string> ESLAV_LANGS = {"ru", "be", "uk"};
const static std::unordered_set<std::string> CYRILLIC_LANGS = {
"ru", "rs_cyrillic", "be", "bg", "uk", "mn", "abq", "ady",
"kbd", "ava", "dar", "inh", "che", "lbe", "lez", "tab"};
const static std::unordered_set<std::string> DEVANAGARI_LANGS = {
"hi", "mr", "ne", "bh", "mai", "ang", "bho",
"mah", "sck", "new", "gom", "sa", "bgc"};
const static std::unordered_set<std::string> SPECIFIC_LANGS = {
"ch", "en", "korean", "japan", "chinese_cht", "te", "ka", "ta"};
// Validate input parameters
if (!ppocr_version.empty() &&
SUPPORT_PPOCR_VERSION.count(ppocr_version) == 0) {
return absl::InvalidArgumentError("Unsupported ppocr_version: " +
ppocr_version);
}
if (lang.empty())
lang = "ch";
// Create combined supported languages set
const static std::unordered_set<std::string> supported_langs = []() {
std::unordered_set<std::string> s;
s.insert(LATIN_LANGS.begin(), LATIN_LANGS.end());
s.insert(ARABIC_LANGS.begin(), ARABIC_LANGS.end());
s.insert(ESLAV_LANGS.begin(), ESLAV_LANGS.end());
s.insert(CYRILLIC_LANGS.begin(), CYRILLIC_LANGS.end());
s.insert(DEVANAGARI_LANGS.begin(), DEVANAGARI_LANGS.end());
s.insert(SPECIFIC_LANGS.begin(), SPECIFIC_LANGS.end());
s.insert("ch");
return s;
}();
if (supported_langs.count(lang) == 0) {
return absl::InvalidArgumentError("Unsupported lang: " + lang);
}
// Determine default ppocr_version if not specified
if (ppocr_version.empty()) {
std::unordered_set<std::string> v5_langs = {"ch", "chinese_cht", "en",
"japan", "korean"};
v5_langs.insert(LATIN_LANGS.begin(), LATIN_LANGS.end());
v5_langs.insert(ESLAV_LANGS.begin(), ESLAV_LANGS.end());
if (v5_langs.count(lang)) {
ppocr_version = "PP-OCRv5";
} else {
std::unordered_set<std::string> v3_langs = LATIN_LANGS;
v3_langs.insert(ARABIC_LANGS.begin(), ARABIC_LANGS.end());
v3_langs.insert(CYRILLIC_LANGS.begin(), CYRILLIC_LANGS.end());
v3_langs.insert(DEVANAGARI_LANGS.begin(), DEVANAGARI_LANGS.end());
v3_langs.insert(SPECIFIC_LANGS.begin(), SPECIFIC_LANGS.end());
if (v3_langs.count(lang)) {
ppocr_version = "PP-OCRv3";
} else {
return absl::InvalidArgumentError(
"Invalid lang and ocr_version combination!");
}
}
}
// Initialize return values
std::string det_model_name;
std::string rec_model_name;
std::string font_name = SIMFANG_FONT; // Default font
// Model and font selection logic
if (ppocr_version == "PP-OCRv5") {
det_model_name = "PP-OCRv5_server_det";
std::string rec_lang;
if (lang == "ch" || lang == "chinese_cht" || lang == "en" ||
lang == "japan") {
rec_model_name = "PP-OCRv5_server_rec";
font_name = SIMFANG_FONT;
} else if (LATIN_LANGS.count(lang)) {
rec_lang = "latin";
font_name = LATIN_FONT;
} else if (ESLAV_LANGS.count(lang)) {
rec_lang = "eslav";
font_name = CYRILLIC_FONT;
} else if (lang == "korean") {
rec_lang = "korean";
font_name = KOREAN_FONT;
}
if (!rec_lang.empty()) {
rec_model_name = rec_lang + "_PP-OCRv5_mobile_rec";
}
} else if (ppocr_version == "PP-OCRv4") {
if (lang == "ch") {
det_model_name = "PP-OCRv4_mobile_det";
rec_model_name = "PP-OCRv4_mobile_rec";
font_name = SIMFANG_FONT;
} else if (lang == "en") {
det_model_name = "PP-OCRv4_mobile_det";
rec_model_name = "en_PP-OCRv4_mobile_rec";
font_name = SIMFANG_FONT;
} else {
return absl::InvalidArgumentError(
"PP-OCRv4 only support ch and en languages!");
}
} else { // PP-OCRv3
det_model_name = "PP-OCRv3_mobile_det";
std::string rec_lang;
if (LATIN_LANGS.count(lang)) {
rec_lang = "latin";
font_name = LATIN_FONT;
} else if (ARABIC_LANGS.count(lang)) {
rec_lang = "arabic";
font_name = ARABIC_FONT;
} else if (CYRILLIC_LANGS.count(lang)) {
rec_lang = "cyrillic";
font_name = CYRILLIC_FONT;
} else if (DEVANAGARI_LANGS.count(lang)) {
rec_lang = "devanagari";
font_name = DEVANAGARI_FONT;
} else if (SPECIFIC_LANGS.count(lang)) {
rec_lang = lang;
if (lang == "ka") {
font_name = KANNADA_FONT;
} else if (lang == "te") {
font_name = TELUGU_FONT;
} else if (lang == "ta") {
font_name = TAMIL_FONT;
} else if (lang == "ch") {
font_name = SIMFANG_FONT;
}
}
if (rec_lang == "ch") {
rec_model_name = "PP-OCRv3_mobile_rec";
} else if (!rec_lang.empty()) {
rec_model_name = rec_lang + "_PP-OCRv3_mobile_rec";
}
}
if (rec_model_name.empty()) {
return absl::InvalidArgumentError(
"Invalid lang and ocr_version combination!");
}
return std::make_tuple(det_model_name, rec_model_name, font_name);
}
const std::set<std::string> Utility::kImgSuffixes = {"jpg", "png", "jpeg",
"bmp"};
+121
View File
@@ -0,0 +1,121 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <algorithm>
#include <fstream>
#include <map>
#include <opencv2/opencv.hpp>
#include <string>
#include <unordered_set>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#define mkdir _mkdir
static const char PATH_SEPARATOR = '\\';
#else
#include <sys/stat.h>
#include <sys/types.h>
static const char PATH_SEPARATOR = '/';
#endif
#include <errno.h>
class Utility {
public:
struct PaddleXConfigVariant {
enum class Type { NONE, STR, MAP };
Type type;
std::string str_val;
std::unordered_map<std::string, std::string> map_val;
PaddleXConfigVariant() : type(Type::NONE) {}
PaddleXConfigVariant(const std::string &val)
: type(Type::STR), str_val(val) {}
PaddleXConfigVariant(const char *val)
: type(Type::STR), str_val(val ? val : "") {}
PaddleXConfigVariant(
const std::unordered_map<std::string, std::string> &val)
: type(Type::MAP), map_val(val) {}
bool IsStr() const { return type == Type::STR; }
bool IsMap() const { return type == Type::MAP; }
const std::string &GetStr() const {
assert(IsStr());
return str_val;
}
const std::unordered_map<std::string, std::string> &GetMap() const {
assert(IsMap());
return map_val;
}
};
static constexpr const char *MODEL_FILE_PREFIX = "inference";
static const std::set<std::string> kImgSuffixes;
static absl::StatusOr<
std::map<std::string, std::pair<std::string, std::string>>>
GetModelPaths(const std::string &model_dir,
const std::string &model_file_prefix = MODEL_FILE_PREFIX);
static absl::StatusOr<std::string>
FindModelPath(const std::string &model_dir, const std::string &model_name);
static absl::StatusOr<std::string>
GetConfigPaths(const std::string &model_dir,
const std::string &model_file_prefix = MODEL_FILE_PREFIX);
static absl::StatusOr<std::string>
GetDefaultConfig(std::string pipeline_name);
static absl::Status FileExists(const std::string &path);
// TODO windows
static bool IsMkldnnAvailable();
static void PrintShape(const cv::Mat &img);
static absl::Status MyCreateDirectory(const std::string &path);
static absl::Status MyCreatePath(const std::string &path);
static absl::Status MyCreateFile(const std::string &filepath);
static absl::StatusOr<std::vector<cv::Mat>> SplitBatch(const cv::Mat &batch);
static absl::StatusOr<cv::Mat> MyLoadImage(const std::string &file_path);
static bool IsDirectory(const std::string &path);
static std::string GetFileExtension(const std::string &file_path);
static void GetFilesRecursive(const std::string &dir_path,
std::vector<std::string> &file_list);
static std::string ToLower(const std::string &str);
static bool IsImageFile(const std::string &file_path);
static int MakeDir(const std::string &path);
static absl::Status CreateDirectoryRecursive(const std::string &path);
static absl::Status CreateDirectoryForFile(const std::string &filePath);
static absl::StatusOr<std::string>
SmartCreateDirectoryForImage(std::string save_path,
const std::string &input_path,
const std::string &suffix = "_res");
static absl::StatusOr<std::string>
SmartCreateDirectoryForJson(const std::string &save_path,
const std::string &input_path,
const std::string &suffix = "_res");
static absl::StatusOr<int> StringToInt(std::string s);
static bool StringToBool(const std::string &str);
static std::string VecToString(const std::vector<int> &input);
static absl::StatusOr<std::tuple<std::string, std::string, std::string>>
GetOcrModelInfo(std::string lang, std::string ppocr_version);
};
+406
View File
@@ -0,0 +1,406 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "yaml_config.h"
#include <iostream>
#include <sstream>
#include "absl/strings/str_cat.h"
#include "src/utils/ilogger.h"
YamlConfig::YamlConfig(const std::string &model_dir) {
auto status_get = GetConfigYamlPaths(model_dir);
if (!status_get.ok()) {
INFOE("Could find files with the .yaml or .yml in %s %s", model_dir.c_str(),
status_get.ToString().c_str());
exit(-1);
}
auto status = LoadYamlFile();
if (!status.ok()) {
INFOE("Failed to load config: ", status.ToString().c_str());
exit(-1);
}
Init();
}
absl::Status YamlConfig::GetConfigYamlPaths(const std::string &model_dir) {
if (Utility::GetFileExtension(model_dir) == "yaml" ||
Utility::GetFileExtension(model_dir) == "yml") {
config_yaml_path_ = model_dir;
return absl::OkStatus();
}
std::string config_path_yml =
model_dir + "/" + Utility::MODEL_FILE_PREFIX + ".yml";
std::string config_path_yaml =
model_dir + "/" + Utility::MODEL_FILE_PREFIX + ".yaml";
if (Utility::FileExists(config_path_yml).ok()) {
config_yaml_path_ = config_path_yml;
return absl::OkStatus();
} else if (Utility::FileExists(config_path_yaml).ok()) {
config_yaml_path_ = config_path_yaml;
return absl::OkStatus();
} else {
return absl::NotFoundError("file is not exist!");
}
};
absl::Status YamlConfig::LoadYamlFile() {
try {
YAML::Node config = YAML::LoadFile(config_yaml_path_);
ParseNode(config);
return absl::OkStatus();
} catch (const YAML::BadFile &e) {
return absl::NotFoundError(
absl::StrCat("Failed to open YAML file: ", config_yaml_path_));
} catch (const YAML::ParserException &e) {
return absl::InvalidArgumentError(
absl::StrCat("Failed to parse YAML file: ", e.what()));
} catch (const YAML::Exception &e) {
return absl::InternalError(absl::StrCat("YAML error: ", e.what()));
} catch (const std::exception &e) {
return absl::InternalError(absl::StrCat("Unexpected error: ", e.what()));
}
}
void YamlConfig::Init() {
for (const auto &info : data_) {
if (info.first.find("DecodeImage.channel_first") != std::string::npos) {
pre_process_op_info_["DecodeImage.channel_first"] = info.second;
} else if (info.first.find("DecodeImage.img_mode") != std::string::npos) {
pre_process_op_info_["DecodeImage.img_mode"] = info.second;
} else if (info.first.find("DetLabelEncode") != std::string::npos) {
pre_process_op_info_["DetLabelEncode"] = info.second;
} else if (info.first.find("DetResizeForTest.resize_long") !=
std::string::npos) {
pre_process_op_info_["DetResizeForTest.resize_long"] = info.second;
} else if (info.first.find("NormalizeImage.mean") != std::string::npos) {
size_t pos = info.first.find("NormalizeImage.mean");
size_t after = pos + std::string("NormalizeImage.mean").size();
if (info.first[after] != '[') {
pre_process_op_info_["NormalizeImage.mean"] = info.second;
}
} else if (info.first.find("NormalizeImage.order") != std::string::npos) {
pre_process_op_info_["NormalizeImage.order"] = info.second;
} else if (info.first.find("NormalizeImage.scale") != std::string::npos) {
pre_process_op_info_["NormalizeImage.scale"] = info.second;
} else if (info.first.find("NormalizeImage.std") != std::string::npos) {
size_t pos = info.first.find("NormalizeImage.std");
size_t after = pos + std::string("NormalizeImage.std").size();
if (info.first[after] != '[') {
pre_process_op_info_["NormalizeImage.std"] = info.second;
}
} else if (info.first.find("ResizeImage.size") != std::string::npos) {
size_t pos = info.first.find("ResizeImage.size");
size_t after = pos + std::string("ResizeImage.size").size();
if (info.first[after] != '[') {
pre_process_op_info_["ResizeImage.size"] = info.second;
}
} else if (info.first.find("ResizeImage.resize_short") !=
std::string::npos) {
pre_process_op_info_["ResizeImage.resize_short"] = info.second;
} else if (info.first.find("CropImage.size") != std::string::npos) {
pre_process_op_info_["CropImage.size"] = info.second;
} else if (info.first.find("ToCHWImage") != std::string::npos) {
pre_process_op_info_["ToCHWImage"] = info.second;
} else if (info.first.find("KeepKeys.keep_keys") != std::string::npos) {
pre_process_op_info_["KeepKeys.keep_keys"] = info.second;
} else if (info.first.find("PostProcess.name") != std::string::npos) {
post_process_op_info_["PostProcess.name"] = info.second;
} else if (info.first.find("PostProcess.thresh") != std::string::npos) {
post_process_op_info_["PostProcess.thresh"] = info.second;
} else if (info.first.find("PostProcess.box_thresh") != std::string::npos) {
post_process_op_info_["PostProcess.box_thresh"] = info.second;
} else if (info.first.find("PostProcess.max_candidates") !=
std::string::npos) {
post_process_op_info_["PostProcess.max_candidates"] = info.second;
} else if (info.first.find("PostProcess.unclip_ratio") !=
std::string::npos) {
post_process_op_info_["PostProcess.unclip_ratio"] = info.second;
} else if (info.first.find("PostProcess.Topk.topk") != std::string::npos) {
post_process_op_info_["PostProcess.Topk.topk"] = info.second;
} else if (info.first.find("PostProcess.Topk.label_list") !=
std::string::npos) {
size_t pos = info.first.find("PostProcess.Topk.label_list");
size_t after = pos + std::string("PostProcess.Topk.label_list").size();
if (info.first[after] != '[') {
post_process_op_info_["PostProcess.Topk.label_list"] = info.second;
}
} else if (info.first.find("PostProcess.character_dict") !=
std::string::npos) {
size_t pos = info.first.find("PostProcess.character_dict");
size_t after = pos + std::string("PostProcess.character_dict").size();
if (info.first[after] != '[') {
post_process_op_info_["PostProcess.character_dict"] = info.second;
}
}
}
}
void YamlConfig::ParseNode(const YAML::Node &node, const std::string &prefix) {
if (node.IsMap()) {
for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = prefix.empty()
? it->first.as<std::string>()
: prefix + "." + it->first.as<std::string>();
ParseNode(it->second, key);
}
} else if (node.IsSequence()) {
std::stringstream ss;
ss << "[";
for (size_t i = 0; i < node.size(); ++i) {
std::string index_key = prefix + "[" + std::to_string(i) + "]";
if (node[i].IsScalar()) {
data_[index_key] = node[i].as<std::string>();
if (i > 0)
ss << ", ";
ss << node[i].as<std::string>();
} else {
ParseNode(node[i], index_key);
}
}
ss << "]";
data_[prefix] = ss.str();
} else if (node.IsScalar()) {
data_[prefix] = node.as<std::string>();
} else if (node.IsNull()) {
data_[prefix] = "null";
}
}
absl::StatusOr<std::string>
YamlConfig::GetString(const std::string &key,
const std::string &default_value) const {
for (const auto &info : data_) {
if (info.first.find(key) != std::string::npos) {
return info.second;
}
}
return default_value;
}
absl::StatusOr<int> YamlConfig::GetInt(const std::string &key,
int default_value) const {
for (const auto &info : data_) {
if (info.first.find(key) != std::string::npos) {
for (int i = 0; i < info.second.size(); i++) {
if (!std::isdigit(static_cast<uchar>(info.second[i]))) {
return absl::InvalidArgumentError("the " + key + " is not int type");
}
}
return std::stoi(info.second);
}
}
return default_value;
}
absl::StatusOr<float> YamlConfig::GetFloat(const std::string &key,
float default_value) const {
for (const auto &info : data_) {
if (info.first.find(key) != std::string::npos) {
return std::stof(info.second);
}
}
INFOW("Key not found %s,will use default value %f.", key.c_str(),
default_value);
return default_value;
}
absl::StatusOr<double> YamlConfig::GetDouble(const std::string &key) const {
auto it = data_.find(key);
if (it == data_.end()) {
return absl::NotFoundError(absl::StrCat("Key not found: ", key));
}
try {
return std::stod(it->second);
} catch (const std::invalid_argument &) {
return absl::InvalidArgumentError(
absl::StrCat("Invalid double value for key '", key, "': ", it->second));
} catch (const std::out_of_range &) {
return absl::OutOfRangeError(absl::StrCat(
"Double value out of range for key '", key, "': ", it->second));
}
}
absl::StatusOr<bool> YamlConfig::GetBool(const std::string &key,
bool default_value) const {
for (const auto &info : data_) {
if (info.first.find(key) != std::string::npos) {
if (Utility::ToLower(info.second) == "true") {
return true;
} else if (Utility::ToLower(info.second) == "false") {
return false;
} else {
return absl::InvalidArgumentError("the " + key + " is not bool type");
}
}
}
return default_value;
}
absl::StatusOr<std::unordered_map<std::string, std::string>>
YamlConfig::GetSubModule(const std::string &key) const {
std::unordered_map<std::string, std::string> submodule_result = {};
for (const auto &info : data_) {
if (info.first.find(key) != std::string::npos) {
submodule_result[info.first] = info.second;
}
}
if (submodule_result.empty()) {
return absl::NotFoundError("the " + key + " is not exits!");
}
return submodule_result;
}
absl::Status YamlConfig::HasKey(const std::string &key) const {
if (data_.find(key) != data_.end()) {
return absl::OkStatus();
}
return absl::NotFoundError(absl::StrCat("Key not found: ", key));
}
absl::Status YamlConfig::PrintAll() const {
for (const auto &it : data_) {
std::cout << it.first << ": " << it.second << std::endl;
}
return absl::OkStatus();
}
absl::Status YamlConfig::PrintWithPrefix(const std::string &prefix) const {
for (const auto &it : data_) {
if (it.first.find(prefix) == 0) {
std::cout << it.first << ": " << it.second << std::endl;
}
}
return absl::OkStatus();
}
absl::Status YamlConfig::FindPreProcessOp(const std::string &prefix) const {
std::unordered_map<std::string, std::string> pre_process_op_info{};
for (const auto &it : data_) {
if (it.first.find(prefix) == 0) {
std::cout << it.first << ": " << it.second << std::endl;
}
}
return absl::OkStatus();
}
VectorVariant YamlConfig::SmartParseVector(const std::string &input) {
auto trimBracketAndSpace = [](const std::string &str) -> std::string {
std::string s = str;
s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
if (!s.empty() && s.front() == '[')
s.erase(0, 1);
if (!s.empty() && s.back() == ']')
s.pop_back();
return s;
};
auto splitComma = [](const std::string &s) -> std::vector<std::string> {
std::vector<std::string> res;
std::string cur;
bool inQuotes = false;
for (size_t i = 0; i < s.size(); ++i) {
char ch = s[i];
if (ch == '"' && s[i + 1] != ',') {
inQuotes = !inQuotes;
}
if (ch == ',' && s[i - 1] == ',' && s[i + 1] == ',') {
cur += ch;
continue;
}
if (ch == ',' && !inQuotes) {
res.push_back(cur);
cur.clear();
} else {
cur += ch;
}
}
if (!cur.empty())
res.push_back(cur);
return res;
};
auto isInt = [](const std::string &s) -> bool {
if (s.empty())
return false;
size_t i = 0;
if (s[0] == '-' || s[0] == '+')
i = 1;
if (i == s.size())
return false;
for (; i < s.size(); ++i) {
if (!isdigit(s[i]))
return false;
}
return true;
};
auto isFloat = [](const std::string &s) -> bool {
std::istringstream iss(s);
float f;
char c;
return (iss >> f) && !(iss >> c);
};
VectorVariant result;
result.type = VECTOR_UNKNOWN;
std::string s = trimBracketAndSpace(input);
std::vector<std::string> items = splitComma(s);
bool allString = true, allInt = true, allFloat = true;
for (size_t i = 0; i < items.size(); ++i) {
std::string tmp = items[i];
if (tmp.size() >= 2 && tmp.front() == '"' && tmp.back() == '"') {
continue;
}
allString = false;
if (!isInt(tmp))
allInt = false;
if (!isFloat(tmp))
allFloat = false;
}
if (allString) {
result.type = VECTOR_STRING;
for (size_t i = 0; i < items.size(); ++i) {
std::string tmp = items[i];
result.vec_string.push_back(tmp.substr(1, tmp.size() - 2));
}
} else if (allInt) {
result.type = VECTOR_INT; // int maybe is string
for (size_t i = 0; i < items.size(); ++i) {
result.vec_int.push_back(std::stoi(items[i]));
}
for (size_t i = 0; i < items.size(); ++i) {
result.vec_string.push_back(items[i]);
}
} else if (allFloat) {
result.type = VECTOR_FLOAT;
for (size_t i = 0; i < items.size(); ++i) {
result.vec_float.push_back(std::stof(items[i]));
}
} else {
result.type = VECTOR_STRING;
for (size_t i = 0; i < items.size(); ++i) {
result.vec_string.push_back(items[i]);
}
}
return result;
}
absl::StatusOr<std::pair<std::string, std::string>>
YamlConfig::FindKey(const std::string &key) {
for (const auto &info : data_) {
if (info.first.find(key) != std::string::npos) {
return info;
}
}
return absl::NotFoundError("Could find key " + key);
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <yaml-cpp/yaml.h>
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "utility.h"
enum VectorType { VECTOR_INT, VECTOR_FLOAT, VECTOR_STRING, VECTOR_UNKNOWN };
struct VectorVariant {
VectorType type;
std::vector<int> vec_int;
std::vector<float> vec_float;
std::vector<std::string> vec_string;
};
class YamlConfig {
public:
YamlConfig(const std::unordered_map<std::string, std::string> &data)
: data_(data) {}
YamlConfig(const std::string &model_dir);
YamlConfig() = default;
~YamlConfig() = default;
void Init();
std::unordered_map<std::string, std::string> PreProcessOpInfo() {
return pre_process_op_info_;
};
std::unordered_map<std::string, std::string> PostProcessOpInfo() {
return post_process_op_info_;
};
absl::StatusOr<std::string>
GetString(const std::string &key,
const std::string &default_value = "") const;
absl::StatusOr<int> GetInt(const std::string &key, int default_value) const;
absl::StatusOr<float> GetFloat(const std::string &key,
float default_value) const;
absl::StatusOr<double> GetDouble(const std::string &key) const;
absl::StatusOr<bool> GetBool(const std::string &key,
bool default_value) const;
absl::StatusOr<std::unordered_map<std::string, std::string>>
GetSubModule(const std::string &key) const;
absl::Status HasKey(const std::string &key) const;
absl::Status PrintAll() const;
absl::Status PrintWithPrefix(const std::string &prefix) const;
absl::Status FindPreProcessOp(
const std::string &prefix = "PreProcess.transform_ops[0]") const;
std::unordered_map<std::string, std::string> &Data() { return data_; };
std::string ConfigYamlPath() { return config_yaml_path_; };
absl::Status GetConfigYamlPaths(const std::string &model_dir);
absl::Status LoadYamlFile();
absl::StatusOr<std::pair<std::string, std::string>>
FindKey(const std::string &key);
static VectorVariant SmartParseVector(const std::string &input);
private:
std::string config_yaml_path_;
void ParseNode(const YAML::Node &node, const std::string &prefix = "");
std::unordered_map<std::string, std::string> data_;
std::unordered_map<std::string, std::string> pre_process_op_info_;
std::unordered_map<std::string, std::string> post_process_op_info_;
};
+22
View File
@@ -0,0 +1,22 @@
OPENCV_DIR=your_opencv_dir
LIB_DIR=your_paddle_lib_dir
CUDA_LIB_DIR=your_cuda_lib_dir
CUDNN_LIB_DIR=your_cudnn_lib_dir
BUILD_DIR=build
rm -rf ${BUILD_DIR}
mkdir ${BUILD_DIR}
cd ${BUILD_DIR}
cmake .. \
-DPADDLE_LIB=${LIB_DIR} \
-DWITH_MKL=ON \
-DWITH_GPU=OFF \
-DWITH_STATIC_LIB=OFF \
-DWITH_TENSORRT=OFF \
-DOPENCV_DIR=${OPENCV_DIR} \
-DCUDNN_LIB=${CUDNN_LIB_DIR} \
-DCUDA_LIB=${CUDA_LIB_DIR} \
-DUSE_FREETYPE=OFF
make -j
+28
View File
@@ -0,0 +1,28 @@
root_path="./third_party/opencv-4.7.0"
install_path=${root_path}/opencv4
build_dir=${root_path}/build
rm -rf ${build_dir}
mkdir ${build_dir}
cd ${build_dir}
cmake .. \
-DCMAKE_INSTALL_PREFIX=${install_path} \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DWITH_IPP=OFF \
-DBUILD_IPP_IW=OFF \
-DWITH_LAPACK=OFF \
-DWITH_EIGEN=OFF \
-DCMAKE_INSTALL_LIBDIR=lib64 \
-DWITH_ZLIB=ON \
-DBUILD_ZLIB=ON \
-DWITH_JPEG=ON \
-DBUILD_JPEG=ON \
-DWITH_PNG=ON \
-DBUILD_PNG=ON \
-DWITH_TIFF=ON \
-DBUILD_TIFF=ON
make -j4
make install