由于我所在的实验室主要是研究无人机智能巡检,因此大多数的项目涉及到图像识别。说到图像识别自然少不了著名的图像处理库OpenCV
。因此想趁着这个史无前例的长假期,好好学习一下OpenCV
。
首先说一下我的开发环境,操作系统Manjaro Linux,开发语言C++,IDE CLion,编译器gcc 9.2.0,OpenCV版本4.2,cmake。至于为什么使用Manjaro Linux而不是用大家经常使用Ubuntu,Ubuntu的软件包管理工具apt中的大多数软件的版本都过低, 想要使用高版本的软件,必须自己下载源代码手动编译。记得有一次要编译一个比较新的版本的库,这个库必须要求高版本的gcc才能编译,害得我不得不编译更高版本的gcc,因此为了避免少折腾,我使用了Manjaro。
关于在C++中使用OpenCV提示一点,我们在CMakeLists.txt中链接库和包含OpenCV头文件的时候,使用下面的语法:
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(xxx ${OpenCV_LIBS})
既然是学习图像处理,那就自然少不了图像,这里我选择的图像是我的女神佟丽娅女士在春晚上的一张图片,真是太美了。
那么接下来就是学习了。
读取图片并显示:
OpenCV提供了imread方法读取图片,这个方法位于imgcodecs.hpp头文件中,提供了imshow函数用于显示图片,这个方法位于highgui头文件中。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly.jpg");
cv::imshow("yaya", image);
cv::waitKey(0);
imread方法接收两个参数,第二个参数默认值为IMREADCOLOR,这个参数表示读取彩色图像,IMREAD_GRAYSCALE表示读取灰度图像。
例如如果是CV_8UC3返回16,如果是CV_8UC1返回0。
读取视频:
OpenCV提供了VideoCapture类用于读取视频文件或者摄像头视频流。该类位于videoio.hpp头文件中。
cv::VideoCapture capture = cv::VideoCapture(0);
if (!capture.isOpened()) {
return -1;
}
char flag = 0;
while (flag != 'q') {
cv::Mat image = {};
capture.read(image);
cv::imshow("cmy", image);
flag = (char)cv::waitKey(30);
}
获取图像的一部分数据:
types.hpp 头文件中提供了Rect类用于指定要截取的部分,被截取的部分通常被成ROI,然后Mat类重载了函数运算符,其中有一个重载接收Rect类型,然后返回新的Mat对象,对应原图像的一部分。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly.jpg", cv::IMREAD_COLOR);
cv::Rect rect = cv::Rect(200, 200, 400, 400);
cv::Mat roi = image(rect);
cv::imshow("roi", roi);
cv::waitKey(0);
通道分离和合并:
core.hpp 头文件中提供了split和merge方法分别用于图像通道的分离和合并操作,在OpenCV中,InputArray通常代表传入的参数为Mat,InputArrayofArrays表示传入的参数的Mat的数组,通常用vector存储。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly.jpg", cv::IMREAD_COLOR);
std::vector channels;
cv::split(image, channels);
cv::imshow("roi", image);
cv::imshow("roi1", channels.at(0));
cv::imshow("roi2", channels.at(1));
cv::imshow("roi3", channels.at(2));
cv::Mat mergeImage;
cv::merge(channels, mergeImage);
cv::imshow("roi4", mergeImage);
cv::waitKey(0);
边界填充:
core.hpp 头文件中提供了copyMakeBorder方法,这个方法接收一个输入图像和一个输出图像,上下左右需要填充的像素个数,填充方式。
填充方式有下面五种:
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi.jpg", cv::IMREAD_COLOR);
cv::Mat img {};
cv::copyMakeBorder(image, img, 50, 50, 50, 50, CV_HAL_BORDER_CONSTANT);
cv::imshow("border", img);
cv::waitKey(0);
数值计算:
Mat类重载了各种数学运算符,比如加法,减法,乘法等。
加减法与数字运算表示图像的每个像素的蓝色分量于该数字进行运算。若想每个分量进行加减法,与cv::Scalar(b, g, r)进行加减,乘法于数字进行运算表示每个通道进行乘法运算,与Mat的加法和减法只能和自己大小相同的Mat进行运算。Mat的乘法表示矩阵的乘法,dot方法实现点乘,结果返回一个值。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi.jpg", cv::IMREAD_COLOR);
cv::Mat img = image + image;
cv::imshow("image_image", img);
cv::imwrite("/home/cenmmy/CLionProjects/opencv_learning/assets/double_tly.jpg", img);
cv::waitKey(0);
图像融合:
core.hpp 提供了addWeighted函数用于两个图像的合并,图像合并的前提是两个图像的大小必须相同,大小不相同的图像进行合并会报错。imgproc.hpp头文件提供了resize方法用于重新设置图像的大小。
cv::Mat image1 = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi.jpg", cv::IMREAD_COLOR);
cv::Mat image2 = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi1.jpg", cv::IMREAD_COLOR);
cv::Mat image3 {};
cv::resize(image2, image3, cv::Size(400, 400));
// gamma的值会加载融合之后的每个像素点上
cv::addWeighted(image1, 0.2, image3, 0.8, 0, image3);
cv::imshow("image_image", image3);
cv::waitKey(0);
addWeighted方法类似于R = aX + bY + c。