第三章:HighGUI图形用户界面初步
一、图像的载入、显示和输出到文件
1、OpenCV的命名空间
说明:opencv定义在命名空间cv之内的,两种写法:
//第一种 #include<opencv2/opencv.hpp> using namespace cv;
//第二种 #include<opencv2/opencv.hpp> int main() { cv::..... }
2、Mat类简析
说明:在此只需了解OpenCV1.0时代的Ip1Image,主要用于保存图像以及其他矩阵数据的数据结构。默认情况下其尺寸为。
用到的方式:
Mat srcImage=imread("dota.jpg"); /* 表示从工程项目下把一幅名为dota.jpg的图像载入到Mat类型的srcImage 的变量中。其中imread函数是将图片读入到Mat类型中。 */
3、图像载入:imread()函数
imread()函数源码
Mat imread(const string& filename,intflags=1);
(1)第一个参数:const string& 类型的filename,需要写的是图像的路径名。该函数支持的图像载入:
- Windows位图:*.bmp, *.dib
- JPEG文件:*.jpeg, *.jpg, *.jpe
- PNG图片:*.png
- 等
(2)第二个参数:int类型的flags,为载入标识,它指定一个加载图像的颜色类型。以下对常用标识的解释:
CV_LOAD_IMAGE_GRAYSCALE:等价取值为0,这个标识将图像转化成灰度图再返回
CV_LOAD_IMAGE_COLOR:等价取值为1,这个标识将图像转化成彩色再返回
CV_LOAD_IMAGE_ANYDEPTH:等价取值为2,取这个标识,且载入的图像的深度(颜色深度就是度量图像中有多少中颜色信息,单位是位)为16位或者32位,就返 回对应深度的图像,否者转换为8位图像
CV_LOAD_IMAGE_UNCHANGED:等价取值为-1,这个标识在新版本已经不使用了
注意:若以彩色模式载入图像,解码后的图像会以B(蓝)G(绿)R(红)的顺序进行存储。
4、图像的显示:imshow()函数
mshow()函数用于在指定的窗口中显示一幅图像,函数原型如下:
void imshow(const string& winname,InputArray mat);
- 第一个参数:const string&类型的winname,显示窗口标识的名称。
- 第二个参数:InputArray类型的mat,需要显示的图像
说明:imshow函数用于在指定的窗口中显示图像。如果窗口是用CV_WIN(默认值)标志创建的,那么就显示图像原始大小,否则将图像进行缩放到适合窗口。imshow函数缩放图像,取决于图像的深度:
5、关于InputArray类型(这里作为小白不太懂,就参考其他博客大佬了解一下)
明明是InputArray类型的形参,而我们一般都使用Mat类型的实参来直接传递。说白了,这是一个类型的对象转换为另一个类型的对象。使用C++中的类型转换构造函数来完成。
6、创建窗口:namedWindow()函数
说明:namedWindow函数用于创建一个窗口。它不同于imread函数简单的进行图片显示,而是在显示窗口之前先用到窗口名。作用:通过指定的名字,创建一个可以作为图像和进度条的容器窗口。
namedWindow的函数原型如下:
void namedWindow(const string& winname,int flags=WINDOW_AUTOSIZE);
- 第一个参数:const string&型的name指的是窗口的名称
- 第二个参数:int类型的flags,窗口的标识,有如下几种值:
(1)WINDOW_NORMAL,设置这个值,用户可以改变窗口的大小(没 有限制)(2)WINDOW_AUTOSIZE,设置这个值,窗口大小会自动调整以适应 所显示的图像,并且用户不能手动改变窗口的大小。
(3)WINDOW_OPENGL,设置这个值,窗口创建的时候支持 OpenGL(OpenGL是用于渲染2D、3D矢量图形的跨语言、跨平台 的应用程序编程接口)。
7、输出图像到文件:imwrite()函数
说明:输出图像到文件一般采用inwrite函数,声明如下:
bool imwrite(const string& filename,InputArray img ,const vector<int>& params=vector<int>());
- 第一个参数:const string& 类型的filename,需要写入的文件名,加上后缀名。
- 第二个参数:InputArray类型的img,一般由一个Mat类型的图像数据
- 第三个参数:const vector< int>&类型的 params,表示为特定格式保存的参数编码。有默认值vector< int>(),所以一般不需要填写。如需要填写,如下:
(1)对于JPEG格式的图片,这个参数表示从0-100的图片质(CV_IMWRITE_JPEG_QUALITY),默认值是95.
(2)对于PNG格式的图片,这个参数表示压缩级别 (CV_IMWRITE_PNE_COMPRESSION)从0-9。较高的值表示更 小的尺寸和更长的压缩时间,默认值3.
(3)对于PPM,PGM或者PBM格式的图片,表示二进制格式标志( CV_IMWRITE_PXM_BINARY),取值0或者1,默认值1.
下面是示例程序以及运行截图:
#include<opencv2/opencv.hpp> #include<vector> using namespace std; using namespace cv; void createAlphaMat(Mat &mat) { for (int i = 0; i < mat.rows; i++) { for (int j=0; j < mat.cols;j++) { Vec4b&rgba = mat.at<Vec4b>(i, j); rgba[0] = UCHAR_MAX; rgba[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols) *UCHAR_MAX); rgba[2] = saturate_cast<uchar>((float(mat.rows - i)) / ((float)mat.rows) *UCHAR_MAX); rgba[3] = saturate_cast<uchar>(0.5*(rgba[1]+rgba[2])); } } } int main(int argc, char *argv[]) { //创建带Alpha通道的Mat Mat mat(480, 640, CV_8UC4); createAlphaMat(mat); vector<int>compression_params; compression_params.push_back(IMWRITE_PNG_COMPRESSION); compression_params.push_back(9); try { imwrite("透明Alpha值图.png", mat, compression_params); imshow("生成的PNG图",mat); fprintf(stdout, "PNG图片文件的alpha数据保存完毕~\n可以在工程目录下查看由imwrite函数生成的图片\n"); waitKey(0); } catch (runtime_error& ex) { fprintf(stderr, "图像转换成PNG格式发生错误:%s\n", ex.what()); return 1; } return 0; }
8、综合示例程序:图像的载入、显示与输出
//描述:头文件和命名空间 #include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> using namespace cv; int main() { //图像的载入和显示 Mat penguin = imread("D:\\1.jpg"); namedWindow("【1】原图"); imshow("【1】原图",penguin); //--------------------------------------------- //初级图像混合 //--------------------------------------------- 载入图片 // Mat image = imread("D:\\dota.jpg",1); Mat logo = imread("D:\\logo.jpg"); 载入后显示 namedWindow("【2】原图"); imshow("【2】原图",image); namedWindow("【3】原图"); imshow("【3】原图", logo); 定义一个Mat类型,用于存放图像的ROI(图像感兴趣的区域) Mat imageROI; imageROI = image(Rect(300, 10, logo.cols, logo.rows)); 将logo加载到原图上 //addWeighted(imageROI, 0.5, logo, 0.3, 0., imageROI); 显示结果 namedWindow("【4】原图+logo图"); imshow("【4】原图+logo图", image); --------------------------------------------- 将一个Mat图像输出到图像文件 --------------------------------------------- imwrite("由imwrite生成的图片.jpg", image); waitKey(0); }
二、 滑动条的创建和使用
说明:滑动条(Trackbar)是OpenCV动态调节参数特别好用的一种工具,它依附于窗口而存在。
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,窗口的名字,表示这个轨迹条依附在那个窗口
- 第三个参数:int *类型的value,一个指向整型的指针,表示滑块的位置。滑块的初始位置就是该变量的当前值
- 第四个参数:int类型的count,表示滑块可以达到的最大位置的值,滑块最小位置为0
- 第五个参数:TrackbarCallback类型的onChange,它的默认值为0。这是一个回调函数的指针,每次滑块位置改变时,这个函数都会进行回调。并且这个函数的原型为void XXXX(int,void *),第一个参数时轨迹条的位置,第二个参数时用户数据。如果回调是NULL指针,则表示没有回调函数的调用
- 第六个参数:void*类型的userdata,也有默认值0,这个参数是用户传给回调函数的数据,用来处理轨迹条事件。注意:如果第三个参数value实参是全局变量的话,可以不用官这个userdata参数
回调函数:把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其指向的函数。回调函数不由该函数的实现方直接调用,而是在特定的事件或者条件发生时由另外的一方调用,用于对该事件或者条件进行响应。
文字描述还是有点羞涩难懂,接下来用一个完整的使用示例,演示如何用轨迹条来控制两幅图像的Alpha混合。
小小的调试一下
sprintf(TrackbarName, "透明值 %d", g_nMaxAlphaValue); //这一行代码运行可能会出现一点问题,需要调试一下 _CRT_SECURE_NO_DEPRECATE _SCL_SECURE_NO_DEPRECATE
//描述:头文件和命名空间 #include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> using namespace cv; #define WINDOW_NAME "线性混合示例" //为窗口标题定义的宏 //插入时,窗口宏附着在光标上并能自由定位于 X 和 Y 方向。 /* 全局变量声明 */ //const是一个关键字,它限定一个变量不允许被改变。 const int g_nMaxAlphaValue = 100;//Alpha值的最大值 int g_nAlphaValueSlider;//滑动条件对应的变量 double g_dAlphaValue; double g_dBetaValue; //声明存储图像的变量 Mat g_srcImage1; Mat g_srcImage2; Mat g_dstImage; /* 声明滑动条的回调函数 */ void on_Trackbar(int, void *) { //求出当前alpha值相对于最大值的比例 g_dAlphaValue = (double)g_nAlphaValueSlider / g_nMaxAlphaValue; //则beta值为1减去alpha值 g_dAlphaValue = (1.0 - g_dAlphaValue); //根据Alpha和beta值进行线性混合 addWeighted(g_srcImage1, g_dAlphaValue, g_srcImage2, g_dBetaValue, 0.0, g_dstImage); //显示效果图 imshow(WINDOW_NAME, g_dstImage); } /* 控制程序入口 */ int main(int argc,char* argv[]) { //加载图像 g_srcImage2 = imread("D:\\1.jpg"); g_srcImage1 = imread("D:\\dota.jpg"); if (!g_srcImage1.data) { printf("读取第一幅图片有误,请确定目录下是否有imread函数指定图像存在\n"); return -1; } if (!g_srcImage2.data) { printf("读取第二幅图片有误,请确定目录下是否有imread函数指定图像存在\n"); return -1; } //设置滑动条的初始值为70 g_nAlphaValueSlider = 70; //创建窗体 namedWindow(WINDOW_NAME, 1); //在创建的窗体中添加一个滑动条控件 char TrackbarName[50]; sprintf(TrackbarName, "透明值 %d", g_nMaxAlphaValue); /* sprintf指的是字符串格式化命令, 函数声明为 int sprintf(char *string, char *format [,argument,...]); 主要功能是把格式化的数据写入某个字符串中, 即发送格式化输出到 string 所指向的字符串。 */ createTrackbar(TrackbarName, WINDOW_NAME, &g_nAlphaValueSlider, g_nMaxAlphaValue, on_Trackbar); //结果在回调函数中显示 on_Trackbar(g_nAlphaValueSlider, 0); //按任意键退出 waitKey(0); return 0; }
2、获取当前轨迹条的位置:getTrackbarPos()函数
int getTrackbarPos(const string& trackbarname,const string& winname);
- 第一次参数:const string&类型的trackbarname,表示轨迹条的名字
- 第二个参数:const string&类型的winname,表示轨迹条的父窗口名称
三、鼠标操作
说明:鼠标操作是通过一个中介函数配合一个回调函数来实现的。创建和指定滑动条回调函数的函数为creatTrackbar,而指定鼠标操作消息回调函数为SetMouseCallback。
void setMouseCallback(const string& winname,MouseCallback onMouse,void* userdata=0)
第一个参数:const string&类型 winname,窗口的名称
第二个参数:MouseCallback 类型onMouse,指定窗口里每次鼠标时间发生的时候,被调用的函数指针。这个函数指针原型为void Foo(int evebt,int x,int y,int flags,void* param)。x和y是鼠标指针在图像坐标系(不是窗口坐标系),flags是EVENT_FLAG的组合,param是用户定义的传递到SetMouseCallback函数调用的参数。
第三个参数:void*类型 userdata,用户定义的传递到回调函数的参数,默认值0
同样,不太理解什么意思,上完整程序。程序有点长。
//描述:头文件和命名空间 #include<opencv2/opencv.hpp> using namespace cv; #define WINDOW_NAME "程序窗口" //为窗口标题定义的宏 //插入时,窗口宏附着在光标上并能自由定位于 X 和 Y 方向。 /* 全局函数声明 */ void on_MouseHandle(int event, int x, int y, int flags, void* param); void DrawRectangle(cv::Mat& img, cv::Rect box); void ShowHelpText(); /* 全局变量声明 */ Rect g_rectangle; bool g_bDrawingBox = false;//是否进行绘制 RNG g_rng(12345); /* 控制程序入口 */ int main(int argc,char* argv[]) { //准备参数 g_rectangle = Rect(-1, -1, 0, 0); Mat srcImage(600, 800, CV_8UC3), tempImage; srcImage.copyTo(tempImage); g_rectangle = Rect(-1, -1, 0, 0); srcImage = Scalar::all(0); //设置鼠标操作的回调函数 namedWindow(WINDOW_NAME); setMouseCallback(WINDOW_NAME, on_MouseHandle,(void*)&srcImage); //程序主循环,当进行绘制的标识符为真时,进行绘制 while (1) { srcImage.copyTo(tempImage); //当进行绘制的标识符为真,则进行绘制 if (g_bDrawingBox) DrawRectangle(tempImage, g_rectangle); imshow(WINDOW_NAME, tempImage); //按下ESC,程序退出 if (waitKey(10) == 27) break; } return 0; } /* 鼠标回调函数,根据不同的鼠标事件进行不同的操作 */ void on_MouseHandle(int event, int x, int y, int flags, void* param) { Mat& image = *(cv::Mat*)param; switch (event) { //鼠标移动消息 case EVENT_MOUSEMOVE: { //如果是否进行绘制的标识符为真,则记录下长和宽到RECT型变量中 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: { //置标识符为false g_bDrawingBox = false; //对宽和高小于0的处理 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 DrawRectangle(cv::Mat& img, cv::Rect box) { //随机颜色 rectangle(img, box.tl(),box.br(), Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255))); }
可以通过鼠标左键按下和松开在黑色的窗口绘制一个一个彩色的矩阵