Linux下编译并打包MNN项目迁移至其他设备
1. 构建项目结构
该项目是利用MNN框架对MTCNN网络进行推理,实现对目标的实时检测
运行环境:Linux
相关库:opencv,MNN
先给出项目的总体结构,如下:
mtcnn_mnn/
├── include/
│ ├── opencv2/ # OpenCV 的头文件
│ ├── MNN/ # MNN 的头文件
│ └── mtcnn.h # 项目内部的头文件
├── lib/
│ ├── libopencv_core.so # OpenCV 的动态库
│ ├── libopencv_imgproc.so
│ ├── libopencv_highgui.so
│ ├── libopencv_videoio.so
│ ├── libopencv_imgcodecs.so
│ └── libMNN.so # MNN 的动态库
├── src/ # 项目源文件
│ ├── main.cpp
│ └── mtcnn.cpp
├── Makefile
└── detection # 生成的可执行文件
接下来我们将逐一进行构建
如果需要将项目迁移到其他 Linux 设备上运行,必须确保以下内容被正确迁移:
(1) 头文件
① OpenCV 的头文件:
路径:/home/opencv-4.10.0/build_shared/include/opencv4/opencv2/
需要复制整个 opencv2 文件夹到项目的 include/ 目录下:
cp -r /home/opencv-4.10.0/build_shared/include/opencv4/opencv2 ./include/
② MNN 的头文件:
路径:/home/MNN/build_shared/include/MNN/
需要复制整个 MNN 文件夹到项目的 include/ 目录下:
cp -r /home/MNN/build_shared/include/MNN ./include/
③ 项目内部头文件
确保项目的 include/ 目录中的头文件已经存在。
(2) 动态库文件
① OpenCV 的动态库:
路径:/home/opencv-4.10.0/build_shared/lib/
需要复制以下文件到项目的 lib/ 目录下:
mkdir -p ./lib
cp /home/opencv-4.10.0/build_shared/lib/libopencv_core.so* ./lib/
cp /home/opencv-4.10.0/build_shared/lib/libopencv_imgproc.so* ./lib/
cp /home/opencv-4.10.0/build_shared/lib/libopencv_highgui.so* ./lib/
cp /home/opencv-4.10.0/build_shared/lib/libopencv_videoio.so* ./lib/
cp /home/opencv-4.10.0/build_shared/lib/libopencv_imgcodecs.so* ./lib/
② MNN 的动态库:
路径:/home/MNN/build_shared/lib/
需要复制以下文件到项目的 lib/ 目录下:
cp /home/MNN/build_shared/lib/libMNN.so* ./lib/
2. 编写Makefile文件(动态链接库版本)
makefile 文件如下:
# 编译器
CXX = g++# 编译选项
CXXFLAGS = -Wall -I./include -O2# OpenCV 的头文件和库文件路径
OPENCV_INCLUDE_DIR = /home/mtcnn_mnn/include
OPENCV_LIB_DIR = /home/mtcnn_mnn/lib# MNN 的头文件和库文件路径
MNN_CFLAGS = -I/home/mtcnn_mnn/include
MNN_LIBS = -L/home/mtcnn_mnn/lib -lMNN # 目标可执行文件名
TARGET = detection# 源文件目录
SRCDIR = src# 头文件目录
INCDIR = include# 链接库路径
OPENCV_LIBS = -L$(OPENCV_LIB_DIR) -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs# 找到所有源文件
SOURCES := $(wildcard $(SRCDIR)/*.cpp)# 生成目标文件列表
OBJECTS := $(patsubst $(SRCDIR)/%.cpp, %.o, $(SOURCES))# 默认目标
all: $(TARGET)# 链接目标文件生成可执行文件
$(TARGET): $(OBJECTS)$(CXX) $(CXXFLAGS) $^ -o $@ $(OPENCV_LIBS) $(MNN_LIBS) -lpthread -ldl -lgomp -Wl,-rpath,'$$ORIGIN/lib' # 设置动态库的运行时搜索路径为当前目录下的 lib 文件夹# 规则:从源文件生成目标文件
%.o: $(SRCDIR)/%.cpp$(CXX) $(CXXFLAGS) \-I$(OPENCV_INCLUDE_DIR) \-I$(INCDIR) \$(MNN_CFLAGS) \-c $< -o $@# 清理生成的文件
clean:rm -f $(OBJECTS) $(TARGET).PHONY: all clean
关键语句说明:
① 使用动态库
- 配置 OpenCV 和 MNN 的动态库:
OPENCV_LIBS = -L$(OPENCV_LIB_DIR) -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs MNN_LIBS = -L$(MNN_LIB_DIR) -lMNN
-L
指定动态库的路径。-l
指定动态库的名称(去掉lib
前缀和.so
后缀)。
② 设置动态库的运行时路径
- 添加
-Wl,-rpath,'$$ORIGIN/lib'
选项,告诉运行时加载器在当前目录的lib/
文件夹中查找动态库。 $$ORIGIN
是一个特殊的变量,表示可执行文件所在的目录。
3. 编译工程
构建完所有项目所需的文件后,开始编译工程
make
4. 在新设备上运行
将整个项目文件夹拷贝到新设备。
确保新设备上有以下依赖项:
libstdc++.so
(通常预装在大多数 Linux 系统中)。
libpthread.so
、libdl.so
等系统库(通常也预装)。
在新设备上运行可执行文件:
./detection
常见运行问题如下:
① 权限问题
-bash: ./detection: Permission denied
首先查看 detection 文件的权限
ls -l detection
如果显示如下:
-rw-rw-r-- 1 root root 67256 4月 26 14:39 detection
说明缺少执行权限,因此需要增加执行权限,运行以下命令:
chmod +x detection
再次查看 detection 文件的权限,变更为:
-rwxrwxr-x 1 root root 67256 4月 26 14:39 detection
再次运行可执行文件:
./detection
② 动态库路径设置问题
如果出现类似以下错误
./detection: error while loading shared libraries: libopencv_imgcodecs.so.410: cannot open shared object file: No such file or directory
首先查看工程下的 lib 中是否有该文件
ls -l /home/mtcnn_mnn/lib/libopencv_imgcodecs.so.410
如果输出如下,说明文件存在,那就是运行时动态库路径未设置
-rw-rw-r-- 1 root root 2238336 4月 26 15:03 /home/mtcnn_mnn/lib/libopencv_imgcodecs.so.410
使用 LD_LIBRARY_PATH
环境变量
在运行程序之前,设置 LD_LIBRARY_PATH
环境变量,指向包含动态库的目录:
export LD_LIBRARY_PATH=export LD_LIBRARY_PATH=/home/mtcnn_mnn/lib:$LD_LIBRARY_PATH
如果你希望永久设置该变量,可以将其添加到 ~/.bashrc
或 ~/.bash_profile
文件中:
echo 'export LD_LIBRARY_PATH=export LD_LIBRARY_PATH=/home/mtcnn_mnn/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
③ 动态库缺失问题
如果出现类似以下错误
./detection: error while loading shared libraries: libdc1394.so.22: cannot open shared object file: No such file or directory
同样,首先查看工程下的 lib 中是否有该文件
ls -l /home/mtcnn_mnn/lib/libdc1394.so.22
如果输出如下,说明文件不存在
ls: cannot access '/home/mtcnn_mnn/lib/libdc1394.so.22': No such file or directory
运行以下命令,检查动态库是否正确加载:
ldd ./detection
如果出现如下情况:
libopencv_imgcodecs.so.410 => /home/mtcnn_mnn/lib/libopencv_imgcodecs.so.410 (0x00007f6287a00000)
libdc1394.so.22 => not found
libavcodec.so.58 => /lib/x86_64-linux-gnu/libavcodec.so.58 (0x00007f6286400000)
libavformat.so.58 => /lib/x86_64-linux-gnu/libavformat.so.58 (0x00007f6286000000)
libavutil.so.56 => /lib/x86_64-linux-gnu/libavutil.so.56 (0x00007f6285c00000)
libswscale.so.5 => /lib/x86_64-linux-gnu/libswscale.so.5 (0x00007f6288d62000)
libmvec.so.1 => /lib/x86_64-linux-gnu/libmvec.so.1 (0x00007f6288c65000)
/lib64/ld-linux-x86-64.so.2 (0x00007f628bbd0000)
libopenblas.so.0 => /lib/x86_64-linux-gnu/libopenblas.so.0 (0x00007f62837b0000)
libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007f6283400000)
libatlas.so.3 => /lib/x86_64-linux-gnu/libatlas.so.3 (0x00007f6283000000)
libjpeg.so.8 => /lib/x86_64-linux-gnu/libjpeg.so.8 (0x00007f628897f000)
libwebp.so.6 => not found
libpng16.so.16 => /lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f628acd8000)
说明存在未加载成功的动态库
libdc1394.so.22 => not found
libwebp.so.6 => not found
此时需要回到最初编译成功的机器上寻找在新设备上缺失的动态库
ls -l /usr/lib/x86_64-linux-gnu/libdc1394.so*
输出以下内容:
lrwxrwxrwx 1 root root 19 Sep 12 2019 /usr/lib/x86_64-linux-gnu/libdc1394.so -> libdc1394.so.22.2.1
lrwxrwxrwx 1 root root 19 Sep 12 2019 /usr/lib/x86_64-linux-gnu/libdc1394.so.22 -> libdc1394.so.22.2.1
-rw-r--r-- 1 root root 225328 Sep 12 2019 /usr/lib/x86_64-linux-gnu/libdc1394.so.22.2.1
缺失的是 libdc1394.so.22,该文件为一个符号链接,它指向 libdc1394.so.22.2.1,因此要同时复制这两个文件到工程的lib文件下
cp /usr/lib/x86_64-linux-gnu/libdc1394.so.22 ./lib/
cp /usr/lib/x86_64-linux-gnu/libdc1394.so.22.2.1 ./lib/
对于缺失的 libwebp.so.6 文件,重复以上操作即可。
最后将完整的工程文件拷贝到新机器上,再次检查动态库是否正确加载:
ldd ./detection
输出应显示所有动态库都已找到。
5. 注意事项
(1) 确保动态库版本一致
如果新设备上的系统库与旧设备不一致,可能会导致运行时错误。建议使用相同版本的 OpenCV 和 MNN 库。
(2) 权限问题
确保动态库文件具有正确的权限:
chmod +x ./lib/*.so