输出数据自动分配内存【OpenCV_C++_VS】
·
2.3.3输出数据自动分配内存
1.代码功能描述
这段 C++ 代码使用 OpenCV 库实现了从摄像头捕获视频流,并对每一帧图像进行处理,将其转换为灰度图像,然后进行高斯模糊和 Canny 边缘检测,最后在窗口中实时显示处理后的边缘图像。当用户按下任意键时,程序会停止运行。
#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
using namespace cv;
int main(int,char**)
{
VideoCapture cap(0);
if (!cap.isOpened()) return -1;
Mat frame, edges;
namedWindow("edges", 1);
for (;;) {
cap >> frame;
cvtColor(frame, edges, COLOR_BGR2GRAY);
GaussianBlur(edges, edges, Size(7, 7), 1.5, 1.5);
Canny(edges, edges, 0, 30, 3);
imshow("edges", edges);
if (waitKey(30) >= 0)
break;
}
return 0;
}
2.代码逐行解释
(1)OpenCV 库的头文件
#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
opencv2/opencv.hpp是一个综合性的头文件,包含了 OpenCV 的大部分核心功能;opencv2/highgui.hpp含用于创建窗口、显示图像和处理用户输入的高级图形用户界面相关的功能。
(2) using 指令的使用
using namespace cv;
- 将
cv命名空间中的所有标识符引入到全局命名空间中,这样在后续代码中就可以直接使用 OpenCV 的类和函数,而不需要加上cv::前缀。
(3) 程序的入口函数 main
int main(int,char**)
{
int表示函数返回一个整数值(int, char**)表示函数接受两个参数,但这里没有使用它们的具体名称,意味着在函数内部不会使用这两个参数。char**C 和 C++ 中是一个指向字符指针的指针,常用于表示字符串数组或者作为main函数的参数来接收命令行参数。
(4)VideoCapture打开摄像头
VideoCapture cap(0);
if (!cap.isOpened()) return -1;
VideoCapture cap(0);:创建一个VideoCapture对象cap,并尝试打开默认的摄像头设备(设备编号为 0)。if (!cap.isOpened()) return -1;:检查摄像头是否成功打开。- 如果打开失败,
cap.isOpened()会返回false,此时程序会返回 -1 并终止。
VideoCapture并不是一个函数,而是 OpenCV 库中用于处理视频捕获的一个类。这个类提供了一系列的方法和属性,可用于从摄像头、视频文件或网络视频流中读取视频帧。下面为你详细介绍VideoCapture类的相关内容。
VideoCapture类
VideoCapture类主要用于捕获视频数据,支持多种视频源,包括:
- 摄像头设备:可以连接到计算机的摄像头,实时捕获视频流。
- 视频文件:读取本地存储的视频文件,如
.mp4、.avi等格式。- 网络视频流:通过网络协议(如 RTSP)获取远程视频流。
构造函数
VideoCapture类有几个常用的构造函数:
VideoCapture():默认构造函数,创建一个未初始化的VideoCapture对象,后续需要使用open()方法来打开视频源。VideoCapture cap; cap.open(0); // 打开默认摄像头
VideoCapture(int index):通过设备索引打开摄像头设备。index是摄像头的编号,通常默认摄像头的编号为 0,如果有多个摄像头,可以使用不同的编号来选择。VideoCapture cap(0); // 打开默认摄像头VideoCapture(const std::string& filename):通过视频文件的路径或网络视频流的 URL 来打开视频源。cv::VideoCapture cap("path/to/video.mp4"); // 打开本地视频文件常用方法
bool isOpened():检查视频捕获设备或文件是否成功打开。如果成功打开,返回true;否则返回false。VideoCapture cap(0); if (!cap.isOpened()) { cerr << "无法打开摄像头" << endl; return -1; }VideoCapture& operator>>(Mat& image):从视频源中读取一帧图像,并将其存储在Mat对象中。这个操作符重载可以方便地使用>>符号来读取视频帧。Mat frame; cap >> frame; // 读取一帧图像bool read(Mat& image):与operator>>功能类似,也是从视频源中读取一帧图像并存储在Mat对象中。如果成功读取到图像,返回true;否则返回false。cv::Mat frame; bool success = cap.read(frame); if (!success) { std::cerr << "无法读取视频帧" << std::endl; }void release():释放视频捕获设备或文件的资源。在不再使用VideoCapture对象时,应该调用这个方法来释放资源。cap.release(); // 释放资源
(5)定义Mat对象
Mat frame, edges;
- 定义两个
Mat对象,frame用于存储从摄像头捕获的每一帧原始图像,edges用于存储处理后的边缘图像。
(6)创建"edges" 窗口
namedWindow("edges", 1);
- 用于显示处理后的边缘图像。参数
1表示窗口可以调整大小。
namedWindow函数
namedWindow函数属于 OpenCV 的highgui模块,该模块主要负责图形用户界面相关的操作,像窗口创建、图像显示、鼠标键盘事件处理等。借助namedWindow函数,你能够创建一个指定名称的窗口,后续可以在这个窗口里显示图像或者视频帧。函数原型
在 OpenCV 4.x 及之后的版本中,
namedWindow函数的原型如下:void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE);参数解释
winname:类型为const String&,实际上String是std::string的别名,所以这里传入的是一个字符串。此参数代表要创建的窗口的名称,该名称在整个程序里必须是唯一的,后续对窗口进行操作(如显示图像、调整窗口大小等)时,会依据这个名称来识别窗口。flags:类型为int,是一个可选参数,默认值为WINDOW_AUTOSIZE。它用于指定窗口的属性,常见的取值及含义如下:
WINDOW_KEEPRATIO:显示图像时,保持图像的纵横比(默认行为)。WINDOW_FREERATIO:显示图像时,不保持图像的纵横比。WINDOW_AUTOSIZE:窗口大小会根据显示的图像自动调整,用户无法手动改变窗口大小。WINDOW_NORMAL:可以手动调整窗口的大小。注意事项
- 窗口名称唯一性:每个窗口的名称必须是唯一的,如果尝试创建一个已经存在的窗口名称,不会创建新的窗口,而是会复用已有的窗口。
- 资源管理:在程序结束时,建议用
cv::destroyAllWindows()或cv::destroyWindow(const String& winname)来销毁创建的窗口,释放相关资源。- 显示图像顺序:
namedWindow函数通常需要在imshow函数之前调用,不过在某些情况下,如果省略namedWindow函数,imshow函数会自动创建一个默认属性的窗口。
(7)重复捕捉视频帧
for (;;) {
cap >> frame;
- 这是一个无限循环,用于不断从摄像头捕获视频帧。
cap >> frame;是将当前摄像头捕获的一帧图像读取到frame对象中。
(8)灰度图像及空间转化
cvtColor(frame, edges, COLOR_BGR2GRAY);
- 使用
cvtColor函数将frame中的彩色图像转换为灰度图像,并将结果存储在edges中。 COLOR_BGR2GRAY表示从 BGR 颜色空间转换为灰度颜色空间。
cvtColor函数void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0);参数解释
src:类型为InputArray,表示输入的图像。它可以是cv::Mat类型的对象,代表要进行颜色空间转换的原始图像。dst:类型为OutputArray,表示输出的图像。转换后的图像将存储在这个对象中,通常也是cv::Mat类型。code:类型为int,是一个非常关键的参数,用于指定颜色空间转换的代码。OpenCV 提供了众多预定义的转换代码,常见的如下:
cv::COLOR_GRAY2BGR:将灰度图像转换为 BGR 颜色空间的图像。cv::COLOR_BGR2HSV:将 BGR 颜色空间的图像转换为 HSV 颜色空间的图像。cv::COLOR_BGR2GRAY:将 BGR(Blue, Green, Red)颜色空间的图像转换为灰度图像。dstCn:类型为int,是一个可选参数,默认值为 0。它用于指定输出图像的通道数。如果为 0,则输出图像的通道数将根据转换代码自动确定。
(9)高斯模糊处理
GaussianBlur(edges, edges, Size(7, 7), 1.5, 1.5);
- 对灰度图像
edges进行高斯模糊处理,以减少图像中的噪声。 Size(7, 7)表示高斯核的大小为 7x7,两个1.5分别表示 X 和 Y 方向的高斯核标准差。
函数原型
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT);参数解释
src:类型为InputArray,表示输入的图像。它通常是一个cv::Mat对象,即要进行高斯模糊处理的原始图像。该图像可以是单通道(如灰度图像)或多通道(如彩色图像)。dst:类型为OutputArray,表示输出的图像。经过高斯模糊处理后的图像将存储在这个对象中,同样一般是cv::Mat类型。其尺寸和类型与输入图像相同。ksize:类型为Size,表示高斯核的大小。Size是一个包含两个整数的结构体,分别表示高斯核的宽度和高度,如Size(3, 3)表示一个 3x3 的高斯核。这两个值必须是正奇数,如果为 0,则会根据sigmaX和sigmaY自动计算核的大小。sigmaX:类型为double,表示高斯核在 X 方向上的标准差。标准差决定了高斯核的形状,较大的标准差会使模糊效果更明显。sigmaY:类型为double,是一个可选参数,默认值为 0。表示高斯核在 Y 方向上的标准差。如果该值为 0,则会将其设为与sigmaX相同的值。borderType:类型为int,是一个可选参数,默认值为BORDER_DEFAULT。它用于指定图像边界的处理方式。在进行卷积操作时,图像边界的像素没有完整的邻域,需要采用特定的方法来处理这些边界像素,常见的处理方式包括复制边界像素、镜像边界像素等。
(10)Canny 函数对模糊后的图像进行边缘检测
Canny(edges, edges, 0, 30, 3);
0和30分别是 Canny 算法的低阈值和高阈值,3表示使用的 Sobel 算子的孔径大小。
Canny函数void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false);参数解释
image:类型为InputArray,表示输入的图像。通常要求输入图像为单通道的灰度图像,因为 Canny 算法是基于灰度值的变化来检测边缘的。可以是cv::Mat类型的对象。edges:类型为OutputArray,表示输出的边缘图像。检测到的边缘将存储在这个对象中,同样一般为cv::Mat类型。输出图像是一个二值图像,边缘像素值为 255,非边缘像素值为 0。threshold1:类型为double,是 Canny 算法中的低阈值。在双阈值处理阶段,用于判断像素是否可能为边缘像素。低于该阈值的像素会被认为不是边缘像素。threshold2:类型为double,是 Canny 算法中的高阈值。高于该阈值的像素会被认为是强边缘像素,直接保留。apertureSize:类型为int,是一个可选参数,默认值为 3。它表示 Sobel 算子的孔径大小,用于计算图像的梯度。孔径大小必须是 3、5 或 7,分别对应不同的 Sobel 核大小。L2gradient:类型为bool,是一个可选参数,默认值为false。用于指定是否使用更精确的 L2 范数来计算梯度幅值。如果设置为true,则使用 L2 范数计算;如果设置为false,则使用近似的 L1 范数计算,L1 范数计算速度更快,但 L2 范数计算结果更精确。
Canny 边缘检测算法步骤
- 高斯平滑:使用高斯滤波器对输入图像进行平滑处理,以减少图像中的噪声。在调用
Canny函数之前,通常需要手动进行这一步,或者在调用时输入已经经过高斯平滑的图像。- 计算梯度幅值和方向:使用 Sobel 算子计算图像在水平和垂直方向上的梯度,然后计算梯度幅值和方向。
- 非极大值抑制:对梯度幅值进行非极大值抑制,以细化边缘。即只保留局部梯度幅值最大的像素,将其他像素置为 0。
- 双阈值处理和边缘连接:使用两个阈值(
threshold1和threshold2)对边缘像素进行分类。高于高阈值的像素被认为是强边缘像素,低于低阈值的像素被认为不是边缘像素,介于两个阈值之间的像素如果与强边缘像素相连,则被认为是边缘像素,否则不是。
(11)显示处理后的边缘图像
imshow("edges", edges);
- 在之前创建的名为 "edges" 的窗口中显示处理后的边缘图像。
(12)检查是否有按键事件发生
if (waitKey(30) >= 0)
break;
waitKey(30)会等待 30 毫秒,检查是否有按键事件发生。- 如果有按键按下,
waitKey会返回按键的 ASCII 码值(大于等于 0),此时程序会跳出循环,结束视频捕获和处理。
(13)退出循环
}
return 0;
}
- 循环结束后,程序返回 0,表示正常退出。
3.总结
这段代码通过一系列 OpenCV 函数,实现了从摄像头捕获视频、图像预处理(颜色转换、高斯模糊)、边缘检测以及结果显示的完整流程。各函数相互协作,完成了实时视频流的边缘检测任务。同时,代码中使用的 cv::Mat 对象会自动管理内存,避免了手动内存管理的复杂性和潜在风险。
更多推荐


所有评论(0)