Day 3 – Opencv 鼠标绘制ROI区域

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对象,调用其他成员变量和函数

贴出原图:
Day 3 - Opencv 鼠标绘制ROI区域
效果截图:
Day 3 - Opencv 鼠标绘制ROI区域

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年4月20日 下午6:48
下一篇 2022年4月20日 下午6:55

相关推荐