相信每一个使用过树莓派进行图像检测的道友在初学时都有过这样的烦心事:安装opencv-python的预编译包有时候代码的实时效果不好,如果结合到外部的一些控制动作时python的语法不是很好写代码。熟悉嵌入式C编程之后还是更喜欢C或者C++来写控制相关的代码,那为什么不可以结合C++的优势,使用CMake进行Opencv的C++的跨平台程序开发呢?
关于在电脑端使用cmake开发的话可以参考另一篇博客 VS2019+CMake
使用C++开发相关程序,就目前我的使用经验上来说,代码的运行速度没有太明显的感受,但是相较于python有以下几个优点:
1、多线程和多进程开发更叫高效;
2、对数据的处理更加精细化,更便于考虑相关的控制部分代码书写
3、对多个程序源文件的处理和相互引用更方便
4、可以使用CMake跨平台开发
当然缺点也很明显:
1、程序在语法上的要求更高,容易出错
2、外部包管理不是很方便,某些数据处理的第三方库没有python丰富
所以我们要仁者见仁,智者见智,首先根据自己的设计需求考虑使用什么方法实现,我相信一个道理“黑猫白猫,能抓到老鼠的就是好猫”,只要能更好的解决需求和问题,那当然是越便利的开发方式越好 。*
首先我们需要在目标平台安装opencv,这里需要注意的是需要通过源码编译进行安装。而非使用python的预编译包。
距离的安装方式jetson nano平台可以参考我的另一篇博客:jetson nano源码编译安装opencv
树莓派的话网上有太多源码安装opencv的教程了,就不重复说。
编译好后我们要确保设备上安装了CMake,这之后我们就开始新建自己的工程模板。
首先创建目录结构如下所示,main.cpp
文件用来写我们的主函数,\src
目录用来存放我们自己的头文件和库程序。
├── build
├── CMakeLists.txt
├── main.cpp
└── src
├── CMakeLists.txt
├── Test1.cpp
└── Test1.h
在此之外,还可以增加readme文件和存放相关说明的doc文件夹,进行快速程序启动的.sh文件。在这里这些不是必须的,有需要自己加上就可以。
这个文件定义了改工程的主要内容,包括要引入那些外部库,定义工程名,编译搜索路径等。
内容如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
PROJECT( main )
#INCLUDE_DIRECTORIES(/usr/local/include/opencv4/opencv2) #添加头文件搜索路径
FIND_PACKAGE(OpenCV REQUIRED)
MESSAGE(STATUS "OpenCV library status:")
MESSAGE(STATUS " version: ${OpenCV_VERSION}")
MESSAGE(STATUS " libraries: ${OpenCV_LIBS}")
MESSAGE(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
ADD_SUBDIRECTORY( src bin )
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(main ${DIR_SRCS})
TARGET_LINK_LIBRARIES(main Test)
TARGET_LINK_LIBRARIES(main ${OpenCV_LIBS})
CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
:指定编译时CMake的最低版本要求,低于这个版本时报错
PROJECT( main )
:定义工程的项目名为“main”
FIND_PACKAGE(OpenCV REQUIRED)
:查找opencv的包安装路径,下面的四个MESSAGE分别打印出opencv的相关信息:版本、安装的库、安装路径
ADD_SUBDIRECTORY( src )
:将src路径添加到文件查找路径中,这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置为bin路径
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
:将当前目录下的main.cpp赋值给变量DIR_SRCS
ADD_EXECUTABLE(main ${DIR_SRCS})
:将DIR_SRCS变量代表的文件编译生成名为’main’的可执行文件,注意这里的’main’和上面的项目工程名无关
TARGET_LINK_LIBRARIES(main Test)
:将Test库连接到’main’可执行文件
TARGET_LINK_LIBRARIES(main ${OpenCV_LIBS})
:将OpenCV_LIBS中的库连接到mian可执行文件
首先我们引用opencv的库和我们自己要写的头文件:
#include "src/Test1.h"
#include
我们可以在主函数中尝试读一副图片:
cv::Mat image = cv::imread("/home/CV2/opencvlogo.jpg"); //j图片路径和图片名修改为自己的
if(image.empty())
{
std::cout << "conld not load image..." << std::endl ;
return -1;
}
cv::imshow("test_opencv_setup", image);
同时我们在自己写的库中定义了一个类A,A定义了一个方法f。我们在主函数中引用它,所以完整的main.cpp文件如下:
#include "src/Test1.h"
#include
int main()
{
A a;
a.f();
std::cout << "Hello World" << std::endl;
cv::Mat image = cv::imread("/home/wuito/CVProjece/CV2/opencvlogo.jpg");
if(image.empty())
{
std::cout << "conld not load image..." << std::endl ;
return -1;
}
cv::imshow("test_opencv_setup", image);
cv::waitKey(0);
return 0;
}
在src文件夹下的CMakeLists.txt主要作用是说明该文件夹下的那些程序要被编译成库,供主程序调用。
AUX_SOURCE_DIRECTORY(. DIR_TEST_SRC)
ADD_LIBRARY(Test STATIC ${DIR_TEST_SRC})
因为src文件夹下的文件内容少,我们就只用了两行定义
AUX_SOURCE_DIRECTORY(. DIR_TEST_SRC)
:将该目录下的.cpp和.h文件添加到变量DIR_TEST_SRC中
ADD_LIBRARY(Test STATIC ${DIR_TEST_SRC})
:这一句指令将DIR_TEST_SRC指向的文件编译为一个名为Test的静态库文件。
首先是定义头文件Test1.h,声明了方法A,以及其私有的函数f()和变量i;
#ifndef TEST1_H_
#define TEST1_H_
#include
class A
{
public:
int i;
void f();
};
#endif
然后就是我们对应的Test1.cpp文件,定义了函数f。
#include"Test1.h"
void A::f()
{
i = 10;
std::cout << i << std::endl;
}
然后我们在创建的build
问价加下打开终端,输入CMake ..
,用于初始化工程并创建makefile。然后在执行make进行编译和链接。最后在输出文件夹bin下找到可执行文件main,执行./main
就可以运行刚才的程序啦。