乐趣区

关于qt5:QT下获取Halcon图形窗口鼠标事件并执行相应操作

1、背景

在视觉我的项目开发过程中碰到了须要应用 Halcon 进行图像算法开发的需要,预计很多视觉工程师都用到过 Halcon 软件开发库,然而实现 Halcon 算法开发后就会遇到一个问题,就是图像的显示、读写、UI 交互等问题,因为 Halcon 具备非凡的图像文件格式 HObject 和数据格式 HTuple,所以说须要格局转换后能力实现绝对应的操作,不过 Halcon 自身也有比拟实用的显示、界面交互的性能,所以如何在 C ++ 或 QT 下应用这些性能成为了接下来须要去钻研和实际的工作。

2、参考信息

Halcon 针对不同的开发环境,给出了不同的开发例程,针对图形显示及界面操作这一块,Halcon 只给出了 C# 的相干例程,其运行后果如下:


其中能够实现在窗口界面创立矩形、圆、椭圆等形态的 Region,并依据鼠标来抉择、拖动和设置尺寸,并实现设置色彩,获取坐标,region 区内二值化、轮廓化等一系列后续操作。

3、指标

实现在 QT 环境下,将 Halcon 窗口贴在 QT 的控件上,并实现上述创立和操作 region 的根本动作。

4、步骤

4.1 Halcon 库的配置
自己应用的是 Halcon12.0 的破解版,目前调用 Halcon 的函数不会出错,然而我的项目中有调用新版本的 Halcon 库有出错情况,目前未查证是不是版本的问题,Halcon 配置次要在 PRO 文件中增加 Include 和 Lib 的援用门路。其中 HALCONROOT 是环境变量中 Halcon 的装置门路。

  #includes
  INCLUDEPATH   += "$$(HALCONROOT)/include"
  INCLUDEPATH   += "$$(HALCONROOT)/include/halconcpp"

  #libs
  QMAKE_LIBDIR  += "$$(HALCONROOT)/lib/$$(HALCONARCH)"
  unix:LIBS     += -lhalconcpp -lhalcon -lXext -lX11 -ldl -lpthread
  win32:LIBS    += "$$(HALCONROOT)/lib/$$(HALCONARCH)/halconcpp.lib" \
                   "$$(HALCONROOT)/lib/$$(HALCONARCH)/halcon.lib"

4.2 读取图像,并实现图像自适应窗体控件大小
这里我首先创立了一个 QHalconWindow 类,而后在 qt 的 ui 界面将 widget 晋升为 QHalconWindow 类,这样就免去了 Halcon 窗口句柄和 ui 句柄的绑定,间接通过 QHalconWindow 类来调用就行。

qhalconwindow.h 文件

#include <QObject>
#include <QWidget>
#include "HalconCpp.h"

class QHalconWindow : public QWidget
{
    Q_OBJECT
public:
    explicit QHalconWindow(QWidget *parent = 0,long Width=0,long Height=0);
    virtual ~QHalconWindow(void);

    HalconCpp::HTuple WindowID(void) {return WinID;}   // f 返回窗口句柄

protected:
    void resizeEvent(QResizeEvent*);                 // 窗口大小尺寸调整事件
private:
    HalconCpp::HTuple WinID;
    void OpenWindow(void);
}

Cpp 文件次要是对于窗口基本操作的实现函数

#include "qhalconwindow.h"

using namespace HalconCpp;

QHalconWindow::QHalconWindow(QWidget *parent,long Width,long Height)
    : QWidget(parent)
{resize(Width,Height);
    show();
    OpenWindow();}

QHalconWindow::~QHalconWindow(void)
{CloseWindow(WindowID());
}

void QHalconWindow::OpenWindow(void)
{SetWindowAttr("border_width",0);
    SetCheck("~father");
    HalconCpp::OpenWindow(0,0,100,100,(Hlong)winId(),"visible","",&WinID);
    SetCheck("father");
}

// 批改窗口尺寸
void QHalconWindow::resizeEvent(QResizeEvent *)
{SetWindowExtents(WindowID(),0,0,width(),height());
}

参考 Halcon 中对于 SetDrawingObjectCallback 函数的形容,须要在 c ++ 上面调用时,调用 C ++ 格局的函数,即下图的 Void 的回调函数指针。

然而这个回调函数在程序中须要定义为一个全局函数,次要根据是 Halcon 中介绍,如下:

所以依据这些需要实现 Halcon 窗口中绘制矩形、圆形和直线的操作
4.3 次要的图形绘制和贴图操作见如下代码,其中重点为全局函数的创立来实现抉择 Select、拖拽 Drag 和尺寸 Resize 事件响应。

Widget.h 文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QList>
#include <QStack>
#include <functional>
#include "HalconCpp.h"
//#include "qhalconwindow.h"

using namespace HalconCpp;

namespace Ui {class Widget;}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void InitWin(void);
    static Widget* getInstance();

protected:
    void resizeEvent (QResizeEvent*);
    void InitFg(void);

private slots:
    void on_HalconWinD_customContextMenuRequested(const QPoint &pos);
    void onTaskBoxContextMenuEvent();
    void  onTaskDeleteObj();
    void on_btn_DrawRectangle_clicked();
    void on_btn_DrawCircle_clicked();
    void on_btn_DrawLine_clicked();
    void on_btn_ClearAllObj_clicked();
    void AttachDrawObj(HDrawingObject obj);
    void slot_ReceiveData(long);

signals:
    void signal_data(long);
private:
    Ui::Widget *ui;
    //Halcon 窗口的参数
    HTuple WindowIDBuf,FGHandle,Width,Height,Area;
    HTuple WindowWidth,WindowHeight;
    HObject Image;
    QStack<HObject> graphic_stack;
    QList<HDrawingObject> drawing_objects;
    HTuple Draw_Text;
    QList<HTuple>Drawing_Index;
};

#endif // WIDGET_H

次要实现代码

#pragma execution_character_set("utf-8")
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QDebug>
#include <QMenu>

void CallBackFunc_Set(long DrawID,long WindowHandle, char* type);
void CallBackFunc_DrawObj(long DrawID,long WindowHandle, char* type);
HTuple selected_drawing_object;
Widget* instance;

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{ui->setupUi(this);
    WindowIDBuf = -1;
    Draw_Text=HTuple();
    InitWin();
    instance = this;
    connect(this,SIGNAL(signal_data(long)),this,SLOT(slot_ReceiveData(long)));
}

Widget::~Widget()
{HalconCpp::CloseWindow(WindowIDBuf);
    delete ui;
}

// 建设本身调用
Widget *Widget::getInstance()
{return instance;}

void Widget::InitFg(void)
{
    Hlong disp_width, disp_height;
    // 读取一张图像并获取图像大小
    ReadImage(&Image,"D:/Test_Image/kobe.jpg");
    GetImageSize(Image,&Width,&Height);
    
    // 依据图像的大小批改界面的尺寸大小
    //    disp_width = ui->HalconWinD->width();
    //    disp_height = ui->HalconWinD->height();
    //    ui->HalconWinD->resize(Width[0].L(),Height[0].L());
    //    resize(width()+Width[0].L()-disp_width,height()+Height[0].L()-disp_height);
}

void Widget::InitWin(void)
{InitFg();
    //    HTuple hv_WindowHandleCurrent;
    Hlong WinIDcurrent = (Hlong)ui->HalconWinD->winId();
    WindowWidth = ui->HalconWinD->width();
    WindowHeight = ui->HalconWinD->height();
    OpenWindow(0,0,WindowWidth,WindowHeight,WinIDcurrent,"","",&WindowIDBuf);    
    AttachBackgroundToWindow(Image,WindowIDBuf);
    //    DispObj(Image,ui->HalconWinD->WindowID());
}

void Widget::resizeEvent(QResizeEvent *)
{if(WindowIDBuf>0)
    {WindowWidth = ui->HalconWinD->width();
        WindowHeight = ui->HalconWinD->height();
        SetWindowExtents(WindowIDBuf,0,0,WindowWidth,WindowHeight);
        //       DispObj(Image,WindowIDBuf);
    }
}

// 右键选取后的 Menu 的对应操作函数
void Widget::onTaskBoxContextMenuEvent()
{QAction *pEven = qobject_cast<QAction *>(this->sender()); //this->sender()就是发信号者 QAction
    
    int iType = pEven->data().toInt();
    HTuple position;
    GetDrawingObjectParams(selected_drawing_object,(HTuple("row1").Append("column1")),&position);    
    switch (iType)
    {
    case 1:
    {SetDrawingObjectParams(selected_drawing_object,"color","green");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    case 2:
    {SetDrawingObjectParams(selected_drawing_object,"color","blue");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    case 3:
    {SetDrawingObjectParams(selected_drawing_object,"color","yellow");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    case 4:
    {SetDrawingObjectParams(selected_drawing_object,"color","black");
        QMessageBox::about(this, "tip", pEven->text());
        break;
    }
    default:
        break;
    }
    int Select_DrawID;
    for(int i=0;i!=Drawing_Index.size();++i)
    {if(Drawing_Index.at(i) == selected_drawing_object)
        {
            Select_DrawID=i;
            qDebug()<<"select ID:"<<i<<endl;}
    }
    for(int i=0;i!=Drawing_Index.size();++i)
    {if(Drawing_Index.at(i) == selected_drawing_object)
        {Select_DrawID=i;}
    }
    QString Message_test = pEven->text();
    QByteArray ba = Message_test.toLocal8Bit();
    const char *str = ba.data();
    HTuple Draw_Message(str);
    HTuple Draw_MesObj;    
    CreateDrawingObjectText(position[0],position[1], Draw_Message,&Draw_MesObj);
    AttachDrawingObjectToWindow(WindowIDBuf,Draw_MesObj);
    Draw_Text[Select_DrawID]=Draw_MesObj;    
}

// 右键选取删除操作对应函数
void Widget::onTaskDeleteObj()
{
    int Select_DrawID;
    for(int i=0;i!=Drawing_Index.size();++i)
    {if(Drawing_Index.at(i) == selected_drawing_object)
        {
            Select_DrawID=i;
            qDebug()<<"select ID:"<<i<<endl;}
    }
    if(Draw_Text.Length() >Select_DrawID)
    {DetachDrawingObjectFromWindow(WindowIDBuf,Draw_Text[Select_DrawID]);
        DetachDrawingObjectFromWindow(WindowIDBuf,selected_drawing_object);
    }
    else
    {DetachDrawingObjectFromWindow(WindowIDBuf,selected_drawing_object);
    }
}

// 右键响应事件
void Widget::on_HalconWinD_customContextMenuRequested(const QPoint &pos)
{    
    HTuple Row_Mouse,Column_Mouse,Button,position;
    GetMposition(WindowIDBuf,&Row_Mouse,&Column_Mouse,&Button);
    GetDrawingObjectParams(selected_drawing_object,(HTuple("column1").Append("column2").Append("row1").Append("row2")),&position);
    qDebug()<<Column_Mouse.D()<<Row_Mouse.D()<<position[0].D()<<position[1].D()<<position[2].D()<<position[3].D()<<endl;
    
    if(Column_Mouse>position[0] && Column_Mouse<position[1])
    {if(Row_Mouse>position[2] && Row_Mouse<position[3])
        {           
            // 创立菜单对象
            QMenu *pMenu = new QMenu(this);
            
            QAction *pTask1 = new QAction(tr("得分王"), this);
            QAction *pTask2 = new QAction(tr("总冠军"), this);
            QAction *pTask3 = new QAction(tr("MVP"), this);
            QAction *pTask4 = new QAction(tr("单场 81 分"), this);
            QAction *action=new QAction(this);
            QAction *pDelete = new QAction(tr("追寻黑曼巴!"), this);
            
            pTask1->setData(1);
            pTask2->setData(2);
            pTask3 ->setData(3);
            pTask4->setData(4);
            action->setSeparator(true);
            pDelete ->setData(5);
            
            // 把 QAction 对象增加到菜单上
            pMenu->addAction(pTask1);
            pMenu->addAction(pTask2);
            pMenu->addAction(pTask3);
            pMenu->addAction(pTask4);
            pMenu->addAction(action);
            pMenu->addAction(pDelete);
                       
            // 连贯鼠标右键点击信号
            connect(pTask1, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pTask2, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pTask3, SIGNAL(triggered()),this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pTask4, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
            connect(pDelete, SIGNAL(triggered()),this, SLOT(onTaskDeleteObj()));
            
            // 在鼠标右键点击的中央显示菜单
            pMenu->exec(cursor().pos());
            qDebug()<<cursor().pos().x()<<cursor().pos().y()<<endl;
            
            // 开释内存
            QList<QAction*> list = pMenu->actions();
            foreach (QAction* pAction, list) delete pAction;
            delete pMenu;
        }
    }
}
// 画矩形框
void Widget::on_btn_DrawRectangle_clicked()
{   
    HTuple Rect_ID;
    CreateDrawingObjectRectangle1(100,100,200,200,&Rect_ID);
    SetDrawingObjectParams(Rect_ID,"color","red");
    qDebug()<<"Rect_ID"<<Rect_ID.D()<<endl;
    Drawing_Index.append(Rect_ID);
    // 转换句柄为 HDrawingObject
    HDrawingObject draw=HDrawingObject(Rect_ID);
    AttachDrawingObjectToWindow(WindowIDBuf,Rect_ID);
    AttachDrawObj(draw);
}


void Widget::AttachDrawObj(HDrawingObject obj)
{drawing_objects.append(obj);
    obj.SetDrawingObjectCallback("on_resize",(void*)CallBackFunc_DrawObj);
    obj.SetDrawingObjectCallback("on_drag",(void*)CallBackFunc_DrawObj);
    //    obj.SetDrawingObjectCallback("on_attach",CallBackFunc_Set);
    obj.SetDrawingObjectCallback("on_select",(void*)CallBackFunc_Set);
    //    AttachDrawingObjectToWindow(ui->HalconWinD->WindowID(),obj);
}

//Drag 和 Resize 对应的回调函数,这里用 UI 的一个本身指针将全局函数的变量传递给 UI,从而调用 UI 下的函数
void CallBackFunc_DrawObj(long DrawID,long WindowHandle, char* type)
{Widget::getInstance()->signal_data(DrawID);
}

//Drag 和 Resize 对应的 UI 中的处理函数
void Widget::slot_ReceiveData(long DrawID)
{
    int Select_DrawID;
    for(int i=0;i!=Drawing_Index.size();++i)
    {if(Drawing_Index.at(i) == (HTuple)DrawID)
        {
            Select_DrawID=i;
            qDebug()<<"delete ID:"<<i<<endl;}
    }
    if(Draw_Text.Length() >Select_DrawID)
    {DetachDrawingObjectFromWindow(WindowIDBuf,Draw_Text[Select_DrawID]);
    }
}

// 选取矩形框对应的回调函数
void CallBackFunc_Set(long DrawID,long WindowHandle, char* type)
{  
    selected_drawing_object=DrawID;
    SetDrawingObjectParams(DrawID,"color","blue");
    HObject Region;
    HTuple Area,row,column;
    GetDrawingObjectIconic(&Region,DrawID);
    AreaCenter(Region,&Area,&row,&column);
}

// 革除窗口所有图形
void Widget::on_btn_ClearAllObj_clicked()
{for(int i=0;i!=Drawing_Index.size();++i)
    {ClearDrawingObject(Drawing_Index.at(i));       
    }
    for(int j=0;j<Draw_Text.Length();++j)
    {ClearDrawingObject(Draw_Text[j]);
    }    
    Drawing_Index.clear();
    Draw_Text=HTuple();}

// 画圆形
void Widget::on_btn_DrawCircle_clicked()
{
    HTuple Circle_ID;
    CreateDrawingObjectCircle(300,300,200,&Circle_ID);
    SetDrawingObjectParams(Circle_ID,"color","red");
    AttachDrawingObjectToWindow(WindowIDBuf,Circle_ID);
}

// 画直线
void Widget::on_btn_DrawLine_clicked()
{
    HTuple Line_ID;
    CreateDrawingObjectLine(300,300,600,600,&Line_ID);
    SetDrawingObjectParams(Line_ID,"color","yellow");
    AttachDrawingObjectToWindow(WindowIDBuf,Line_ID);
}

5、总结

这个知识点自身并不难,而且 Halcon 也带有 c# 的例程,次要当初碰到的难点是无奈了解其回调函数的 Draw_ID 是如何传递的,最初查到 Halcon 的帮忙材料才发现,依照全局回调函数的样子去定义,回调会主动返回你以后所抉择的 Draw_ID,从而能够应用该 Draw_ID 进行你所须要的操作。
最初放上最终的成果:

退出移动版