在 C++ 項目中,通過源碼使用 PaddlePaddle 實現 OCR 功能

My-PaddleOCR

介紹

如何在 C++ 項目中,通過源碼使用 PaddlePaddle 實現 OCR 功能。
本項目的所有源碼:gitee: paddleocr

目前,官方提供使用 PaddleOcr 的方案有:

  • 在 Python 項目中,調用 paddlepaddle + paddleocr 包。

  • 在 C++項目中,調用一個可執行文件。(由編譯 PaddleOCR 中的 deploy/cpp_infer 下的代碼形成)

    Paddle OCR 提供了一個通過編譯 deploy/cpp_infer 下的代碼爲 ppocr.exe,然後通過命令行調用獲取 OCR 的結果。
    具體過程見: 服務器端 C++預測

其它方法:

  • 使用 Python 寫一個 RESTful 服務,然後讓 C++項目調用這個服務功能。

這裏主要介紹一個更加直接的方法:

  • deploy/cpp_infer 的源碼引入到我們的 C++項目。
  • 做適當修改後,我們就可以直接使用這些 API。
  • 當然,如果你願意的話,也可以將這些源碼形成一個新的項目,編譯成一個 dll。(這裏並不介紹)
    說明:
  • 下面的方法只在 release 版中有效。可能是因爲paddle_inference.dll 只支持 release 版。
    如果要支持 debug 版,可能需要重新編譯 PaddlePaddle。

Paddle OCR C++ 源碼

Paddle OCR 的倉庫,在github: PaddleOCR 或者 gitee: PaddleOCR
C++ 相關的代碼在目錄 deploy/cpp_infer 裏。

如何引入 Paddle OCR C++ 源碼

需要安裝的組件

  • opencv
    我在 opencv 4.6 版本上測試通過。
    注意:opencv 4.5 版本存在一些問題,會導致功能異常。
    設置環境變量:
@REM 設置opencv目錄環境變量
setx OPENCV_ROOT "C:\3rd\opencv4.6\build"
  • paddle_inference
    我用的是 2.6 版。
    設置環境變量:
@REM 設置paddle_inference目錄環境變量
setx PADDLE_ROOT "C:\3rd\paddle_inference"
  • cuda (optional)
    你應該懂得。
  • cudnn (optional)
    你應該懂得。

創建 C++項目

  • 創建一個空的 C++ 項目。

  • 在項目中創建文件:OpenCVDebug.props

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup>
    <ClCompile>
      <AdditionalIncludeDirectories>$(OPENCV_ROOT)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <AdditionalLibraryDirectories>$(OPENCV_ROOT)\x64\vc15\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>opencv_world460d.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>
  • 在項目中創建文件:OpenCVRelease.props
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup>
    <ClCompile>
      <AdditionalIncludeDirectories>$(OPENCV_ROOT)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <AdditionalLibraryDirectories>$(OPENCV_ROOT)\x64\vc15\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>opencv_world460.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>
  • 在項目中創建文件:PaddleDebug.props
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup>
    <ClCompile>
      <AdditionalIncludeDirectories>./;$(PADDLE_ROOT)/paddle/include;$(PADDLE_ROOT)/third_party/install/zlib/include;$(PADDLE_ROOT)/third_party/boost;$(PADDLE_ROOT)/third_party/eigen3;$(PADDLE_ROOT)/third_party/install/mklml/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <AdditionalLibraryDirectories>$(PADDLE_ROOT)/third_party/install/zlib/lib;$(PADDLE_ROOT)/third_party/install/mklml/lib;$(PADDLE_ROOT)/paddle/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>paddle_inference.lib;mklml.lib;libiomp5md.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>
  • 在項目中創建文件:PaddleRelease.props
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup>
    <ClCompile>
      <AdditionalIncludeDirectories>./;$(PADDLE_ROOT)/paddle/include;$(PADDLE_ROOT)/third_party/install/zlib/include;$(PADDLE_ROOT)/third_party/boost;$(PADDLE_ROOT)/third_party/eigen3;$(PADDLE_ROOT)/third_party/install/mklml/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <AdditionalLibraryDirectories>$(PADDLE_ROOT)/third_party/install/zlib/lib;$(PADDLE_ROOT)/third_party/install/mklml/lib;$(PADDLE_ROOT)/paddle/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>paddle_inference.lib;mklml.lib;libiomp5md.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>
  • 修改 C++項目文件:my-paddleocr.vcxproj
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
    <Import Project=".\OpenCVDebug.props" />
    <Import Project=".\PaddleDebug.props" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
    <Import Project=".\OpenCVRelease.props" />
    <Import Project=".\PaddleRelease.props" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
    <Import Project=".\OpenCVDebug.props" />
    <Import Project=".\PaddleDebug.props" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
    <Import Project=".\OpenCVRelease.props" />
    <Import Project=".\PaddleRelease.props" />
  </ImportGroup>

引入 Paddle OCR C++ 源碼

  • 克隆 Paddle OCR 倉庫到本地。
  • 切換到分支 release/2.6
git clone https://gitee.com/paddlepaddle/PaddleOCR.git
cd PaddleOCR
git checkout release/2.6
  • deploy/cpp_infer 目錄下的 includesrc兩個目錄的內容複製到我們的 C++ 項目中。
  • 修改新的 src 目錄名稱爲 ocr
  • 刪除ocr/main.cpp
  • include目錄下創建ocr_flags.h文件,內容如下:

這個文件是爲了替換 google 的 gflags 庫的使用。

#pragma once

// common args
#include <string>
using std::string;

#define DECLARE_bool(name) extern bool FLAGS_##name;
#define DECLARE_int32(name) extern int FLAGS_##name;
#define DECLARE_string(name) extern string FLAGS_##name;
#define DECLARE_double(name) extern double FLAGS_##name;

#define DEFINE_VARIABLE(type, name, value, help)                               \
    static const type FLAGS_nono##name = value;                                \
    type FLAGS_##name = FLAGS_nono##name;                                      \
    static type FLAGS_no##name = FLAGS_nono##name;

#define DEFINE_bool(name, val, txt) DEFINE_VARIABLE(bool, name, val, txt)
#define DEFINE_int32(name, val, txt) DEFINE_VARIABLE(int, name, val, txt)
#define DEFINE_string(name, val, txt) DEFINE_VARIABLE(string, name, val, txt)
#define DEFINE_double(name, val, txt) DEFINE_VARIABLE(double, name, val, txt)
  • 修改include/args.h文件,內容如下:

不使用 gflags/gflags.h
註釋行 // #include <gflags/gflags.h>
增加行 #include "ocr_flags.h"

...
// #include <gflags/gflags.h>
#include "ocr_flags.h"
...
  • 修改ocr/args.cpp文件,內容如下:

不使用 gflags/gflags.h
註釋行 // #include <gflags/gflags.h>
增加行 #include "include/ocr_flags.h"

...
// #include <gflags/gflags.h>
#include "include/ocr_flags.h"
...
  • 修改include/paddleocr.cpp文件,內容如下:

不使用 auto_log/autolog.h
註釋行 // #include "auto_log/autolog.h"
註釋方法 PPOCR::benchmark_log 的內容。

// #include "auto_log/autolog.h"
...
void PPOCR::benchmark_log(int img_num) {
  // if (this->time_info_det[0] + this->time_info_det[1] +
  // this->time_info_det[2] >
  //     0) {
  //   AutoLogger autolog_det("ocr_det", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                          FLAGS_enable_mkldnn, FLAGS_cpu_threads, 1,
  //                          "dynamic", FLAGS_precision, this->time_info_det,
  //                          img_num);
  //   autolog_det.report();
  // }
  // if (this->time_info_rec[0] + this->time_info_rec[1] +
  // this->time_info_rec[2] >
  //     0) {
  //   AutoLogger autolog_rec("ocr_rec", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                          FLAGS_enable_mkldnn, FLAGS_cpu_threads,
  //                          FLAGS_rec_batch_num, "dynamic", FLAGS_precision,
  //                          this->time_info_rec, img_num);
  //   autolog_rec.report();
  // }
  // if (this->time_info_cls[0] + this->time_info_cls[1] +
  // this->time_info_cls[2] >
  //     0) {
  //   AutoLogger autolog_cls("ocr_cls", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                          FLAGS_enable_mkldnn, FLAGS_cpu_threads,
  //                          FLAGS_cls_batch_num, "dynamic", FLAGS_precision,
  //                          this->time_info_cls, img_num);
  //   autolog_cls.report();
  // }
}
...
  • 修改include/paddlestructure.cpp文件,內容如下:

不使用 auto_log/autolog.h
註釋行 // #include "auto_log/autolog.h"
註釋方法 PPOCR::benchmark_log 的內容。

// #include "auto_log/autolog.h"
...
void PaddleStructure::benchmark_log(int img_num) {
  // if (this->time_info_det[0] + this->time_info_det[1] +
  // this->time_info_det[2] >
  //     0) {
  //   AutoLogger autolog_det("ocr_det", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                          FLAGS_enable_mkldnn, FLAGS_cpu_threads, 1,
  //                          "dynamic", FLAGS_precision, this->time_info_det,
  //                          img_num);
  //   autolog_det.report();
  // }
  // if (this->time_info_rec[0] + this->time_info_rec[1] +
  // this->time_info_rec[2] >
  //     0) {
  //   AutoLogger autolog_rec("ocr_rec", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                          FLAGS_enable_mkldnn, FLAGS_cpu_threads,
  //                          FLAGS_rec_batch_num, "dynamic", FLAGS_precision,
  //                          this->time_info_rec, img_num);
  //   autolog_rec.report();
  // }
  // if (this->time_info_cls[0] + this->time_info_cls[1] +
  // this->time_info_cls[2] >
  //     0) {
  //   AutoLogger autolog_cls("ocr_cls", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                          FLAGS_enable_mkldnn, FLAGS_cpu_threads,
  //                          FLAGS_cls_batch_num, "dynamic", FLAGS_precision,
  //                          this->time_info_cls, img_num);
  //   autolog_cls.report();
  // }
  // if (this->time_info_table[0] + this->time_info_table[1] +
  //         this->time_info_table[2] >
  //     0) {
  //   AutoLogger autolog_table("table", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                            FLAGS_enable_mkldnn, FLAGS_cpu_threads,
  //                            FLAGS_cls_batch_num, "dynamic", FLAGS_precision,
  //                            this->time_info_table, img_num);
  //   autolog_table.report();
  // }
  // if (this->time_info_layout[0] + this->time_info_layout[1] +
  //         this->time_info_layout[2] >
  //     0) {
  //   AutoLogger autolog_layout("layout", FLAGS_use_gpu, FLAGS_use_tensorrt,
  //                             FLAGS_enable_mkldnn, FLAGS_cpu_threads,
  //                             FLAGS_cls_batch_num, "dynamic",
  //                             FLAGS_precision, this->time_info_layout,
  //                             img_num);
  //   autolog_layout.report();
  // }
}
...
  • 修改include/utility.cpp文件,內容如下:

不使用 dirent.h
註釋行 // #include <dirent.h>
註釋方法 Utility::GetAllFiles 的內容。

// #include <dirent.h>
...
// list all files under a directory
void Utility::GetAllFiles(const char *dir_name,
                          std::vector<std::string> &all_inputs) {
  // if (NULL == dir_name) {
  //   std::cout << " dir_name is null ! " << std::endl;
  //   return;
  // }
  // struct stat s;
  // stat(dir_name, &s);
  // if (!S_ISDIR(s.st_mode)) {
  //   std::cout << "dir_name is not a valid directory !" << std::endl;
  //   all_inputs.push_back(dir_name);
  //   return;
  // } else {
  //   struct dirent *filename; // return value for readdir()
  //   DIR *dir;                // return value for opendir()
  //   dir = opendir(dir_name);
  //   if (NULL == dir) {
  //     std::cout << "Can not open dir " << dir_name << std::endl;
  //     return;
  //   }
  //   std::cout << "Successfully opened the dir !" << std::endl;
  //   while ((filename = readdir(dir)) != NULL) {
  //     if (strcmp(filename->d_name, ".") == 0 ||
  //         strcmp(filename->d_name, "..") == 0)
  //       continue;
  //     // img_dir + std::string("/") + all_inputs[0];
  //     all_inputs.push_back(dir_name + std::string("/") +
  //                          std::string(filename->d_name));
  //   }
  // }
}
...

編寫調用程序

  • 在項目中創建文件:paddle_util.h
    內容如下:
#pragma once
using namespace std;

#include <include/paddleocr.h>

class PaddleUtil {
public:
  PaddleUtil();

public:
  static PaddleUtil &get();
  static void init();
  void rec_image(const string &imageFile);

private:
  PaddleOCR::PPOCR ocr;
};
  • 在項目中創建文件:paddle_util.cpp
    內容如下:
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <vector>

#include "paddle_util.h"
#include <include/args.h>

using namespace PaddleOCR;

PaddleUtil::PaddleUtil() {}

PaddleUtil &PaddleUtil::get() {
  static PaddleUtil self;
  return self;
}

void PaddleUtil::init() {
  FLAGS_det = true;
  FLAGS_rec = true;
  FLAGS_cls = false;
  FLAGS_use_angle_cls = false;
  FLAGS_det_model_dir = "model/whl/det/en/en_PP-OCRv3_det_infer";
  FLAGS_rec_model_dir = "model/whl/rec/en/en_PP-OCRv4_rec_infer";
  FLAGS_rec_char_dict_path = "model/en_dict.txt";
}

void PaddleUtil::rec_image(const string &imageFile) {
  if (FLAGS_benchmark) {
    ocr.reset_timer();
  }

  cv::Mat img = cv::imread(imageFile, cv::IMREAD_COLOR);
  if (!img.data) {
    std::cerr << "[ERROR] image read failed! image path: " << imageFile
              << std::endl;
    return;
  }

  std::vector<OCRPredictResult> ocr_result =
      ocr.ocr(img, FLAGS_det, FLAGS_rec, FLAGS_cls);

  Utility::print_result(ocr_result);
  if (FLAGS_visualize && FLAGS_det) {
    std::string file_name = Utility::basename(imageFile);
    Utility::VisualizeBboxes(img, ocr_result, FLAGS_output + "/" + file_name);
  }
}
  • 在項目中創建主程序文件:my-paddleocr.cpp
    內容如下:
#include <iostream>

#include "paddle_util.h"

int main() {

  PaddleUtil::init();
  PaddleUtil::get().rec_image("model/254.jpg");
  std::cout << "Done!\n";
}

編譯運行

  • 在 Visual Studio 中使用 Release 版本編譯。
  • 運行 copy_paddle_dll.bat,複製 paddle_inference.dll 到輸出目錄。
@REM copy dll to Release
copy /Y %PADDLE_ROOT%\paddle\lib\paddle_inference.dll .\x64\Release\
copy /Y %PADDLE_ROOT%\third_party\install\mkldnn\lib\mkldnn.dll .\x64\Release\
copy /Y %PADDLE_ROOT%\third_party\install\mklml\lib\mklml.dll .\x64\Release\
copy /Y %PADDLE_ROOT%\third_party\install\mklml\lib\libiomp5md.dll .\x64\Release\
copy /Y %PADDLE_ROOT%\third_party\install\paddle2onnx\lib\paddle2onnx.dll .\x64\Release\
copy /Y %PADDLE_ROOT%\third_party\install\onnxruntime\lib\onnxruntime.dll .\x64\Release\
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章