OpenCV计算机视觉开发入门与实践<七>:OpenCV 界面编程之窗口

  OpenCV 支持有限的界面编程,主要针对窗口、一些控件和一些鼠标事件等,比如滑块。有了这些窗口和控件,可以更好地展现图像并调节图像的一些参数。这些界面编程主要由 High-level GUI(高级图形用户界面)模块支持。

1.新建窗口并显示

  在 High-Level GUI 模块中,用于新建窗口的函数是 nameWindow,同时可以指定窗口的类型。该函数声明如下:

cpp 复制代码
void namedWindow(const string& winname, int flags);

  其中参数 winname 表示新建的窗口的名称,自己随便取;flags 表示窗口的标识,一般默认为WINDOW_AUTOSIZE,WINDOW_AUTOSIZE 表示窗口大小自动适应图片大小,并且不可手动更改,WINDOW_NORMAL 表示用户可以改变这个窗口大小,WINDOW_OPENGL 表示窗口创建的时候支持 OpenGL。

  在 High-Level GUI 模块中,用于显示窗口的函数是 imshow,该函数声明如下:

c 复制代码
void imshow(const string& winname, InputArray image);

  其中参数 winname 表示显示的窗口名,可以使用 cv::namedWindow 函数创建窗口,若不创建,则 imshow 函数将自动创建;image 表示窗口中需要显示的图像。

  根据图像的深度,imshow 函数会自动对其进行缩放,规则如下:

(1)如果图像数据类型是 8U(8 位无符号),就直接显示。

(2)如果图像数据类型是 16U(16 位无符号)或 32S(32 位有符号整数),imshow 函数内部就会自动将每个像素值除以 256 并显示,即将原图像素值的范围由(0-255X256)映射到0\~255

(3)如果图像数据类型是 32F(32 位浮点数)或 64F(64 位浮点数),imshow 函数内部就会自动将每个像素值乘以 255 并显示,即将原图像素值的范围由0~1映射到0~255(注意:原图像素值必须归一化)。

需要注意的是,imshow 之后必须有 waitKey 函数,否则显示窗口将一闪而过,不会驻留屏幕。waitKey 函数声明如下:

cpp 复制代码
int cv::waitKey(int delay=0);

  其中参数 delay 表示延时值,单位为毫秒,默认是 0,表示一直等待,直到用户按键。如果在指定时间之前没有按任何键,就返回-1,如果在指定时间之前按键,就返回按键的对应值。

  当 delay≤0 时,函数 waitKey 无限地等待一个按键事件,或当 delay 是正数的时等待 delay 毫秒。由于操作系统在切换线程之间有一个最短的时间间隔,因此该函数不会等待确切(精确)的延迟毫秒,它将至少等待延迟毫秒,这取决于当时计算机上运行的其他操作。如果在指定时间之前没有按任何键,就返回按键的代码或-1。

  注意,此函数是 HighGUI 模块中唯一可以获取和处理事件的方法,因此需要定期调用它以进行正常的事件处理,除非 HighGUI 在负责事件处理的环境中使用。另外,只有至少创建了一个 HighGUI 窗口并且该窗口处于活动状态,该函数才起作用。如果有几个 HighGUI 窗口,其中任何一个都可以是活动的。

示例程序:

cpp 复制代码
#include <iostream>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2\opencv.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace cv;
int main()
{
	Mat img;
	img = imread("test.jpg", 1);//参数 1:图片路径。参数 2:显示原图
	namedWindow("window1", WINDOW_AUTOSIZE);
	imshow("window1", img);//在"窗口 1"这个窗口输出图片
	waitKey(5000);//等待 5 秒,程序自动退出。改为 0,不自动退出
	return 0;
}

2.单窗口显示多幅图像

  前面我们新建的窗口中只显示了一幅图,有时在某些场景下,比如有多个视频图像,如果一个视频图像显示在一个窗口中,就会因为窗口过多而显得凌乱。此时需要一个窗口能显示多个视频图像。要达到这个效果,原理并不复杂,只需要调整每个视频的尺寸大小为窗口的一部分,这样多幅图像组合起来正好占满一个窗口。

  在 OpenCV 中,我们可以综合利用坐标变换与 Rect 区域提取来实现单窗口显示多幅图像。首先根据输入图像个数与尺寸确定输入源图像小窗口的构成形态,然后设定每个图像小窗口的具体构成,包括边界、间隙等,最后根据小窗口确定输出图像的尺寸,利用缩放函数 resize 进行图像缩放,完成单窗口下多幅图像的显示。

cpp 复制代码
#include<iostream>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2\opencv.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace cv;
using namespace std;

void showManyImages(const vector<Mat>& srcImages, Size imageSize)
{
	int nNumImages = srcImages.size();
	Size nSizeWindows;
	if (nNumImages > 12)
	{
		cout << "no more than 12 images" << endl;
		return;
	}
	//根据图像序列数量来确定分割小窗口的形态
	switch (nNumImages)
	{
	case 1:nSizeWindows = Size(1, 1); break;
	case 2:nSizeWindows = Size(2, 1); break;
	case 3:
	case 4:nSizeWindows = Size(2, 2); break;
	case 5:
	case 6:nSizeWindows = Size(3, 2); break;
	case 7:
	case 8:nSizeWindows = Size(4, 2); break;
	case 9:nSizeWindows = Size(3, 3); break;
	default:nSizeWindows = Size(4, 3);
	}
	//设置小图像尺寸、间隙、边界
	int nShowImageSize = 200;
	int nSplitLineSize = 15;

	int nAroundLineSize = 50;

	//创建输出图像,图像大小根据输入源来确定
	const int imagesHeight = nShowImageSize *
		nSizeWindows.width + nAroundLineSize +
		(nSizeWindows.width - 1) * nSplitLineSize;
	const int imagesWidth = nShowImageSize *
		nSizeWindows.height + nAroundLineSize +
		(nSizeWindows.height - 1) * nSplitLineSize;
	cout << imagesWidth << " " << imagesHeight << endl;
	Mat showWindowsImages(imagesWidth, imagesHeight, CV_8UC3, Scalar(0, 0,
		0));
	//提取对应小图像的左上角坐标 x、y
	int posX = (showWindowsImages.cols - (nShowImageSize * nSizeWindows.width +
		(nSizeWindows.width - 1) * nSplitLineSize)) / 2;
	int posY = (showWindowsImages.rows - (nShowImageSize * nSizeWindows.height +
		(nSizeWindows.height - 1) * nSplitLineSize)) / 2;
	cout << posX << " " << posY << endl;
	int tempPosX = posX;
	int tempPosY = posY;
	//将每一幅小图像整合成一幅大图像
	for (int i = 0; i < nNumImages; i++) 
	{
		//小图像坐标转换
		if ((i % nSizeWindows.width == 0) && (tempPosX != posX)) {
			tempPosX = posX;;
			tempPosY += (nSplitLineSize + nShowImageSize);
		}
		//利用 Rect 区域将小图像置于大图像的相应区域
		Mat tempImage = showWindowsImages
		(Rect(tempPosX, tempPosY, nShowImageSize, nShowImageSize));
		//利用 resize 函数实现图像缩放
		resize(srcImages[i], tempImage,
			Size(nShowImageSize, nShowImageSize));
		tempPosX += (nSplitLineSize + nShowImageSize);
	}
	imshow("showImages", showWindowsImages);
}

int main() 
{
	//图像源输入
	vector<Mat>srcImage(9);
	srcImage[0] = imread("test.jpg");
	srcImage[1] = imread("test.jpg");
	srcImage[2] = imread("test.jpg");
	srcImage[3] = imread("test.jpg");
	srcImage[4] = imread("test.jpg");
	srcImage[5] = imread("test.jpg");
	srcImage[6] = imread("test.jpg");
	srcImage[7] = imread("test.jpg");
	srcImage[8] = imread("test.jpg");
	//判断当前 vector 读入的正确性
	for (int i = 0; i < srcImage.size(); i++) 
	{
		if (srcImage[i].empty()) 
		{
			cout << "read error" << endl;
			return -1;
		}
	}
	//调用单窗口显示图像
	showManyImages(srcImage, Size(147, 118));
	waitKey(0);
	system("pause");
	return 0;
}

3. 销毁窗口

  既然有新建窗口,那么也有销毁窗口。在 OpenCV 中,销毁窗口时窗口将自动关闭,可以通过函数 destroyWindow 和 destroyAllWindows 来实现,前者是销毁某一个指定名称的窗口,后者是销毁所有新建的窗口。其中函数 destroyWindow 声明如下:

cpp 复制代码
void destroyWindow(const String& winname);

其中参数 winname 是要销毁窗口的名称。

destroyWindow 函数更加简单,声明如下:

cpp 复制代码
void destroyAllWindows();

下面试着来新建三个窗口,每个窗口显示 5 秒,再分别销毁。

cpp 复制代码
#include <opencv2/highgui/highgui_c.h>
#include <opencv2\opencv.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace cv;
#include<iostream>
using namespace std;
int main() 
{
	//图像源输入
	vector<Mat>srcImage(3);
	char szName[50] = "";
	for (int i = 0; i < srcImage.size(); i++)
	{
		sprintf_s(szName, "%d.jpg", i + 1);
		srcImage[i] = imread(szName); //读取图像文件
		if (srcImage[i].empty()) { //判断当前 vector 读入的正确性
			cout << "read " << szName << " error" << endl;
			return -1;
		}
		//调用单窗口显示图像
		namedWindow(szName, WINDOW_AUTOSIZE);
		imshow(szName, srcImage[i]);//在"窗口 1"这个窗口输出图像
		waitKey(5000);//等待 5 秒,程序自动退出。改为 0,不自动退出
		destroyWindow(szName);
	}
	//destroyAllWindows();
	//cout << "所有的窗口已经销毁了" << endl;
	waitKey(0);
	system("pause");
	return 0;
}

4.调整窗口大小

  窗口大小可以手工拖拉窗口边框来调整,也可以通过函数方式来调整。调整窗口大小的函数是 resizeWindow,声明如下:

cpp 复制代码
void resizeWindow(const String& winname, int width, int height);

  其中参数 winname 是要调整尺寸的窗口的标题,width 是调整后的窗口宽度,height 是调整后的窗口高度。但要注意的是,新建窗口函数 namedWindow 的第二个参数必须为WINDOW_NORMAL 才可以手动拉动窗口边框来调整大小,虽然此时 resizeWindow 依旧可以调整窗口,但是图片不会随着窗口大小而改变。

cpp 复制代码
#include <opencv2/highgui/highgui_c.h>
#include <opencv2\opencv.hpp>
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库
using namespace cv;
#include<iostream>
using namespace std;
int main() 
{
	vector<Mat>srcImage(1);
	char szName[50] = "";
	
	sprintf_s(szName, "%d.jpg", 1);
	srcImage[0] = imread(szName);
	if (srcImage[0].empty()) 
	{ //判断当前 vector 读入的正确性
		cout << "read " << szName << " error" << endl;
		return -1;
	}
	namedWindow(szName, WINDOW_NORMAL); //新建窗口
	imshow(szName, srcImage[0]); //在窗口显示图片
	waitKey(2000);

	int width = 640, height = 320;
	resizeWindow(szName, width, height); //调整窗口大小
	
	system("pause");
	return 0;
}
相关推荐
a1122998211 小时前
团体标准 T/CGCC 119-2026 正式实施:如何利用合规框架重构企业的 AI 可见性考核标准?
人工智能·重构
樊小肆1 小时前
DeepSeeker-Code源码导读01-agentNudges
人工智能·agent
Anhty1 小时前
2026 实测 4 款 AI 变声器|QQ 聊天伪装声线,告别僵硬假声
人工智能·功能测试·ios·智能手机·安卓
甲维斯1 小时前
DeepSeek Pro正式版太难用了!这还对标Fable5?!
人工智能
郝学胜_神的一滴1 小时前
C++20 高级编程 002:夯实现代C++底层基本功
c++·编程语言
甲维斯1 小时前
DeepSeek 官方Harness开源,一夜44.6K Star,缓存99%!
人工智能
suaizai_1 小时前
MCP 2026:AI世界的USB-C新升级
人工智能
浅安的邂逅1 小时前
免费AI生图模型 agnes-ai和智谱GLM-4V-Flash
人工智能·ai作画·ai编程·ai生图
新新学长搞科研1 小时前
【双一流高校主办 | EI快稳检索】第五届图像处理、目标检测与跟踪国际学术会议(IPODT 2026)
人工智能