写在前面
本文主要使用g++进行编译,介绍相关指令。
编译so文件
需要预先编译安装OpenCV。
pch.h
#ifndef PCH_H
#define PCH_H
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/dnn.hpp>
using namespace std;
using namespace cv;
using namespace dnn;
// 使用C++时必须添加extern "C",可通过__cplusplus判断编译环境
#ifdef __cplusplus
extern "C"
{
#endif
const char* predict(const char* filepath);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
extern "C"
{
#endif
int add(int num1, int num2);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
extern "C"
{
#endif
string cv_predict(const char* filepath);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
extern "C"
{
#endif
const char* stringToCharP(std::string str);
#ifdef __cplusplus
}
#endif
#endif
pch.cpp
#include "pch.h"
String modelWeights = "model.pb";
Net model = readNetFromTensorflow(modelWeights);
clock_t start_time, end_time;
char letter[36] = {
'0','1','2','3','4','5','6','7','8','9','a','b',
'c','d','e','f','g','h','i','j','k','l','m','n',
'o','p','q','r','s','t','u','v','w','x','y','z'
};
int region[4][2] = { {0,16},{14,31},{30,46},{44,60} };
string cv_predict(const char* filepath) {
Mat img = imread(filepath);
Mat img_split, blob, out;
string result = "";
int max_location = 0;
float max_value = 0.0;
cvtColor(img, img, CV_BGR2GRAY);
for (int r = 0; r < 4; r++) {
max_location = 0;
max_value = 0.0;
img_split = img(Rect(region[r][0], 0, region[r][1] - region[r][0], 25));
resize(img_split, img_split, Size(15, 25));
blob = blobFromImage(img_split, 1.0, Size(15, 25), Scalar(0, 0, 0), true, false);
model.setInput(blob);
out = model.forward();
for (int i = 0; i < 36; i++) {
if (out.at<float>(0, i) > max_value) {
max_value = out.at<float>(0, i);
max_location = i;
}
}
result += letter[max_location];
}
return result;
}
// 将string转换为char*,直接返回c_str()会乱码
const char* stringToCharP(std::string str) {
char* result = new char[str.length() + 1];
strcpy(result, str.c_str());
return (const char*)result;
}
const char* predict(const char* filepath)
{
model.setPreferableBackend(DNN_BACKEND_OPENCV);
model.setPreferableTarget(DNN_TARGET_CPU);
string result = cv_predict(filepath);
const char* c_char_p_result = stringToCharP(result);
return c_char_p_result;
}
// 测试用的函数
int add(int num1,int num2)
{
return num1 + num2;
}
编译指令
必须和OpenCV的库文件关联起来。
g++ pch.cpp -L/usr/local/lib/ -lopencv_imgproc -lopencv_highgui -lopencv_dnn -lopencv_imgcodecs -fPIC -shared -o libmodel.so
另附一种cmake编译的方法:
CMakeLists.txt
# cmake needs this line
cmake_minimum_required(VERSION 3.1)
# Define project name
project(model)
# Find OpenCV, you may need to set OpenCV_DIR variable
# to the absolute path to the directory containing OpenCVConfig.cmake file
# via the command line or GUI
find_package(OpenCV REQUIRED)
# If the package has been found, several variables will
# be set, you can find the full list with descriptions
# in the OpenCVConfig.cmake file.
# Print some message showing some of them
message(STATUS "OpenCV library status:")
message(STATUS " config: ${OpenCV_DIR}")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
# Declare the executable target built from your sources
add_library(model SHARED pch.cpp pch.h)
# Link your application with OpenCV libraries
target_link_libraries(model PRIVATE ${OpenCV_LIBS})
cmake指令:
将CMakeLists.txt和pch.h、pch.cpp放到同一目录。
cmake .
make
编译完成后得到libmodel.so。
查看动态库依赖关系:
ldd -r libmodel.so
如果没有无法解析的符号(引用下VS2019的报错),说明编译成功。
调用库文件
pch.h
#include <iostream>
using namespace std;
extern "C"
{
const char* predict(const char* filepath);
}
extern "C"
{
int add(int num1, int num2);
}
test.cpp
#include <pch.h>
int main() {
int a = add(123,123);
printf("%d\n",a);
const char* b = predict("captcha.png");
printf("%s\n",b);
}
编译指令
g++ test.cpp -L. -lmodel -o test
编译完成后得到一个二进制文件,类似于Windows的exe,运行指令(记得放一张测试用的验证码,名字修改为captcha.png):
./test