EasyPR 编译使用说明 (Debian)
EasyPR 是一个车牌识别库,识别率高,功能齐全,非常适合直接拿来使用。 官方地址:
https://github.com/liuruoze/EasyPR
这里记录一下 EasyPR 在 Linux 底下编译及再次封装供 Python 使用的过程。
一、准备工作
安装相应的编译工具包
1
| 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 上签出所有源码。
1
2
3
4
5
6
7
8
9
| # 下载
git clone https://github.com/opencv/opencv.git
cd opencv
# 查看 tag 列表
git tag
# 签出特定版本。
git checkout 3.4.8
|
2.2 编译
这里假设把源码放在了 ~/src/opencv 目录下,路径按自己实际路径。
编译方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # 进入 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 目录中。
1
2
| # 下载
git clone https://github.com/liuruoze/EasyPR.git
|
3.2 修改配置
我们需要的是动态库,因此需要对源码进行部分修改。
- 文件
include/easypr/config.h ,把 OpenCV 版本宏改成 3.2 以上。
1
2
3
| #define CV_VERSION_THREE_ZERO
==>
#define CV_VERSION_THREE_TWO
|
- 文件
CMakeLists.txt ,easypr 模块改成动态编译,同时修改 OpenCV 查找路径。
1
2
3
| add_library(easypr STATIC ${SOURCE_FILES})
==>
add_library(easypr SHARED ${SOURCE_FILES})
|
1
2
| # 添加下列代码,指定 opencv 的库路径
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "~/.local/opencv-3.4.8")
|
- 文件
thirdparty/CMakeLists.txt ,thirdparty 模块改成动态编译。
1
2
3
| add_library(thirdparty STATIC ${SOURCE_FILES})
==>
add_library(thirdparty SHARED ${SOURCE_FILES})
|
3.3 编译
- 编译
直接运行 build.sh
此时会生成 build 目录,里面的 build/libeasypr.so 和 build/thirdparty/libthirdparty.so 就是我们需要的最终文件。
四、python 封装
- 新建开发目录,如 ~/src/libpr。
1
2
| mkdir ~/src/libpr
cd ~/src/libpr
|
- 把前面生成的库放到一起,方便打包调用。
1
2
3
4
5
6
7
8
| # 把 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
|
- 编写自己的 c++ 程序(示例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| #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;
}
|
- 编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 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 库文件
- 编写自己的 python 程序。(示例)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| 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"))
|
- 运行程序
特别注意 : EasyPR 需要依赖它目录下的 model 定义,需要把 model 目录整个拷贝过来。
1
2
3
| # 准备工作
cp -r ~/src/EasyPR/model .
export LD_LIBRARY_PATH=`pwd`/libeasypr:`pwd`/libopencv-3.4.8:$LD_LIBRARY_PATH
|