Bootstrap

VS+Qt应用开发-Qt+Halcon显示图片,实现鼠标缩放、移动图片

VS+Qt应用开发-Qt+Halcon显示图片,实现鼠标缩放、移动图片

关于显示图像以及前后浏览的功能请参照我的另一篇博客:
VS+Qt应用开发-实现从文件夹打开图片,前后浏览

关于控件提升请参照我的另一篇博客:
VS+Qt应用开发-控件提升-以Label为例


效果展示

  • 动态展示

在这里插入图片描述

  • 显示图像在这里插入图片描述

  • 鼠标滚轮缩放图像
    在这里插入图片描述
    在这里插入图片描述

  • 鼠标按下拖动平移图像在这里插入图片描述

  • 实时显示坐标和灰度值在这里插入图片描述

  • 恢复图像在这里插入图片描述



界面设计

在这里插入图片描述

在文章VS+Qt应用开发-实现从文件夹打开图片,前后浏览
的界面基础上进行以下增改:

  • 添加一个Push Button,对象名设为btn_resetPic,用于恢复图片;
  • 添加一个Label,对象名设为label_status,用于实时显示坐标和灰度值;
  • 将label_show控件提升为CMyLabel类,用于接收鼠标事件。


代码例程

完整代码

首先贴上完整代码:

CMyLabel.h

#pragma once

#include <QLabel>
#include <QWheelEvent>
#include <HalconCpp.h>

using namespace Halcon;

class CMyLabel : public QLabel
{
	Q_OBJECT

public:
	CMyLabel(QWidget *parent = Q_NULLPTR);
	~CMyLabel();

	//设置Halcon图像和Halcon窗口句柄,用户响应鼠标事件后实时更新图像
	void setHalconWnd(Hobject img, HTuple hHalconID, QLabel *label);

	//鼠标滚轮缩放事件
	void wheelEvent(QWheelEvent *ev);

	//鼠标按下事件
	void mousePressEvent(QMouseEvent *ev);

	//鼠标释放事件
	void mouseReleaseEvent(QMouseEvent *ev);

	//鼠标移动事件
	void mouseMoveEvent(QMouseEvent *ev);

public:
	HTuple m_labelID;			//Qt标签句柄
	HTuple m_hHalconID;			//Halcon窗口句柄
	
	Hobject m_currentImg;		//当前的图像
	
	//主界面显示坐标的标签
	QLabel *m_label;

	//鼠标按下的位置 
	HTuple m_tMouseDownRow;
	HTuple m_tMouseDownCol;

	bool m_bIsMove;				//是否移动图像标识
};

CMyLabel.cpp

#include "CMyLabel.h"

//定义单步放大倍率
#define ZOOMRATIO 2.0

CMyLabel::CMyLabel(QWidget *parent)
	: QLabel(parent)
{
	m_bIsMove = false;
}

CMyLabel::~CMyLabel()
{
}

//设置Halcon图像和Halcon窗口句柄,用户响应鼠标事件后实时更新图像
void CMyLabel::setHalconWnd(Hobject img, HTuple hHalconID, QLabel *label)
{
	m_hHalconID = hHalconID;
	m_currentImg = img;
	m_label = label;
}

//鼠标滚轮缩放事件,用于缩放图像
void CMyLabel::wheelEvent(QWheelEvent *ev)
{
	double Zoom;			//放大或缩小倍率
	HTuple  mouseRow, mouseCol, Button;
	HTuple startRowBf, startColBf, endRowBf, endColBf, Ht, Wt, startRowAft, startColAft, endRowAft, endColAft;

	//滚轮前滑,放大
	if (ev->delta() > 0)
	{
		Zoom = ZOOMRATIO;
	}
	else
	{
		Zoom = 1 / ZOOMRATIO;
	}

	//获取光标在原图上的位置,注意是原图坐标,不是Label下的坐标
	set_check("~give_error");
	Herror ret = get_mposition(m_hHalconID, &mouseRow, &mouseCol, &Button);
	set_check("give_error");

	//当光标不在Halcon窗口内时返回,否则会报错
	if (ret != H_MSG_TRUE)
	{
		return;
	}

	//获取原图显示的部分,注意也是原图坐标
	get_part(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);

	//缩放前显示的图像宽高
	Ht = endRowBf - startRowBf;
	Wt = endColBf - startColBf;

	//普通版halcon能处理的图像最大尺寸是32K*32K。如果无限缩小原图像,导致显示的图像超出限制,则会造成程序崩溃
	if ((Ht * Wt < 20000 * 20000 || Zoom == ZOOMRATIO))
	{
		//计算缩放后的图像区域
		startRowAft = mouseRow - ((mouseRow - startRowBf) / Zoom);
		startColAft = mouseCol - ((mouseCol - startColBf) / Zoom);

		endRowAft = startRowAft + (Ht / Zoom);
		endColAft = startColAft + (Wt / Zoom);

		//如果放大过大,则返回
		if (endRowAft - startRowAft < 2)
		{
			return;
		}

		if (m_hHalconID != NULL)
		{
			//如果有图像,则先清空图像
			detach_background_from_window(m_hHalconID);
		}
		set_part(m_hHalconID, startRowAft, startColAft, endRowAft, endColAft);
		attach_background_to_window(m_currentImg, m_hHalconID);
	}
}

//鼠标按下事件
void CMyLabel::mousePressEvent(QMouseEvent *ev)
{
	HTuple mouseRow, mouseCol, Button;
	set_check("~give_error");
	Herror ret = get_mposition(m_hHalconID, &mouseRow, &mouseCol, &Button);
	set_check("give_error");

	//当光标不在Halcon窗口内时返回,否则会报错
	if (ret != H_MSG_TRUE)
	{
		return;
	}

	//鼠标按下时的行列坐标
	m_tMouseDownRow = mouseRow;
	m_tMouseDownCol = mouseCol;

	m_bIsMove = true;
}

//鼠标释放事件
void CMyLabel::mouseReleaseEvent(QMouseEvent *ev)
{
	m_bIsMove = false;
}

//鼠标移动事件
void CMyLabel::mouseMoveEvent(QMouseEvent *ev)
{
	HTuple startRowBf, startColBf, endRowBf, endColBf, mouseRow, mouseCol, Button;

	//获取当前鼠标在原图的位置
	set_check("~give_error");
	Herror ret=	get_mposition(m_hHalconID, &mouseRow, &mouseCol, &Button);
	set_check("give_error");

	//当光标不在Halcon窗口内时返回,否则会报错
	if (ret != H_MSG_TRUE)
	{
		return;
	}

	//鼠标按下并移动时,移动图像,否则只显示坐标
	if (m_bIsMove)
	{
		//计算移动值
		double RowMove = mouseRow[0].D() - m_tMouseDownRow[0].D();
		double ColMove = mouseCol[0].D() - m_tMouseDownCol[0].D();

		//得到当前的窗口坐标
		get_part(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);

		//移动图像
		if (m_hHalconID != NULL)
		{
			//如果有图像,则先清空图像
			detach_background_from_window(m_hHalconID);
		}
		set_part(m_hHalconID, startRowBf - RowMove, startColBf - ColMove, endRowBf - RowMove, endColBf - ColMove);
		attach_background_to_window(m_currentImg, m_hHalconID);
	}

	//获取灰度值
	HTuple pointGray;
	set_check("~give_error");
	ret = get_grayval(m_currentImg, mouseRow, mouseCol, &pointGray);  
	set_check("give_error");

	//当光标不在Halcon窗口内时返回,否则会报错
	if (ret != H_MSG_TRUE)
	{
		m_label->setText(QString::fromLocal8Bit("X坐标:-    Y坐标:-    灰度值:-"));

		return;
	}

	//设置坐标
	m_label->setText(QString::fromLocal8Bit("X坐标:%1    Y坐标:%2    灰度值:%3").arg(mouseCol[0].D()).arg(mouseRow[0].D()).arg(pointGray[0].D()));
}

BrowsePic.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_BrowsePic.h"
#include <HalconCpp.h>
#include <QToolBar>
using namespace Halcon;

#pragma execution_character_set("utf-8");

class BrowsePic : public QWidget
{
	Q_OBJECT

public:
	BrowsePic(QWidget *parent = Q_NULLPTR);
	~BrowsePic();

	//初始化
	void init();

	//打开窗口
	void showImg();

	//窗体大小改变事件
	void resizeEvent(QResizeEvent *ev);
public:
	//显示图像的控件id
	HTuple m_hLabelID;			//QLabel控件句柄
	HTuple m_hHalconID;			//Halcon显示窗口句柄

	//原始图像的尺寸
	HTuple m_imgWidth, m_imgHeight;

	//图片路径列表
	HTuple m_imgFiles;

	//当前图像
	Hobject m_hCurrentImg;

	//当前图像的下标
	HTuple index;

	//是否等比缩放
	bool m_bIsEqualScale;

	//是否居中显示
	bool m_bIsShowCenter;

	//缩放后的图像
	Hobject m_hResizedImg;

	//缩放系数
	HTuple m_hvScaledRate;

	//缩放后图像的大小
	HTuple m_scaledHeight, m_scaledWidth;

	QToolBar *m_toolBar;
public slots:

	//打开图片
	void on_btn_openPic_clicked();

	//浏览前一张
	void on_btn_prePic_clicked();

	//浏览后一张
	void on_btn_nextPic_clicked();

	//恢复图片
	void on_btn_resetPic_clicked();

	//等比缩放复选框状态改变
	void on_checkBox_equalScale_stateChanged(int);

	//居中显示复选框状态改变
	void on_checkBox_showCenter_stateChanged(int);
private:
	Ui::BrowsePicClass ui;
};

BrowsePic.cpp

#include "BrowsePic.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QLabel>
BrowsePic::BrowsePic(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	//初始化 
	init();
}

BrowsePic::~BrowsePic()
{

}

//初始化
void BrowsePic::init()
{
	//设置halcon的文件路径为utf8,解决中文乱码
	set_system("filename_encoding", "utf8");

	//生成空图像
	gen_empty_obj(&m_hCurrentImg);

	//获取图像显示控件的句柄
	m_hHalconID = NULL;
	m_hLabelID = (Hlong)ui.label_show->winId();

	m_bIsEqualScale = false;
	m_bIsShowCenter = true;

	ui.checkBox_equalScale->setChecked(m_bIsEqualScale);
	ui.checkBox_showCenter->setChecked(m_bIsShowCenter);
	ui.checkBox_showCenter->setEnabled(false);
}

//打开窗口
void BrowsePic::showImg()
{
	//判断图像是否为空
	Hlong isEqual;
	Hobject emptyObj;
	gen_empty_obj(&emptyObj);
	test_equal_obj(emptyObj, m_hCurrentImg, &isEqual);

	if (isEqual)
	{
		return;
	}

	if (m_hHalconID != NULL)
	{
		//如果有图像,则先清空图像
		detach_background_from_window(m_hHalconID);
	}
	else
	{
		//打开窗口
		open_window(0, 0, ui.label_show->width(), ui.label_show->height(), m_hLabelID, "visible", "", &m_hHalconID);

	}

	ui.label_show->setHalconWnd(m_hCurrentImg, m_hHalconID, ui.label_status);


	get_image_size(m_hCurrentImg, &m_imgWidth, &m_imgHeight);

	//判断是否等比缩放
	if (m_bIsEqualScale)
	{
		//获取缩放系数
		tuple_min2(1.0 * ui.label_show->width() / m_imgWidth, 1.0 * ui.label_show->height() / m_imgHeight, &m_hvScaledRate);

		//进行图像缩放
		zoom_image_factor(m_hCurrentImg, &m_hResizedImg, m_hvScaledRate, m_hvScaledRate, "constant");

		get_image_size(m_hResizedImg, &m_scaledWidth, &m_scaledHeight);
		set_window_extents(m_hHalconID, 0, 0, m_scaledWidth, m_scaledHeight);

		if (m_bIsShowCenter)
		{
			if (1.0*ui.label_show->width() / m_imgWidth < 1.0*ui.label_show->height() / m_imgHeight)
			{
				set_window_extents(m_hHalconID, ui.label_show->height() / 2.0 - m_scaledHeight / 2.0, 0, ui.label_show->width(), m_scaledHeight);
			}
			else
			{
				set_window_extents(m_hHalconID, 0, ui.label_show->width() / 2.0 - m_scaledWidth / 2.0, m_scaledWidth, ui.label_show->height());
			}
		}
		
	}
	else
	{
		set_window_extents(m_hHalconID, 0, 0, ui.label_show->width(), ui.label_show->height());		
	}

	set_part(m_hHalconID, 0, 0, m_imgHeight - 1, m_imgWidth - 1);
	attach_background_to_window(m_hCurrentImg, m_hHalconID);
}

//窗体大小改变事件
void BrowsePic::resizeEvent(QResizeEvent *ev)
{
	if (m_hHalconID != NULL)
	{
		//防止窗口闪烁
		set_system("flush_graphic","false");
		showImg();
		set_system("flush_graphic", "true");
		detach_background_from_window(m_hHalconID);

		attach_background_to_window(m_hCurrentImg, m_hHalconID);

	}
}

//打开图片
void BrowsePic::on_btn_openPic_clicked()
{
	//打开选择文件对话框,选择文件
	QString qtFilePath = QFileDialog::getOpenFileName(this, "选择图片", "./", "图像文件(*.jpg *.png *.bmp *.tif)");

	if (qtFilePath.isEmpty())
	{
		return;
	}

	QFileInfo fileInfo(qtFilePath);

	qtFilePath.replace("/", "\\");
	QString hPath = fileInfo.path().replace("/", "\\");

	//由halcon算子获取选中的文件夹中的所有文件
	list_files(hPath.toStdString().c_str(), HTuple("files").Append("follow_links"), &m_imgFiles);

	//筛选符合图片格式的文件
	tuple_regexp_select(m_imgFiles, HTuple("\\.(jpg|png|bmp|tif)$").Append("ignore_case"), &m_imgFiles);

	//找到选择的文件

	tuple_find(m_imgFiles, qtFilePath.toStdString().c_str(), &index);

	//读取图像
	read_image(&m_hCurrentImg, HTuple(m_imgFiles[index]));

	showImg();
}

//浏览前一张
void BrowsePic::on_btn_prePic_clicked()
{
	if (index.Num()>0&&index[0].I() > 0)
	{
		index[0] = index[0].I() - 1;
		read_image(&m_hCurrentImg, HTuple(m_imgFiles[index]));
		showImg();
	}

}

//浏览后一张
void BrowsePic::on_btn_nextPic_clicked()
{
	if (index.Num()>0 && index[0].I() < m_imgFiles.Num() - 1)
	{
		index[0] = index[0].I() + 1;
		read_image(&m_hCurrentImg, HTuple(m_imgFiles[index]));
		showImg();
	}
}

//恢复图片
void BrowsePic::on_btn_resetPic_clicked()
{
	showImg();
}

//等比缩放复选框状态改变
void BrowsePic::on_checkBox_equalScale_stateChanged(int state)
{
	ui.checkBox_showCenter->setEnabled(state);
	m_bIsEqualScale = state;
	showImg();
}

//居中显示复选框状态改变
void BrowsePic::on_checkBox_showCenter_stateChanged(int state)
{
	m_bIsShowCenter = state;
	showImg();
}



关键代码解释

;