Qt 联合Halcon视觉框架(1)

文章目录

  • 效果
  • QHalconWind 类
    • 回调函数刷新窗口
    • 构造函数
    • 保证窗口大小和Halcon 窗口大小一致
    • 绘制图片
    • 获取坐标点
    • 设置坐标点
    • 鼠标拖动图片
    • 鼠标按下
    • 鼠标抬起
    • 鼠标双击
    • 滚轮放大缩小图片

效果

请添加图片描述

QHalconWind 类

// HALCON/Qt pattern matching and measure example
//
// (c) 2004-2017 MVTec Software GmbH

// QHalconWindow.h : Class used for opening HALCON windows in Qt
//

#include <QWidget>
#include <QPainter>
#include <QScopedPointer>

#ifndef __APPLE__
#  include "HalconCpp.h"
#else
#  ifndef HC_LARGE_IMAGES
#    include <HALCONCpp/HalconCpp.h>
#  else
#    include <HALCONCppxl/HalconCpp.h>
#  endif
#endif


class QHalconWindow : public QWidget
{
  Q_OBJECT

public:
    // 构造函数,初始化一个新的 HALCON 窗口。接受一个可选的父窗口部件、宽度和高度参数。
    // parent 参数允许将这个窗口作为另一个窗口的小部件嵌入。
    // Width 和 Height 参数用于设置窗口的初始大小。
    QHalconWindow(QWidget *parent = nullptr, long Width = 0, long Height = 0);

    // 提供对 HALCON 图形缓冲区的访问。返回指向 HALCON HWindow 对象的指针,
    // 允许外部代码与 HALCON 图像处理环境交互。
    HalconCpp::HWindow* GetHalconBuffer(void) { return halconBuffer.data(); }

protected:
    // 当窗口大小改变时被调用。这里可以处理窗口大小变化后的逻辑,
    // 例如调整 HALCON 缓冲区的大小。
    void resizeEvent(QResizeEvent* event) override;

    // 当需要更新窗口内容时被调用。在这里实现实际的绘制逻辑,
    // 比如从 HALCON 缓冲区获取图像并绘制到窗口上。
    void paintEvent(QPaintEvent* event) override;

    // 鼠标移动事件处理函数,响应用户的鼠标移动操作。
    void mouseMoveEvent(QMouseEvent* event) override;

    // 鼠标按下事件处理函数,响应用户按下鼠标按键的操作。
    void mousePressEvent(QMouseEvent* event) override;

    // 鼠标释放事件处理函数,响应用户释放鼠标按键的操作。
    void mouseReleaseEvent(QMouseEvent* event) override;

    // 鼠标双击事件处理函数,响应用户的鼠标双击操作。
    void mouseDoubleClickEvent(QMouseEvent* event) override;

    // 鼠标滚轮事件处理函数,响应用户的鼠标滚轮滚动操作。
    void wheelEvent(QWheelEvent* event) override;

private:
    // 获取 HALCON 窗口中的某个区域坐标
    void GetPartFloat(double* row1, double* col1, double* row2, double* col2);

    // 设置 HALCON 窗口中的某个区域坐标
    void SetPartFloat(double row1, double col1, double row2, double col2);

    // 智能指针,管理 HALCON 的 HWindow 对象,确保其生命周期与 QHalconWindow 实例一致。
    // halconBuffer 是 HALCON 图像处理的图形缓冲区。
    QScopedPointer<HalconCpp::HWindow> halconBuffer;

    // 成员变量,用于保存最近一次鼠标位置和 HALCON 窗口的某些区域坐标。
    // 可能在处理鼠标事件或更新显示时使用这些值。
    QPoint lastMousePos;
    double lastRow1, lastCol1, lastRow2, lastCol2;
};

回调函数刷新窗口

Herror __stdcall ContentUpdateCallback(void *context)
{
  // 定义一个名为 ContentUpdateCallback 的回调函数,
  // 使用 Windows API 标准调用约定 (__stdcall)。
  // 函数接收一个指向用户定义数据的指针 (void *context),该数据将被用于回调操作。

  // 注释解释了这个回调会在何时被调用:
  // 当缓冲区刷新时会调用此回调;如果启用了自动刷新(默认情况下是启用的),则每次图形缓冲区更改后都会调用。

  QHalconWindow* hwindow = (QHalconWindow*)context;

  // 将传入的 context 参数强制转换为 QHalconWindow 类型的指针,
  // 并赋值给局部变量 hwindow。这假设传递给回调的上下文是指向 QHalconWindow 实例的指针。

  // schedule redraw in Qt thread
  hwindow->update();

  // 调用 hwindow 指向的对象的 update() 方法来安排重绘。
  // 这个方法不会立即导致窗口重绘,而是告诉 Qt 应用程序在下一次事件循环迭代时重绘窗口。

  return H_MSG_OK;
  // 返回 H_MSG_OK 值,表示回调成功完成。
  // H_MSG_OK 可能是一个预定义的宏或常量,用来表示消息处理状态。
}

构造函数

QHalconWindow::QHalconWindow(QWidget *parent, long Width, long Height)
        : QWidget(parent), lastMousePos(-1, -1)
{
    // 显示窗口。这确保窗口在创建后立即可见。
    show();

    // 设置窗口的大小为指定的宽度和高度。如果 Width 和 Height 为 0,则使用默认大小。
    resize(Width, Height);


    //打开图形窗口
    halconBuffer.reset(new HalconCpp::HWindow(0, 0, 100, 100, 0, "buffer", ""));

    // 启用图形堆栈功能,使得图像和区域在缩放或调整窗口大小后仍然保持不变。
    halconBuffer->SetWindowParam("graphics_stack", "true");

    // 关闭自动刷新(flush),启用显式刷新模式。
    halconBuffer->SetWindowParam("flush", "false");

    // 注册一个回调函数 ContentUpdateCallback,用于响应缓冲区内容更新事件。
    // 第二个参数 this 表示当前对象实例作为回调函数的上下文,允许回调函数访问 QHalconWindow 的成员。
    //刷新窗口
    halconBuffer->SetContentUpdateCallback((void*)&ContentUpdateCallback, this);
}

保证窗口大小和Halcon 窗口大小一致

// 当 QHalconWindow 小部件的大小发生变化时调整 HALCON 窗口的大小。
void QHalconWindow::resizeEvent(QResizeEvent* event)
{
  
  Q_UNUSED(event);

  //设置窗口的坐标和宽高
  halconBuffer->SetWindowExtents(0, 0, width(), height());

  // 触发缓冲区刷新,以确保 HALCON 窗口内容根据新的尺寸重新绘制。
  halconBuffer->FlushBuffer();
}

绘制图片

void QHalconWindow::paintEvent(QPaintEvent *event)
{
  // 使用命名空间以避免每次调用 HALCON 函数时都需要前缀。
  using namespace HalconCpp;

  Q_UNUSED(event);
 
  HString type;
  Hlong width, height;

  // 获取 HALCON 缓冲区的内容。
 	// 将图片加载进窗口
  HImage image = halconBuffer->DumpWindowImage();

  // 将 HALCON 图像转换为适合 Qt 使用的格式。
  // InterleaveChannels() 方法将图像通道重新排列为 ARGB 格式,并确保与 Qt 的颜色匹配。
  HImage imageInterleaved = image.InterleaveChannels("argb", "match", 0);

  // 获取转换后图像的原始数据指针。
  // GetImagePointer1() 返回指向图像数据的指针,并提供有关图像类型和尺寸的信息。
  unsigned char* pointer = (unsigned char*)imageInterleaved.GetImagePointer1(&type, &width, &height);

  // 使用原始图像数据创建一个 QImage 对象。
  // 注意:宽度除以 4 是因为每个像素包含 4 个字节(ARGB),而 QImage::Format_RGB32 每个像素占用 4 字节。
  QImage qimage(pointer, width / 4, height, QImage::Format_RGB32);

  // 创建 QPainter 对象并开始绘制。
  // 将 QImage 绘制到小部件上,从左上角 (0, 0) 开始。
  QPainter painter(this);
  painter.drawImage(QPoint(0, 0), qimage);
}

获取坐标点

void QHalconWindow::GetPartFloat(double *row1, double *col1, double *row2, double *col2)
{
  // 为了从 get_part 获取浮点值,使用 HTuple 参数。
  // HTuple 是 HALCON 中用于处理多个数值或不同类型数据的类,
  // 它可以存储和操作任意数量的元素,并且支持多种数据类型。

  HalconCpp::HTuple trow1, tcol1, trow2, tcol2;

  // 调用 halconBuffer 的 GetPart 方法来获取当前窗口显示区域的边界坐标。
  // 这些坐标将被存储在 HTuple 对象中。
  halconBuffer->GetPart(&trow1, &tcol1, &trow2, &tcol2);

  // 将 HTuple 对象中的第一个元素(即浮点数)转换为 double 类型,并赋值给输出参数。
  // .D() 方法用于将 HTuple 中的第一个元素转换为双精度浮点数。
  *row1 = trow1.D();
  *col1 = tcol1.D();
  *row2 = trow2.D();
  *col2 = tcol2.D();
}

设置坐标点

void QHalconWindow::SetPartFloat(double row1, double col1, double row2, double col2)
{
  // 将 double 类型的值转换为 HTuple。如果不进行转换,将会调用 SetPart 的整数版本。
  // 使用 HTuple 确保可以传递浮点数值,从而实现平滑的移动和缩放,即使在放大时也能保持精度。

  // 调用 halconBuffer 的 SetPart 方法来设置 HALCON 窗口显示区域的边界坐标。
  // 通过将 double 参数包装在 HTuple 中,确保 HALCON 接收到的是浮点数而不是整数。
  // 这样可以保证窗口边界坐标的精确性,支持更加精细和平滑的操作。
  halconBuffer->SetPart(
      HalconCpp::HTuple(row1),  // 左上角行坐标 (row1)
      HalconCpp::HTuple(col1),  // 左上角列坐标 (col1)
      HalconCpp::HTuple(row2),  // 右下角行坐标 (row2)
      HalconCpp::HTuple(col2)   // 右下角列坐标 (col2)
  );
}

鼠标拖动图片

void QHalconWindow::mouseMoveEvent(QMouseEvent *event)
{
  // 检查是否按下了鼠标左键,并且 lastMousePos 已经被初始化(即 x() 不等于 -1)。
  if ((event->buttons() == Qt::LeftButton) && lastMousePos.x() != -1)
  {
    // 计算鼠标移动的距离(delta),从上一次的位置到当前事件位置。
    // 使用全局坐标系以确保即使窗口移动,计算仍然准确。
    QPoint delta = lastMousePos - event->globalPos();

    // 根据当前窗口的缩放比例调整 delta 的大小。
    // scalex 和 scaley 分别是水平和垂直方向上的缩放因子,
    // 它们基于图像的显示区域与窗口尺寸的比例。
    double scalex = (lastCol2 - lastCol1 + 1) / (double)width();
    double scaley = (lastRow2 - lastRow1 + 1) / (double)height();

    try
    {
      // 设置新的可见区域。根据鼠标移动的距离(delta)调整显示区域的边界坐标。
      // 这里使用了缩放因子来确保平移量与当前缩放级别匹配。
      SetPartFloat(lastRow1 + (delta.y() * scaley),
                   lastCol1 + (delta.x() * scalex),
                   lastRow2 + (delta.y() * scaley),
                   lastCol2 + (delta.x() * scalex));

      // 触发缓冲区刷新,以更新 HALCON 窗口的内容。
      // 这将导致重新绘制,显示新的可见区域。
      halconBuffer->FlushBuffer();
    }
    catch (HalconCpp::HOperatorException &e)
    {
      // 如果设置新可见区域失败(例如,图像部分移出窗口),捕获异常并忽略错误。
      // 这种情况可能发生在用户尝试将图像拖动到窗口外时。
      // 可选:可以在这里添加日志记录或用户通知。
      // qDebug() << "Failed to set new visible part: " << e.what();
    }

    // 更新 lastMousePos 为当前事件的位置,以便下次计算 delta。
    lastMousePos = event->globalPos();
  }
}

鼠标按下

void QHalconWindow::mousePressEvent(QMouseEvent *event)
{
  // 保存当前鼠标位置和图像显示区域。
  // 这些信息将在后续的鼠标移动事件中用于计算平移量。

  // 获取当前 HALCON 窗口显示区域的边界坐标,并保存到成员变量中。
  // lastRow1, lastCol1 是显示区域的左上角坐标,
  // lastRow2, lastCol2 是显示区域的右下角坐标。
  GetPartFloat(&lastRow1, &lastCol1, &lastRow2, &lastCol2);

  // 记录当前鼠标按下时的全局位置(屏幕坐标)。
  // 这个位置将用于后续的鼠标移动事件中计算鼠标移动的距离。
  lastMousePos = event->globalPos();
}

鼠标抬起

void QHalconWindow::mouseReleaseEvent(QMouseEvent *event)
{
  // 使用 Q_UNUSED 宏来避免编译器对未使用的参数发出警告。
  // 在这个方法中,event 参数实际上没有被使用。
  Q_UNUSED(event);

  // 取消设置参考鼠标位置。
  // 将 lastMousePos 设置为一个无效值 (-1, -1),表示不再有有效的参考点。
  // 这样可以确保在下次鼠标按下事件之前,不会有旧的鼠标位置影响新的拖动操作。
  lastMousePos = QPoint(-1, -1);
}

鼠标双击

void QHalconWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
  // 使用 Q_UNUSED 宏来避免编译器对未使用的参数发出警告。
  // 在这个方法中,event 参数实际上没有被使用。
  Q_UNUSED(event);

  // 检查是否是左键双击事件。
  if (event->buttons() == Qt::LeftButton)
  {
    // 重置图像显示区域为默认值。
    // SetPart 的参数 (0, 0, -1, -1) 表示将显示区域设置为整个图像,
    // 即取消任何缩放或平移效果,恢复到原始视图。
    halconBuffer->SetPart(0, 0, -1, -1);

    // 刷新 HALCON 缓冲区以应用更改并更新显示。
    // 这会触发窗口内容的重新绘制,显示重置后的图像。
    halconBuffer->FlushBuffer();
  }
}

滚轮放大缩小图片

void QHalconWindow::wheelEvent(QWheelEvent *event)
{
  // event->delta() 是 120 的倍数。较大的倍数表示用户滚动了多个刻度。
  int num_notch = std::abs(event->delta()) / 120;

  // 计算缩放因子。每次滚轮向上滚动时(正 delta),图像放大;向下滚动时(负 delta),图像缩小。
  double factor = (event->delta() > 0) ? std::sqrt(2.0) : 1.0 / std::sqrt(2.0);

  // 如果用户滚动了多个刻度,调整缩放因子以匹配滚动次数。
  while (num_notch > 1)
  {
    factor = factor * ((event->delta() > 0) ? std::sqrt(2.0) : 1.0 / std::sqrt(2.0));
    num_notch--;
  }

  // 获取缩放中心点。将鼠标指针位置从窗口坐标转换为图像坐标。
  double centerRow, centerCol;
  halconBuffer->ConvertCoordinatesWindowToImage(event->y(), event->x(), &centerRow, &centerCol);

  // 获取当前图像显示区域的边界坐标。
  double row1, col1, row2, col2;
  GetPartFloat(&row1, &col1, &row2, &col2);

  // 计算相对于中心点的四个边界的距离。
  double left = centerRow - row1;
  double right = row2 - centerRow;
  double top = centerCol - col1;
  double bottom = col2 - centerCol;

  // 根据缩放因子调整新的显示区域边界。
  double newRow1 = centerRow - left * factor;
  double newRow2 = centerRow + right * factor;
  double newCol1 = centerCol - top * factor;
  double newCol2 = centerCol + bottom * factor;

  try
  {
    // 设置新的显示区域。
    SetPartFloat(newRow1, newCol1, newRow2, newCol2);

    // 刷新 HALCON 缓冲区以应用更改并更新显示。
    halconBuffer->FlushBuffer();
  }
  catch (HalconCpp::HOperatorException &e)
  {
    // 如果设置新显示区域失败(例如,部分区域过小或过大),捕获异常并忽略错误。
    // 这种情况可能发生在用户尝试过度缩放时。
    // 可选:可以在这里添加日志记录或用户通知。
    // qDebug() << "Failed to set new visible part: " << e.what();
  }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI大模型学习笔记|人工智能的发展历程、智能体的发展、机器学习与深度学习的基本理论

学习链接&#xff1a;冒死上传&#xff01;价值2W的大模型入门到就业教程分享给大家&#xff01;轻松打造专属大模型助手&#xff0c;—多模态、Agent、LangChain、ViT、NLP_哔哩哔哩_bilibili 百度网盘自己整理的笔记&#xff1a; 通过网盘分享的文件&#xff1a;1-人工智能的…

Vue项目打包部署到服务器

1. Vue项目打包部署到服务器 1.1. 配置 &#xff08;1&#xff09;修改package.json文件同级目录下的vue.config.js文件。 // vue.config.js module.exports {publicPath: ./, }&#xff08;2&#xff09;检查router下的index.js文件下配置的mode模式。   检查如果模式改…

docker的网络类型和使用方式

docker的网络类型 5种网络类型 bridge 默认类型&#xff0c;桥接到宿主机docker0的网络&#xff0c;有点类似于VM虚拟机的NAT网络模型。 案例: docker run --rm -itd --network bridge --name wzy666wzy-bridge alpine host host类型&#xff0c;共享宿主机的网络空间&#…

数字IC后端实现常见的physical only cell都有哪些?如何添加这些cell?

数字IC后端实现阶段常见功能cell有哪些&#xff1f;比如AND&#xff0c;AOI&#xff0c;NAND等。 physical cell有哪些&#xff1f;都是干什么用的&#xff1f; 数字后端零基础入门系列 | Innovus零基础LAB学习Day9 &#xff08;1&#xff09; well tap cells&#xff1a;防止…

c++中类的应用综合练习

整理思维导图 课上类实现> 、<、!、||、&#xff01;和后自增、前自减、后自减运算符的重载 代码部分&#xff1a; #include <iostream> using namespace std; class complex {int rel;int vir; public:complex(int rel,int vir):rel(rel),vir(vir){}complex(){}…

ElasticSearch 搜索、排序、分页功能

一、DSL 查询文档 ElasticSearch 的查询依然是基于 json 风格的 DSL 来实现的。 官方文档&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl.html 1.1 DSL 查询分类 常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数…

mybatis常见错误

1.没有在mybatis.xml里面引入映射文件 2. 连接数据库部分有误 3.控制台输出无误&#xff0c;数据库里只插入了id sql语句有误 正确 <insert id"add" useGeneratedKeys"true" keyProperty"id">insert into t_teacher values (null,#{nam…

GLM-4-Plus初体验

引言&#xff1a;为什么高效的内容创作如此重要&#xff1f; 在当前竞争激烈的市场环境中&#xff0c;内容创作已成为品牌成功的重要支柱。无论是撰写营销文案、博客文章、社交媒体帖子&#xff0c;还是制作广告&#xff0c;优质的内容不仅能够帮助品牌吸引目标受众的注意力&a…

Mac/Windows端长期破解myBase8方法(无需安装火绒)

提醒 不管哪个端&#xff0c;都需要先退出myBase。 Mac 进入用户根目录/Users/c0ny100&#xff0c;即下边是Macintosh HD > 用户 > [你的用户名]这个界面然后按ShiftCommond.&#xff0c;显示隐藏文件。找到.Mybase8.ini文件 打开.Mybase8.ini文件&#xff0c;删除Fir…

Capture绘制元器件(Candance 17.4)

step1&#xff1a;新建元器件库 step2&#xff1a;新建元器件 step3&#xff1a;新建元器件,填写元器件名称以及类型 step4&#xff1a;绘制元器件形状 step5&#xff1a;添加引脚 添加引脚名称以及序号 将GND、VIN等电源属性引脚从Passive改为Power&#xff0c;其余为Passive …

支持自定义离线地图地理区域,查询组件及数据源功能增强,DataEase开源BI工具v2.10.3 LTS发布

2024年12月9日&#xff0c;人人可用的开源BI工具DataEase正式发布v2.10.3 LTS版本。 这一版本的功能变动包括&#xff1a;数据源方面&#xff0c;API数据源和Excel数据源支持对字段类型和长度进行设置&#xff1b;图表方面&#xff0c;离线类地图支持自定义地理区域设置&#…

【Unity学习笔记·第十二】Unity New Input System 及其系统结构和源码浅析

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/132534422 作者&#xff1a;CSDN|Ringleader| 主要参考&#xff1a; 官方文档&#xff1a;Unity官方Input System手册与API官方测试用例&#xff1a;Unity-Technologies/InputS…

STM32F103单片机HAL库串口通信卡死问题解决方法

在上篇文章 STM32F103单片机使用STM32CubeMX创建IAR串口工程 中分享了使用cubeMX直接生成串口代码的方法&#xff0c;在测试的过程中无意间发现&#xff0c;串口会出现卡死的问题。 当串口一次性发送十几个数据的时候&#xff0c;串口感觉像卡死了一样&#xff0c;不再接收数据…

【指南】03 CSC联系外导

确定外导 课题组有合作关系的国外导师与自己研究方向密切相关的国外导师国外高校官网、谷歌学术、Research Gate等平台检索不可以是中国港澳台的高校科研院所或机构注意外导所在高校排名和科研水平可列表记录注意外国签证政策 发送邮件 自我介绍简要介绍CSC介绍自己的研究对…

umi实现动态获取菜单权限

文章目录 前景登录组件编写登录逻辑菜单的时机动态路由页面刷新手动修改地址 前景 不同用户拥有不同的菜单权限&#xff0c;现在我们实现登录动态获取权限菜单。 登录组件编写 //当我们需要使用dva的dispatch函数时&#xff0c;除了通过connect函数包裹组件还可以使用这种方…

swagger-codegen

一、通过Swagger生成客户端代码 下载&#xff1a;https://github.com/swagger-api/swagger-codegen#编译打包 cd E:\软件空间\代码生成\swagger-codegen-3.0.64 mvn clean package#指定swagger地址生成客户端代码 cd E:\软件空间\代码生成\swagger-codegen-3.0.64\modules\swa…

Kael‘thas Sunstrider Ashes of Al‘ar

Kaelthas Sunstrider 凯尔萨斯逐日者 <血精灵之王> Kaelthas Sunstrider - NPC - 魔兽世界怀旧服TBC数据库_WOW2.43数据库_70级《燃烧的远征》数据库 Ashes of Alar 奥的灰烬 &#xff08;凤凰 310%速度&#xff09; Ashes of Alar - Item - 魔兽世界怀旧服TBC数据…

7.Vue------$refs与$el详解 ------vue知识积累

$refs 与 $el是什么&#xff1f; 作用是什么? ref&#xff0c;$refs&#xff0c;$el &#xff0c;三者之间的关系是什么&#xff1f; ref (给元素或者子组件注册引用信息) 就像你要给元素设置样式&#xff0c;就需要先给元素设定一个 class 一样&#xff0c;同理&#xff0c;…

医院门诊预约挂号管理系统设计与实现

文末获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 医院门诊预约挂号管理系统设计与实现 摘 要 本医院门诊预约挂号管理系统是针对目前医院门诊预约挂号管理的实际需求&#xff0c;从实际工作出发&#xff0c;对过去的医院门诊预约挂号管理系统存在的问题…

学习记录,泛型界限1

泛型界限 上限 泛型的上限&#xff0c;下限。对类型的更加具体的约束&#xff01; 如果给某个泛型设置了上界&#xff1a;这里的类型必须是上界 如果给某个泛型设置了下界&#xff1a;这里的类型必须是下界