Bootstrap

Qt图片交互——QGraphicsView+鼠标选点+放大缩小+OpenCV

1.功能目标

通过 QGraphicsView 实现 OpenCV Mat 的显示,并且可以响应鼠标事件,绘制选择的点,同时可以进行放大缩小操作

备注:QLabel 也能够完成同样的操作,但是QLabel的放缩是对控件本身缩放,像素显示会自动插值,图片放大时无法显示单个像素点,控件放大太大时响应会很慢,如果仅固定大小图片进行展示,可以选择使用QLabel。需要放缩操作时,QGraphicsView 更好用。

2.核心代码

2.1.初始化

在窗体中增加QGraphicsView控件,示例中命名为 imageBox。
窗体类中增加变量:

	//控制缩放的比例因子
	double m_scaleFactor;
	//场景
	QGraphicsScene * m_scene;
	//画布
	QGraphicsPixmapItem * m_imageItem;
	//绘制的OpenCV图像
	cv::Mat m_imageShow;

变量初始化:

	//缩放因子初始化为1
	m_scaleFactor = 1.0;
	m_scene = new QGraphicsScene();
	m_imageItem = new QGraphicsPixmapItem();
	//场景增加画布
	m_scene->addItem(m_imageItem);
	//控件绑定场景
	ui.imageBox->setScene(m_scene);
	//对QGraphcisView控件注册事件响应
	ui.imageBox->installEventFilter(this);
	//使能QGraphcisView控件的鼠标跟踪
	ui.imageBox->setMouseTracking(true);

2.2.图片绘制、放大缩小

将 OpenCV Mat 绘制到控件上的代码如下:

//将OpenCV Mat绘制到QGraphicsView
ShowImage(const cv::Mat & image)
{
	//记录绘制图片
	m_imageShow = image.clone();
	//三通道RGB
	if (image.type() == CV_8UC3)
		m_imageItem->setPixmap(QPixmap::fromImage(QImage((const unsigned char*)image.data, image.cols, image.rows, QImage::Format::Format_RGB888)));
	//四通道RGBA
	else if (image.type() == CV_8UC4)
		m_imageItem->setPixmap(QPixmap::fromImage(QImage((const unsigned char*)image.data, image.cols, image.rows, QImage::Format::Format_RGBA8888_Premultiplied)));
	//单通道Gray
	else if (image.type() == CV_8UC1)
		m_imageItem->setPixmap(QPixmap::fromImage(QImage((const unsigned char*)image.data, image.cols, image.rows, QImage::Format::Format_Grayscale8)));
	else
		return;
	//更新显示
	ui.imageBox->show();
}

//放缩图片(例如:放大时factor=1.2f,缩小时factor=0.8f)
ScaleImage(float factor)
{
	//累计放缩因子
	m_scaleFactor *= factor;
	//构造放缩矩阵
	QMatrix matrix;
	matrix.scale(m_scaleFactor, m_scaleFactor);
	//QGraphicsView执行放缩
	ui.imageBox->setMatrix(matrix);
}

2.3.鼠标响应

首先在类函数中增加:

private slots: bool eventFilter(QObject* watched, QEvent* event);

该函数是初始化中的installEventFilter信号对应的槽。
函数的实现如下:

bool eventFilter(QObject* watched, QEvent* event)
{
	//如果信号不是来自于QGraphicsView,返回。
	if (watched != ui.imageBox)
		return false;

	switch (event->type())
	{
	//按键事件(操作放缩)
	case QEvent::KeyPress:
	{
		QKeyEvent * kEvent = (QKeyEvent*)event;
		//‘+’即键盘中的‘=’,执行放大
		if (kEvent->key() == '=')
		{
			ScaleImage(1.2);
		}
		//‘-’执行缩小
		else if (kEvent->key() == '-')
		{
			ScaleImage(0.8);
		}
	}
	//鼠标事件响应
	case QEvent::MouseButtonPress:
	{
		//执行鼠标左键选点
		QMouseEvent * mEvent = (QMouseEvent*)event;
		if (mEvent->button() != Qt::LeftButton)
			break;
		
		//鼠标点在QGraphics控件上的坐标
		QPoint p = mEvent->pos();
		//控件的视场尺寸
		QSize size = ui.imageBox->viewport()->size();
		//当前显示图片的尺寸,换算为放缩后的尺寸
		cv::Size imgSize = m_imageShow.size();
		imgSize.width *= m_scaleFactor;
		imgSize.height *= m_scaleFactor;

		//运算鼠标点对应图像中的像素坐标
		float xErr = (size.width() - imgSize.width) / 2.;
		float yErr = (size.height() - imgSize.height) / 2.;
		if (xErr < 0)xErr = 0;
		if (yErr < 0)yErr = 0;
		int hv = ui.imageBox->horizontalScrollBar()->value();
		int vv = ui.imageBox->verticalScrollBar()->value();
		int x = (hv + p.x() - xErr) / _scaleFactor;
		int y = (vv + p.y() - yErr) / _scaleFactor;

		//执行选点操作
		SelectPoint(x, y);
		break;
	}

	default:
		break;
	}

	return false;
}

如上即完成了鼠标的选点和放缩操作。

3.示例结果

在这里插入图片描述

;