1.声明一个绘制相关的类 GCApplication
class GCApplication
{
public:
enum { NOT_SET = 0, IN_PROCESS = 1, SET = 2 };
void reset();
void setImageAndWinName(const cv::Mat& _image, const std::string& _winName);
void showImage() const;
void mouseClick(int event, int x, int y, int flags, void* param);
private:
const std::string* winName; //图像窗口指针
const cv::Mat* image; //图像指针
uchar rectState; //绘制状态标识
cv::Rect rect; //绘制矩形
};
- 声明一个枚举变量,标识当前的绘制状态,增强程序的可读性:NOT_SET=未开启绘制,IN_PROCESS=正在绘制,SET=绘制完成;
- reset()函数:重置绘制事件;
- showImage():显示图像与绘制的矩形ROI。由于showImage只显示图像内容,而不改变成员变量,所以在函数后添加const关键字作为标识;
- mouseClick():定义绘制事件所执行的内容
2.定义 GCApplication类中的内容
2.1 reset()函数
重置绘图状态
void GCApplication::reset()
{
rectState = NOT_SET; //未开启绘制
}
2.2 setImageAndWinName()函数
显示图像与绘制的矩形ROI
void GCApplication::setImageAndWinName(const cv::Mat& _image, const std::string& _winName)
{
if (_image.empty() || _winName.empty())
return;
image = &_image; //将内存地址传递给image
winName = &_winName;
reset();
}
下面简单介绍一下“&”的作用
- & 是一元运算符
- const Mat& _image 声明时加&,声明引用变量,_image是image的别名,两者指向的是同一变量
- image = &_image 调用前加&,返回操作数的内存地址
2.3 showImage() const 函数
显示图像与绘制的矩形ROI
void GCApplication::showImage() const
{
if (image->empty() || winName->empty())
return;
cv::Mat res;
image->copyTo(res); //图像拷贝到res中
if (rectState == IN_PROCESS || rectState == SET)
rectangle(res, cv::Point(rect.x, rect.y), cv::Point(rect.x + rect.width, rect.y + rect.height), GREEN, 2);
imshow(*winName, res);
}
- 形参加const,限制传递参数不可改变
- 常量成员函数是不能改变成员变量值的函数
- const 成员函数详细讲解可以去传送门
2.4 mouseClick() 函数
定义绘画事件的作用
void GCApplication::mouseClick(int event, int x, int y, int flags, void*)
{
// 鼠标回调事件
switch (event)
{
case cv::EVENT_LBUTTONDOWN: // 鼠标左键按下事件
{
//程序启动时,rectState已被设置为NOT_SET
//鼠标左键按下时,设置rectState的状态为IN_PROCESS
//并将此时鼠标的位置设定为rect的起始点
if (rectState == NOT_SET)
{
rectState = IN_PROCESS;
rect = cv::Rect(x, y, 1, 1);
}
}
break;
case cv::EVENT_LBUTTONUP: //鼠标左键抬起事件
//当鼠标左键抬起时,如果rectState不处于在绘制的状态,则不做任何处理
//如rectState==IN_PROCESS时,则执行rect的绘制与显示,将当前鼠标位置作为
//矩形的右下顶点最终坐标,同时,要调用showImage,来实时显示绘制效果
if (rectState == IN_PROCESS)
{
if (rect.x == x || rect.y == y) {
rectState = NOT_SET;
}
else {
rect = cv::Rect(cv::Point(rect.x, rect.y), cv::Point(x, y));
rectState = SET;
}
showImage();
}
break;
case cv::EVENT_MOUSEMOVE: //鼠标移动事件
//当鼠标移动,且左键按下时,开始绘制
if (rectState == IN_PROCESS)
{
rect = cv::Rect(cv::Point(rect.x, rect.y), cv::Point(x, y));
showImage();
}
break;
}
}
3. 运行实例
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
const cv::Scalar GREEN = cv::Scalar(0, 255, 0); //定义一个颜色常量
GCApplication gcapp; //声明GCApplication的全局变量,以便调用相关函数
static void on_mouse(int event, int x, int y, int flags, void* param)
{
gcapp.mouseClick(event, x, y, flags, param);
}
int main(int argc, char** argv)
{
//1.读取图像
cv::Mat image = cv::imread("messi5.jpg", cv::IMREAD_COLOR);
if (image.empty())
{
std::cout << "\n Durn, couldn't read image filename " << std::endl;
return 1;
}
//2.创建显示窗口
const std::string winName = "image";
cv::namedWindow(winName, cv::WINDOW_AUTOSIZE);
//3.创建鼠标回调
cv::setMouseCallback(winName, on_mouse, 0);
//4.将image和winName传递给GCApplication类的成员变量
gcapp.setImageAndWinName(image, winName);
//5.通过showImage函数显示图像与ROI区域
gcapp.showImage();
//6.设定一个循环函数,来执行键盘上一些按键的操作
for (;;)
{
char c = (char)cv::waitKey(0);
switch (c)
{
case '\x1b': //Esc退出
std::cout << "Exiting ..." << std::endl;
goto exit_main;
case 'r': //R 重置
std::cout << std::endl;
gcapp.reset();
gcapp.showImage();
break;
}
}
exit_main:
cv::destroyWindow(winName); //退出时销毁窗体
return 0;
}
备注:为甚要定义一个静态on_mouse函数呢?
- 定义on_mouse函数的原因是setMouseCallback不支持非静态函数的调用,否则会报错,“C++提示非标准语法;请使用来创建指向成员的指针”
- 那么,为什么不在类中直接将mouseClick定义成静态函数呢,如果这样做的话,函数里调用的成员变量和成员函数都要改变成静态的,否则会编译报错,
- 另外一种做法就是在mouseClick里面对类声明一个GCApplication对象,调用其他成员变量和函数
贴出原图:
效果截图:
文章出处登录后可见!
已经登录?立即刷新