摘要

本篇博文记录一下,用VS+Qt+Halcon实现对图片的读取以及鼠标缩放,移动(鼠标事件调用了halcon自带的算子)的过程。以及遇到的坑.....

先来看一下动态效果图:

主要控件:

  • 添加一个Label控件,对象名设为label,用于显示图片,并将背景设为黑色,设置方法为:选中Label控件,在属性编辑器中找到styleSheet属性,在其后的值中输入background-color:black即可;
  • 添加四个Push Button控件,如上图所示从左至右,对象名依次为:btn_prePic、btn_openPic、btn_nextPic,btn_resetPic,用于打开图片和前后浏览,以及恢复原图;
  • 添加一个Label,对象名设为label_status,用于实时显示坐标和灰度值;
  • 将label_show控件提升为CMyLabel类,用于接收鼠标事件。

代码例程

在Visual Studio中新建一个Qt GUI项目,名称设为BrowsePic,并新建Mylabel类(继承自QLabel)用于label控件的提升。

  • Mylabel.h
#pragma once
#include "qlabel.h"
#include"QWheelEvent"
#include<HalconCpp.h>
using namespace HalconCpp; class Mylabel :
public QLabel
{
Q_OBJECT public:
Mylabel(QWidget* parent = Q_NULLPTR);
~Mylabel();
//设置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; //是否移动图像标识
};
  • Mylabel.cpp
#include "Mylabel.h"
//定义单步放大倍率
#define ZOOMRATIO 2.0 Mylabel::Mylabel(QWidget* parent)
: QLabel(parent)
{
m_bIsMove = false;
this->setMouseTracking(true); }
Mylabel::~Mylabel()
{ } //设置Halcon图像和Halcon窗口句柄,用户响应鼠标事件后实时更新图像
void Mylabel::setHalconWnd(HObject img, HTuple hHalconID, QLabel* label)
{
m_hHalconID = hHalconID;
m_currentImg = img;
m_label = label;
} //鼠标滚轮缩放事件,用于缩放图像
void Mylabel::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下的坐标
HTuple hv_Exception, hv_ErrMsg;
try
{
GetMposition(m_hHalconID, &mouseRow, &mouseCol, &Button); }
catch (HException& HDevExpDefaultException)
{
return;
}
//获取原图显示的部分,注意也是原图坐标
GetPart(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)
{
//如果有图像,则先清空图像
DetachBackgroundFromWindow(m_hHalconID);
}
SetPart(m_hHalconID, startRowAft, startColAft, endRowAft, endColAft);
AttachBackgroundToWindow(m_currentImg, m_hHalconID);
}
} void Mylabel::mousePressEvent(QMouseEvent* ev)
{
HTuple mouseRow, mouseCol, Button;
try
{
GetMposition(m_hHalconID, &mouseRow, &mouseCol, &Button); }
catch (HException)
{
return;
}
//鼠标按下时的行列坐标
m_tMouseDownRow = mouseRow;
m_tMouseDownCol = mouseCol;
m_bIsMove = true;
}
//鼠标释放事件
void Mylabel::mouseReleaseEvent(QMouseEvent* ev)
{
m_bIsMove = false;
}
//鼠标移动事件
void Mylabel::mouseMoveEvent(QMouseEvent* ev)
{
HTuple startRowBf, startColBf, endRowBf, endColBf, mouseRow, mouseCol, Button;
try
{
GetMposition(m_hHalconID, &mouseRow, &mouseCol, &Button); }
catch (HException)
{
return;
}
//鼠标按下并移动时,移动图像,否则只显示坐标
if (m_bIsMove)
{
//计算移动值
double RowMove = mouseRow[0].D() - m_tMouseDownRow[0].D();
double ColMove = mouseCol[0].D() - m_tMouseDownCol[0].D();
//得到当前的窗口坐标
GetPart(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);
//移动图像
if (m_hHalconID!=NULL)
{
//如果有图像,则先清空图像
DetachBackgroundFromWindow(m_hHalconID);
}
SetPart(m_hHalconID, startRowBf - RowMove, startColBf - ColMove, endRowBf - RowMove, endColBf - ColMove);
AttachBackgroundToWindow(m_currentImg, m_hHalconID);
}
//获取灰度值
HTuple pointGray;
try
{
GetGrayval(m_currentImg, mouseRow, mouseCol, &pointGray);
}
catch (HException)
{
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.h"
using namespace HalconCpp;
#pragma execution_character_set("utf-8"); class BrowsePic : public QWidget
{
Q_OBJECT public:
BrowsePic(QWidget *parent = Q_NULLPTR);
~BrowsePic();
//初始化
void init(); //显示图像
void showImg();
int currentIndex;
//显示图像的控件id
HTuple m_hLabelID; //QLabel控件句柄
HTuple m_hHalconID; //Halcon显示窗口句柄 //原始图像的尺寸
HTuple m_imgWidth, m_imgHeight; //图片路径列表
HTuple m_imgFiles; //当前图像
HObject m_hCurrentImg;
//缩放后的图像
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();
private:
Ui::BrowsePicClass ui;
};
  • BrowsePic.cpp
#include "browsepic.h"
#include"Mylabel.h"
#include <QFileDialog>
#include <QFileInfo>
BrowsePic::BrowsePic(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this); init(); }
BrowsePic::~BrowsePic()
{ } void BrowsePic::init()
{
//设置halcon的文件路径为utf8,解决中文乱码
SetSystem("filename_encoding", "utf8");
//生成空图像
GenEmptyObj(&m_hCurrentImg);
m_hHalconID = NULL;
m_hLabelID = (Hlong)ui.label->winId();
currentIndex = -1;
}
//显示图像
void BrowsePic::showImg()
{
if (m_hHalconID!=NULL)
{
//如果有图像,则先清空图像
DetachBackgroundFromWindow(m_hHalconID); }
else
{
//打开窗口
OpenWindow(0, 0, ui.label->width(), ui.label->height(), m_hLabelID, "visible", "", &m_hHalconID);
}
ui.label-> setHalconWnd(m_hCurrentImg, m_hHalconID, ui.label_status); //获取图像大小
GetImageSize(m_hCurrentImg, &m_imgWidth, &m_imgHeight);
//获取缩放系数
TupleMin2(1.0 * ui.label->width() / m_imgWidth, 1.0 * ui.label->height() / m_imgHeight, &m_hvScaledRate);
//缩放图像
ZoomImageFactor(m_hCurrentImg, &m_hResizedImg, m_hvScaledRate, m_hvScaledRate, "constant");
//获取缩放后的大小
GetImageSize(m_hResizedImg, &m_scaledWidth, &m_scaledHeight);
//打开窗口
if (1.0 * ui.label->width() / m_imgWidth < 1.0 * ui.label->height() / m_imgHeight)
{
SetWindowExtents(m_hHalconID, ui.label->height() / 2.0 - m_scaledHeight / 2.0, 0, ui.label->width(), m_scaledHeight);
}
else
{
SetWindowExtents(m_hHalconID, 0, ui.label->width() / 2.0 - m_scaledWidth / 2.0, m_scaledWidth, ui.label->height()); }
SetPart(m_hHalconID, 0, 0, m_imgHeight - 1, m_imgWidth - 1);
AttachBackgroundToWindow(m_hCurrentImg, m_hHalconID);
} //打开图片
void BrowsePic::on_btn_openPic_clicked()
{
QString path = QFileDialog::getOpenFileName(this, "加载图像", "./", "图像文件(*.bmp *.png *.jpg)");
QFileInfo fileInfo(path);
QString dir = fileInfo.path(); if (!path.isEmpty())
{
ListFiles(dir.toStdString().c_str(), "files", &m_imgFiles); TupleRegexpSelect(m_imgFiles, HTuple("\\.bmp|png|jpg").Append("ignore_case"), &m_imgFiles); for (int i = 0; i < m_imgFiles.Length(); i++)
{ QString currentPath = m_imgFiles[i];
currentPath.replace("\\", "/"); if (currentPath == path)
{
currentIndex = i;
ReadImage(&m_hCurrentImg, m_imgFiles[i]);
showImg();
} }
} } //浏览前一张
void BrowsePic::on_btn_prePic_clicked()
{
if (currentIndex > 0)
{
currentIndex--;
ReadImage(&m_hCurrentImg, m_imgFiles[currentIndex]);
showImg(); }
}
//浏览后一张
void BrowsePic::on_btn_nextPic_clicked()
{
if (currentIndex >= 0 && currentIndex < m_imgFiles.Length() - 1)
{
currentIndex++;
ReadImage(&m_hCurrentImg, m_imgFiles[currentIndex]);
showImg();
}
}
//恢复图片
void BrowsePic::on_btn_resetPic_clicked()
{
showImg();
}

关键代码解释

1️⃣Qt函数与Halcon算子获取的文件路径字符串的区别

  • Halcon算子获取的文件路径格式

list_files()的原型如下:

第一个参数为路径,提取的文件路径格式与参数Directory的形式有关,在HDevelop中测试:

– Directory以"\\"分隔时,即list_files ('E:\\TEST', 'files', Files)

– Directory以“/”分隔时,即list_files ('E:/TEST', 'files', Files)

可以发现两种方式提取的文件路径字符串的区别。

  • Qt函数获取的文件路径格式

getOpenFileName()获得的路径:

如何将二者路径保持一致?

先读取halcon算子获取的路径:

            QString currentPath = m_imgFiles[i];

然后将" \ "全部换成" /":

            currentPath.replace("\\", "/");

2️⃣在VS中使用Halcon时的编码及中文乱码问题

默认条件下,可使用以下C++语句获取Halcon的文件名编码:

HTuple codeType;
get_system("filename_encoding", &codeType);
QString strCodeType = codeType[0].S();

可以发现默认的编码是locale,此时用Halcon算子list_files获取的文件路径中如果包含中文,则会出现乱码

解决方法:将Halcon的文件名编码格式设置为utf8,代码如下:

set_system("filename_encoding", "utf8");

参考链接:(4条消息) VS+Qt应用开发-使用Halcon算子实现从文件夹打开图片、前后浏览、缩放居中显示_羽士的博客-CSDN博客

VS+Qt+Halcon——显示图片,实现鼠标缩放、移动图片的更多相关文章

  1. arcgis 加载png图片实现图片跟随地图缩放 和图片的动态播放

    效果图: 主要原理: png加载到地图上是不可能的, 图像本身是没有地理信息的. 这里采用一种办法, 在地图上创建一个图形图层, 图形图层放一个矩形,给这个矩形用一个图片填充符号填充. 关键技术点: ...

  2. 20.QT-Qpixmap实现图片鼠标缩放,鼠标拖动示例(详解)

    通过 QPainter 绘画实现,以本地图片985*740为例 如下图所示: 效果如下所示: 实现原理 主要通过以下函数实现: , ); //平铺显示pixmap //x y w h :表示绘画区域 ...

  3. Magnifier.js - 支持鼠标滚轮缩放的图片放大镜效果

    Magnifier.js 是一个 JavaScript 库,能够帮助你在图像上实现放大镜效果,支持使用鼠标滚轮放大/缩小功能.放大的图像可以显示在镜头本身或它的外部容器中.Magnifier.js 使 ...

  4. QT中给各控件增加背景图片(可缩放可旋转)的几种方法

    http://blog.csdn.net/liukang325/article/details/44832397 1. 给QPushButton 增加背景图片:背景图片可根据Button大小自由缩放. ...

  5. Discuz!图片查看插件(支持鼠标缩放、实际大小、旋转、下载)

    Discuz!图片查看插件(支持鼠标缩放.实际大小.旋转.下载) 图片查看是网站中的常用功能,用于展示详细的图片.在discuz图片插件的基础上进行了改造,因此这篇文章主要从以下几个方面来讨论图片查看 ...

  6. jQuery hover事件鼠标滑过图片半透明标题文字滑动显示隐藏

    1.效果及功能说明 hover事件制作产品图片鼠标滑过图片半透明,标题文字从左到右滑动动画移动显示隐藏 2.实现原理 首先把效果都隐藏,然后定义一个伪类来触发所有的效果,接下来当触发伪类后会有一个遍历 ...

  7. 一天JavaScript示例-点击图片显示大图片添加鼠标

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  8. C#,一种简单的方式实现滚动鼠标缩放图片,平移

    1.缩放 private void ImageShow_Load(object sender, EventArgs e) { pictureBox1.Load(@"E:\SQ1.jpg&qu ...

  9. 在WPF里面实现以鼠标位置为中心缩放移动图片

    原文:在WPF里面实现以鼠标位置为中心缩放移动图片 在以前的文章使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果里面,介绍了如何在WPF里面移动和放大缩小图片, ...

随机推荐

  1. FreeRTOS常用函数

    一.任务 任务创建和删除xTaskCreate                                 任务创建xTaskDelete                              ...

  2. Asp.net mvc使用SignaIR

    一.Asp.net SignalR 是个什么东东 Asp.net SignalR是微软为实现实时通信的一个类库.一般情况下,SignalR会使用JavaScript的长轮询(long polling) ...

  3. ES异地双活方案

    对于单机房而言,只要参考Elastic Search 官方文档,搭建一个集群即可,示意图如下: 原理类似分布式选举那一套,当一个master节点宕机时,剩下2个投票选出1个新老大,整个集群可以继续服务 ...

  4. YAOI Round #1 题解

    前言 比赛网址:http://47.110.12.131:9016/contest/3 总体来说,这次比赛是有一定区分度的, \(\text{ACM}\) 赛制也挺有意思的. 题解 A. 云之彼端,约 ...

  5. C语言 c++区别

    C语言是C89标准,C++是C++99标准的.C89就是在1989年制定的标准,如今最新的是C11和C++11标准.根据不同的标准,它们的功能也会有所不同,但是越新的版本支持的编译器越少

  6. Jmeter之事务控制器

    性能测试的结果统计时我们一定会关注TPS,TPS代表的是每秒事务数,每个事务对应的是我们的请求.虽然JMeter能够帮我们把每个请求统计成一个事务,但有时候我们希望把多个操作统计成一个事务,JMete ...

  7. Git初始化本地已有项目

    1.初始化仓库 git init 2.remote git remote add origin 仓库地址 3.从远程分支拉取master分支并与本地master分支合并 git pull origin ...

  8. Redis中一个String类型引发的惨案

    ​      曾经看到这么一个案例,有一个团队需要开发一个图片存储系统,要求这个系统能快速记录图片ID和图片存储对象ID,同时还需要能够根据图片的ID快速找到图片存储对象ID.我们假设用10位数来表示 ...

  9. .net 5+ 知新:【1】 .Net 5 基本概念和开发环境搭建

    最近一两年搞了很多其它事情,.net web方面的基本没做,之前做过几个小的项目零星的学习了些,从.net core 发布后其实都没正真的系统学习过. 就是上手做项目,平时也有关注和看些资料,所以项目 ...

  10. 优化 Workerman 检查主进程是否存活的逻辑

    主要新增了判断进程是否为 Workerman 进程的逻辑,从而优化了确定主进程是否存活的准确性 发现问题 年前逛 GitHub 的时候,发现 Workerman 有一个 2017 年打开的 Issue ...