感谢电子发烧友论坛和算能提供的Milk-V Duo 开发板试用机会。
Milk-V Duo是一款基于算能 CV1800B芯片的MPU,能够运行基于LINUX和RTOS的操作系统。算能 CV1800B使用的是平头哥的玄铁C906核,具有很强的处理能力,所以我们尝试交叉编译试用于Milk-V Duo的OpenCV图像处理库。平头哥的网站(https://xuantie.t-head.cn/community/download?id=4112956065753141248)提供了优化的定制版OpenCV,据说这个版本可以最大限度地发挥芯片的性能,让人很是期待。
1. 下载和编译OpenCV
交叉编译器采用的是算能官方提供的https://sophon-file.sophon.cn/sophon-prod-s3/drive/23/03/07/16/host-tools.tar.gz。这个包中包括了用于裸机的elf工具链和gnu、musl两个版本的工具链。我们使用cat /proc/version目录查看了官方镜像所使用编译器的版本,和官方提供的工具链一致,而且是musl版本的。
在主机上建立一个env.sh文件,输入如下内容:
HOST_TOOL_PATH=/home/xxx/milk-v/CV180x/host-tools
export PATH="$HOST_TOOL_PATH/gcc/riscv64-linux-x86_64/bin:$HOST_TOOL_PATH/gcc/riscv64-linux-musl-x86_64/bin:$HOST_TOOL_PATH/gcc/riscv64-elf-x86_64/bin:$PATH"
然后在主机的命令行执行source env.sh以设置环境变量。
修改OpenCV/platforms/linux/riscv64-gcc.toolchain.cmake中的编译器设置:
set(CMAKE_C_COMPILER
riscv64-unknown-linux-musl-gcc)
set(CMAKE_CXX_COMPILER
riscv64-unknown-linux-musl-g++)
使用如下的命令生成OpenCV的Makefile:
cmake ..
-DCMAKE_TOOLCHAIN_FILE="../platforms/linux/riscv64-gcc.toolchain.cmake"
-DWITH_OPENCL=OFF -DBUILD_opencv_calib3d=ON -DBUILD_ZLIB=ON -DBUILD_PNG=ON
-DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_WITH_INSTALL_RPATH=1
-DCMAKE_INSTALL_PREFIX=../install -DBUILD_CSI_CV=ON -DWITH_OPENMP=OFF
-DWITH_PTHREADS_PF=OFF -DCORE=C906FDV
命令行中只有-DCORE=C906FDV是C906特有的内容,其他的部分和OpenCV在其他平台上编译差不多。这里定义的是-DBUILD_SHARED_LIBS=OFF,所以生成的是静态库,这样部署的时候比较方便。如果希望得到动态库版本,可以改为-DBUILD_SHARED_LIBS=ON,也可以从https://github.com/yue-xiaomin/Milkv-duo_Cross_Compile_software/tree/main/OpenCV下载编译好的动态库。
CMake成功之后使用make命令编译就可以生成库文件,再执行make install就可以将所需要的库文件、头文件都拷贝到OpenCV/install目录中。
2. 修改开发板的解释器链接
虽然我们使用的编译器和厂商生成镜像使用的编译器完全相同,但是当我们编译个最简单的Hello World程序到板子上运行时都无法运行,会出现如下错误:
[root@milkv]~# ./hello
-sh: ./hello: not found
参考了Milk-V论坛的帖子(https://community.milkv.io/t/opencv-4-5-4/82),发现编译器生成的程序中INTERP段中指定的程序解析器(/lib/ld-musl-riscv64xthead.so.1)在开发板上不存在,所以程序无法运行。
解决的办法是在Milk-Duo环境下建立一个软连接:
ln -s /lib/ld-musl-riscv64v_xthead.so.1 /lib/ld-musl-riscv64xthead.so.1
这样编译后的程序都可以运行了。
有趣的是OpenCV自带的opencv_version在开发板上是可以正常运行的,使用readelf -l 查看,可执行文件中不包含INTERP段,不知道它是采用什么编译选项实现的。
3. 编写测试程序
我们对OpenCV自带的example_cmake进行了一些小修改,测试OpenCV的运行。该测试程序在/home/test/OpenCV/samples/cpp/example_cmake/目录下。
先修改其CMakeFiles.txt文件如下:
cmake_minimum_required(VERSION 3.1)
# Define project name
project(opencv_example_project)
# 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
set (OpenCV_DIR /home/test/OpenCV/install/lib/cmake/opencv4/)
find_package(OpenCV REQUIRED)
set(CMAKE_CXX_FLAGS "-latomic")
# 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_executable(opencv_example example.cpp)
# Link your application with OpenCV libraries
target_link_libraries(opencv_example PRIVATE ${OpenCV_LIBS})
然后修改example.cpp文件如下:
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/videoio.hpp"
#include <iostream>
using namespace cv;
using namespace std;
void drawText(Mat & image);
int main()
{
cout << "Built with OpenCV " << CV_VERSION << endl;
Mat image;
image = imread("test.jpg");
drawText(image);
imwrite("test2.png",image);
return 0;
}
void drawText(Mat & image)
{
putText(image, "Hello OpenCV", Point(20, 50),
FONT_HERSHEY_COMPLEX, 1, // font face and scal
Scalar(255, 255, 255), // white
1, LINE_AA); // line thickness and type
}
然后执行如下命令行:
cmake -DCMAKE_TOOLCHAIN_FILE="~/OpenCV/platforms/linux/riscv64-gcc.toolchain.cmake" .
make
这个测试程序读入test.jpg文件,在图上加上“Hello OpenCV”字符串,然后写入test2.png文件。
原始图片和加上文字后的图片如下:
在开发板成功运行OpenCV测试程序后,我们就可以尝试编写更复杂的程序了。
本文转载自:https://bbs.elecfans.com/jishu_2366639_1_1.html,作者:zealsoft