openCV 之 C++ 学习笔记
本机环境:VS studio 2022;opencv-4.10.0-windows
一、(读取图片,视频和摄像头)chapter1-Read images Vodeos and Webcams
包含主要头文件
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
主要知识点:
Mat类:
简介:Mat类是OpenCV中最重要的类之一,它代表着图像,视频帧或者其他二维数据。Mat类提供了许多方法来对图像进行处理,包括读取、写入、显示、转换、拼接、裁剪、缩放等。
常用构造方法:
Mat():默认构造方法,创建一个空的Mat对象,其大小为0x0。
Mat(int rows, int cols, int type):构造方法,创建一个Mat对象,其大小为rows x cols,数据类型为type。
Mat(Size size, int type):构造方法,创建一个Mat对象,其大小为size.height x size.width,数据类型为type。
Mat(int rows, int cols, int type, void* data):构造方法,创建一个Mat对象,其大小为rows x cols,数据类型为type,数据指针为data。
VideoCapture类:
简介:VideoCapture类是OpenCV中用于读取视频的类,它可以打开摄像头、视频文件或者网络流,并返回一个Mat类型的对象,代表视频的每一帧。VideoCapture类提供了许多方法来控制视频的播放,包括设置播放速率、获取当前帧、获取视频的帧数、获取视频的尺寸、设置视频的位置等。
常用方法:
bool open(const string& filename):打开视频文件,并返回true。
bool open(int device):打开摄像头,并返回true。
bool isOpened():判断视频是否打开成功。
bool read(Mat& image):读取视频的当前帧,并将其存储在image中。
double get(int propId):获取视频的属性,包括帧数、尺寸、播放速率等。
bool set(int propId, double value):设置视频的属性,包括帧数、尺寸、播放速率等。
void release():释放VideoCapture对象。
imread方法:
简介:imread方法是OpenCV中用于读取图像的函数,它可以读取一张图片,并返回一个Mat类型的对象,代表图像。imread方法提供了许多参数来控制图像的读取,包括图像路径、图像编码格式、图像是否透明等。
常用方法:
//参数:filename:图像路径,flags:图像编码格式,img:输出图像
Mat imread(const string& filename, int flags = IMREAD_COLOR):读取图像。
//参数:img:输入图像,filename:图像路径,params:图像编码格式
bool imwrite(const string& filename, InputArray img, const vector& params = vector()):保存图像。
imshow方法:
简介:imshow方法是OpenCV中用于显示图像的函数,它可以显示一个Mat类型的对象,代表图像。imshow方法提供了许多参数来控制图像的显示,包括窗口名称、窗口大小、窗口位置、是否显示网格、是否显示图像的轮廓等。
常用方法:
//参数:winname:窗口名称,mat:要显示的图像
void imshow(const string& winname, InputArray mat):显示图像。
//参数:winname:窗口名称
void destroyWindow(const string& winname):销毁窗口。
waitKey方法:
简介:waitKey方法是OpenCV中用于等待按键的函数,它可以等待用户按下某个键,或者等待一段时间,然后返回按键的ASCII码。waitKey方法提供了许多参数来控制等待时间,包括等待时间的单位、是否显示等待信息等。
//参数:delay:等待时间(单位:毫秒)
int waitKey(int delay = 0):等待按键,并返回按键的ASCII码。
flip方法:
简介:flip方法是OpenCV中用于图像翻转的函数,它可以水平或者垂直翻转一个Mat类型的对象,代表图像。flip方法提供了许多参数来控制翻转方向,包括水平、垂直、水平垂直等。
常用方法:
//参数:src:输入图像,dst:输出图像,flipCode:翻转方向(1:水平翻转,0:垂直翻转,-1:水平垂直翻转)
void flip(InputArray src, OutputArray dst, int flipCode):翻转图像。
1.显示一张图片
void main() {//读取图片
string path = "Resources/test.png";//图片路径
// Read image
Mat img = imread(path);
imshow("Image", img);
waitKey(0);
}
2.读取视频
//视频读取
void main() {
string path = "Resources/test_video.mp4";
VideoCapture cap(path);//定义VideoCapture对象,并传入视频路径
Mat img;//定义Mat变量
while (true) {
cap.read(img);//读取视频帧
imshow("Image", img);//显示视频帧
waitKey(20);//延时20ms
}
}
3.读取摄像头
//摄像头读取
void main() {
VideoCapture cap(0);//创建VideoCapture对象,并传入0,表示打开默认摄像头
Mat img;//定义Mat变量
while (true) {
cap.read(img);//读取视频帧
//图像左右反转一下
flip(img, img, 1); //1表示水平翻转,0表示垂直翻转
imshow("Image", img);//显示视频帧
waitKey(1);//延时1ms
}
}
二、(基本功能)chapter2-BaseFunction
主要知识点:
常用的基础功能:
颜色空间转换 cvtColor(src, dst, code);//canshu:src原图,dst目标图,code转换类型
高斯模糊 GaussianBlur(src, dst, ksize, sigmaX, sigmaY);//canshu:src原图,dst目标图,ksize卷积核大小,sigmaX,sigmaY高斯核标准差
Canny边缘检测 Canny(src, dst, threshold1, threshold2);//canshu:src原图,dst目标图,threshold1,threshold2边缘检测阈值(低阈值,高阈值)
膨胀 dilation(src, dst, kernel);//canshu:src原图,dst目标图,kernel卷积核
腐蚀 erode(src, dst, kernel);//canshu:src原图,dst目标图,kernel卷积核
主要方法详解:
1.颜色空间转换方法 cvtColor()
**示例:**cvtColor(src, dst, code);//canshu:src原图,dst目标图,code转换类型
/**
* @brief 将图像从一种颜色空间转换为另一种颜色空间。
*
* 此函数可把输入图像从一种颜色空间转换到另一种颜色空间。当进行与RGB颜色空间之间的转换时,
* 必须明确指定通道的顺序(RGB或者BGR)。需要注意的是,OpenCV里默认的颜色格式虽常被称为RGB,
* 但实际上是BGR(字节顺序相反)。所以在标准的(24位)彩色图像里,第一个字节是8位的蓝色分量,
* 第二个字节是绿色分量,第三个字节是红色分量。第四、五、六个字节依次是第二个像素的(蓝、绿、红),依此类推。
*
* R、G、B通道值的常规范围如下:
* - 对于CV_8U类型的图像,范围是0到255。
* - 对于CV_16U类型的图像,范围是0到65535。
* - 对于CV_32F类型的图像,范围是0到1。
*
* 若为线性变换,取值范围无关紧要。但在非线性变换的情况下,为了得到正确的结果,
* 输入的RGB图像应被归一化到合适的值范围,例如在RGB \f$\rightarrow\f$ L*u*v*变换时。
* 例如,若你有一个直接从8位图像转换而来、未经过任何缩放的32位浮点型图像,
* 那么它的值范围会是0到255,而非函数所假定的0到1。所以,在调用#cvtColor之前,
* 你得先对图像进行缩放:
* @code
* img *= 1./255;
* cvtColor(img, img, COLOR_BGR2Luv);
* @endcode
* 若使用#cvtColor处理8位图像,转换过程会有部分信息丢失。对于很多应用而言,这可能并不明显,
* 但对于那些需要完整颜色范围,或者在操作前后都要进行图像转换的应用,建议使用32位图像。
*
* 若转换过程添加了alpha通道,其值会被设为对应通道范围的最大值:
* 对于CV_8U类型为255,对于CV_16U类型为65535,对于CV_32F类型为1。
*
* @param src 输入图像:可以是8位无符号、16位无符号(CV_16UC...)或单精度浮点型。
* @param dst 输出图像,与src的尺寸和深度相同。
* @param code 颜色空间转换代码(参见#ColorConversionCodes)。
* @param dstCn 目标图像的通道数;若该参数为0,则通道数会根据src和code自动推导得出。
*
* @see @ref imgproc_color_conversions
*/
CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );
2.高斯模糊 GaussianBlur()
示例:
GaussianBlur(src, dst, ksize, sigmaX, sigmaY);//canshu:src原图,dst目标图,ksize卷积核大小,sigmaX,sigmaY高斯核标准差
卷积核定义:
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); //固定写法,定义卷积核:MORPH_RECT矩形结构,Size(3,3)表示卷积核大小
/**
* @brief 使用高斯滤波器对图像进行模糊处理。
*
* 该函数将源图像与指定的高斯核进行卷积操作。支持原地滤波。
*
* @param src 输入图像;图像可以有任意数量的通道,各通道会独立处理,
* 但其深度应为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F。
* @param dst 输出图像,与src具有相同的尺寸和类型。
* @param ksize 高斯核的大小。ksize.width和ksize.height可以不同,
* 但它们都必须为正奇数。或者,它们可以为0,此时将根据sigma计算得到。
* @param sigmaX 高斯核在X方向上的标准差。
* @param sigmaY 高斯核在Y方向上的标准差;如果sigmaY为0,则将其设置为与sigmaX相等。
* 如果两个sigma都为0,则分别根据ksize.width和ksize.height计算得到(详情见#getGaussianKernel);
* 为了完全控制结果,不受未来可能的语义修改影响,建议同时指定ksize、sigmaX和sigmaY。
* @param borderType 像素外推方法,见#BorderTypes。#BORDER_WRAP不支持。
*
* @sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur
*/
CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
3.Canny边缘检测 Canny();
示例:Canny(src, dst, threshold1, threshold2);//参数:src原图,dst目标图,threshold1,threshold2边缘检测阈值(低阈值,高阈值)
```cpp
/**
* @brief 使用Canny算法在图像中查找边缘 @cite Canny86 。
*
* 该函数使用Canny算法在输入图像中查找边缘,并将这些边缘标记在输出边缘图 edges 中。
* threshold1 和 threshold2 中的较小值用于边缘连接,较大值用于查找强边缘的初始片段。
* 更多信息请参考 <http://en.wikipedia.org/wiki/Canny_edge_detector>。
*
* @param image 8位输入图像。
* @param edges 输出边缘图;单通道8位图像,与输入图像 image 尺寸相同。
* @param threshold1 滞后过程的第一个阈值。
* @param threshold2 滞后过程的第二个阈值。
* @param apertureSize Sobel算子的孔径大小。
* @param L2gradient 一个标志,指示是否应使用更精确的 \f$L_2\f$ 范数
* \f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ 来计算图像梯度幅度(L2gradient=true),
* 还是默认的 \f$L_1\f$ 范数 \f$=|dI/dx|+|dI/dy|\f$ 就足够了(L2gradient=false)。
*/
CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
double threshold1, double threshold2,
int apertureSize = 3, bool L2gradient = false );
```
4.膨胀dilation();
示例:dilation(src, dst, kernel);//canshu:src原图,dst目标图,kernel卷积核
/**
* @brief 使用特定的结构元素对图像进行膨胀操作。
*
* 该函数使用指定的结构元素对源图像进行膨胀操作,此结构元素决定了取最大值时像素邻域的形状:
* \f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
*
* 该函数支持原地操作模式。膨胀操作可以多次(iterations 次)执行。
* 对于多通道图像,每个通道会独立进行处理。
*
* @param src 输入图像;通道数可以任意,但深度必须是 CV_8U、CV_16U、CV_16S、CV_32F 或 CV_64F 之一。
* @param dst 输出图像,与 src 具有相同的尺寸和类型。
* @param kernel 用于膨胀操作的结构元素;如果 element=Mat(),则使用一个 3 x 3 的矩形结构元素。
* 可以使用 #getStructuringElement 来创建结构元素。
* @param anchor 结构元素内锚点的位置;默认值 (-1, -1) 表示锚点位于结构元素的中心。
* @param iterations 膨胀操作的执行次数。
* @param borderType 像素外推方法,见 #BorderTypes。#BORDER_WRAP 不支持。
* @param borderValue 常量边界情况下的边界值
*
* @sa erode, morphologyEx, getStructuringElement
*/
CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
5.腐蚀erode();
示例:erode(src, dst, kernel);//参数:src原图,dst目标图,kernel卷积核
/**
* @brief 使用特定的结构元素对图像进行腐蚀操作。
*
* 该函数使用指定的结构元素对源图像进行腐蚀操作,此结构元素决定了取最小值时像素邻域的形状:
*
* \f[\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
*
* 该函数支持原地操作模式。腐蚀操作可以多次(iterations 次)执行。
* 对于多通道图像,每个通道会独立进行处理。
*
* @param src 输入图像;通道数可以任意,但深度必须是 CV_8U、CV_16U、CV_16S、CV_32F 或 CV_64F 之一。
* @param dst 输出图像,与 src 具有相同的尺寸和类型。
* @param kernel 用于腐蚀操作的结构元素;如果 `element = Mat()`,则使用一个 `3 x 3` 的矩形结构元素。
* 可以使用 #getStructuringElement 来创建结构元素。
* @param anchor 结构元素内锚点的位置;默认值 (-1, -1) 表示锚点位于结构元素的中心。
* @param iterations 腐蚀操作的执行次数。
* @param borderType 像素外推方法,见 #BorderTypes。#BORDER_WRAP 不支持。
* @param borderValue 常量边界情况下的边界值
*
* @sa dilate, morphologyEx, getStructuringElement
*/
CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
示例代码
#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 imgCray,imgBlur,imgCanny,imgDil,imgErode;//定义四个Mat变量,用于存储处理后的图片
cvtColor(img,imgCray,COLOR_BGR2GRAY); //灰度图像转换
GaussianBlur(img,imgBlur,Size(5,5),5,0); //高斯模糊,size(奇数,奇数)里面只能使用奇数注意!!!
Canny(imgCray, imgCanny, 40, 120);//Canny边缘检测
//固定写法
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); //定义卷积核:MORPH_RECT矩形结构,Size(3,3)表示卷积核大小
dilate(imgCanny, imgDil, kernel); //膨胀
erode(imgDil,imgErode, kernel); //腐蚀
imshow("img", img);//显示图片
imshow("img Cray", imgCray);//显示灰度图片
imshow("img Blur", imgBlur);//显示高斯模糊图片
imshow("img Blur to Canny", imgCanny);//显示Canny边缘检测图片
imshow("img Canny to Dil", imgDil);//显示膨胀图片
imshow("img Dil to Erode", imgErode);//显示腐蚀图片
waitKey(0);//延时,等待按键按下
destroyAllWindows();//销毁所有窗口
}
三、(图像缩放和裁剪)Chapter3-ResizeAndCrop
主要内容:
- resize()方法:对图片进行缩放
//指定大小缩放,参数:原图,缩放后的图片,缩放后的宽和高
resize( src, dst, Size(640,480));
//指定长宽缩放,参数:原图,缩放后的图片,长宽比例(不填就行),插值方式(x缩放倍数,y缩放倍数)
resize( src, dst, Size(), fx, fy)
- 裁剪图像
- Rect类:设置裁剪区域
- 利用Mat类的重载构造函数,将裁剪后的图片赋值给Mat对象
//设置裁剪区域,参数:左上角坐标,右下角坐标,裁剪区域的宽和高
Rect roi(100, 100, 200, 200); //设置裁剪区域
Mat imgCrop = img(roi);
//或者Mat imgCrop = img(Rect( 100, 100, 200, 200 ));
示例代码:
void main() {
string path = "Resources/test.png";
Mat img = imread(path);
Mat imgResize,imgCrop;
//调用resize()函数对图片进行缩放,参数:原图,缩放后的图片,缩放比例,插值方式(x缩放倍数,y缩放倍数)
resize(img, imgResize, Size(),0.5,0.5);
//调用Rect()函数设置裁剪区域,参数:左上角坐标,右下角坐标,裁剪区域的宽和高
//Rect roi(100, 100, 200, 200); //设置裁剪区域
//imgCrop = img(roi);
imgCrop = img(Rect(100, 100, 200, 200));
imshow("img", img);//显示原图
imshow("imgResize", imgResize);//显示缩放后的图片
imshow("imgCrop", imgCrop);//显示裁剪后的图片
waitKey(0);
}
四、(图像绘图和文本显示)Chapter4-DrawSharpsAndText
主要内容:
1.画圆:
circle(img,Point(256, 256),100,Scalar(0, 0, 255 ),10);
参数:图像,圆心坐标,半径,颜色,线宽(FILLED:表示填充)
2.画矩形:
rectangle(img,Point(100, 100),Point(300, 300),Scalar(0, 255, 0),5);
参数:图像,矩形左上角坐标,矩形右下角坐标,颜色,线宽(FILLED:表示填充)
3.画线:
line(img,Point(130, 296),Point(382, 296),Scalar(255, 255, 255),2);
参数:图像,起点坐标,终点坐标,颜色,线宽
4.文本显示:
putText(img, “OpenCV - amos”, Point(137, 262), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 69, 255), 3);
参数:图像,文字内容,左上角坐标,字体,字体大小,颜色,字体粗细
示例代码:
void main() {
// Blank Image
//设置图像大小和通道数,参数:图像大小(宽x高),图像类型,颜色空间Scalar()
Mat img(512, 512, CV_8UC3, Scalar(255, 255, 255));
//画圆,参数:图像,圆心坐标,半径,颜色,线宽(FILLED:表示填充)
//circle(img,Point(256, 256),100,Scalar(0, 0, 255 ),10);
circle(img,Point(256, 256),155,Scalar(0, 69, 255 ),FILLED);
//画矩形,参数:图像,矩形左上角坐标,矩形右下角坐标,颜色,线宽(FILLED:表示填充)
//rectangle(img,Point(100, 100),Point(300, 300),Scalar(0, 255, 0),5);
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),2);
//写字,参数:图像,文字内容,左上角坐标,字体,字体大小,颜色,字体粗细
putText(img, "OpenCV - amos", Point(137, 262), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 69, 255), 3);//注意中文不能正常显示
imshow("Image", img);
waitKey(0);
}
五、(扭曲图像转换)Chapter5-WarpImages——扭曲图像的透视
主要内容:
仅需要两个函数:实现透视变换的函数getPerspectiveTransform()和实现透视变换的函数warpPerspective()。
matrix = getPerspectiveTransform(src_K, dst);//计算透视变换矩阵,参数:源图像四个角点坐标、目标图像四个角点坐标
warpPerspective(img, imgWarp_K, matrix, Point(w, h));//透视变换,参数:原图、目标图、变换矩阵、目标图大小
(1)实现前提:构建原图像和目标图像的顶点坐标
Point2f src_K[4] = { {529,142},{771,190},{405,395},{674,457} };//构造源图像四个角点坐标
Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };//构造目标图像四个角点坐标
(2)然后调用getPerspectiveTransform()和warpPerspective()函数即可实现图像的透视变换。
(3)显示图像imshow();
示例代码:
void main() {
string path = "Resources/cards.jpg";
Mat img = imread(path);
Mat matrix, imgWarp_K, imgWarp_J, imgWarp_9, imgWarp_Q;
float w = 250, h = 350;
Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };//构造目标图像四个角点坐标
//扑克牌K的原图的四个角点坐标
Point2f src_K[4] = { {529,142},{771,190},{405,395},{674,457} };//构造源图像四个角点坐标
//扑克牌J的原图的四个角点坐标
Point2f src_J[4] = { {777,105},{1018,82},{846,361},{1115,335} };//构造源图像四个角点坐标
//扑克牌9的原图的四个角点坐标
Point2f src_9[4] = { {737,383},{1022,439},{647,707},{974,780} };//构造源图像四个角点坐标
//扑克牌Q的原图的四个角点坐标
Point2f src_Q[4] = { {61,319},{341,276},{92,634},{404,572} };//构造源图像四个角点坐标
matrix = getPerspectiveTransform(src_K, dst);//计算透视变换矩阵,参数:源图像四个角点坐标、目标图像四个角点坐标
warpPerspective(img, imgWarp_K, matrix, Point(w, h));//透视变换,参数:原图、目标图、变换矩阵、目标图大小
for (int i = 0; i < 4; i++) circle(img, src_K[i], 10, Scalar(0, 0, 255), FILLED);//画出K的源图像的四个角点
matrix = getPerspectiveTransform(src_J, dst);//计算透视变换矩阵,参数:源图像四个角点坐标、目标图像四个角点坐标
warpPerspective(img, imgWarp_J, matrix, Point(w, h));//透视变换,参数:原图、目标图、变换矩阵、目标图大小
for (int i = 0; i < 4; i++) circle(img, src_J[i], 10, Scalar(0, 0, 255), FILLED);//画出J的源图像的四个角点
matrix = getPerspectiveTransform(src_9, dst);//计算透视变换矩阵,参数:源图像四个角点坐标、目标图像四个角点坐标
warpPerspective(img, imgWarp_9, matrix, Point(w, h));//透视变换,参数:原图、目标图、变换矩阵、目标图大小
for (int i = 0; i < 4; i++) circle(img, src_9[i], 10, Scalar(0, 0, 255), FILLED);//画出9的源图像的四个角点
matrix = getPerspectiveTransform(src_Q, dst);//计算透视变换矩阵,参数:源图像四个角点坐标、目标图像四个角点坐标
warpPerspective(img, imgWarp_Q, matrix, Point(w, h));//透视变换,参数:原图、目标图、变换矩阵、目标图大小
for (int i = 0; i < 4; i++) circle(img, src_Q[i], 10, Scalar(0, 0, 255), FILLED);//画出Q的源图像的四个角点
imshow("Image", img);
imshow("Image Warp_K", imgWarp_K);
imshow("Image Warp_J", imgWarp_J);
imshow("Image Warp_9", imgWarp_9);
imshow("Image Warp_Q", imgWarp_Q);
waitKey(0);
}
六、(颜色筛选)Chapter6-ColorDetection
主要内容:
1.图像空间转换
cvtColor();//
2.图像分割(掩膜)
inRange(imgHSV, lower, upper, mask);//对图像进行颜色分割(掩膜:范围内为白色,范围外为黑色),参数:输入图像,下限,上限,输出图像
3.opencv窗口控件:窗口,滑动条
namedWindow(“Trackbars”,(600,250));//新建一个窗口,参数:窗口名称,窗口大小
createTrackbar(“Hue Min”, “Trackbars”, &hmin, 179);//创建滑动条.参数:滑动条名称,窗口名称,滑动条值指针,最大值
示例代码:
void main()
{
String path = "Resources/shapes.png";
Mat img = imread(path);
Mat imgHSV, mask;//定义HSV图像和掩膜图像
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;
Scalar lower(hmin, smin, vmin);//设置颜色的下限
Scalar upper(hmax, smax, vmax);//设置颜色的下限
cvtColor(img, imgHSV, COLOR_BGR2HSV);//原图转换为HSV图像
//创建窗口和滚动条
namedWindow("Trackbars",(600,250));//新建一个窗口,参数:窗口名称,窗口大小
createTrackbar("Hue Min", "Trackbars", &hmin, 179);//创建滑动条.参数:滑动条名称,窗口名称,滑动条值指针,最大值
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) {
//获取滑动条的值
hmin = getTrackbarPos("Hue Min", "Trackbars");
hmax = getTrackbarPos("Hue Max", "Trackbars");
smin = getTrackbarPos("Sat Min", "Trackbars");
smax = getTrackbarPos("Sat Max", "Trackbars");
vmin = getTrackbarPos("Val Min", "Trackbars");
vmax = getTrackbarPos("Val Max", "Trackbars");
//设置颜色的下限和上限
lower = Scalar(hmin, smin, vmin);
upper = Scalar(hmax, smax, vmax);
//对图像进行颜色分割(掩膜:范围内为白色,范围外为黑色),参数:输入图像,下限,上限,输出图像
inRange(imgHSV, lower, upper, mask);
imshow("Image", img);//显示原图
imshow("Image HSV", imgHSV);//显示HSV图像
imshow("Image Mask", mask);//显示颜色区域
waitKey(1);//延时1ms
}
七、(图形识别)Chapter7-ShapeOrContourDetection
主要内容:
/*学习笔记:
作者:凡
时间:2025年3月30日21:52:53
内容:颜色检测是指识别图像中特定颜色的物体,并对其进行标记。OpenCV 提供了以下几种颜色检测的方法:
1.图像预处理;得到边缘检测图像(可膨胀或腐蚀处理)
2.检测轮廓findContours();
3.制定精度逼近多边形approxPolyDP();
4.寻找多边形最小外接矩形进行图像标识boundingRect();
5.外接矩形左上角显示文字putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 1);
步骤详解:
1.图像预处理;得到边缘检测图像(可膨胀或腐蚀处理)
首先,我们需要对图像进行预处理,即对图像进行灰度化、高斯模糊、边缘检测等操作,以提取图像中的边缘信息。
cvtColor(img, imgGray, COLOR_BGR2GRAY);//灰度化
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);//高斯模糊
Canny(imgBlur, imgCanny, 25, 75);//边缘检测
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));//形态学操作核
dilate(imgCanny, imgDil, kernel);//膨胀
得到的 imgDil 即为边缘检测图像。
2.检测轮廓findContours();
轮廓检测是指识别图像中物体的边界,并将其标记出来。OpenCV 提供了 findContours() 函数来实现轮廓检测。
vector<vector<Point>> contours;//存储若干个轮廓信息
vector<Vec4i> hierarchy;//层级
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
得到的 contours 即为图像中的所有轮廓。
3.制定精度逼近多边形approxPolyDP();
在实际应用中,我们可能需要对轮廓进行精度逼近,以提高轮廓的精确度。OpenCV 提供了 approxPolyDP() 函数来实现精度逼近。
其中精度可以用其周长(arcLength(contours[i], true))来作为精度参考
//以指定的精度逼近多边形曲线。参数:输入的曲线、输出的近似曲线、近似精度(精度越小逼近越精确)、是否闭合
approxPolyDP(contours[i], conPoly[i], 0.02 * peri , true); //近似多边形
得到 conPoly 即为精度逼近后的多边形。
4.寻找多边形最小外接矩形进行图像标识boundingRect();
在实际应用中,我们可能需要对轮廓进行最小外接矩形的寻找,以确定轮廓的形状和位置。OpenCV 提供了 boundingRect() 函数来实现最小外接矩形的寻找。
vector<Rect> boundRect(contours.size());//存储若干个轮廓的最小外接矩形信息
//或者使用 Rect boundRect[contours.size()];
for (int i = 0; i < contours.size(); i++) {
boundRect[i] = boundingRect(conPoly[i]);//寻找最小外接矩形
}
得到 boundRect 即为轮廓的最小外接矩形。
5.外接矩形左上角显示文字putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 1);
在实际应用中,我们可能需要在图像中对轮廓进行标记,以便于后续的分析和处理。OpenCV 提供了 putText() 函数来实现图像标记。
for (int i = 0; i < contours.size(); i++) {
string objectType = "Shape";//对象类型
putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 1);
}
得到标记后的图像。
总结:颜色检测是指识别图像中特定颜色的物体,并对其进行标记。OpenCV 提供了几种颜色检测的方法,包括图像预处理、轮廓检测、精度逼近多边形、最小外接矩形寻找、图像标记等。通过这些方法,我们可以对图像进行分析、处理和识别。
*/
主要方法详解:
findContours()//寻找轮廓
//寻找轮廓,参数:图像、轮廓、层级、模式、近似方法
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//寻找轮廓
/**
* @brief 在二值图像中查找轮廓。
*
* 该函数使用文献 @cite Suzuki85 中提到的算法从二值图像中提取轮廓。
* 轮廓在形状分析、目标检测与识别等方面是非常有用的工具。
* 可参考OpenCV示例目录中的 squares.cpp 文件。
* @note 从OpenCV 3.2版本开始,此函数不会修改源图像。
*
* @param image 源图像,为8位单通道图像。非零像素会被视为1,零像素保持为0,
* 因此该图像被当作二值图像处理。你可以使用 #compare、#inRange、#threshold、
* #adaptiveThreshold、#Canny 等函数将灰度图或彩色图转换为二值图像。
* 如果 mode 参数设置为 #RETR_CCOMP 或 #RETR_FLOODFILL,输入图像也可以是32位整数标记图像(CV_32SC1)。
* @param contours 检测到的轮廓。每个轮廓都存储为一个点的向量(例如 std::vector<std::vector<cv::Point> >)。
* @param hierarchy 可选的输出向量(例如 std::vector<cv::Vec4i>),包含有关图像拓扑结构的信息。
* 它的元素数量与轮廓数量相同。对于第 i 个轮廓 contours[i],hierarchy[i][0]、hierarchy[i][1]、
* hierarchy[i][2] 和 hierarchy[i][3] 分别被设置为同一层级中下一和上一轮廓、第一个子轮廓和父轮廓在 contours 中的基于0的索引。
* 如果对于轮廓 i 没有下一、上一、父轮廓或嵌套轮廓,hierarchy[i] 中对应的元素将为负数。
* @note 在Python中,hierarchy 嵌套在一个顶级数组中。使用 hierarchy[0][i] 来访问第 i 个轮廓的层级元素。
* @param mode 轮廓检索模式,详见 #RetrievalModes
* @param method 轮廓近似方法,详见 #ContourApproximationModes
* @param offset 可选的偏移量,每个轮廓点都会按此偏移量进行移动。
* 如果轮廓是从图像的感兴趣区域(ROI)中提取的,然后需要在整个图像上下文中进行分析,此参数会很有用。
*/
CV_EXPORTS_W void findContours( InputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());
drawContours()//绘制轮廓
//绘制轮廓,参数:图像、轮廓、轮廓参数(负数表示绘制所有轮廓)、颜色、线宽
drawContours(img, contours, -1, Scalar(0, 0, 0), 3);//绘制轮廓
/**
* @brief 绘制轮廓的轮廓线或填充轮廓区域。
*
* 当 \f$\texttt{thickness} \ge 0\f$ 时,该函数在图像中绘制轮廓的轮廓线;当 \f$\texttt{thickness}<0\f$ 时,填充轮廓所包围的区域。
* 下面的示例展示了如何从二值图像中检索连通组件并对其进行标记:
* @include snippets/imgproc_drawContours.cpp
*
* @param image 目标图像。
* @param contours 所有输入的轮廓。每个轮廓都存储为一个点向量。
* @param contourIdx 用于指定要绘制的轮廓的参数。如果为负数,则绘制所有轮廓。
* @param color 轮廓的颜色。
* @param thickness 绘制轮廓线的线宽。如果为负数(例如,thickness=#FILLED ),则绘制轮廓内部区域。
* @param lineType 线条连接方式。详见 #LineTypes
* @param hierarchy 关于轮廓层级结构的可选信息。只有当你只想绘制部分轮廓时(见 maxLevel )才需要该参数。
* @param maxLevel 绘制轮廓的最大层级。如果为0,仅绘制指定的轮廓。如果为1,函数会绘制指定轮廓及其所有嵌套轮廓。
* 如果为2,函数会绘制轮廓、所有嵌套轮廓、所有嵌套轮廓的嵌套轮廓,依此类推。只有在有层级结构信息时,该参数才会生效。
* @param offset 可选的轮廓偏移参数。将所有绘制的轮廓按指定的 \f$\texttt{offset}=(dx,dy)\f$ 进行偏移。
* @note 当 thickness=#FILLED 时,即使没有提供层级结构数据,该函数也能正确处理带有空洞的连通组件。
* 这是通过使用奇偶规则一起分析所有轮廓线来实现的。如果你有分别检索到的轮廓的联合集合,这可能会给出错误的结果。
* 为了解决这个问题,你需要对每个轮廓子组分别调用 #drawContours 函数,或者使用 contourIdx 参数遍历集合。
*/
CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX, Point offset = Point() );
arcLength();//计算轮廓的周长或曲线的长度。
/**
* @brief 计算轮廓的周长或曲线的长度。
*
* 该函数用于计算一条曲线的长度或者一个闭合轮廓的周长。
*
* @param curve 输入的二维点向量,可存储于 std::vector 或 Mat 中。
* @param closed 一个标志,用于指示曲线是否为闭合曲线。
*/
CV_EXPORTS_W double arcLength( InputArray curve, bool closed );
approxPolyDP();//以指定的精度逼近多边形曲线。
approxPolyDP(contours[i], conPoly[i], 0.02 * peri , true); //近似多边形
/**
* @brief 以指定的精度逼近多边形曲线。
*
* 函数 cv::approxPolyDP 使用更少顶点的另一条曲线/多边形来逼近一条曲线或多边形,
* 使得它们之间的距离小于或等于指定的精度。它使用的是 Douglas-Peucker 算法
* <http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm>。
*
* @param curve 存储在 std::vector 或 Mat 中的二维点的输入向量。
* @param approxCurve 逼近的结果。其类型应与输入曲线的类型相匹配。
* @param epsilon 指定逼近精度的参数。这是原始曲线与其逼近曲线之间的最大距离。
* @param closed 如果为 true,则逼近的曲线是闭合的(其第一个和最后一个顶点是相连的)。
* 否则,它是不闭合的。
*/
CV_EXPORTS_W void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, bool closed );
boundingRect();计算点集或灰度图像中非零像素的直立外接矩形。
boundRect[i] = boundingRect(conPoly[i]);//外接矩形
/**
* @brief 计算点集或灰度图像中非零像素的直立外接矩形。
*
* 该函数计算并返回指定点集或灰度图像中非零像素的最小直立外接矩形。
*
* @param array 输入的灰度图像或二维点集,存储在std::vector或Mat中。
*/
CV_EXPORTS_W Rect boundingRect( InputArray array );
示例代码:
void main() {
string path = "Resources/shapes.png";
Mat img = imread(path);//读取图像
Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;
// 预处理
cvtColor(img, imgGray, COLOR_BGR2GRAY);//灰度化
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);//高斯模糊
Canny(imgBlur, imgCanny, 25, 75);//边缘检测
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));//形态学操作核
dilate(imgCanny, imgDil, kernel);//膨胀
//getContours(imgDil, img);//轮廓检测
mygetContours(imgDil, img);//轮廓检测
imshow("Image", img);//显示图像
//imshow("Image Gray", imgGray);
//imshow("Image Blur", imgBlur);
//imshow("Image Canny", imgCanny);
//imshow("Image Dil", imgDil);
waitKey(0);
}
/**
* 找出图像中的轮廓并进行形状识别
* @param imgDil 已经进行过膨胀操作的二值图像
* @param img 原始图像,用于绘制轮廓和外接矩形
// */
void mygetContours(Mat imgDil, Mat img) {
vector<vector<Point>> contours;//解包
vector<Vec4i> hierarchy;//层级
/*vector<vector<Point>> contours;解释:
1.这个数据结构可以理解为一个二维的点集数组。外层的 vector 存储了多个轮廓,
每个轮廓由内层的 vector<Point> 表示。也就是说, contours 中的每个元素(即内层的 vector<Point>)
代表图像中的一个轮廓,而每个轮廓是由一系列的点(Point 对象)组成的。
2.在这个上下文中,“解包” 可以理解为对 vector<vector<Point>> contours 数据结构进行遍历和访问,
以获取其中存储的轮廓信息和点坐标。例如,在上面的示例代码中,我们通过两层循环遍历了 contours
中的每个轮廓和每个点,这就是一种 “解包” 操作。通过这种方式,我们可以对每个轮廓进行进一步的处理,
如计算轮廓的面积、周长、绘制轮廓等。
3.总结来说,vector<vector<Point>> contours 是一个用于存储图像轮廓信息的数据结构,“解包” 就是对
这个数据结构进行遍历和访问,以获取其中的具体信息。
*/
/*vector<Vec4i> hierarchy;解释:
1.数据结构解释
vector:它属于 C++ 标准模板库(STL)里的动态数组容器,能够动态调整大小,还支持随机访问。
Vec4i:这是 OpenCV 定义的一个四维整数向量类型,可用来存储四个整数值。在轮廓层级关系的情境下,这四个整数值分别代表特定的轮廓索引信息。
vector<Vec4i> hierarchy:整体而言,它是一个存储多个 Vec4i 元素的动态数组,每个 Vec4i 元素对应一个轮廓的层级信息。
2.Vec4i 存储的层级信息
Vec4i 中的四个整数值分别表示当前轮廓的下一个轮廓、前一个轮廓、第一个子轮廓和父轮廓的索引,具体解释如下:
hierarchy[i][0]:表示当前轮廓 i 的下一个同级轮廓的索引。若不存在下一个同级轮廓,该值为 -1。
hierarchy[i][1]:表示当前轮廓 i 的前一个同级轮廓的索引。若不存在前一个同级轮廓,该值为 -1。
hierarchy[i][2]:表示当前轮廓 i 的第一个子轮廓的索引。若没有子轮廓,该值为 -1。
hierarchy[i][3]:表示当前轮廓 i 的父轮廓的索引。若没有父轮廓(即该轮廓为最顶层轮廓),该值为 -1。
*/
//寻找轮廓,参数:二值化后要处理的图像、轮廓、层级、模式、近似方法
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//寻找轮廓
//绘制轮廓,参数:图像、轮廓、轮廓参数(负数表示绘制所有轮廓)、颜色、线宽
//drawContours(img, contours, -1, Scalar(0, 0, 0), 3);//绘制轮廓
vector<vector<Point>> conPoly(contours.size());//多边形
vector<Rect> boundRect(contours.size());//外接矩形
String objectType;//类型
for (int i = 0; i < contours.size(); i++)
{
//计算轮廓的面积函数 int contourArea(vector<Point> contour );
int area = contourArea(contours[i]);//面积
cout << "第" << i << " 个轮廓的面积为:" << area << endl;//输出面积
if (area > 1000)//面积大于1000的轮廓
{
//计算轮廓的周长.参数:轮廓,是否为闭合曲线
double peri = arcLength(contours[i], true);//周长
//以指定的精度逼近多边形曲线。参数:输入的曲线、输出的近似曲线、近似精度、是否闭合
approxPolyDP(contours[i], conPoly[i], 0.02 * peri , true); //近似多边形
//drawContours(img, conPoly, i, Scalar(0, 0, 0), 3);//绘制轮廓,参数:图像、轮廓、轮廓参数(负数表示绘制所有轮廓)、颜色、线宽
cout << "识别出来的轮廓的点数:" << conPoly[i].size() << endl;//输出点数
//绘制外接矩形,放在下面绘制了
boundRect[i] = boundingRect(conPoly[i]);//外接矩形,参数:输入的灰度图像或二维点集,存储在std::vector或Mat中
//rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);//绘制外接矩形
int objCor = (int)conPoly[i].size();//点数
if (objCor == 3) { objectType = "Tri"; }//三角形
if (objCor == 4)
{
float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
cout << aspRatio << endl;//输出长宽比
if (aspRatio > 0.95 && aspRatio < 1.05) { objectType = "Square"; }
else { objectType = "Rect"; }
}
if (objCor > 4) { objectType = "Circle"; }//圆形
drawContours(img, conPoly, i, Scalar(0, 0, 0), 3);//绘制轮廓,参数:图像、轮廓、轮廓参数(负数表示绘制所有轮廓)、颜色、线宽
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);//绘制外接矩形
putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 1);//绘制类型
}
}
}
八、(人脸识别)Chapter8-FaceDetection
主要内容:
CascadeClassifier类:
CascadeClassifier` 是 OpenCV 库中的一个类,它的作用是实现级联分类器。级联分类器是一种基于机器学习的目标检测方法,常用于人脸、眼睛等物体的检测。
示例:CascadeClassifier faceCascade;// 构建人脸检测器
load()方法
faceCascade.load(“Resources/haarcascade_frontalface_default.xml”);// 加载XML文件,参数:XML文件路径
detectMultiScale()方法
faceCascade.detectMultiScale(img, faces, 1.1, 10);// 人脸检测,参数:输入图像、输出矩形坐标、缩放比例、最小检测窗口大小
主要内容详解:
detectMultiScale()方法
/**
* @brief 在输入图像中检测不同大小的物体。检测到的物体将以矩形列表的形式返回。
*
* @param image 类型为 CV_8U 的矩阵,包含要进行物体检测的图像。
* @param objects 矩形向量,其中每个矩形包含检测到的物体,这些矩形可能部分超出原始图像范围。
* @param scaleFactor 该参数指定在每次图像缩放时图像尺寸缩小的比例。
* @param minNeighbors 该参数指定每个候选矩形需要拥有多少个相邻矩形才能被保留。
* @param flags 对于旧的级联分类器,该参数的含义与函数 cvHaarDetectObjects 中的相同。对于新的级联分类器,此参数不使用。
* @param minSize 物体可能的最小尺寸。小于该尺寸的物体将被忽略。
* @param maxSize 物体可能的最大尺寸。大于该尺寸的物体将被忽略。如果 `maxSize == minSize`,则模型将在单一尺度上进行评估。
*/
CV_WRAP void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
double scaleFactor = 1.1,
int minNeighbors = 3, int flags = 0,
Size minSize = Size(),
Size maxSize = Size() );
CascadeClassifier类详解:
/**
* @示例 samples/cpp/facedetect.cpp
* 此程序展示了级联分类器类的使用方法。
* \image html Cascade_Classifier_Tutorial_Result_Haar.jpg "示例截图" width=321 height=254
*/
/**
* @brief 用于物体检测的级联分类器类。
*/
class CV_EXPORTS_W CascadeClassifier
{
public:
/**
* @brief 默认构造函数,创建一个空的级联分类器对象。
*/
CV_WRAP CascadeClassifier();
/**
* @brief 从文件加载分类器。
*
* @param filename 要从中加载分类器的文件名称。
*/
CV_WRAP CascadeClassifier(const String& filename);
/**
* @brief 析构函数,释放级联分类器对象占用的资源。
*/
~CascadeClassifier();
/**
* @brief 检查分类器是否已成功加载。
*
* @return 如果分类器未加载则返回 true,已加载则返回 false。
*/
CV_WRAP bool empty() const;
/**
* @brief 从文件加载分类器。
*
* @param filename 要从中加载分类器的文件名称。该文件可以包含由 haartraining 应用程序训练的旧 HAAR 分类器,
* 或者由 traincascade 应用程序训练的新级联分类器。
* @return 如果加载成功则返回 true,失败则返回 false。
*/
CV_WRAP bool load( const String& filename );
/**
* @brief 从 FileStorage 节点读取分类器。
*
* @note 文件只能包含由 traincascade 应用程序训练的新级联分类器。
* @param node 用于读取分类器的 FileStorage 节点。
* @return 如果读取成功则返回 true,失败则返回 false。
*/
CV_WRAP bool read( const FileNode& node );
/**
* @brief 在输入图像中检测不同大小的物体。检测到的物体将以矩形列表的形式返回。
*
* @param image 类型为 CV_8U 的矩阵,包含要进行物体检测的图像。
* @param objects 矩形向量,其中每个矩形包含检测到的物体,这些矩形可能部分超出原始图像范围。
* @param scaleFactor 该参数指定在每次图像缩放时图像尺寸缩小的比例。
* @param minNeighbors 该参数指定每个候选矩形需要拥有多少个相邻矩形才能被保留。
* @param flags 对于旧的级联分类器,该参数的含义与函数 cvHaarDetectObjects 中的相同。对于新的级联分类器,此参数不使用。
* @param minSize 物体可能的最小尺寸。小于该尺寸的物体将被忽略。
* @param maxSize 物体可能的最大尺寸。大于该尺寸的物体将被忽略。如果 `maxSize == minSize`,则模型将在单一尺度上进行评估。
*/
CV_WRAP void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
double scaleFactor = 1.1,
int minNeighbors = 3, int flags = 0,
Size minSize = Size(),
Size maxSize = Size() );
/**
* @重载
*
* @param image 类型为 CV_8U 的矩阵,包含要进行物体检测的图像。
* @param objects 矩形向量,其中每个矩形包含检测到的物体,这些矩形可能部分超出原始图像范围。
* @param numDetections 对应物体的检测次数向量。一个物体的检测次数是指合并在一起形成该物体的相邻正分类矩形的数量。
* @param scaleFactor 该参数指定在每次图像缩放时图像尺寸缩小的比例。
* @param minNeighbors 该参数指定每个候选矩形需要拥有多少个相邻矩形才能被保留。
* @param flags 对于旧的级联分类器,该参数的含义与函数 cvHaarDetectObjects 中的相同。对于新的级联分类器,此参数不使用。
* @param minSize 物体可能的最小尺寸。小于该尺寸的物体将被忽略。
* @param maxSize 物体可能的最大尺寸。大于该尺寸的物体将被忽略。如果 `maxSize == minSize`,则模型将在单一尺度上进行评估。
*/
CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
CV_OUT std::vector<int>& numDetections,
double scaleFactor=1.1,
int minNeighbors=3, int flags=0,
Size minSize=Size(),
Size maxSize=Size() );
/**
* @重载
* 此函数允许你获取分类的最终阶段决策确定性。
* 为此,需要将 `outputRejectLevels` 设置为 true,并提供 `rejectLevels` 和 `levelWeights` 参数。
* 对于每个检测结果,`levelWeights` 将包含最终阶段分类的确定性。
* 这个值可以用来区分强分类和弱分类。
*
* 以下是一个如何有效使用它的代码示例:
* @code
* Mat img;
* vector<double> weights;
* vector<int> levels;
* vector<Rect> detections;
* CascadeClassifier model("/path/to/your/model.xml");
* model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true);
* cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl;
* @endcode
*
* @param image 类型为 CV_8U 的矩阵,包含要进行物体检测的图像。
* @param objects 矩形向量,其中每个矩形包含检测到的物体,这些矩形可能部分超出原始图像范围。
* @param rejectLevels 拒绝级别向量,用于存储每个检测的拒绝级别。
* @param levelWeights 级别权重向量,用于存储每个检测的最终阶段分类确定性。
* @param scaleFactor 该参数指定在每次图像缩放时图像尺寸缩小的比例。
* @param minNeighbors 该参数指定每个候选矩形需要拥有多少个相邻矩形才能被保留。
* @param flags 对于旧的级联分类器,该参数的含义与函数 cvHaarDetectObjects 中的相同。对于新的级联分类器,此参数不使用。
* @param minSize 物体可能的最小尺寸。小于该尺寸的物体将被忽略。
* @param maxSize 物体可能的最大尺寸。大于该尺寸的物体将被忽略。如果 `maxSize == minSize`,则模型将在单一尺度上进行评估。
* @param outputRejectLevels 是否输出拒绝级别和级别权重。如果为 true,则会填充 `rejectLevels` 和 `levelWeights`。
*/
CV_WRAP_AS(detectMultiScale3) void detectMultiScale( InputArray image,
CV_OUT std::vector<Rect>& objects,
CV_OUT std::vector<int>& rejectLevels,
CV_OUT std::vector<double>& levelWeights,
double scaleFactor = 1.1,
int minNeighbors = 3, int flags = 0,
Size minSize = Size(),
Size maxSize = Size(),
bool outputRejectLevels = false );
/**
* @brief 检查分类器是否为旧格式的级联分类器。
*
* @return 如果是旧格式则返回 true,否则返回 false。
*/
CV_WRAP bool isOldFormatCascade() const;
/**
* @brief 获取原始窗口大小。
*
* @return 原始窗口的大小。
*/
CV_WRAP Size getOriginalWindowSize() const;
/**
* @brief 获取特征类型。
*
* @return 特征类型的整数表示。
*/
CV_WRAP int getFeatureType() const;
/**
* @brief 获取旧级联分类器的指针。
*
* @return 指向旧级联分类器的指针。
*/
void* getOldCascade();
/**
* @brief 将旧的级联分类器文件转换为新的级联分类器文件。
*
* @param oldcascade 旧级联分类器文件的名称。
* @param newcascade 新级联分类器文件的名称。
* @return 如果转换成功则返回 true,失败则返回 false。
*/
CV_WRAP static bool convert(const String& oldcascade, const String& newcascade);
/**
* @brief 设置掩码生成器。
*
* @param maskGenerator 指向掩码生成器对象的智能指针。
*/
void setMaskGenerator(const Ptr<BaseCascadeClassifier::MaskGenerator>& maskGenerator);
/**
* @brief 获取掩码生成器。
*
* @return 指向掩码生成器对象的智能指针。
*/
Ptr<BaseCascadeClassifier::MaskGenerator> getMaskGenerator();
/**
* @brief 指向基础级联分类器的智能指针。
*/
Ptr<BaseCascadeClassifier> cc;
};
示例代码:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void mytest();
/// 图像处理 //
// 主函数,用于加载图像并检测人脸
void main() {
//mytest();
string path = "Resources/test.png";
Mat img = imread(path);
Mat face;
CascadeClassifier faceCascade;// 构建人脸检测器
faceCascade.load("Resources/haarcascade_frontalface_default.xml");// 加载XML文件,参数:XML文件路径
if (faceCascade.empty()) { cout << "XML file not loaded" << endl; }// 检查XML文件是否加载成功
vector<Rect> faces;// 存储人脸的矩形坐标
faceCascade.detectMultiScale(img, faces, 1.1, 10);// 人脸检测,参数:输入图像、输出矩形坐标、缩放比例、最小检测窗口大小
cout << "Faces: " << faceCascade.getFeatureType() <<endl; // 获取人脸检测器的特征类型
for (int i = 0; i < faces.size(); i++)
{
rectangle(img, faces[i].tl(), faces[i].br(), Scalar(255, 0, 255), 3);// 绘制矩形框
//裁剪出人脸部分
face = img(faces[i]);
//左上角写入人脸序号
//putText(img, to_string(i + 1), faces[i].tl(), FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 255, 255), 2);
}
imshow("Image", img);// 显示图像
imshow("Face", face);// 显示人脸部分
waitKey(0);// 等待按键
}
九、鸣谢
b站博主:
https://www.bilibili.com/video/BV11A411T7rL/?spm_id_from=333.1391.0.0&vd_source=52d8c13b128556087ebf0611685d0cfe
参考youtube.com博主资料:
https://www.computervision.zone/courses/opencv-cv/?ld-registered=true
十、文章说明
此为本人学习过程中所记录的笔记,若存在错漏或不够详尽的地方,还望大家不吝赐教,予以谅解。
更新时间:2025年3月31日18:47:18