QT实现机器视觉图片查看窗口

QT实现机器视觉常用图像查看器

在机器视觉行业中最常见的控件就是图像查看器了,使用QT实现其实也非常简单,在我出的项目【降龙:算法软件框架】和【重明:工业相机二次开发】中都有用到。可以说只要你要开发一个和机器视觉相关的软件,就离不开图像查看器。
在这里插入图片描述

如上图时重明项目的软件界面,中间的就是图像查看器。

本文将图像查看器的代码给大家拆解独立出来,并和大家讲解一下图像查看器的实现原理。完整代码工程在公众号【周旋机器视觉】后台发送【十二生肖控件】获取。

效果动图展示:
在这里插入图片描述

1、实现思路

首先介绍一下实现的大体思路,常见图像查看器的实现思路有两种,分别是

  1. 使用QWidget和QLabel相结合的方式。这种方式如果你仅仅是想实现图像的显示,那很简单,直接将图像放到QLabel里就可以了,但如果你还想实现图像放大缩小平移查看等功能,就需要自己重写各类鼠标事件,处理复杂的逻辑,所以我们不采用这种方式。
  2. 实现思路2就是借助QT的视图模型框架,通过重写自己的QGraphicsView类,就可以轻松实现一个如上文展示效果的图像查看器。

2、QT视图模型介绍

在我们常规认知里,例如显示一张图像,那只需要一个QWidget(也可以说是画布),然后我们将图像显示在QWidget上(也可以说画在画布上),就完成了,只需要两个对象,一个图像,一个QWidget窗口。

但在视图模型中,会有三个东西,分别是:

  1. Graphics Scene:场景 /场景管理器( Scene 同时担负着管理场景中的对象,建立索引等工作)。
  2. Graphics View:图形视图,也可以说是窗口。
  3. Graphics Item:场景中可以被显示的元素,可以是我们的图像,也可以是矩形圆形等任何东西。

在网上的一段对三者的描述非常好:

Scene就好比天空,无限大,而Item就是天空中的云朵,可以有很多云,而view就好比一扇窗户,透过窗户可以看到天空中的云,而一片天空可以通过很多扇窗户去看。所以一个Scene可以同时对应多个View,但是一个View只能对应一个Scene。

在这里插入图片描述

3、如何使用QGraphics

QT有现成的视图类,我们直接调用即可,调用也很简单,如下所示:

    //创建SceneQGraphicsScene* pScene = new QGraphicsScene(this);//创建View并为其绑定SceneQGraphicsView* pView = new QGraphicsView(this);pView->setScene(pScene);//使用我们的图像初始化一个ItemQImage srcImage("C:\\Users\\Administrator\\Pictures\\Laner\\Laner.png");QGraphicsPixmapItem* pItem = new QGraphicsPixmapItem();//设置元素可以选中和拖动pItem->setAcceptHoverEvents(true);pItem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);pItem->setPixmap(QPixmap::fromImage(srcImage));pScene->addItem(pItem);//将我们上面实现的View添加到主界面QVBoxLayout* pMainLayout = new QVBoxLayout();pMainLayout->setContentsMargins(0,0,0,0);pMainLayout->setSpacing(0);pMainLayout->addWidget(pView);QWidget* pCenterWidget = new QWidget(this);pCenterWidget->setLayout(pMainLayout);this->setCentralWidget(pCenterWidget);

运行效果如下:
在这里插入图片描述

效果并不是我们预想的那样,有几个问题:

  1. 背景颜色不是我们想要的黑白格或者是任何其它样式,但实际上背景是可以自定义绘制的
  2. 图像元素的尺寸没有放大适配我们的窗口界面
  3. 双击窗口界面,图像元素不能居中显示
  4. 并没有我们左下角半透明的Label,可以显示鼠标的坐标,以及对应图像元素位置的像素值
  5. 等等其它问题… …

所以想实现我们文章开头的预期效果,并不是这么几行就可以搞定的,我们需要重写QGraphicsView类,实现我们预期的自定义功能,例如双击鼠标事件,背景绘制等等。

4、重写QGraphicsView类

对于如何重写,我们在文章里就不做详细说明了,代码就是最好的介绍。对于代码关键位置,我也写了详细的注释:

CustomGraphicsView.h:

#ifndef CUSTOMGRAPHICSVIEW_H
#define CUSTOMGRAPHICSVIEW_H
/****************************************************重写视图类,该类为视觉窗口的核心代码***************************************************/#include <QWidget>
#include <QGraphicsView>
#include <QEvent>
#include <QLabel>class CustomImageItem;
class CustomGraphicsView : public QGraphicsView
{Q_OBJECT
public:CustomGraphicsView(QWidget *parent = 0);~CustomGraphicsView();//界面初始化bool InitWidget();//设置视觉窗口的图像void SetImage(const QImage & qImage);protected:virtual void wheelEvent(QWheelEvent *event) override;virtual void mouseDoubleClickEvent(QMouseEvent *event) override;virtual void paintEvent(QPaintEvent* event) override;virtual void resizeEvent(QResizeEvent *event) override;public slots://视图居中显示void onCenter();//视图缩放void onZoom(float fScaleFactor);private://辅助函数:自适应大小void fitFrame();void setBackground(bool enabled = true,bool invertColor = false);private:double m_dZoomValue = 1;QGraphicsScene* m_pScene;//场景CustomImageItem* m_pImageItem;//图像元素QWidget* m_pPosInfoWidget;//视觉窗口左下方,用于显示鼠标位置以及对应位置像素灰度值QLabel* m_pPosInfoLabel; //显示灰度值的标签QPixmap m_Image;//视觉窗口所显示的图像QImage m_qImage;QPixmap m_tilePixmap = QPixmap(36, 36);//背景图片方格
};#endif // CUSTOMGRAPHICSVIEW_H

CustomGraphicsView.cpp:

#include "CustomGraphicsView.h"
#include <QMutexLocker>
#include <QLayout>
#include <QWheelEvent>
#include "CustomImageItem.h"#define ZOOMMAX 50   //最大放大倍数
#define ZOOMMIN 0.02 //最小缩小倍数CustomGraphicsView::CustomGraphicsView(QWidget *parent): QGraphicsView(parent), m_pScene(Q_NULLPTR), m_pImageItem(Q_NULLPTR), m_pPosInfoWidget(Q_NULLPTR), m_pPosInfoLabel(Q_NULLPTR)
{this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);this->setRenderHint(QPainter::Antialiasing);this->setTransformationAnchor(QGraphicsView::AnchorViewCenter);this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);this->setSceneRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX);setBackground();centerOn(0, 0);if(false == InitWidget()){throw std::bad_alloc();}
}CustomGraphicsView::~CustomGraphicsView()
{
}bool CustomGraphicsView::InitWidget()
{//创建变量对象m_pScene = new QGraphicsScene(this);m_pImageItem = new CustomImageItem(this);m_pImageItem->setAcceptHoverEvents(true);m_pImageItem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);this->setScene(m_pScene);m_pScene->addItem(m_pImageItem);m_pPosInfoLabel = new QLabel(this);m_pPosInfoWidget = new QWidget(this);//在视觉窗口下方显示鼠标坐标以及图像的灰度值m_pPosInfoLabel->setStyleSheet("color:rgb(200,255,200); ""background-color:rgba(50,50,50,160); ""font: Microsoft YaHei;""font-size: 15px;");m_pPosInfoLabel->setText(" W:0,H:0 | X:0,Y:0 | R:0,G:0,B:0");//显示区域窗口m_pPosInfoWidget->setFixedHeight(25);m_pPosInfoWidget->setGeometry(0, this->height() - 25, this->width(), 25);m_pPosInfoWidget->setStyleSheet("background-color:rgba(0,0,0,0);");QHBoxLayout* pInfoLayout = new QHBoxLayout();pInfoLayout->setSpacing(0);pInfoLayout->setContentsMargins(0,0,0,0);pInfoLayout->addWidget(m_pPosInfoLabel);m_pPosInfoWidget->setLayout(pInfoLayout);//初始化信号槽connect(m_pImageItem, &CustomImageItem::RGBValue, this, [&](QString InfoVal) {m_pPosInfoLabel->setText(InfoVal);});return true;
}//为视觉窗口设置图像,是一个公共对外接口
void CustomGraphicsView::SetImage(const QImage &image)
{static QMutex mutex;QMutexLocker locker(&mutex);m_qImage = image.copy();m_Image = QPixmap::fromImage(image);m_pImageItem->w = m_Image.width();m_pImageItem->h = m_Image.height();m_pImageItem->setPixmap(m_Image);fitFrame();onCenter();show();
}//重写鼠标滚轮滚动的事件函数
//主要依赖于Zoom()方法
void CustomGraphicsView::wheelEvent(QWheelEvent *event)
{//滚轮的滚动量QPoint scrollAmount = event->angleDelta();if ((scrollAmount.y() > 0) && (m_dZoomValue >= ZOOMMAX)) //最大放大到原始图像的50倍{return;}else if ((scrollAmount.y() < 0) && (m_dZoomValue <= ZOOMMIN))//最小缩小到原始图像的50倍{return;}// 正值表示滚轮远离使用者,为放大;负值表示朝向使用者,为缩小scrollAmount.y() > 0 ? onZoom(1.1) : onZoom(0.9);
}//在视觉窗口上双击鼠标左键,会有图像居中效果,主要依赖于onCenter()方法。
void CustomGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton){//自适应图像大小至视觉窗口的大小fitFrame();//居中显示onCenter();}QGraphicsView::mouseDoubleClickEvent(event);
}//绘制函数,用于视觉窗口背景绘制
void CustomGraphicsView::paintEvent(QPaintEvent* event)
{QPainter paint(this->viewport());//绘制背景paint.drawTiledPixmap(QRect(QPoint(0, 0), QPoint(this->width(), this->height())), m_tilePixmap);QGraphicsView::paintEvent(event);
}//当窗口尺寸发生变化时,实时更新视觉窗口位置
void CustomGraphicsView::resizeEvent(QResizeEvent *event)
{fitFrame();onCenter();m_pPosInfoWidget->setGeometry(0, this->height() - 25, this->width(), 25);QGraphicsView::resizeEvent(event);
}//视图居中
void CustomGraphicsView::onCenter()
{//调用QGraphicsView自带的方法centerOn,使视觉窗口的中心位于图像元素的中心点//并设置m_pImageItem的坐标,使其也位于中心点this->centerOn(0,0);m_pImageItem->setPos(-m_pImageItem->pixmap().width()/2,-m_pImageItem->pixmap().height()/2);
}void CustomGraphicsView::onZoom(float scaleFactor)
{//记录下当前相对于图像原图的缩放比例,可以记录下当前图像真实放大缩小了多少倍//可以借此来限制图像的最大或最小缩放比例m_dZoomValue *= scaleFactor;//调用视图类QGraphicsView自带的scale缩放方法,来对视图进行缩放,实现放大缩小的功能//缩放的同时,视图里的所有元素也会进行缩放,也就达到了视觉窗口放大缩小的效果this->scale(scaleFactor, scaleFactor);
}//图片自适应方法,根据图像原始尺寸和当前视觉窗口的大小计算出应缩放的尺寸,再根据已经缩放的比例计算还差的缩放比例,
//补齐应缩放的比例,使得图像和视觉窗口大小相适配
void CustomGraphicsView::fitFrame()
{if (this->width() < 1 || m_Image.width() < 1)return;//计算缩放比例double winWidth = this->width();double winHeight = this->height();double ScaleWidth = (m_Image.width() + 1) / winWidth;double ScaleHeight = (m_Image.height() + 1) / winHeight;double s_temp = ScaleWidth >= ScaleHeight ? 1 / ScaleWidth : 1 / ScaleHeight;double scale = s_temp / m_dZoomValue;if ((scale >= ZOOMMAX) || (scale <= ZOOMMIN)) //最大放大到原始图像的50倍{return;}onZoom(scale);m_dZoomValue = s_temp;
}//设置视觉窗口背景为棋盘格样式
void CustomGraphicsView::setBackground(bool enabled, bool invertColor)
{if (enabled){m_tilePixmap.fill(invertColor ? QColor(220, 220, 220) : QColor(35, 35, 35));QPainter tilePainter(&m_tilePixmap);constexpr QColor color(50, 50, 50, 255);constexpr QColor invertedColor(210, 210, 210, 255);tilePainter.fillRect(0, 0, 18, 18, invertColor ? invertedColor : color);tilePainter.fillRect(18, 18, 18, 18, invertColor ? invertedColor : color);tilePainter.end();//当取消注释时,视觉窗口背景格会跟随图像一起缩放//setBackgroundBrush(m_tilePixmap);}else{//setBackgroundBrush(Qt::transparent);}
}

运行效果截图:
在这里插入图片描述

THE END

完整项目工程,大家公众号【周旋机器视觉】后台发送【十二生肖控件】获取。

文章所能包含的内容有限,大家还是需要看代码理解学习吸收。本项目我录制了更详细的视频教程,在BiliBili搜索【周旋机器视觉】观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/504311.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的钢材表面缺陷检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发钢材表面缺陷检测系统对于保障制造质量和提高生产效率具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个钢材表面缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#…

ABAP - SALV教程 01- 开篇:打开SALV的三种方式之一

关于SALV&#xff0c;这里参考巨佬江正军的文章讲解&#xff0c;在做SAP开发的遇到困难和瓶颈的时候&#xff0c;每每读到巨佬的文章都会灵感爆发、醍醐灌顶。https://www.cnblogs.com/jiangzhengjun/p/4291387.html 博主由于是由JAVA转型的ABAP开发&#xff0c;刚接触ABAP的时…

Android和Linux的嵌入式开发差异

最近开始投入Android的怀抱。说来惭愧&#xff0c;08年就听说这东西&#xff0c;当时也有同事投入去看&#xff0c;因为恶心Java&#xff0c;始终对这玩意无感&#xff0c;没想到现在不会这个嵌入式都快要没法搞了。为了不中年失业&#xff0c;所以只能回过头又来学。 首先还是…

董兆祥出席工业废水资源化,开创变废为宝新途径演讲

演讲嘉宾&#xff1a;董兆祥 董事长 河北奥博水处理有限公司 演讲题目&#xff1a;工业废水资源化&#xff0c;开创变废为宝新途径 会议简介 “十四五”规划中提出&#xff0c;提高工业、能源领城智能化与信息化融合&#xff0c;明确“低碳经济”新的战略目标&#xff0c;热…

Flask入门一(介绍、Flask安装、Flask运行方式及使用、虚拟环境、调试模式、配置文件、路由系统)

文章目录 一、Flask介绍二、Flask创建和运行1.安装2.快速使用3.Flask小知识4.flask的运行方式 三、Werkzeug介绍四、Jinja2介绍五、Click CLI 介绍六、Flask安装介绍watchdog使用python--dotenv使用&#xff08;操作环境变量&#xff09; 七、虚拟环境介绍Mac/linux创建虚拟环境…

AI智能分析网关V4智慧环保/智慧垃圾站视频智能分析与监控方案

一、背景介绍 随着城市化进程的加速&#xff0c;垃圾处理问题日益受到人们的关注&#xff0c;传统的垃圾站管理方式已经无法满足现代社会的需求。针对当前垃圾站的监管需求&#xff0c;TSINGSEE青犀可基于旗下视频智能检测AI智能分析网关V4与安防监控视频综合管理系统EasyCVR平…

【MySQL】表的约束——空属性、默认值、列描述、zerofill、主键、自增长、唯一键、外键

文章目录 MySQL表的约束1. 空属性2. 默认值3. 列描述4. zerofill5. 主键6. 自增长7. 唯一键8. 外键 MySQL 表的约束 MySQL中的表的约束是一种规则&#xff0c;用于限制或保护表中数据的完整性和合法性。约束可以确保数据在插入、更新或删除时满足特定的条件&#xff0c;从而维护…

大数据毕业设计之前端04:管理系统为什么要自己实现图标组件

关键字&#xff1a;BuildAdmin、Icon、图标、Vue、ElementUI 前言 说到图标&#xff0c;在BuildAdmin中用到的地方很多。比如上一篇中的折叠图标&#xff0c;还有菜单栏图标、导航菜单栏图标等。常见的图标有&#xff1a;ElementUI图标、font-awesome、iconfont阿里图标以及本…

YOLOv5 + Flask + Vue实现基于深度学习算法的垃圾检测系统源码+数据库

✨界面展示 登录 注册 垃圾检测 用户管理 404 Not Found页面 403 拒绝访问页面 黑暗模式 深蓝模式 灰色模式 色弱模式 ✨技术特性 深度学习 YOLOv5&#x1f680;&#xff1a;高效、准确的目标检测算法&#xff0c;实时识别检测图像和视频中的各种对象PyTorch&#xff1a;机器…

linux环境安装cuda toolkit

1 全新安装 如果环境中没安装过cuda版本&#xff0c; 这种情况下比较简单。 直接在https://developer.nvidia.com/cuda-toolkit-archive选择对应版本下载安装即可。 如下为安装cuda toolkit 11.8. 2 环境中已经存在其他版本 这种情况下比较复杂一些。 首先要确认最高支持的…

COMSOL传热建模

目录 传热机制 热传导相关接口 热对流相关接口 热辐射 来源&#xff1a;1-4传热接口简介_哔哩哔哩_bilibili 传热机制 &#xff08;1&#xff09;热传导 &#xff08;2&#xff09;热对流 &#xff08;3&#xff09;热辐射 热传导相关接口 &#xff08;1&#xff09;固体传…

吸猫毛空气净化器哪个好?推荐除猫毛效果好宠物空气净化器品牌

当下有越来越多的家庭选择养宠物&#xff01;尽管家里变得更加温馨&#xff0c;但养宠可能会带来异味和空气中的毛发增多可能会带来健康问题&#xff0c;这是一个大问题&#xff01; 不想家里弥漫着异味&#xff0c;特别是来自宠物便便的味道&#xff0c;所以需要一款能够处理…