Opencv学习三滑动条的创建与鼠标操作

前言

  • 创建滑动条
  • 鼠标操作

又是基础操作,容易学,也容易忘。懂了一点和没懂一样,必须熟练多用。别像我之前一样,乱学到靠猜API怎么用的地步。

**

创建滑动条

**
createTrackbar用于调整数值的滑动条,并将滑动条附加到指定的窗口上,使用方便。

int createTrackbar(const string& trackbarname,//滑动条名
					const string& winname,//窗口名
					int* value,//滑块位置
					int count,//滑块可以到达的最大位置
					TrackbarCallbcak onChange=0,//指向回调函数的指针
					void* userdata=0);/*用户传给回调函数的数据,用来处理滑动条事件。如果value是全局的,可以不管这个userdata*/

比如下面用一个示例,演示滑动条如何控制两幅图像的alpha混合:

#include<opencv2/opencv.hpp>
#include<iostream>

#define WINDOW_NAME  "【线性混合示例】"

using namespace std;
using namespace cv;


//全局变量声明
const int g_MaxAlphaValue = 100;//Alpha值的最大值
int g_AlphaValueSlider;//滑动条对应的变量
double g_AlphaValue;

//声明存储图像的变量
Mat g_Src1, g_Src2, g_Dst;

//响应滑动条的回调函数
void on_callBack(int, void*) {
	//拿到alpha对最大值的比例
	g_AlphaValue = (double)g_AlphaValueSlider / g_MaxAlphaValue;
	addWeighted(g_Src1, g_AlphaValue, g_Src2, 1 - g_AlphaValue, 0.0, g_Dst);
	imshow(WINDOW_NAME, g_Dst);
}

int main() {

	g_Src1 = imread("E:/File/002.jpg");
	//两种判断方式,另外,不要贪图简单把两个判断写在一起
	if (g_Src1.empty()) {
		cout << "第一张读取错误" << endl;
		system("pause");
	}
	g_Src2 = imread("E:/File/004.jpg");
	if (!g_Src2.data) {
		cout << "第二张读取错误" << endl;
		system("pause");
	}
	resize(g_Src1, g_Src1, Size(320, 240));//一般Opencv都是第一个是输入图像,第二个是输出图像
	resize(g_Src2, g_Src2, Size(320, 240));
	
	//设置滑动条初始值
	g_AlphaValueSlider = 70;
	//创建窗体
	namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
	//创建一个滑动条控件
	char TrackbarName[50];
	sprintf_s(TrackbarName, "src2透明值%d", g_MaxAlphaValue);
	//创建滑动条
	createTrackbar(TrackbarName, WINDOW_NAME, &g_AlphaValueSlider, g_MaxAlphaValue, on_callBack);
	//在回调函数中显示
	on_callBack(0, 0);



	
	int preIndex = 0;
	//按任意键退出
	while (char(waitKey(10)) <= 0) {

		//显示滑动条位置
		int index = getTrackbarPos(TrackbarName, WINDOW_NAME);
		if (index != preIndex) {
			cout << "TrackbarPos = " << index << endl;
			preIndex = index;
		}
	}

	destroyAllWindows();

}

最后结果:
在这里插入图片描述

获取当前滑动条位置
这个的使用当然前面有。

int getTrackbarPos(const string& trackbarname,const string& winname);

销毁窗口
除了destroyAllWindows()外,还有destroyWindow(winname),销毁特定的窗口。

**

鼠标操作

**

opencv中的鼠标操作和滑动条的消息映射方式类似,都是通过一个中间函数配合一个回调函数来实现的。创建和指定滑动条回调函数的API是createTrackbar,指定鼠标操作消息回调函数的函数是setMouseCallback。

void setMouseCallback(const String& winname, //窗口名
						MouseCallback onMouse, //指定被调用的函数指针
						void* userdata = 0);//用户定义的传递到回调函数的参数

下面的一个示例演示此函数用法及如何在opencv中使用鼠标进行交互:

#include<opencv2/opencv.hpp>
#include<iostream>

#define WINDOW_NAME  "【交互窗口】"

using namespace std;
using namespace cv;


//全局函数声明
void on_MouseHandle(int event, int x, int y, int flags, void* param);
void DrawRectangle(Mat& img, Rect box);
void ShowHelpText(char& key);

//全局变量声明
Rect g_rect;//绘制矩形
bool g_drawingBox = false;//是否在绘制
RNG g_rng(12345);//随机数产生器


int main() {

	
	g_rect = Rect(-1, -1, 0, 0);
	Mat src(600, 800, CV_8UC3), temp;
	src.copyTo(temp);//两个相同的矩阵诞生了

	src = Scalar::all(0);//Scalar结构体4个参数全为0,赋值给src就是,src所有位置三个通道都为0

	namedWindow(WINDOW_NAME);
	setMouseCallback(WINDOW_NAME, on_MouseHandle,(void*)&src);
	
	while (true) {

		src.copyTo(temp);
		if (g_drawingBox) {
			//绘制在temp中间变量上,而中间变量总是在变化,所以只能在左键按下时显示出来。
			//用这个的好处是,可以在左键按下移动时,绘制的矩形也在不停的变化(宽高,左上角坐标、颜色)
			DrawRectangle(temp, g_rect);
		}
		imshow(WINDOW_NAME, temp);
	

		//处理键盘事件
		char key= waitKey(10);
		if (key == 27)
			break;
		else
			ShowHelpText(key);

	}

	
	destroyAllWindows();

}


void on_MouseHandle(int event, int x, int y, int flags, void* param) {

	Mat& image = *(cv::Mat*)param;//param指向src,先转换成Mat指针,再取其地址
	switch (event) {
	case EVENT_MOUSEMOVE:
		if (g_drawingBox) {//3在鼠标左键按着移动时,矩形的宽高就出来了
			g_rect.width = x - g_rect.x;
			g_rect.height = y - g_rect.y;
		}
		break;
	case EVENT_LBUTTONDOWN://1按下左键开始绘制
		g_drawingBox = true;
		g_rect = Rect(x, y, 0, 0);//2记录起始点,此时没有宽高
		break;
	case EVENT_LBUTTONUP:
		g_drawingBox = false;//4只在鼠标左键按下时绘制

		//处理width、height小于零的情况
		if (g_rect.width < 0) {
			g_rect.x += g_rect.width;
			g_rect.width *= -1;
		}
		if (g_rect.height < 0) {
			g_rect.y += g_rect.height;
			g_rect.height *= -1;
		}

		/*
		这个必须要,在左键抬起的时候矩形虽然固定了,但是绘制的东西不能保留。所以需要在抬起的时候
		再绘制一遍。
		*/
		DrawRectangle(image, g_rect);

		break;
	default:
		break;
	}

}
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)
	),
		4	//线条宽度
	);
	

}
void ShowHelpText(char& key) {


	switch (key) {
	case 'h':
		cout << "你按下了h\n";
		break;
	case 'i':
		cout << "你按下了i\n";
		//......
		break;
	default:
		break;
	}

}

其中需要额外了解的有一个g_rng,产生随机数。g_rng.uniform(0,255)是产生0~255内的平均分布的随机数,而g_rng.gaussian(sigma)是产生均值为0,标准差为sigma的随机数(高斯正太分布)。uniform类似randu,而gaussian类似randn。

参考:《Opencv3编程入门》

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/86563203