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
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:
@@ -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.
|
||||
@@ -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",
|
||||
[¶ms](const std::string &input) {
|
||||
return PaddleOCR(std::get<0>(params)).Predict(input);
|
||||
}},
|
||||
{"doc_preprocessor",
|
||||
[¶ms](const std::string &input) {
|
||||
return DocPreprocessor(std::get<1>(params)).Predict(input);
|
||||
}},
|
||||
{"doc_img_orientation_classification",
|
||||
[¶ms](const std::string &input) {
|
||||
return DocImgOrientationClassification(std::get<2>(params))
|
||||
.Predict(input);
|
||||
}},
|
||||
{"text_image_unwarping",
|
||||
[¶ms](const std::string &input) {
|
||||
return TextImageUnwarping(std::get<3>(params)).Predict(input);
|
||||
}},
|
||||
{"text_detection",
|
||||
[¶ms](const std::string &input) {
|
||||
return TextDetection(std::get<4>(params)).Predict(input);
|
||||
}},
|
||||
{"textline_orientation_classification",
|
||||
[¶ms](const std::string &input) {
|
||||
return TextLineOrientationClassification(std::get<5>(params))
|
||||
.Predict(input);
|
||||
}},
|
||||
{"text_recognition",
|
||||
[¶ms](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 ¶ms)
|
||||
: 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 ¶ms =
|
||||
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 ¶ms)
|
||||
: 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 ¶ms = 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 ¶ms)
|
||||
: 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 ¶ms = 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 ¶ms)
|
||||
: 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 ¶ms = 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 ¶ms)
|
||||
: 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 ¶ms =
|
||||
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_;
|
||||
};
|
||||
@@ -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 ¶ms)
|
||||
: 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 ¶ms = 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_;
|
||||
};
|
||||
@@ -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 ¶ms) : 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;
|
||||
}
|
||||
@@ -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 ¶ms = 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"
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 ¶ms);
|
||||
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 ¶ms)
|
||||
: 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
|
||||
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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
|
||||
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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
|
||||
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 ¶ms) {
|
||||
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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
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 ¶ms);
|
||||
|
||||
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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
|
||||
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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
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 ¶ms)
|
||||
: 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 ¶ms)
|
||||
: 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 ¶ms);
|
||||
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 ¶ms)
|
||||
: 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_;
|
||||
};
|
||||
@@ -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.");
|
||||
@@ -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
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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"};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
Executable
+22
@@ -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
|
||||
Executable
+28
@@ -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
|
||||
Reference in New Issue
Block a user