4h上手C++版OpenCV学习记录

视频教程:4h上手C++版Opencv_哔哩哔哩_bilibili

教程源网站:OpenCV C++ - Computer Vision Zone

源网站需注册登录才能查看课件

Text图像显示

出现的问题:

VS编译报错:dll缺少依赖文件

解决方法:把自己opencv文件目录下的\opencv\build\x64\vc15\bin中的三个dll文件,拷贝到C:\Windows\System32 中

Chapter1:图像显示

图像的读取

图像的读取是使用OpenCV最最最基本的操作,我们使用如下的方式进行读取。

imread( const String& filename, int flags = IMREAD_COLOR )
    //参数解读: filename 文件路径,可以是相对路径也可以是绝对路径。
    //IMREAD_COLOR 可以从cv::ImreadModes 中选择不同的读取模式,初学使用默认的就可以,非必须的参数。
视频的读取

OpenCV 对视频的读取使用了VideoCapture 这个类,下面列出了初学常用的几个构造函数

VideoCapture();//默认的构造函数
VideoCapture(const String& filename);//filename 为需要打开视频的路径
VideoCapture(int index);//如果需要打开本地摄像头,使 index = 0 即可, 插了usb摄像头的话 使用 index =1

图像的显示

void imshow(const String& winname, InputArray mat);//参数1 是窗口名称 参数2 需要显示的图像的名称
int waitKey(int delay = 0);
//一般会和imshow()联合使用,否则图像一闪而过,delay 的单位是ms, delay=0代表永远不关闭

Chapter2:基本功能

高斯模糊

GaussianBlur()参数含义

void GaussianBlur(InputArray src, 
                  OutputArray dst, 
                  Size ksize, 
                  double sigmaX, 
                  double sigmaY=0, 
                  int borderType=BORDER_DEFAULT);


src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,
    但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。

dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,
    来初始化得到如假包换的目标图。

ksize,高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数
     (并不能理解)。或者,它们可以是零的,它们都是由sigma计算而来。

sigmaX,表示高斯核函数在X方向的的标准偏差。

sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,
       如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。

Canny边缘性检测

CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
                         double threshold1, double threshold2,
                         int apertureSize = 3, bool L2gradient = false );

threshold1; threshold2表示处理过程中的两个阈值,其数值越小,捕捉到的边缘信息越多。

进行边缘性检测前通常进行高斯模糊(去噪:高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。消除图像在数字化过程中产生或者混入的噪声。):

未进行高斯模糊处理得到的图像:

经过高斯模糊处理得到的图像:

图像膨胀

	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));//size增大膨胀的多,size减小膨胀的小;只能使用奇数
	dilate(imgCanny, imgDil, kernel);//图像膨胀(厚度)

定义一个Mat类型的变量来获得getStructingElement函数的返回值,而getStructingElement函数的返回值为指定形状和尺寸的结构元素(内核矩阵)

膨胀后的图片:

图像侵蚀

	Mat kernel2 = getStructuringElement(MORPH_RECT, Size(5, 5));//size增大侵蚀的多,size减小侵蚀的小;只能使用奇数
	erode(imgDil, imgErode, kernel2);//图像侵蚀(厚度)

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Basic Functions
 
void main() {

	string path = "Resources/test.png";
	Mat img = imread(path);
	Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;
	
	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);//高斯模糊
	Canny(imgGray, imgCanny, 100, 150);//边缘性检测

	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));//size增大膨胀的多,size减小膨胀的小;只能使用奇数
	dilate(imgCanny, imgDil, kernel1);//图像膨胀(厚度)

	Mat kernel2 = getStructuringElement(MORPH_RECT, Size(5, 5));//size增大侵蚀的多,size减小侵蚀的小;只能使用奇数
	erode(imgDil, imgErode, kernel2);//图像侵蚀(厚度)

	imshow("Image", img);
	imshow("Image Gray", imgGray);
	imshow("Image Blur", imgBlur);
	imshow("Image Canny", imgCanny);
	imshow("Image Dilation", imgDil);
	imshow("Image Erode", imgErode);
	waitKey(0);

}

Chapter3:调整/裁剪图片

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Resize and Crop

void main() {

	string path = "Resources/test.png";
	Mat img = imread(path);
	Mat imgResize, imgCrop;

	//cout << img.size() << endl;
	resize(img, imgResize, Size(640, 480));//调整图片大小
	//resize(img, imgResize, Size(), 0.5, 0.5);//按比例调整图片大小

	Rect roi(100, 100, 300, 250);//裁剪图片x,y,宽高
	imgCrop = img(roi);

	imshow("Image", img);
	imshow("Image Resize", imgResize);
	imshow("Image Crop", imgCrop);
	waitKey(0);

}

Chapter4:画图与文字

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Draw Shapes and Text

void main() {

	//Blank Image
	Mat img(512, 512, CV_8UC3, Scalar(255, 0, 0));//cv_8 unsigned 8位0-255共2^8个 singed 8位-127~127; C3表示三个颜色通道;Scalar(颜色的RGB值)
	
	//circle(img, Point(256, 256), 155, Scalar(255, 0, 255), 10);//画圆(在哪个图片上画,圆心位置,圆半径,颜色,线粗)
	circle(img, Point(256, 256), 155, Scalar(255, 0, 255), FILLED);//画圆(在哪个图片上画,圆心位置,圆半径,颜色,填满)
	rectangle(img, Point(130, 226), Point(382, 286), Scalar(255, 255, 255), FILLED);//画矩形(在哪个图片上画,左上角点,右下角点,颜色,线宽)
	line(img, Point(130, 296), Point(382, 296), Scalar(255, 255, 255), 3);//画直线(在哪个图片上画,起点,终点,颜色,线宽)

	putText(img, "Zhang XY 666", Point(137, 262), FONT_HERSHEY_DUPLEX, 1, Scalar(255, 0, 255), 3);//加文字(在哪个图片上加,“内容”,位置,比例,颜色,线宽)

	imshow("Image", img);
	waitKey(0);

}

Chapter5:图像透视变换

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Warp Image

float w = 250, h = 350;
Mat matrix, imgWarp;
 
void main() {

	string path = "Resources/cards.jpg";
	Mat img = imread(path);

	Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
	Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };

	matrix = getPerspectiveTransform(src, dst);
	warpPerspective(img, imgWarp, matrix, Point(w, h));

	for (int i = 0; i < 4; i++)//放在warp操作后,防止imgWarp图片中出现圆圈部分
	{
		circle(img, src[i], 10, Scalar(0, 0, 255), FILLED);
	}

	imshow("Image", img);
	imshow("Image Warp", imgWarp);
	waitKey(0);

}

 

Chapter6:颜色检测

图像HSV模型

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)(参考百度)。在HSV模型中,颜色是由色度(Hue),饱和度(Saturation),明度(Value)共同组成。

如图所示,HSV模型中


参考链接:https://blog.csdn.net/qq_37541097/article/details/119478023

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Color Detection

Mat imgHSV, mask;
int hmin = 0, smin = 0, vmin = 0;
int hmax = 179, smax = 255, vmax = 255;
 
void main() {

	string path = "Resources/lambo.png";
	Mat img = imread(path);

	cvtColor(img, imgHSV, COLOR_BGR2HSV);//图像转换

	namedWindow("Trackbars", (640, 200));//创建窗口(窗口名,窗口大小)
	createTrackbar("Hue Min", "Trackbars", &hmin, 179);//创建色相0-180最小值滑动条(滑动条名,窗口名,要拖动的值的地址,最大值)
	createTrackbar("Hue Max", "Trackbars", &hmax, 179);
	createTrackbar("Sat Min", "Trackbars", &smin, 255);//创建饱和度滑动条
	createTrackbar("Sat Max", "Trackbars", &smax, 255);
	createTrackbar("Val Min", "Trackbars", &vmin, 255);
	createTrackbar("Val Max", "Trackbars", &vmax, 255);

	while (true) {

		Scalar lower(hmin, smin, vmin);
		Scalar upper(hmax, smax, vmax);
		inRange(imgHSV, lower, upper, mask);

		imshow("Image", img);
		imshow("Image HSV", imgHSV);
		imshow("Image Mask", mask);//蒙版
		waitKey(1);//修改为waitkey(1)
		//waitkey(0)将无限的显示该窗口
		//放在循环当中显示逐帧
	}
}

运行结果:

shapes图片:

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/// Color Detection

Mat imgHSV, mask;
int hmin = 0, smin = 0, vmin = 0;
int hmax = 197, smax = 255, vmax = 255;
 
void main() {

	string path = "Resources/shapes.png";
	Mat img = imread(path);

	cvtColor(img, imgHSV, COLOR_BGR2HSV);//图像转换HSV模型

	namedWindow("Trackbars", (640, 200));//创建窗口(窗口名,窗口大小)
	createTrackbar("Hue Min", "Trackbars", &hmin, 179);//创建色度0-180最小值滑动条(滑动条名,窗口名,要拖动的值的地址,最大值)
	createTrackbar("Hue Max", "Trackbars", &hmax, 179);
	createTrackbar("Sat Min", "Trackbars", &smin, 255);//创建饱和度滑动条
	createTrackbar("Sat Max", "Trackbars", &smax, 255);
	createTrackbar("Val Min", "Trackbars", &vmin, 255);//创建明度滑动条
	createTrackbar("Val Max", "Trackbars", &vmax, 255);

	while (true) {

		Scalar lower(hmin, smin, vmin);
		Scalar upper(hmax, smax, vmax);
		inRange(imgHSV, lower, upper, mask);

		imshow("Image", img);
		imshow("Image HSV", imgHSV);
		imshow("Image Mask", mask);//蒙版
		waitKey(1);//修改为waitkey(1)
		//waitkey(0)将无限的显示该窗口
		//放在循环当中显示逐帧
	}
}

Chapter7:形状检测

1、图像预处理

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

//Shapes/Contour Detection
 
void main() {

	string path = "Resources/shapes.png";
	Mat img = imread(path);
	Mat imgGray, imgBlur, imgCanny, imgDil;

	//Preprocessing图像预处理

	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);//高斯模糊
	Canny(imgGray, imgCanny, 25, 75);//边缘性检测

	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));//size增大膨胀的多,size减小膨胀的小;只能使用奇数
	dilate(imgCanny, imgDil, kernel1);//图像膨胀(厚度)

	imshow("Image", img);
	imshow("Image Gray", imgGray);
	imshow("Image Blur", imgBlur);
	imshow("Image Canny", imgCanny);
	imshow("Image Dilation", imgDil);
	waitKey(0);

}

运行结果:

2、形状检测函数

//形状检测
void getContours(Mat imgDil, Mat img) {

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	drawContours(img, contours, -1, Scalar(255, 0, 255), 2);

	for (int i = 0; i < contours.size(); i++)
	{
		cout << "输出轮廓" << i + 1 << "的向量" << contours[i] << endl;
		for (int j = 0;j < contours[i].size();j++)
		{
			cout << "输出轮廓" << i + 1 << "第" << j + 1 << "个点的坐标  " << contours[i][j] << endl;
		}
	}

}
findContours函数:

先从findContours函数原型看起:

findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset=Point());

第一个参数:image,单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;

第二个参数:contours,定义为“vector<vector<Point>> contours”,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓。有多少轮廓,向量contours就有多少元素。

第三个参数:hierarchy,定义为“vector<Vec4i> hierarchy”,先来看一下

Vec4i的定义:typedef    Vec<int, 4>   Vec4i;                                                                                                 Vec4i是Vec<int,4>的别名,定义了一个“向量内每一个元素包含了4个int型变量”的向量。

       所以从定义上看,hierarchy也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。

 向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。

hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1。

第四个参数:int型的mode,定义轮廓的检索模式:

取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略

取值二:CV_RETR_LIST   检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到

取值三:CV_RETR_CCOMP  检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层

取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

第五个参数:int型的method,定义轮廓的近似方法:

取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内

取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留

取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

第六个参数:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值!

参考链接:https://blog.csdn.net/dcrmg/article/details/51987348

OpenCV轮廓vector<vector<Point>>,vector<Vec4i>,vector<Rect>,vector<RotatedRect>

vector<vector<Point>> contours:

contours.size():表示轮廓的个数
contours[i].size():表示第i个轮廓中点的个数

vector容器里面放了一个vector容器,子容器里放点

vector<Vec4i>:放了4维int向量

vector<Rect>: 像素width * height   from  位置(x*y)

vector<RotatedRect>:如图三个成员



参考:https://blog.csdn.net/Ahuuua/article/details/80593388

运行结果 

 

3、对检测的形状进行噪声过滤:

//过滤噪声:通过遍历轮廓面积的方法
for (int i = 0; i< contours.size(); i++)
{
	int area = contourArea(contours[i]);
	cout << area << endl;
	if (area > 1000)
	{
		drawContours(img, contours, i, Scalar(255, 0, 255), 2);//img:要绘制轮廓在什么图片上,contours:要绘制的轮廓, - 1定义要绘制的轮廓号( - 1表示所有轮廓),Saclar表示轮廓颜色,2表示厚度
	}
}

 运行结果:

4、轨迹近似,输出近似轨迹的角点数量

	//过滤噪声:通过遍历轮廓面积的方法
	for (int i = 0; i< contours.size(); i++)
	{
		int area = contourArea(contours[i]);
		cout << area << endl;

		vector<vector<Point>> conPloy(contours.size());

		if (area > 1000)
		{
			//计算轮廓周长arcLength函数(输入的向量/二维点可为std::vector或Mat类型,bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true)
			float peri = arcLength(contours[i], true);
			//轮廓近似,把连续光滑的曲线折现化
			approxPolyDP(contours[i], conPloy[i], 0.02 * peri, true);
			//drawContours(img, contours, i, Scalar(255, 0, 255), 2);//img:要绘制轮廓在什么图片上,contours:要绘制的轮廓, -1定义要绘制的轮廓号( -1表示所有轮廓),Saclar表示轮廓颜色,2表示厚度
			drawContours(img, conPloy, i, Scalar(255, 0, 255), 2);
			cout << "输出近似后的轮廓" << i+1 << "的角点坐标\n" << conPloy[i] << endl;
			cout << "输出轮廓" << i+1 << "的角点数量: " << conPloy[i].size() << endl;
		}
	}
 approxPolyDP()函数详解
CV_EXPORTS_W void approxPolyDP( InputArray curve,OutputArray approxCurve,double epsilon, bool closed );
 
@param curve Input vector of a 2D point stored in std::vector or Mat
@param approxCurve Result of the approximation. The type should match the type of the input curve.
@param epsilon Parameter specifying the approximation accuracy. This is the maximum distance
between the original curve and its approximation.
@param closed If true, the approximated curve is closed (its first and last vertices are
connected). Otherwise, it is not closed.
The function cv::approxPolyDP approximates a curve or a polygon with another curve/polygon with less
vertices so that the distance between them is less or equal to the specified precision. It uses the
Douglas-Peucker algorithm <http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm>

主要功能是把一个连续光滑曲线折线化:

参数有4个:

InputArray curve:输入曲线,数据类型可以为vector<Point>。

OutputArray approxCurve:输出折线,数据类型可以为vector<Point>。

double epsilon:判断点到相对应的line segment 的距离的阈值。(距离大于此阈值则舍弃,小于此阈值则保留,epsilon越小,折线的形状越“接近”曲线。)

bool closed:曲线是否闭合的标志位。

参考链接: OpenCV approxPolyDP()函数详解_cv::approxpolydp-CSDN博客

运行结果:

5、轮廓最小矩阵边框:

	for (int i = 0; i< contours.size(); i++)
	{
		int area = contourArea(contours[i]);
		cout << area << endl;

		vector<vector<Point>> conPloy(contours.size());
		vector<Rect> boundRect(contours.size());

		if (area > 1000)
		{
			//计算轮廓周长arcLength函数(输入的向量/二维点可为std::vector或Mat类型,bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true)
			float peri = arcLength(contours[i], true);
			//轮廓近似,把连续光滑的曲线折现化
			approxPolyDP(contours[i], conPloy[i], 0.02 * peri, true);
			//drawContours(img, contours, i, Scalar(255, 0, 255), 2);//img:要绘制轮廓在什么图片上,contours:要绘制的轮廓, -1定义要绘制的轮廓号( -1表示所有轮廓),Saclar表示轮廓颜色,2表示厚度
			drawContours(img, conPloy, i, Scalar(255, 0, 255), 2);
			cout << "输出近似后的轮廓" << i+1 << "的角点坐标\n" << conPloy[i] << endl;
			cout << "输出轮廓" << i+1 << "的角点数量: " << conPloy[i].size() << endl;
			//boundingRect(): 计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
			boundRect[i] = boundingRect(conPloy[i]);
			cout << boundRect[i] << endl;
			cout << boundRect[i].tl() << endl;
			cout << boundRect[i].br() << endl;
			rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 2);//rectangle(画在哪个图像上,左上角坐标,右下角坐标,线条颜色,线宽)
		}
	}
cv2.boundingRect()和cv2.rectangle()

cv2.boundingRect(img)这个函数可以获得一个图像的最小矩形边框一些信息,参数img是一个二值图像,它可以返回四个参数,左上角坐标,矩形的宽高,一般形式为:

x,y,w,h = cv2.boundingRect(img)

配合cv2.rectangle()可以画出该最小边框,cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)这个函数功能就是画框的函数。
参考链接:https://blog.csdn.net/a_eastern/article/details/104413618

运行结果:

6、判断轮廓形状

int objCor = (int)conPloy[i].size();//conPloy[i].size()转换成整型
if (objCor == 3) { objectType = "Tri"; }
if (objCor == 4) {
	//通过定义长宽比来区别正方形和矩形
	float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
	if (aspRatio > 0.95 && aspRatio < 1.05) { objectType = "Square"; }
	else { objectType = "Rect"; }
}
if (objCor > 4) { objectType = "circle"; }

drawContours(img, conPloy, i, Scalar(255, 0, 255), 2);
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 2);//rectangle(画在哪个图像上,左上角坐标,右下角坐标,线条颜色,线宽)
putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_DUPLEX, 0.5, Scalar(0, 0, 0), 1);//加文字(在哪个图片上加,“内容”,位置,比例,颜色,线宽)

运行结果: 

Chapter7代码:

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

//Shapes/Contour Detection

//形状检测
void getContours(Mat imgDil, Mat img) {

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

	//for (int i = 0; i < contours.size(); i++)
	//{
	//	cout << "输出轮廓" << i + 1 << "的向量" << contours[i] << endl;
	//	for (int j = 0;j < contours[i].size();j++)
	//	{
	//		cout << "输出轮廓" << i + 1 << "第" << j + 1 << "个点的坐标  " << contours[i][j] << endl;
	//	}
	//}
	//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);

	//过滤噪声:通过遍历轮廓面积的方法
	for (int i = 0; i< contours.size(); i++)
	{
		int area = contourArea(contours[i]);
		cout << area << endl;

		vector<vector<Point>> conPloy(contours.size());
		vector<Rect> boundRect(contours.size());
		string objectType;

		if (area > 1000)
		{
			//计算轮廓周长arcLength函数(输入的向量/二维点可为std::vector或Mat类型,bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true)
			float peri = arcLength(contours[i], true);
			//轮廓近似,把连续光滑的曲线折现化
			approxPolyDP(contours[i], conPloy[i], 0.02 * peri, true);
			//drawContours(img, contours, i, Scalar(255, 0, 255), 2);//img:要绘制轮廓在什么图片上,contours:要绘制的轮廓, -1定义要绘制的轮廓号( -1表示所有轮廓),Saclar表示轮廓颜色,2表示厚度

			//cout << "输出近似后的轮廓" << i+1 << "的角点坐标\n" << conPloy[i] << endl;
			//cout << "输出轮廓" << i+1 << "的角点数量: " << conPloy[i].size() << endl;

			//boundingRect(): 计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
			boundRect[i] = boundingRect(conPloy[i]);
			//cout << boundRect[i] << endl;
			//cout << boundRect[i].tl() << endl;
			//cout << boundRect[i].br() << endl;

			//
			int objCor = (int)conPloy[i].size();//conPloy[i].size()转换成整型
			if (objCor == 3) { objectType = "Tri"; }
			if (objCor == 4) {
				//通过定义长宽比来区别正方形和矩形
				float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
				if (aspRatio > 0.95 && aspRatio < 1.05) { objectType = "Square"; }
				else { objectType = "Rect"; }
			}
			if (objCor > 4) { objectType = "circle"; }

			drawContours(img, conPloy, i, Scalar(255, 0, 255), 2);
			rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 2);//rectangle(画在哪个图像上,左上角坐标,右下角坐标,线条颜色,线宽)
			putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_DUPLEX, 0.5, Scalar(0, 0, 0), 1);//加文字(在哪个图片上加,“内容”,位置,比例,颜色,线宽)
		}
	}
}
 
void main() {

	string path = "Resources/shapes.png";
	Mat img = imread(path);
	Mat imgGray, imgBlur, imgCanny, imgDil;

	//Preprocessing图像预处理
	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);//高斯模糊
	Canny(imgGray, imgCanny, 25, 75);//边缘性检测

	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));//size增大膨胀的多,size减小膨胀的小;只能使用奇数
	dilate(imgCanny, imgDil, kernel1);//图像膨胀(厚度)

	getContours(imgDil, img);

	imshow("Image", img);
	//imshow("Image Gray", imgGray);
	//imshow("Image Blur", imgBlur);
	//imshow("Image Canny", imgCanny);
	//imshow("Image Dilation", imgDil);
	waitKey(0);
}

 Chapter8:面部检测

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>

using namespace cv;
using namespace std;

// Face Detection
 
void main() {

	string path = "Resources/test.png";
	Mat img = imread(path);

	CascadeClassifier faceCascade;//级联分类器
	faceCascade.load("Resources/haarcascade_frontalface_default.xml");

	if (faceCascade.empty()) { cout << "XML file not loaded" << endl; }

	vector<Rect> faces;
	faceCascade.detectMultiScale(img, faces, 1.1, 10);//detectMultiScale(待检测图片,被检测物体矩阵框向量组,搜索窗口的比例系数默认为1.1即每次搜索窗口依次扩大10%,构成检测目标的相邻矩形的最小个数)

	for (int i = 0;i < faces.size(); i++)
	{
		rectangle(img, faces[i].tl(), faces[i].br(), Scalar(255, 0, 255), 3);//画矩形(在哪个图片上画,左上角点,右下角点,颜色,线宽)
	}

	imshow("Image", img);
	waitKey(0);

}

1、级联分类器CascadeClassifier

OpenCV官方文档:https://docs.opencv.org/3.4.3/d5/d54/group__objdetect.html

分类器: 判别某个事物是否属于某种分类的器件,两种结果:是、否 。
级联分类器: 可以理解为将N个单类的分类器串联起来。如果一个事物能属于这一系列串联起来的的所有分类器,则最终结果就是 是,若有一项不符,则判定为否。

比如人脸,它有很多属性,我们将每个属性做一成个分类器,如果一个模型符合了我们定义的人脸的所有属性,则我们人为这个模型就是一个人脸。那么这些属性是指什么呢? 比如人脸需要有两条眉毛,两只眼睛,一个鼻子,一张嘴,一个大概U形状的下巴或者是轮廓等等。

CascadeClassifier为OpenCV下用来做目标检测的级联分类器的一个类。该类中封装的目标检测机制,简而言之是滑动窗口机制+级联分类器的方式

CascadeClassifier检测的基本原理:
       xml中存放的是训练后的特征池,特征size大小根据训练时的参数而定,检测的时候可以简单理解为就是将每个固定size特征(检测窗口)与输入图像的同样大小区域比较,如果匹配那么就记录这个矩形区域的位置,然后滑动窗口,检测图像的另一个区域,重复操作。由于输入的图像中特征大小不定,比如在输入图像中眼睛是50x50的区域,而训练时的是25x25,那么只有当输入图像缩小到一半的时候,才能匹配上,所以这里还有一个逐步缩小图像,也就是制作图像金字塔的流程.

       由于人脸可能出现在图像的任何位置,在检测时用固定大小的窗口对图像从上到下、从左到右扫描,判断窗口里的子图像是否为人脸,这称为滑动窗口技术(sliding window)。为了检测不同大小的人脸,还需要对图像进行放大或者缩小构造图像金字塔,对每张缩放后的图像都用上面的方法进行扫描。由于采用了滑动窗口扫描技术,并且要对图像进行反复缩放然后扫描,因此整个检测过程会非常耗时。

2、detectMultiScale函数详解

     cvHaarDetectObjects是opencv1中的函数,opencv2中人脸检测使用的是 detectMultiScale函数。它可以检测出图片中所有的人脸,并将人脸用vector保存各个人脸的坐标、大小(用矩形表示),函数由分类器对象调用:

void detectMultiScale(
	const Mat& image,
	CV_OUT vector<Rect>& objects,
	double scaleFactor = 1.1,
	int minNeighbors = 3, 
	int flags = 0,
	Size minSize = Size(),
	Size maxSize = Size()
);

函数介绍:

image--待检测图片,一般为灰度图像加快检测速度;

objects--被检测物体的矩形框向量组,其中每个矩形包含被检测的对象,矩形可以部分位于原始图像之外

scaleFactor--表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%;

minNeighbors--指定每个候选矩形需要保留多少个相邻矩形,表示构成检测目标的相邻矩形的最小个数(默认为3个)。

        如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。

        如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,

        这种设定值一般用在用户自定义对检测结果的组合程序上;

flags=0:可以取如下这些值:
          CASCADE_DO_CANNY_PRUNING=1, 利用canny边缘检测来排除一些边缘很少或者很多的图像区域
          CASCADE_SCALE_IMAGE=2, 正常比例检测
          CASCADE_FIND_BIGGEST_OBJECT=4, 只检测最大的物体
          CASCADE_DO_ROUGH_SEARCH=8 初略的检测

minSize和maxSize用来限制得到的目标区域的范围。

           minSize:对象最小大小,小于该值的对象被忽略。

           maxSize:最大可能的对象大小,大于这个值的对象被忽略

参考链接:https://blog.csdn.net/qq_30815237/article/details/88525378

运行结果:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值