Opencv学习-窗口交互

交互操作能够增加用户对程序流程的控制,使程序可以根据用户需求实现不同的处理结果。有时某一个参数需要反复尝试不同的数值,这时交互操作可以实现在程序运行过程中改变参数数值的作用,避免重复运行程序,节省时间,同时能够增强结果的对比效果。本文将介绍 OpenCV 4 中提供的图像窗口滑动条和鼠标响应两种窗口交互操作。

1. 图像窗口滑动条

图像窗口滑动条,顾名思义,就是在显示图像的窗口中创建能够通过滑动改变数值的滑动条。有时,我们需要动态调节某些参数,以使图像处理的效果更加明显,能够改变参数数值的滑动条可以很好地胜任这项工作。OpenCV 4 通过 createTrackbar() 函数在显示图像的窗口中创建滑动条

1.1 createTrackbar函数原型

int cv::createTrackbar(const String & trackbarname, 
const String & winname, 
int * value, 
int count, 
TrackbarCallback onChange = 0, 
void * userdata = 0 
)

• trackbarname:滑动条的名称。

• winname:创建滑动条窗口的名称。

• value:指向整数变量的指针,该指针指向的值反映滑块的位置,创建后,滑块位置由此变量定义。

• count:滑动条的最大取值。

• onChange:每次滑块更改位置时要调用的函数的指针,其中函数原型为 void Foo(int, void*);,其中第一个参数是轨迹栏位置,第二个参数是用户数据。如果回调是 NULL 指针,则不会调用任何回调,而只是更新数值。

• userdata:传递给回调函数的可选参数。
该函数能够在图像窗口的上方创建一个范围从 0 开始的整数滑动条。由于滑动条默认只能输出整数,因此,如果需要得到小数,就必须进行后续处理,例如输出值除以 10 得到含有一位小数的数据。该函数第一个参数是滑动条的名称。第二个参数是创建滑动条的图像窗口的名称。第三个参数是指向整数变量的指针,该指针指向的值反映滑块的位置,在创建滑动条时,该参数确定了滑块的初始位置,当滑动条创建完成后,该指针指向的整数随着滑块的移动而改变。第四个参数是滑动条的最大取值。第五个参数是每次滑块更改位置时要调用的函数的指针。最后一个参数是传递给回调函数的 void * 类型数据,如果使用的第三个参数是全局变量,则可以不用修改最后一个参数,使用参数的默认值即可。
在示例程序中,滑动条控制图像亮度系数,将图像原始灰度值乘以亮度系数得到最终的图像。为了使图像亮度变化比较平滑,将滑动条参数除以 100 以得到含有两位小数的亮度系数。为了保证每次亮度的改变都是在原始图像的基础上,设置了 img1 、 img2两个表示图像的全局变量,其中 img1 表示原始图像, img2 表示亮度改变后的图像。在该程序中,通过拖曳滑块可以动态地改变图像的亮度。

1.2 示例代码

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

using namespace std; 
using namespace cv; 

//为了能在被调用函数中使用,设置成全局的
int value; 
void callBack(int, void*); //滑动条回调函数
Mat img1, img2; 

int main() 
{ 
    img1 = imread("../pic/gril.jpg"); 
    if (!img1.data) 
    { 
        cout << "请确认是否输入正确的图像文件" << endl; 
        return -1; 
    } 

    Scalar scalar = mean(img1);
	float imgChannel1 = scalar.val[0];
	float imgChannel2 = scalar.val[1];
	float imgChannel3 = scalar.val[2];
	//float imgChannel4 = scalar.val[3];
	//qDebug() << "--4.method mean picture Light : " << QString::number(picMeanLight, 10, 4);
	float imgLight = (imgChannel1 + imgChannel2 + imgChannel3) / 3;
    
    cout << "111 imgLight == " << imgLight << endl;

    namedWindow("滑动条改变图像亮度"); 
    imshow("滑动条改变图像亮度", img1); 
    value = 100; //滑动条创建时的初始值
    //创建滑动条
    createTrackbar("亮度值百分比", "滑动条改变图像亮度", &value, 600, callBack, 0); 
    waitKey(); 
} 

void callBack(int, void*) 
{ 


    float a = value / 100.0; 
    img2 = img1 * a; 

    Scalar scalar = mean(img2);
	float imgChannel1 = scalar.val[0];
	float imgChannel2 = scalar.val[1];
	float imgChannel3 = scalar.val[2];
	//float imgChannel4 = scalar.val[3];
	//qDebug() << "--4.method mean picture Light : " << QString::number(picMeanLight, 10, 4);
	float imgLight = (imgChannel1 + imgChannel2 + imgChannel3) / 3;
    
    cout << "222 imgLight == " << imgLight << endl;
    imshow("滑动条改变图像亮度", img2); 
}

1.3 运行结果

打印图片亮度时,随着滑动条的值增大而增大

2. 鼠标响应

有时,我们需要在图像中标记出重要的区域,这时通过鼠标可以很好地完成这项任务,因此,OpenCV 4 中也提供了鼠标响应相关函数 setMouseCallback()。

2.1 setMouseCallback()函数原型

void cv::setMouseCallback(const String & winname, 
MouseCallback onMouse, 
void * userdata = 0 
)

• winname :添加鼠标响应的窗口的名字。
• onMouse :鼠标响应的回调函数。
• userdata :传递给回调函数的可选参数。
该函数能够为指定的图像窗口创建鼠标响应。该函数第一个参数是需要创建鼠标响应的图像
窗口的名字。第二个参数为鼠标响应的回调函数,该函数在鼠标状态发生改变时被调用,是一个
MouseCallback 类型的函数。最后一个参数是传递给回调函数的可选参数,一般情况下,使用默认
值 0 即可。

2.2 MouseCallback 类型原型

typedef void(* cv::MouseCallback)(int event, 
int x, 
int y, 
int flags, 
void *userdata 
)

• event :鼠标响应事件,参数为 EVENT_* 形式 。
• x :鼠标指针在图像坐标系中的 x 坐标。
• y :鼠标指针在图像坐标系中的 y 坐标。
• flags :鼠标响应标志,参数为 EVENT_FLAG_* 形式。
• userdata :传递给回调函数的可选参数。
MouseCallback 类型的回调函数是一个无返回值的函数,函数名可以任意设置,有 5 个参数,在鼠标状态发生改变的时候被调用。该函数第一个参数是鼠标响应事件标志。第二个参数和第三个参数分别是鼠标当前位置在图像坐标系中的 x 坐标和 y 坐标。第四个参数是鼠标响应标志。最
后一个参数是传递给回调函数的可选参数,一般情况下,使用 void*默认即可。
鼠标响应事件标志可选参数及含

鼠标响应标志及含义


简单来说,鼠标响应就是当鼠标位于对应的图像窗口内时,时刻检测鼠标状态,当鼠标状态发生改变时,调用回调函数,并根据回调函数中的判断逻辑选择执行相应的操作。例如,回调函数中只处理鼠标左键按下的事件,即判断 event 标志是否为 EVENT_LBUTTONDOWN ,只有当
event==EVENT_ LBUTTONDOWN 时,才有相应的逻辑操作,否则将不会执行任何操作。
为了了解鼠标响应的使用方法,在示例代码 中给出了绘制鼠标移动轨迹的示例程序。在该程序中,如果鼠标右键被按下,就会提示"点击鼠标左键才可以绘制轨迹",若单击左键,就会输出当前鼠标的坐标,并将该点坐标定义为某段轨迹的起始位置。之后按住左键移动鼠标,会进入到第三个逻辑判断,绘制鼠标的移动轨迹。 在该示例程序中,提供了两种绘制轨迹的方法,第一种是每次调用回调函数获得鼠标位置时更改周围的图像像素值,这种方式比较直观,但是,由于回调函数有一定的执行时间,因此,当鼠标移动较快时,绘制的图像轨迹会出现断点;第二种是在前一时刻和当前时刻鼠标位置间绘制直线,这种方式可以避免因鼠标移动过快而带来轨迹出现断点的问题

2.3 示例代码

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

using namespace std; 
using namespace cv; 

Mat img,imgPoint; //全局的图像
Point prePoint; //前一时刻鼠标的坐标,用于绘制直线
void mouse(int event, int x, int y, int flags, void*); 
 
int main() 
{ 
    img = imread("../pic/gril.jpg"); 
    if (!img.data) 
    { 
        cout << "请确认输入图像名称是否正确!" << endl;
        return -1; 
    } 
    img.copyTo(imgPoint); 
    imshow("图像窗口 1", img); 
    imshow("图像窗口 2", imgPoint); 
    setMouseCallback("图像窗口 1", mouse,0 ); //鼠标响应
    waitKey(0); 
    return 0; 
} 
 
void mouse(int event, int x, int y, int flags, void*) 
{ 
    if (event == EVENT_RBUTTONDOWN) //单击右键
    { 
        cout << "点击鼠标左键才可以绘制轨迹" << endl; 
    } 
    if (event == EVENT_LBUTTONDOWN) //单击左键,输出坐标
    { 
        prePoint = Point(x, y); 
        cout << "轨迹起始坐标" << prePoint << endl;  
    } 
    
    if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //按住鼠标左键移动
    { 
        //通过改变图像像素显示鼠标移动轨迹
        imgPoint.at<Vec3b>(y, x) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y, x-1) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y, x+1) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255); 
        imshow("图像窗口 2", imgPoint); 
        
        //通过绘制直线显示鼠标移动轨迹
        Point pt(x, y); 
        line(img, prePoint, pt, Scalar(0, 0, 255), 2, 5, 0); 
        prePoint = pt; 
        imshow("图像窗口 1", img); 
    } 
}

2.4 运行结果

相关推荐
lulu_gh_yu27 分钟前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
Re.不晚1 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
幼儿园老大*2 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
2 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
ctrey_2 小时前
2024-11-4 学习人工智能的Day21 openCV(3)
人工智能·opencv·学习
啦啦右一2 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
霍格沃兹测试开发学社测试人社区3 小时前
软件测试学习笔记丨Flask操作数据库-数据库和表的管理
软件测试·笔记·测试开发·学习·flask
今天我又学废了3 小时前
Scala学习记录,List
学习
王俊山IT3 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
Mephisto.java4 小时前
【大数据学习 | kafka高级部分】kafka中的选举机制
大数据·学习·kafka