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 进行你所须要的操作。
最初放上最终的成果: