第3章 HighGUI图形用户界面初步
在OpenCV 架构分析一节中我们讲过,HighGUI 模块为高层GUI 图形用户界 面模块,包含媒体的输入输出、视频捕捉、图像和视频的编码解码、图形交互界 面的接口等内容。而在1.5节中讲到的VideoCapture 视频类,就是出自此HighGUI 模块。本章旨在为大家展开讲解OpenCV 中最常用到的一些交互操作,包括图像 的载入、显示和输出,为程序添加滑动条,以及鼠标操作等常用内容。
本章中,你将学到:
- 图像的载入、显示和输出到文件的详细分析
- 滑动条(Trackbar) 的创建和使用 ·OpenCV 中的鼠标操作
3.1 图像的载入、显示和输出到文件
学习过以往版本OpenCV 的读者应该都清楚,对于OpenCV1.0 时代的基于C 语言接口而建的图像存储格式 Ipllmage*,如果在退出前忘记 release 掉的话,会 造成内存泄露,而且用起来十分繁琐。我们在debug 程序的时候,往往很大一部 分时间会去纠结手动释放内存相关的问题。虽然对于小型的程序来说,手动管理 内存不是什么难题,但一旦开发的项目日益庞大,代码量达到一定的规模,我们 便会开始越来越多地纠缠于内存管理的问题,而不能把全部精力用于解决核心开 发目标。因为不合适的图像存储数据结构而疲于维护日益庞大的项目,就有些舍 本逐末的感觉了。
自踏入2.0版本的时代以来,OpenCV 采用了Mat 类作为数据结构进行图像 存取。这一改进使OpenCV 变得和几乎零门槛入门的Matlab 一样,很容易上手和 用于实际开发。新版OpenCV 中甚至有些函数名称都和Matlab 中的一样,比如大 家所熟知的imread 、imwrite 、imshow等函数。这对于广大图像处理和计算机视觉 领域的研究者们来说,的确是一件可喜可贺的事情。而这一小节中,我们主要来 详细讲解OpenCV2 、OpenCV3 入门最基本的问题,即图像的载入、显示和输出。
3.1.1 OpenCV的命名空间
OpenCV 中 的C++ 类和函数都是定义在命名空间cv 之内的,有两种方法可以 访问:第一种,是在代码开头的适当位置加上usingnamespace cv;这句代码,规 定程序位于此命名空间之内;另外一种,是在使用OpenCV 的每一个类和函数时, 都加入cv:: 命名空间。不过这种情况会很繁琐,每用一个OpenCV 的类或者函数, 都要多敲四下键盘写出cv::。所以,推荐大家在代码开头的适当位置,加上using namespace cv`;这句。
比如在写简单的OpenCV 程序的时候,以下三句可以作为标配:
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
3.1.2 Mat类简析
Mat 类是用于保存图像以及其他矩阵数据的数据结构,默认情况下其尺寸为 0。我们也可以指定其初始尺寸,比如定义一个Mat 类对象,就要写cv::Mat pic (320,640,cv::Scalar(100));
Mat 类型作为OpenCV2 、OpenCV3 新纪元的重要代表,在稍后的章节中,笔 者会花长篇幅详细讲解它,现在我们只要理解它是对应于 OpenCV1.0 时代的 IplImage,主要用来存放图像的数据结构就行了。
对于本节,我们需要用到关于 Mat的其实就简单的这样一句代码:
Mat srcImage=imread("dota.jpg");
这表示从工程目录下把一幅名为dota.jpg的 jpg 类型的图像载入到Mat 类型 的srclmage 变量中。对于这里的 imread函数,用于将图片读入Mat 类型中,会在 下文进行详细剖析。而Mat 类,也会在第4章中进行更加全面的讲解。
3.1.3 图像的载入与显示概述
在新版本的OpenCV2中,最简单的图像载入和显示只需要两句代码,非常便捷。这两句代码分别对应了两个函数,它们分别是imread()以及imshow()。
3.1.4 图像的载入: imread()函 数
首先来看 imread 函数,其用于读取文件中的图片到 OpenCV 中。可以在 OpenCV官方文档中查到它的原型,如下。
Mat imread(const string& filename,int flags=1);
(1)第一个参数,const string&类型的filename, 填我们需要载入的图片路径 名。在Windows操作系统下,OpenCV的 imread函数支持如下类型的图像载入。
- Windows 位图:.bmp,.dib
- JPEG文件:.jpeg,.jpg,*jpe
- JPEG 2000文件:*jp2
- PNG图片:*.png
- 便携文件格式:.pbm,.pgm,.ppm ·Sun rasters光栅文件:.sr,*.ras
- TIFF文件:.tiff,.tif
(2)第二个参数,int 类型的flags, 为载入标识,它指定一个加载图像的颜色 类型。可以看到它自带默认值1,所以有时候这个参数在调用时可以忽略。在看 了下面的讲解之后,我们就会发现,如果在调用时忽略这个参数,就表示载入三 通道的彩色图像。这个参数可以在OpenCV中标识图像格式的枚举体中取值。通 过转到定义,我们可以在higui_c.h中发现这个枚举的定义是这样的:
对常用标识符相应的解释:
- CV_LOAD_IMAGE_UNCHANGED—— 等价取值为-1,这个标识在新版本
中已被废置,忽略。 - CV_LOAD_IMAGE_GRAYSCALE—— 等价取值为0,如果取这个标识的
话,始终将图像转换成灰度再返回。 - CV_LOAD_IMAGE_COLOR—— 等价取值为1,如果取这个标识,总是转
换图像到彩色再返回。 - CV_LOAD_IMAGE_ANYDEPTH—— 等价取值为2,如果取这个标识,且载 入的图像的深度为16位或者32位,就返回对应深度的图像,否则,就转换为
8位图像再返回。
需要说明的是,如果输入有冲突的标志,将采用较小的数字值。比如CVLOAD_ IMAGE_COLOR|CV_LOAD_IMAGE_ANYCOLOR 将载入三通道图。而如果想 要载入最真实无损的源图像,可以选择 CV_LOAD_IMAGE_ANYDEPTH|
CV_LOAD_IMAGE_ANYCOLOR。
OpenCV3 中此处列举的CV_LOAD_IMAGE_UNCHANGED、CV_LOAD_
IMAGE_GRAYSCALE 、CV_LOAD_IMAGE_COLOR 、CV_LOAD_ IMAGE_ANYDEPTH 、CV_LOAD_IMAGE_ANYCOLOR 等宏已经失 效,但在官方文档和源代码中暂时没有发现新的可以替换它们的宏,所 以请暂时用与其等价的-1、0、1、2、4代替。相信后续OpenCV 版 本 中一定会进行修复。
因为flags 是 int 型的变量,若我们不在这个枚举体中取固定的值,可以这样进行:
- flags>0 返回一个3通道的彩色图像; * flags=0 返回灰度图像;
- flags<0 返回包含Alpha 通道的加载图像。
输出的图像默认情况下不返回Alpha 通道。如果想要载入 Alpha 通道, 这里就需要取负值。比如,将flags 取1999也是可以的,和取1的效果 一样,它们同样表示返回一个3通道的彩色图像。
另外,需要额外注意,若以彩色模式载入图像,解码后的图像会以BGR 的通道顺序进行存储,即蓝、绿、红的顺序,而不是通常的RGB 的顺序。
经过上面详细的讲解,我们一起看几个载入示例,以便多方位地掌握 imread 函数的用法。
Mat image0=imread("1.jpg",214);//载入无损的源图像
Mat imagel=imread("1.jpg",0);//载入灰度图
Mat image2=imread("1.jpg",199);//载入3通道的彩色图像
3.1.5 图像的显示:imshow()函 数
imshow(函数用于在指定的窗口中显示一幅图像),函数原型如下。
void imshow(const string&winname, InputArray mat);
- 第一个参数:const string&类型的winname, 填需要显示的窗口标识名称。
- 第二个参数:InputArray类型的mat, 填需要显示的图像。
imshow 函数用于在指定的窗口中显示图像。如果窗口是用CV_WINDOW_
AUTOSIZE (默认值)标志创建的,那么显示图像原始大小。否则,将图像进行 缩放以适合窗口。而imshow 函数缩放图像,取决于图像的深度,具体如下。 - 如果载入的图像是8位无符号类型(8-bit unsigned),就显示图像本来的样子。
- 如果图像是16位无符号类型(16-bit unsigned)或32位整型(32-bit integer), 便用像素值除以256。也就是说,值的范围是[0,255×256]映射到[0,255]。
- 如果图像是32位浮点型(32-bit floating-point),像素值便要乘以255。也 就是说,该值的范围是[0,1]映射到[0,255]。
还有一点,在窗口创建的时候,如果设定了支持OpenGL(WINDOW_OPENGL), 那么 imshow 还支持 ogl::Buffer 、ogl:Texture2D 以 及gpu::GpuMat 作为输入。
3.1.6 关于InputArray类型
对于这里的 InputArray 类型,通过对 InputArray 转到定义,我们可以在 core.hpp中查到一个typedef 声明,如下:
typedef const_InputArray& InputArray;
这其实一个类型声明引用,就是说_InputArray 和 InputArray 是一个意思,因 此我们就来做最后一步:对_InputArray 进行转到定义,可以在core.hpp 头文件中 发现InputArray 的真身。InputArray 的源代码略显冗长,不在这里贴出,若读者 自己去实践并通过转到定义的方法找到 InputArray 的真身,便可以看到 _InputArray 类的里面首先定义了一个枚举,然后是各类的模板类型和一些方法。 而很多时候,遇到函数原型中的InputArray/OutputArray 类型,我们把它简单地当 做Mat 类型即可。
3.1.7 创 建 窗 口 namedWindow ()函 数
namedWindow 函数用于创建一个窗口。若是简单地进行图片显示,可以略去 namedWindow函数的调用,即先调用imread 读入图片,然后用imshow 直接指定 出窗口名进行显示即可。但需要在显示窗口之前就用到窗口名时,比如我们后面 会马上讲到滑动条的使用,要指定滑动条依附到某个窗口上,就需要 namedWindow 函数先创建出窗口,显式地规定窗口名称了。
namedWindow 的函数原型如下:
void namedwindow(const string& winname,int flags=WINDOW_AUTOSIZE);
(1)第一个参数,const string&型的name, 填写被用作窗口的标识符的窗口名称。
(1)第一个参数,const string&型的name, 填写被用作窗口的标识符的窗口名称。
(2)第二个参数,int 类 型 的flags, 窗口的标识,可以填如下几种值。
- WINDOw_NORMAL, 设置这个值,用户可以改变窗口的大小(没有限制)。 OpenCV2中它还可以写为CV_WINDOW_NORMAL。
- WINDOW_AUTOSIZE, 设置这个值,窗口大小会自动调整以适应所显示 的图像,并且用户不能手动改变窗口大小。OpenCV2 中它还可以写为 CV_WINDOW_AUTOSIZE。
- WINDOW_OPENGL, 设置这个值,窗口创建的时候会支持OpenGL。 OpenCV2中它还可以写为CV_WINDOW_OPENGL。
首先需要注意的是,namedWindow 函数有默认值WINDOW_AUTOSIZE, 所以,一般情况下,这个函数我们填一个变量就行了。
namedWindow 函数的作用是 通过指定的名字,创建一个可以作为图像和进度条的容器窗口。如果具有相同名 称的窗口已经存在,则函数不做任何事情。我们可以调用destroyWindow() 或者destroyAlIWindows()函数来关闭窗口,并取消之前分配的与窗口相关的所有内存空间。
但是事实上,对于代码量不大的简单程序来说,我们完全没有必要手动调用 上述的destroyWindow() 或者destroyAlIWindows() 函数,因为在退出时,所有的资 源和应用程序的窗口会被操作系统自动关闭。
3.1.8 输出图像到文件: imwrite()函 数
在OpenCV 中,输出图像到文件一般采用imwrite 函数,它的声明如下。
bool imwrite(const string&filename,InputArray img,const vector<int>¶ms=vector<int>());
(1)第 一 个参数,const string&类型的filename, 填需要写入的文件名。注意 要带上后缀,如“123.jpg”。
(2)第二个参数,InputArray类型的img, 一般填一个Mat 类型的图像数据。
(3)第三个参数,const vector&类型的params, 表示为特定格式保存的 参数编码。它有默认值vector0), 所以一般情况下不需要填写。而如果要填写 的话,有下面这些需要了解的地方:
- 对于 JPEG 格式的图片,这个参数表示从0到100的图片质量 (CV_IMWRITE_JPEG_QUALITY), 默认值是95。
- 对于 PNG 格式的图片,这个参数表示压缩级别(CV_IMWRITE_PNG_
COMPRESSION) 从0到9。较高的值意味着更小的尺寸和更长的压缩时 间,默认值是3。 - 对 于PPM,PGM, 或 PBM 格式的图片,这个参数表示一个二进制格式标 志 (CV_IMWRITE_PXM_BINARY), 取值为0或1,默认值是1。
imwrite 函数用于将图像保存到指定的文件。图像格式是基于文件扩展名的, 可保存的扩展名和 imread 中可以读取的图像扩展名一致。
下面是一个示例程序,讲解 imwrite函数的用法——在OpenCV中生成一幅 png图片,并写入到当前工程目录下。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
void createAlphaMat(Mat& mat) {
for (int i = 0; i < mat.rows; ++i) {
for (int j = 0; j < mat.cols; ++j) {
Vec4b& bgra = mat.at<Vec4b>(i, j);
// 从左到右红色通道(Red)线性从0到255渐变
bgra[0] = saturate_cast<uchar>((float)(j) / (mat.cols) * UCHAR_MAX);
// 从左到右 绿色通道(Green)线性从255到0渐变
bgra[1] = saturate_cast<uchar>((float)(mat.cols - j) / (mat.cols) * UCHAR_MAX);
// 从上到下 蓝色通道(Blue)线性从255到0渐变
bgra[2] = saturate_cast<uchar>((float)(mat.rows - i) / (mat.rows) * UCHAR_MAX);
// 从上到下 Alpha 通道(透明度)计算:线性从0到255渐变
bgra[3] = saturate_cast<uchar>((float)(i) / (mat.rows) * UCHAR_MAX);
}
}
}
int main() {
// 创建一个大小为480x640的Mat,具有RGBA通道
Mat mat(480, 640, CV_8UC4);
// 填充Alpha通道的渐变图像
createAlphaMat(mat);
// 显示图像
namedWindow("Alpha_Picture", WINDOW_NORMAL);
imshow("Alpha_Picture", mat);
waitKey(0);
// 保存为PNG图片,使用最高的压缩率
std::vector<int> compression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);
try {
imwrite("Alpha_Picture.png", mat, compression_params);
std::cout << "generate successfully!" << std::endl;
}
catch (std::runtime_error& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}
得到的图片如下:
3.1.9 综合示例程序:图像的载入、显示与输出
本小节将给出一个综合示例,演示如何载入图像,进行简单的图像混合,显 示图像,并且输出混合后的图像到jpg 格式的文件中。
图片的载入、显示与输出
//图像的载入和显示
void Test2() {
Mat mat = imread("image.jpg", IMREAD_UNCHANGED);//载入图片
namedWindow("image_window"); // 创建窗口
imshow("image_window", mat); //展示图片
cv::waitKey(0); //等待响应
}
图像的初步混合
//图片的初步混合
void Test3() {
Mat image = imread("image.jpg", IMREAD_UNCHANGED);
Mat logo = imread("logo.jpg", IMREAD_UNCHANGED);
Mat imageROI = image(Rect(300, 400, logo.cols, logo.rows));//创建需要混合的矩形部分
//设置混合比例,src、dst比例,亮度
addWeighted(imageROI, 0.7, logo, 0.3, 0, imageROI); //添加logo
imshow("mixedImage", image);//展示图片
imwrite("mixImage.jpg", image); //生成图片
cv::waitKey(0);
}
3.2 滑动条的创建和使用
滑动条(Trackbar) 是 OpenCV 动态调节参数特别好用的一种工具,它依附 于窗口而存在。
由于OpenCV 中并没有实现按钮的功能,所以很多时候,我们还可以用仅含 0-1的滑动条来实现按钮的按下、弹起效果。
3.2.1 创建滑动条 :createTrackbar() 函 数
createTrackbar 函数用于创建一个可以调整数值的滑动条(常常也被称作轨迹 条),并将滑动条附加到指定的窗口上,使用起来很方便。需要记住,它往往会和 一个回调函数配合起来使用。先看下它的函数原型,如下。
int createTrackbar(const string& trackbarname,const string& winname,int*value,int count,TrackbarCallback onChange=0,void*userdata=0);
- 第一个参数,const string&类型的trackbarname,轨迹条的名字,用来代表 我们创建的轨迹条。
- 第二个参数,const string&类型的winname, 窗口的名字,表示这个轨迹条 会依附到哪个窗口上,即对应 namedWindow() 创建窗口时填的某一个窗口 名。
- 第三个参数,int* 类型的value,一个指向整型的指针,表示滑块的位置。 在创建时,滑块的初始位置就是该变量当前的值。
- 第四个参数,int类型的count, 表示滑块可以达到的最大位置的值。滑块 最小位置的值始终为0。
- 第五个参数,TrackbarCallback类型的onChange, 它有默认值0。这是一个 指向回调函数的指针,每次滑块位置改变时,这个函数都会进行回调。并 且这个函数的原型必须为void XXXX(int,void*);,其中第一个参数是轨迹 条的位置,第二个参数是用户数据(看下面的第六个参数)。如果回调是 NULL 指针,则表示没有回调函数的调用,仅第三个参数value有变化。
- 第六个参数,void*类型的userdata,也有默认值0。这个参数是用户传给回 调函数的数据,用来处理轨迹条事件。如果使用的第三个参数 value 实参是 全局变量的话,完全可以不去管这个 userdata参数。
createTrackbar 函数为我们创建了 一 个具有特定名称和范围的轨迹条 (Trackbar, 或者说是滑块范围控制工具),指定一个和轨迹条位置同步的变量,而 且要指定回调函数 onChange (第五个参数),在轨迹条位置改变的时候来调用这 个回调函数,并且,创建的轨迹条显示在指定的winname (第二个参数)所代表 的窗口上。
至于回调函数,就是一个通过函数指针调用的函数。如果我们把函数的指针 (地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时, 就称其为回调函数。回调函数不由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用,用于对该事件或条件进行响应。
在函数讲解之后,给大家一个createTrackbar 函数使用的小例子作为参照。
//创建轨迹条
createTrackbar(" 对比度:“,”【效果图窗口】",&g_nContrastValue,
300,on_Change);//g_nContrastValue 为全局的整型变量,on_change 为
回调函数的函数名(在C/C++ 中,函数名为指向函数地址的指针)
接着,我们一起来欣赏一个完整的使用示例,它演示了如何用轨迹条来控制 两幅图像的Alpha 混合。
//滚动条的初步使用
//value
const int g_nMaxAlphaValue = 100; //maximum
int g_nAlphaValueSlider; //slider value
//mixed value
double g_dAlpherValue;
double g_dBetaValue;
//store image
Mat g_srcImage1;
Mat g_srcImage2;
Mat g_dstImage;
//Trackbar Callback
void on_Trackbar(int, void*) {
g_dAlpherValue = (double)g_nAlphaValueSlider / g_nMaxAlphaValue; //获取比例
g_dBetaValue = 1 - g_dAlpherValue;
//mixed
addWeighted(g_srcImage1, g_dAlpherValue, g_srcImage2, g_dBetaValue, 0, g_dstImage);
imshow("mixed_picture", g_dstImage);
}
void Test4() {
g_srcImage1 = imread("image.jpg");
g_srcImage2 = imread("background.jpg");
if (g_srcImage1.empty()) {
std::cerr << "image empty" << std::endl;
return;
}
if (g_srcImage2.empty()) {
std::cerr << "background empty" << std::endl;
return;
}
//make initial value
g_nAlphaValueSlider = 50;
namedWindow("mixed_picture");
createTrackbar("TrackBar", "mixed_picture", &g_nAlphaValueSlider, g_nMaxAlphaValue, on_Trackbar);
waitKey(0);
}
混合结果如下,可以使用滚动条改变比例
3.2.2 获取当前轨迹条的位置:getTrackbarPos() 函数
本小节介绍 一 个配合createTrackbar 使用的函数 —getTrackbarPos(), 它 用 于
获取当前轨迹条的位置。
下面这个函数用于获取当前轨迹条的位置并返回。
int getTrackbarPos(const string&trackbarname,const string&winname);
- 第 一 个 参 数 ,const string&类 型 的trackbarname, 表示轨迹条的名字。
- 第 二 个 参 数 ,const string&类 型 的winname, 表示轨迹条的父窗口的名称。
3.3鼠标操作
OpenCV 中的鼠标操作和滑动条的消息映射方式很类似,都是通过 一 个中介 函 数 配 合 一 个 回 调 函 数 来 实 现 的 。 创 建 和 指 定 滑 动 条 回 调 函 数 的 函 数 为 createTrackbar, 而指定鼠标操作消息回调函数的函数为 SetMouseCallback 。 下 面 一 起来了解 一 下它。
SetMouseCallback 函数的作用是为指定的窗口设置鼠标回调函数,原型如下。
void setMouseCallback(const string&winname,MouseCallback onMouse,void*userdata=0)
- 第 一 个 参 数 ,const string&类 型 的winname, 窗 口 的 名 字 。
- 第 二 个 参 数 ,MouseCallback 类 型 的onMouse, 指定窗口里每次鼠标时间发生的时候,被调用的函数指针。这个函数的原型的大概形式为
void Foo(int event,int x,int y,int flags,void*param)。
-
其 中event 是EVENT_+变量之一, x 和 y 是鼠标指针在图像坐标系(需要注意,不是窗口坐标系)中的坐标 值 ,flags 是 EVENT_FLAG 的 组 合 ,param 是用户定义的传递到 SetMouseCallback函数调用的参数。如EVENT_MOUSEMOVE为鼠标移动 消息、EVENT_LBUTTONDOWN为鼠标左键按下消息等。
-
第三个参数,void* 类型的userdata, 用户定义的传递到回调函数的参数,有 默 认 值 0 。
在OpenCV2中,上述“EVENT_” 之前可以加上“CV_” 前缀。
下面看一个详细注释的示例程序,在实战中了解此函数的用法以及如何在 OpenCV中使用鼠标进行交互。
Rect g_rectangle;
bool g_bDrawingBox = false; //是否进行绘制
RNG g_rng(12345); //随机种子
//画图函数
void DrawRectangle(Mat& img, Rect box) {
rectangle(img, box.tl(), box.br(), Scalar(g_rng.uniform(0, 255)
, g_rng.uniform(0, 255), g_rng.uniform(0, 255))); //随机颜色
}
//鼠标响应
void on_MouseHandle(int event, int x, int y, int flags, void* param) {
Mat& image = *(Mat*)param;
switch (event) {
case EVENT_MOUSEMOVE: { //鼠标移动
if (g_bDrawingBox) { //如果正在绘制
g_rectangle.width = x - g_rectangle.x;
g_rectangle.height = y - g_rectangle.y;
}
}break;
case EVENT_LBUTTONDOWN: { //左键按下
g_bDrawingBox = true;
g_rectangle = Rect{ x,y,0,0 };
}break;
case EVENT_LBUTTONUP: { //左键抬起
g_bDrawingBox = false;
if (g_rectangle.width < 0) {
g_rectangle.x += g_rectangle.width;
g_rectangle.width *= -1;
}
if (g_rectangle.height < 0) {
g_rectangle.y += g_rectangle.height;
g_rectangle.height *= -1;
}
DrawRectangle(image, g_rectangle);
}break;
}
}
void Test5() {
g_rectangle = Rect(-1, -1, 0, 0);
Mat srcImage(600, 800, CV_8UC3), tempImage;
srcImage.copyTo(tempImage);
g_rectangle = Rect(-1, -1, 0, 0);
namedWindow("window");
//设置鼠标回调函数
setMouseCallback("window", on_MouseHandle, (void*)&srcImage);
while (1) {
srcImage.copyTo(tempImage);
if (g_bDrawingBox) {
DrawRectangle(tempImage, g_rectangle);
}
imshow("window", tempImage);
if (waitKey(10) == 27) break; //Esc返回
}
}
结果如下,可以使用鼠标画出随机颜色的矩形
上述示例程序中的on_MouseHandle 就是我们的鼠标消息回调函数。其中用 一个 switch 语句指出了各种类型的鼠标消息,如鼠标移动消息 EVENT_MOUSEMOVE、左键按下消息EVENT_LBUTTONDOWN、左键抬起消 息 EVENT_LBUTTONUP, 并对其进行了处理,以得到随机颜色的矩形绘制的功 能
3.4 本章小结
本章中,我们学习了OpenCV 的高层GUI 图形用户界面模块highgui中最重 要的几个方面,分别是图像的载入、显示与输出图像到文件,以及如何使用滑动 条如何进行鼠标操作。