目录

EasyPR 编译使用说明 (Debian)

EasyPR 是一个车牌识别库,识别率高,功能齐全,非常适合直接拿来使用。 官方地址: https://github.com/liuruoze/EasyPR

这里记录一下 EasyPR 在 Linux 底下编译及再次封装供 Python 使用的过程。

一、准备工作

安装相应的编译工具包

sudo apt install build-essential cmake

二、编译 OpenCV

EasyPR 依赖于 OpenCV ,且其官方未提供 Linux 版本的 OpenCV 二进制库,因此需要自行编译。对于 EasyPR 项目,最新测试通过的 OpenCV 版本为 3.4.8, 下面以 OpenCV 3.4.8 版本为例。

2.1 下载

方法1. 从 OpenCV 官方下载页面找到 3.4.8 版本的 Source 包,并下载解压。

方法2. 从 github 上签出所有源码。

# 下载
git clone https://github.com/opencv/opencv.git
 
cd opencv
# 查看 tag 列表
git tag
 
# 签出特定版本。
git checkout 3.4.8

2.2 编译

这里假设把源码放在了 ~/src/opencv 目录下,路径按自己实际路径。

编译方式:

# 进入 opencv 源码的上层路径。
cd ~/src
 
# 在 opencv 源码的同级目录下创建一个 opencv-build-3.4.8 目录。 没有在 opencv 里面创建 build 目录,是方便后续 checkout 其他版本时,还保留当前编译好的版本。
mkdir opencv-build-3.4.8
cd opencv-build-3.4.8
 
# cmake
# 1. 由于是跟 opencv 同级,所以源码路径为 ../opencv 。
# 2. 安装目录设置为自己的 home 目录,方便随时删除,而且 make install 不需要 sudo 权限。
# 3. ippicv 下载较慢,使用本地共享中的文件。
# 4. 不编译 cuda 。
# 以上各个选项按自己喜好增加或删除。
cmake ../opencv \
      -D CMAKE_INSTALL_PREFIX=~/.local/opencv-3.4.8 \
      -D OPENCV_IPPICV_URL=file:///ext/data/shared/3rdparty/ippicv/ \
      -D WITH_CUDA=no
 
# 以 8 个线程进行编译
make -j8
 
# 安装。此时会把 opencv 安装到 ~/.local/opencv-3.4.8 目录下。
make install

此时 OpenCV 准备完毕。

三、编译 EasyPR (动态库)

3.1 下载

用 git 从 EasyPR 官方下载最新代码,这里把源码存放在 ~/src/EasyPR 目录中。

# 下载
git clone https://github.com/liuruoze/EasyPR.git

3.2 修改配置

我们需要的是动态库,因此需要对源码进行部分修改。

1. 文件 include/easypr/config.h ,把 OpenCV 版本宏改成 3.2 以上。

#define CV_VERSION_THREE_ZERO
==>
#define CV_VERSION_THREE_TWO

2. 文件 CMakeLists.txt ,easypr 模块改成动态编译,同时修改 OpenCV 查找路径。

add_library(easypr STATIC ${SOURCE_FILES})
==>
add_library(easypr SHARED ${SOURCE_FILES})
# 添加下列代码,指定 opencv 的库路径
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "~/.local/opencv-3.4.8")

3. 文件 thirdparty/CMakeLists.txt ,thirdparty 模块改成动态编译。

add_library(thirdparty STATIC ${SOURCE_FILES})
==>
add_library(thirdparty SHARED ${SOURCE_FILES})

3.3 编译

4. 编译

直接运行 build.sh

./build.sh

此时会生成 build 目录,里面的 build/libeasypr.sobuild/thirdparty/libthirdparty.so 就是我们需要的最终文件。

四、python 封装

1. 新建开发目录,如 ~/src/libpr。

mkdir ~/src/libpr
cd ~/src/libpr

2. 把前面生成的库放到一起,方便打包调用。

# 把 easypr 的库移动到 libeasypr 中。
mkdir ~/src/libpr/libeasypr
cd ~/src/libpr/libeasypr
cp ~/src/EasyPR/build/libeasypr.so .
cp ~/src/EasyPR/build/thirdparty/libthirdparty.so .
 
# 把 opencv 的库移动到 libopencv-3.4.4 中。
cp -r ~/.local/opencv-3.4.8/lib /opt/src/libpr/libopencv-3.4.8

3. 编写自己的 c++ 程序(示例)

test.cpp
#include <iostream>
 
#include "easypr.h"
 
using namespace easypr;
 
static std::vector<std::string> plate_recognize_self(const char* image,
                                                const char* model_svm,
                                                const char* model_ann,
                                                const bool life_mode = true) {
  cv::Mat img = cv::imread(image);
  assert(!img.empty());
 
  CPlateRecognize pr;
  pr.setResultShow(false);
  pr.setLifemode(true);
  pr.setMaxPlates(1);
  pr.setDetectType(PR_DETECT_CMSER | PR_DETECT_COLOR);
 
  std::vector<std::string> results;
  std::vector<CPlate> plates;
  pr.plateRecognize(img, plates, 0);
 
  for (auto plate : plates) {
    results.push_back(plate.getPlateStr());
 
    results.push_back(std::to_string(plate.getPlatePos().center.x));
    results.push_back(std::to_string(plate.getPlatePos().center.y));
    results.push_back(std::to_string(plate.getPlatePos().size.width));
    results.push_back(std::to_string(plate.getPlatePos().size.height));
  }
 
  if (plates.size() == 1) {
    if (1) {
      std::stringstream ss(std::stringstream::in | std::stringstream::out);
      ss << "result.jpg";
      imwrite(ss.str(), plates.at(0).getPlateMat());
    }
  }
 
  return std::move(results);
}
 
char* wrap_for_python_plate_recognize(const char* image,
                                      const char* model_svm,
                                      const char* model_ann,
                                      const int life_mode) {
    std::vector<std::string> results= plate_recognize_self(image, model_svm,
        model_ann, life_mode);
    std::string str = "nil";
    if(! results.empty()) {
        std::ostringstream oss;
        for (std::vector<std::string>::const_iterator it = results.begin();
            it != results.end(); it ++)
            oss << (*it) << "$";
        str = oss.str();
    }
    char *ret = new char[str.length() + 1];
    strcpy(ret, str.c_str());
    return ret;
}
 
 
int main() {
  const char* image = "./test.jpg";
  wrap_for_python_plate_recognize(image, "", "", 0)
  return 0;
}

4. 编译

g++ -Wall -std=c++11 -fPIC --shared -o libpr.so test.cpp \
-I ~/src/EasyPR/include \
-L ~/src/EasyPR/build \
-L ~/src/EasyPR/build/thirdparty \
-I ~/.local/opencv-3.4.8/include \
-L ~/.local/opencv-3.4.8/lib \
-leasypr \
-lthirdparty \
-lopencv_core \
-lopencv_objdetect \
-lopencv_ml \
-lopencv_imgcodecs \
-lopencv_highgui \
-Wno-unused-function \
-Wno-unused-variable

此时会生成 libpr.so 库文件

5. 编写自己的 python 程序。(示例)

test.py
import ctypes
import math
import os
 
try:
    libpr = ctypes.cdll.LoadLibrary('./libpr.so')
except Exception as e:
    print(str(e))
 
# 设定 libpr 的参数类型和返回值类型
libpr.wrap_for_python_plate_recognize.restype = ctypes.c_char_p
libpr.wrap_for_python_plate_recognize.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
 
func_plate_recognize = libpr.wrap_for_python_plate_recognize
 
if __name__ == '__main__':
    file_path = b"./test.jpg"
    result = func_plate_recognize(file_path, b'', b'', 0)
    print(result.decode("UTF-8"))

6. 运行程序

特别注意 : EasyPR 需要依赖它目录下的 model 定义,需要把 model 目录整个拷贝过来。

# 准备工作
cp -r ~/src/EasyPR/model .
export LD_LIBRARY_PATH=`pwd`/libeasypr:`pwd`/libopencv-3.4.8:$LD_LIBRARY_PATH
# 运行
python3 test.py