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