前言
- 创建滑动条
- 鼠标操作
又是基础操作,容易学,也容易忘。懂了一点和没懂一样,必须熟练多用。别像我之前一样,乱学到靠猜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编程入门》