0 配置环境
配置环境网上很多资料,这里就不赘述了。
笔者使用的是VS2022+opencv4.9.0
测试配置环境
// 打开摄像头样例
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/core/core.hpp>
using namespace cv;int main()
{VideoCapture cap(0);Mat frame;while (1){cap >> frame;imshow("调用摄像头", frame);waitKey(30);}return 0;
}
如果配置正确,会调用电脑摄像头
1 读图片和视频
读图片
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <string>images ///
int main()
{std::string path = "Resources/test.png"; // 图片路径cv::Mat img = cv::imread(path); // 读数据cv::imshow("Image", img); // 显示数据cv::waitKey(0); // 增加延时,0表示无穷return 0;
}
读视频
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <string>video ///
// 视频是一系列的图像,因此需要遍历所有的图像/帧,捕获并且显示
int main()
{std::string path = "Resources/test_video.mp4"; // 视频路径cv::VideoCapture capture(path);cv::Mat img;while (1){capture.read(img); // 读到img中cv::imshow("Vedio", img); // 显示cv::waitKey(20); // 延时20ms,数值越大,视频越慢}return 0;
}
读相机
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <string>Webcam ///
// 视频是一系列的图像,因此需要遍历所有的图像/帧,捕获并且显示
int main()
{cv::VideoCapture capture(0); // 只有一个相机cv::Mat img;while (1){capture.read(img); // 读到img中cv::imshow("Vedio", img); // 显示cv::waitKey(1); // 延时1ms,数值越大,视频越慢}return 0;
}
2 基本函数
cvtColor
// cvt是convert的缩写,将图像从一种颜色空间转换为另一种颜色空间
// 把img转换成imgGray
cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);
img
:输入图像,这是一个BGR格式的彩色图像。
imgGray
:输出图像,转换后的灰度图像。
cv::COLOR_BGR2GRAY
:转换代码,表示将BGR格式的图像转换为灰度图像。
GaussianBlur
高斯模糊处理
// 使用高斯滤波器模糊图像。该函数将源图像与指定的高斯核进行卷积,Size(7,7)是核大小,数字越大越模糊
// 把img模糊成imgBlur
cv::GaussianBlur(img, imgBlur, cv::Size(7, 7), 5, 0);
cv::GaussianBlur
是OpenCV中的一个函数,用于对图像进行高斯模糊处理。高斯模糊是一种图像处理技术,通过使用高斯核对图像进行卷积,可以有效地减少图像的噪声和细节,使图像变得模糊。参数解释:
img
:输入图像,这是一个cv::Mat
类型的对象,表示要进行高斯模糊处理的图像。
imgBlur
:输出图像,经过高斯模糊处理后的图像,也是一个cv::Mat
类型的对象。
cv::Size(7, 7)
:高斯核的大小,表示高斯核的宽度和高度。这里使用7×7的高斯核。高斯核的大小必须是正奇数,以确保核的中心能够对准图像的每个像素。
5
:高斯核的标准差(σ)在X方向上的值。标准差越大,模糊效果越明显。如果设置为0,OpenCV会根据高斯核的大小自动计算标准差。
0
:高斯核的标准差(σ)在Y方向上的值。如果设置为0,OpenCV会使用与X方向相同的标准差。
Canny
边缘检测
// 边缘检测,阈值1,2可调,目的:显示更多的边缘,使用模糊的图像
cv::Canny(imgBlur, imgCanny, 25, 75);
cv::Canny
是OpenCV中的一个函数,用于执行Canny边缘检测算法。Canny边缘检测是一种多步骤的算法,包括高斯模糊、梯度计算、非极大值抑制和双阈值检测等步骤。参数解释:
imgBlur
:输入图像,这是一个经过高斯模糊处理的cv::Mat
类型的对象。高斯模糊处理可以减少图像的噪声,使边缘检测更加准确。
imgCanny
:输出图像,经过Canny边缘检测后的图像,也是一个cv::Mat
类型的对象。输出图像是一个二值图像,边缘部分的像素值为255,非边缘部分的像素值为0。
25
:低阈值(threshold1
),用于Canny边缘检测中的双阈值检测。低阈值用于确定弱边缘。
75
:高阈值(threshold2
),用于Canny边缘检测中的双阈值检测。高阈值用于确定强边缘。
dilate和erode
膨胀和侵蚀
// 扩大和侵蚀图像
// 创建一个核,增加Size(只能是奇数)会扩张/侵蚀更多
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::dilate(imgCanny, imgDila, kernel); // 扩张边缘,增加边缘厚度
cv::erode(imgDila, imgErode, kernel); // 侵蚀边缘,减小边缘厚度
cv::getStructuringElement
:
cv::getStructuringElement
是OpenCV中的一个函数,用于创建形态学操作的结构元素(也称为核或掩模)。参数解释:
cv::MORPH_RECT
:指定结构元素的形状为矩形。
cv::Size(3, 3)
:指定结构元素的大小为3×3。这意味着结构元素是一个3×3的矩形,中心像素为(1, 1)。
cv::dilate
:
cv::dilate
是OpenCV中的一个函数,用于对图像进行膨胀操作。膨胀操作可以扩大图像中的白色区域,通常用于连接断开的部分或填补小孔。参数解释:
imgCanny
:输入图像,这是一个经过Canny边缘检测的二值图像。
imgDila
:输出图像,经过膨胀操作后的图像。
kernel
:结构元素,用于定义膨胀操作的邻域。
cv::erode
:
cv::erode
是OpenCV中的一个函数,用于对图像进行腐蚀操作。腐蚀操作可以缩小图像中的白色区域,通常用于去除小的噪点或分离粘连的部分。参数解释:
imgDila
:输入图像,这是一个经过膨胀操作的图像。
imgErode
:输出图像,经过腐蚀操作后的图像。
kernel
:结构元素,用于定义腐蚀操作的邻域。
总体代码
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>basic functions ///
int main()
{std::string path = "Resources/test.png"; // 图片路径cv::Mat img = cv::imread(path); // 读数据cv::Mat imgGray, imgBlur, imgCanny, imgDila, imgErode;// cvt是convert的缩写,将图像从一种颜色空间转换为另一种颜色空间// 把img转换成imgGraycv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);// 使用高斯滤波器模糊图像。该函数将源图像与指定的高斯核进行卷积,Size(7,7)是核大小,数字越大越模糊// 把img模糊成imgBlurcv::GaussianBlur(img, imgBlur, cv::Size(7, 7), 5, 0);// 边缘检测,阈值1,2可调,目的:显示更多的边缘,使用模糊的图像cv::Canny(imgBlur, imgCanny, 25, 75);// 扩大和侵蚀图像// 创建一个核,增加Size(只能是奇数)会扩张/侵蚀更多cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::dilate(imgCanny, imgDila, kernel); // 扩张边缘,增加边缘厚度cv::erode(imgDila, imgErode, kernel); // 侵蚀边缘,减小边缘厚度cv::imshow("Image", img); // 显示数据cv::imshow("Image Gray", imgGray); // 显示灰色数据cv::imshow("Image Blur", imgBlur); // 显示模糊数据cv::imshow("Image Canny", imgCanny); // 显示边缘检测数据cv::imshow("Image imgDila", imgDila); // 显示扩张边缘数据cv::imshow("Image imgErode", imgErode); // 显示侵蚀检测数据cv::waitKey(0); // 增加延时,0表示无穷return 0;
}
测试
3 resize and crop
调整图片和裁剪图片
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>using namespace std;
using namespace cv;resize and crop ///int main(int argc, char** argv)
{string path = "Resources/test.png"; // 图片路径cv::Mat img = cv::imread(path); // 读图片cv::Mat imgResize, imgCrop; // 调整图片和裁剪图片// [768 x 559]//std::cout << img.size() << std::endl;// 调整大小//cv::resize(img, imgResize, Size(), 0.5, 0.5); // 长宽各自缩小0.5cv::resize(img, imgResize, Size(680, 400)); // 缩小到680*400// 裁剪// 从200*100处开始裁剪,各自裁剪300cv::Rect rect(200, 100, 300, 300);imgCrop = img(rect);// 显示cv::imshow("Image", img);cv::imshow("Image Resize", imgResize);cv::imshow("Image Crop", imgCrop);cv::waitKey(0); // 增加延时,0表示无穷return 0;
}
测试
4 Draw shapes and Text
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>using namespace std;
using namespace cv;Draw shapes and Text ///int main(int argc, char** argv)
{// blank image// 8bit3通道,白色cv::Mat img(512, 512, CV_8UC3, cv::Scalar(255, 255, 255));// 基于上图画一个圆,圆心在(256,256)处,半径是155,颜色是0, 69, 255,厚度是10// cv::circle(img, cv::Point(256, 256), 155, cv::Scalar(0, 69, 255), 10);cv::circle(img, cv::Point(256, 256), 155, cv::Scalar(0, 69, 255), FILLED); // 填充// 画一个矩形// 左上角是(130, 226),右下角是(382, 286)// 颜色是cv::Scalar(255, 255, 255),厚度是3//cv::rectangle(img, cv::Point(130, 226), cv::Point(382, 286), cv::Scalar(255, 255, 255), 3);cv::rectangle(img, cv::Point(130, 226), cv::Point(382, 286), cv::Scalar(255, 255, 255), FILLED);// 画一条线cv::line(img, Point(130, 296), cv::Point(382, 296), cv::Scalar(255, 255, 255), 2);//cv::line(img, Point(130, 296), cv::Point(382, 450), cv::Scalar(255, 255, 255), 2);// 本文// FONT_HERSHEY_DUPLEX字体,0.75是比例cv:putText(img, "StudyWinter", cv::Point(160, 262), cv::FONT_HERSHEY_DUPLEX, 0.75, cv::Scalar(0, 69, 255));cv::imshow("Image", img);cv::waitKey(0);return 0;
}
测试
5 wrap images
图像扭曲
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>using namespace std;
using namespace cv;wrap images ///
float w = 250, h = 350;
cv::Mat matrix, imgWrap;int main(int argc, char** argv)
{string path = "Resources/cards.jpg"; // 图片路径cv::Mat img = cv::imread(path); // 读数据cv::Point2f src[4] = { {529, 142}, {771, 190}, {405, 395}, {674, 457} };cv::Point2f dst[4] = { {0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h} };使用圆圈,取四个点在图中标记,放在这里转换后图片有圆//for (int i = 0; i < 4; i++)//{// cv::circle(img, src[i], 10, cv::Scalar(0, 0, 255), FILLED);//}// 转换matrix = getPerspectiveTransform(src, dst);cv::warpPerspective(img, imgWrap, matrix, cv::Point(w, h));// 使用圆圈,取四个点在图中标记for (int i = 0; i < 4; i++){cv::circle(img, src[i], 10, cv::Scalar(0, 0, 255), FILLED);}cv::imshow("Image", img); // 显示数据cv::imshow("Image Warp", imgWrap); // 显示数据cv::waitKey(0);return 0;
}
这段代码的主要功能是读取一张图像,定义四个源点和对应的目标点,计算透视变换矩阵,对图像进行透视变换,并在源图像中标记源点,最后显示原始图像和变换后的图像。通过这种方式,可以将图像中的某个区域(如一张卡片)从任意角度变换为正视图。
测试
6 Color detection
test1
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <string>Color detection ///
cv::Mat imgHSV, mask;
// h色调 s饱和度 v明度
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;int main()
{std::string path = "Resources/lambo.png"; // 图片路径cv::Mat img = cv::imread(path); // 读数据// 颜色转换cv::cvtColor(img, imgHSV, cv::COLOR_BGR2HSV);// 创建窗口cv::namedWindow("Trackbars");// 创建跟踪栏cv::createTrackbar("Hue Min", "Trackbars", &hmin, 179);cv::createTrackbar("Hue Max", "Trackbars", &hmax, 179);cv::createTrackbar("Sat Min", "Trackbars", &smin, 255);cv::createTrackbar("Sat Max", "Trackbars", &smax, 255);cv::createTrackbar("Val Min", "Trackbars", &vmin, 255);cv::createTrackbar("Val Max", "Trackbars", &vmax, 255);while (1){// 定义标量cv::Scalar lower(hmin, smin, vmin);cv::Scalar upper(hmax, smax, vmax);cv::inRange(imgHSV, lower, upper, mask);cv::imshow("Image", img); // 显示数据cv::imshow("image HSV", imgHSV); // 显示数据cv::imshow("image Mask", mask); // 显示数据cv::waitKey(0); // 增加延时,0表示无穷}return 0;
}
这段代码的主要功能是读取一张图像,将其从BGR颜色空间转换为HSV颜色空间,通过创建跟踪栏动态调整HSV颜色空间中的阈值,生成掩码图像,并显示原始图像、HSV图像和掩码图像。通过调整跟踪栏,可以实时看到不同阈值下检测到的颜色区域。
7 形状轮廓检测
这小节,将会通过OpenCV提供的函数进行,简单的形状检测,并给检测出来的形状添加boundingbox。整个过程的流程如下图所示:
图像的预处理阶段,实质上就是通过灰度处理、高斯模糊、边缘检测然后再加粗边缘得到一个二值化的图像,便于边界检测函数进行检测。
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>///int main(int argc, char** argv)
{std::string path = "Resources/shapes.png"; // 图片路径cv::Mat img = cv::imread(path); // 读数据// 预处理// cvt是convert的缩写,将图像从一种颜色空间转换为另一种颜色空间。cv::Mat imgGray, imgBlur, imgCanny, imgDil;cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);// 使用高斯滤波器模糊图像。该函数将源图像与指定的高斯核进行卷积,Size(7,7)是核大小,数字越大越模糊cv::GaussianBlur(imgGray, imgBlur, cv::Size(3, 3), 3, 0);// 边缘检测,阈值1,2可调,目的:显示更多的边缘cv::Canny(imgBlur, imgCanny, 25, 75);// 创建一个核,增加Size(只能是奇数),会扩张/侵蚀更多cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::dilate(imgCanny, imgDil, kernel);cv::imshow("Image", img);cv::imshow("Image Gray", imgGray);cv::imshow("Image Blur", imgBlur);cv::imshow("Image Canny", imgCanny);cv::imshow("Image dilate", imgDil);cv::waitKey(0);return 0;
}
测试
图片预处理之后,便可以进行形状检测了,需要使用到以下的函数:
void findContours(InputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode,int method);
InputArray image :这需要我们预处理之后得到的二值化图像(8bit单通道图像);
OutputArrayOfArrays contours:这是函数的输出,它是std::vector<vector> 类型的,可以看成是一个存储多个向量的组。每一个向量代表一个形状。
OutputArray hierarchy:该变量存储了contours中对应元素的相关拓扑信息,其类型为std::vector< cv::Vec4i > 。Vec4i 即 std::vector<int, 4>类型。
int mode,int method :这里的mode 和 method是指形状检测的模式和方法,OpenCV提供了多种的模式和方法,这里不细讲,详细的可以去官网查一下。
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <vector>
#include <iostream>///// imgDil是传入的扩张边缘的图像用来查找轮廓,img是要在其上绘制轮廓的图像
void getContours(cv::Mat imgDil, cv::Mat img)
{// 轮廓检测到的轮廓。每个轮廓线存储为一个点的向量std::vector<std::vector<cv::Point>> contours;// 包含关于映像拓扑的信息 typedef Vec<int, 4> Vec4i;具有4个整数值std::vector<cv::Vec4i> hierarchy;// 在二值图像中查找轮廓cv::findContours(imgDil, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);// 获得提取的轮廓以后,通过下面的函数将检测到的轮廓在图上表示出来// 其中 -1 表示把全部检测到的轮廓都输出, 向量Scalar(255, 0, 255) 表示颜色, 2 表示轮廓厚度cv::drawContours(img, contours, -1, cv::Scalar(255, 0, 255), 2);}int main(int argc, char** argv)
{std::string path = "Resources/shapes.png"; // 图片路径cv::Mat img = cv::imread(path); // 读数据// 预处理// cvt是convert的缩写,将图像从一种颜色空间转换为另一种颜色空间。cv::Mat imgGray, imgBlur, imgCanny, imgDil;cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);// 使用高斯滤波器模糊图像。该函数将源图像与指定的高斯核进行卷积,Size(7,7)是核大小,数字越大越模糊cv::GaussianBlur(imgGray, imgBlur, cv::Size(3, 3), 3, 0);// 边缘检测,阈值1,2可调,目的:显示更多的边缘cv::Canny(imgBlur, imgCanny, 25, 75);// 创建一个核,增加Size(只能是奇数),会扩张/侵蚀更多cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::dilate(imgCanny, imgDil, kernel);// 形状检测getContours(imgDil, img);cv::imshow("Image", img);//cv::imshow("Image Gray", imgGray);//cv::imshow("Image Blur", imgBlur);//cv::imshow("Image Canny", imgCanny);//cv::imshow("Image dilate", imgDil);cv::waitKey(0);return 0;
}
测试
不难发现,图像中的所有形状都被检测出来了,因为这张图像是比较理想情况,如果图像中出现一些噪点(例如图像中右上角的小黑圆圈),很可能也会被检测出来,这是不想看到的,这个时候,需要采取一些手段来去除这些噪点,这里提供一种根据形状图像面积过滤小形状的方法:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <vector>
#include <iostream>///// imgDil是传入的扩张边缘的图像用来查找轮廓,img是要在其上绘制轮廓的图像
void getContours(cv::Mat imgDil, cv::Mat img)
{// 轮廓检测到的轮廓。每个轮廓线存储为一个点的向量std::vector<std::vector<cv::Point>> contours;// 包含关于映像拓扑的信息 typedef Vec<int, 4> Vec4i;具有4个整数值std::vector<cv::Vec4i> hierarchy;// 在二值图像中查找轮廓cv::findContours(imgDil, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);// 获得提取的轮廓以后,通过下面的函数将检测到的轮廓在图上表示出来// 其中 -1 表示把全部检测到的轮廓都输出, 向量Scalar(255, 0, 255) 表示颜色, 2 表示轮廓厚度//cv::drawContours(img, contours, -1, cv::Scalar(255, 0, 255), 2);// 根据形状图像面积过滤小形状for (int i = 0; i < contours.size(); i++){// 获取每个形状的面积double area = cv::contourArea(contours[i]);if (area - 15000 > 0.00000001 || area - 15000 > -0.00000001){// 输出cv::drawContours(img, contours, i, cv::Scalar(255, 0, 255), 2);}}}int main(int argc, char** argv)
{std::string path = "Resources/shapes.png"; // 图片路径cv::Mat img = cv::imread(path); // 读数据// 预处理// cvt是convert的缩写,将图像从一种颜色空间转换为另一种颜色空间。cv::Mat imgGray, imgBlur, imgCanny, imgDil;cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);// 使用高斯滤波器模糊图像。该函数将源图像与指定的高斯核进行卷积,Size(7,7)是核大小,数字越大越模糊cv::GaussianBlur(imgGray, imgBlur, cv::Size(3, 3), 3, 0);// 边缘检测,阈值1,2可调,目的:显示更多的边缘cv::Canny(imgBlur, imgCanny, 25, 75);// 创建一个核,增加Size(只能是奇数),会扩张/侵蚀更多cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::dilate(imgCanny, imgDil, kernel);// 形状检测getContours(imgDil, img);cv::imshow("Image", img);//cv::imshow("Image Gray", imgGray);//cv::imshow("Image Blur", imgBlur);//cv::imshow("Image Canny", imgCanny);//cv::imshow("Image dilate", imgDil);cv::waitKey(0);return 0;
}
测试
添加此函数以后,可以看到,面积小于15000的没有被表示出来。
8 检测人脸
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>Webcam ///
// 视频是一系列的图像,因此需要遍历所有的图像/帧,捕获并且显示int main()
{cv::VideoCapture capture(0); // 只有一个相机cv::Mat img;while (1){capture.read(img); // 读到img中// opencv提供的分类器cv::CascadeClassifier faceCascade;// 从加载模型(已经训练好的)faceCascade.load("Resources/haarcascade_frontalface_default.xml");// 检测文件是否加载成功if (faceCascade.empty()){std::cout << "XML file not loaded" << std::endl;}// 定义用于接收检测结果的std::vector<cv::Rect> faces;// 在输入图像中检测不同大小的对象,检测到的对象以矩形的列表的形式返回// 通过增加最近邻的值可以消除误报,但是过大将会导致漏检测// img/*输入*/, faces/*输出*/, 1.1/*比例因子*/, 3/*最近邻*/faceCascade.detectMultiScale(img, faces, 1.1, 3);// 绘制矩形for (int i = 0; i < faces.size(); i++){cv::rectangle(img, faces[i].tl(), faces[i].br(), cv::Scalar(255, 0, 255), 3);}cv::imshow("Vedio", img); // 显示cv::waitKey(1); // 延时1ms,数值越大,视频越慢}return 0;
}
自行测试
学习:4h上手C++版Opencv_哔哩哔哩_bilibili