目录
- 1、背景
- 2、参考信息
- 3、目标
- 4、步骤
- 4.1 Halcon库的配置
- 4.2 读取图像,并实现图像自适应窗体控件大小
- 4.3 主要的图形绘制和贴图操作见如下代码,其中重点为全局函数的创建来实现选择Select、拖拽Drag和尺寸Resize事件响应。
- 5、总结
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的安装路径。
#includesINCLUDEPATH += "$$(HALCONROOT)/include"INCLUDEPATH += "$$(HALCONROOT)/include/halconcpp"#libsQMAKE_LIBDIR += "$$(HALCONROOT)/lib/$$(HALCONARCH)"unix:LIBS += -lhalconcpp -lhalcon -lXext -lX11 -ldl -lpthreadwin32: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_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()就是发信号者 QActionint 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);//转换句柄为HDrawingObjectHDrawingObject 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进行你所需要的操作。
最后放上最终的效果: