文章目录
- 匹配类
- 构造函数初始化布局变量
- 初始化图片
- 匹配
- 匹配计时器事件
- 开启计时器
- 停止
- 析构函数
Qt 联合Halcon视觉框架(1)
匹配类
#if QT_VERSION >= 0x050000
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qpushbutton.h>
#else
#include <qlabel.h>
#include <qpushbutton.h>
#endif
// 根据 Qt 版本选择合适的头文件。对于 Qt 5 及以上版本,使用 Qt Widgets 模块中的头文件。
// 对于较早版本的 Qt,则直接包含旧版的头文件。
#include <QScopedPointer>
#include "qhalconwindow.h"
class Matching: public QWidget
{
Q_OBJECT
public:
// 构造函数,初始化 Matching 小部件,并可选地指定父小部件。
// parent 参数允许将这个小部件嵌入到另一个窗口或小部件中。
Matching(QWidget *parent = nullptr);
// 析构函数,确保正确清理资源。
virtual ~Matching(void);
// 初始化前景(Foreground)变量。
void InitFg(void);
protected slots:
// 创建模型的槽函数。
void Create(void);
// 开始匹配过程的槽函数。
void Start(void);
// 停止匹配过程的槽函数。
void Stop(void);
protected:
// 定时器事件处理函数,用于周期性任务(如更新显示或执行匹配)。
void timerEvent(QTimerEvent*);
// 开始匹配过程的辅助函数。
void StartMatching(void);
private:
// HALCON 相关变量
Hlong ImageWidth, ImageHeight; // 图像宽度和高度
HalconCpp::HTuple FGHandle; // 前景句柄
HalconCpp::HImage Image; // 当前图像
QScopedPointer<HalconCpp::HShapeModel> ShapeModel; // 形状模型智能指针
HalconCpp::HTuple ModelRow, ModelColumn; // 模型参考点的行和列坐标
HalconCpp::HTuple Rect1Row, Rect1Col, Rect2Row, Rect2Col; // 区域矩形的角点坐标
HalconCpp::HTuple RectLength1, RectLength2; // 矩形的边长
HalconCpp::HObject ShapeModelRegion; // 形状模型区域对象
// GUI 元素
QLabel *MatchTimeLabel; // 显示匹配时间的标签
QLabel *MatchScoreLabel; // 显示匹配得分的标签
QLabel *MeasTimeLabel; // 显示测量时间的标签
QLabel *NumLeadsLabel; // 显示导线数量的标签
QLabel *MinLeadsDistLabel; // 显示最小导线距离的标签
QPushButton *CreateButton; // 创建模型按钮
QPushButton *StartButton; // 开始匹配按钮
QPushButton *StopButton; // 停止匹配按钮
QHalconWindow *Disp; // 显示 HALCON 图像的窗口
// 定时器
long Timer; // 定时器 ID,用于周期性任务
};
构造函数初始化布局变量
Matching::Matching(QWidget *parent)
: QWidget(parent), Timer(-1)
{
// 创建按钮并设置信号槽连接
// CreateButton:创建模型按钮,点击时调用 Create 槽函数。
CreateButton = new QPushButton(tr("Create Model"), this);
connect(CreateButton, SIGNAL(clicked()), SLOT(Create()));
// StartButton:开始匹配按钮,初始状态下禁用,点击时调用 Start 槽函数。
StartButton = new QPushButton(tr("Start"), this);
StartButton->setEnabled(false);
connect(StartButton, SIGNAL(clicked()), SLOT(Start()));
// StopButton:停止匹配按钮,初始状态下禁用,点击时调用 Stop 槽函数。
StopButton = new QPushButton(tr("Stop"), this);
StopButton->setEnabled(false);
connect(StopButton, SIGNAL(clicked()), SLOT(Stop()));
// 创建标签用于显示信息
QLabel *match_time = new QLabel(tr("Matching:"), this);
QLabel *match_time2 = new QLabel(tr("Time:"), this);
MatchTimeLabel = new QLabel(" ", this); // 显示匹配时间
QLabel *match_score = new QLabel(tr("Score: "), this);
MatchScoreLabel = new QLabel(" ", this); // 显示匹配得分
QLabel *meas_time = new QLabel(tr("Measure:"), this);
QLabel *meas_time2 = new QLabel(tr("Time:"), this);
MeasTimeLabel = new QLabel(" ", this); // 显示测量时间
QLabel *num_leads = new QLabel(tr("Number of leads: "), this);
NumLeadsLabel = new QLabel(" ", this); // 显示导线数量
QLabel *min_lead_dist = new QLabel(tr("Minimum Lead Distance: "), this);
MinLeadsDistLabel = new QLabel(" ", this); // 显示最小导线距离
// 创建 Mvtec 标签并设置字体样式
QLabel *MvtecLabel = new QLabel("\xa9 2004-2017 MVTec Software GmbH", this);
MvtecLabel->setFont(QFont(nullptr, 10, QFont::Bold));
// 创建 HALCON 小部件使用说明标签
QLabel *DispHintLabel = new QLabel(
"Zoom: mouse wheel; Move: left mouse button; Reset: double click", this);
// 设置布局
// 最外层垂直布局 TopBox
QVBoxLayout *TopBox = new QVBoxLayout(this);
// Mvtec 标签水平布局
QHBoxLayout *Mvtec = new QHBoxLayout;
Mvtec->addStretch(1); // 左侧伸缩空间
Mvtec->addWidget(MvtecLabel); // 添加 Mvtec 标签
Mvtec->addStretch(1); // 右侧伸缩空间
// 内部垂直布局 TopVBox
QVBoxLayout *TopVBox = new QVBoxLayout;
// 包含 HALCON 小部件和按钮的水平布局 HBoxDispAndButtons
QHBoxLayout *HBoxDispAndButtons = new QHBoxLayout;
// 创建 HALCON 窗口小部件 Disp,并设置最小尺寸
Disp = new QHalconWindow(this);
Disp->setMinimumSize(50, 50);
// 包含 HALCON 小部件和提示标签的垂直布局 DispVBox
QVBoxLayout *DispVBox = new QVBoxLayout;
DispVBox->addWidget(Disp, 1); // 添加 HALCON 小部件
DispVBox->addSpacing(8); // 添加间距
DispVBox->addWidget(DispHintLabel); // 添加提示标签
// 包含按钮的垂直布局 Buttons
QVBoxLayout *Buttons = new QVBoxLayout;
Buttons->addWidget(CreateButton); // 添加创建按钮
Buttons->addSpacing(8); // 添加间距
Buttons->addWidget(StartButton); // 添加开始按钮
Buttons->addSpacing(8); // 添加间距
Buttons->addWidget(StopButton); // 添加停止按钮
Buttons->addStretch(1); // 底部伸缩空间
// 将 HALCON 小部件和按钮布局添加到 HBoxDispAndButtons
HBoxDispAndButtons->addSpacing(15); // 左侧间距
HBoxDispAndButtons->addLayout(DispVBox, 1); // 添加 HALCON 和提示标签布局
HBoxDispAndButtons->addSpacing(15); // 中间间距
HBoxDispAndButtons->addLayout(Buttons); // 添加按钮布局
HBoxDispAndButtons->addSpacing(15); // 右侧间距
// 创建包含标签的水平布局 HBoxLabels
QHBoxLayout *HBoxLabels = new QHBoxLayout;
// 创建网格布局 Labels 用于排列多个标签
QGridLayout *Labels = new QGridLayout();
Labels->addWidget(match_time, 0, 0); // 第一行第零列
Labels->addWidget(match_time2, 0, 1); // 第一行第一列
Labels->addWidget(MatchTimeLabel, 0, 2); // 第一行第二列
Labels->addWidget(match_score, 0, 3); // 第一行第三列
Labels->addWidget(MatchScoreLabel, 0, 4); // 第一行第四列
Labels->addWidget(meas_time, 1, 0); // 第二行第零列
Labels->addWidget(meas_time2, 1, 1); // 第二行第一列
Labels->addWidget(MeasTimeLabel, 1, 2); // 第二行第二列
Labels->addWidget(num_leads, 1, 3); // 第二行第三列
Labels->addWidget(NumLeadsLabel, 1, 4); // 第二行第四列
Labels->addWidget(min_lead_dist, 1, 5); // 第二行第五列
Labels->addWidget(MinLeadsDistLabel, 1, 6); // 第二行第六列
// 将网格布局添加到 HBoxLabels
HBoxLabels->addSpacing(15); // 左侧间距
HBoxLabels->addLayout(Labels); // 添加网格布局
HBoxLabels->addSpacing(130); // 右侧间距
// 将 HBoxDispAndButtons 和 HBoxLabels 添加到 TopVBox
TopVBox->addLayout(HBoxDispAndButtons);
TopVBox->addSpacing(15); // 中间间距
TopVBox->addLayout(HBoxLabels);
// 将 TopVBox 和 Mvtec 布局添加到最外层布局 TopBox
TopBox->addSpacing(15); // 上方间距
TopBox->addLayout(TopVBox);
TopBox->addSpacing(15); // 中间间距
TopBox->addLayout(Mvtec);
TopBox->addSpacing(10); // 下方间距
}
初始化图片
void Matching::InitFg(void)
{
using namespace HalconCpp;
// 打开帧采集器并获取初始图像
// 使用 OpenFramegrabber 函数打开一个帧采集器(这里使用文件作为源),并获取句柄 FGHandle。
// 参数解释:
// - "File":指定帧采集器类型为文件。
// - 后续参数为默认值或特定配置,如宽度、高度等。
// - "board/board.seq":指定要读取的图像序列文件路径。
// - 最后两个参数 "-1, 1" 分别表示最小和最大图像编号,-1 表示无限制。
OpenFramegrabber("File", 1, 1, 0, 0, 0, 0, "default", -1, "default", -1, "default",
"board/board.seq", "default", -1, 1, &FGHandle);
// 使用 GrabImage 函数从帧采集器中抓取一幅图像,并存储到 Image 变量中。
GrabImage(&Image, FGHandle);
// 获取图像尺寸,并将宽度和高度存储到 ImageWidth 和 ImageHeight 成员变量中。
Image.GetImageSize(&ImageWidth, &ImageHeight);
// 设置 HALCON 窗口显示区域为整个图像大小。
// SetPart 的参数 (0, 0, ImageHeight-1, ImageWidth-1) 表示将显示区域设置为图像的完整范围。
Disp->GetHalconBuffer()->SetPart(0, 0, ImageHeight - 1, ImageWidth - 1);
// 设置 HALCON 窗口中线条的宽度为 3。
Disp->GetHalconBuffer()->SetLineWidth(3);
// 在 HALCON 窗口中显示抓取到的图像。
Disp->GetHalconBuffer()->DispObj(Image);
// 刷新 HALCON 缓冲区以应用更改并更新显示。
Disp->GetHalconBuffer()->FlushBuffer();
}
匹配
void Matching::StartMatching(void)
{
using namespace HalconCpp;
double S1, S2;
HTuple Rect1RowCheck, Rect1ColCheck, Rect2RowCheck, Rect2ColCheck;
HTuple MeasureHandle1, MeasureHandle2, NumLeads;
HTuple RowCheck, ColumnCheck, AngleCheck, Score, HomMat2D, MinDistance;
HTuple RowEdgeFirst1, ColumnEdgeFirst1, AmplitudeFirst1;
HTuple RowEdgeSecond1, ColumnEdgeSecond1, AmplitudeSecond1;
HTuple IntraDistance1, InterDistance1;
HTuple RowEdgeFirst2, ColumnEdgeFirst2, AmplitudeFirst2;
HTuple RowEdgeSecond2, ColumnEdgeSecond2, AmplitudeSecond2;
HTuple IntraDistance2, InterDistance2;
HObject ShapeModelTrans;
HObject Rectangle1, Rectangle2;
HImage Image;
char buf[MAX_STRING];
QString string;
// 抓取下一帧图像并显示
GrabImage(&Image, FGHandle);
Disp->GetHalconBuffer()->DispObj(Image);
// 开始计时以测量匹配时间
S1 = HSystem::CountSeconds();
// 在当前图像中查找模型实例
ShapeModel->FindShapeModel(Image, 0, 2 * PI, 0.7, 1, 0.5, "least_squares", 4, 0.7,
&RowCheck, &ColumnCheck, &AngleCheck, &Score);
// 结束计时并更新匹配时间标签
S2 = HSystem::CountSeconds();
sprintf(buf, "%5.2f", (S2 - S1) * 1000);
string = buf;
MatchTimeLabel->setText(string);
// 如果找到了一个匹配结果(即 Score 的长度为 1)
if (Score.Length() == 1)
{
// 更新匹配得分标签
sprintf(buf, "%7.5f", (double)Score[0]);
string = buf;
MatchScoreLabel->setText(string);
// 根据匹配结果旋转模型用于可视化
VectorAngleToRigid(ModelRow, ModelColumn, 0, RowCheck, ColumnCheck, AngleCheck, &HomMat2D);
AffineTransRegion(ShapeModelRegion, &ShapeModelTrans, HomMat2D, "false");
Disp->GetHalconBuffer()->SetColor("green");
Disp->GetHalconBuffer()->DispObj(ShapeModelTrans);
// 计算测量矩形的参数
AffineTransPixel(HomMat2D, Rect1Row, Rect1Col, &Rect1RowCheck, &Rect1ColCheck);
AffineTransPixel(HomMat2D, Rect2Row, Rect2Col, &Rect2RowCheck, &Rect2ColCheck);
// 生成两个测量矩形作为区域并显示它们
GenRectangle2(&Rectangle1, Rect1RowCheck, Rect1ColCheck, AngleCheck, RectLength1, RectLength2);
GenRectangle2(&Rectangle2, Rect2RowCheck, Rect2ColCheck, AngleCheck, RectLength1, RectLength2);
Disp->GetHalconBuffer()->SetColor("blue");
Disp->GetHalconBuffer()->SetDraw("margin");
Disp->GetHalconBuffer()->DispObj(Rectangle1);
Disp->GetHalconBuffer()->DispObj(Rectangle2);
Disp->GetHalconBuffer()->SetDraw("fill");
// 执行实际测量
S1 = HSystem::CountSeconds();
// 创建测量句柄
GenMeasureRectangle2(Rect1RowCheck, Rect1ColCheck, AngleCheck, RectLength1, RectLength2,
ImageWidth, ImageHeight, "bilinear", &MeasureHandle1);
GenMeasureRectangle2(Rect2RowCheck, Rect2ColCheck, AngleCheck, RectLength1, RectLength2,
ImageWidth, ImageHeight, "bilinear", &MeasureHandle2);
// 进行边缘检测和测量
MeasurePairs(Image, MeasureHandle1, 2, 90, "positive", "all",
&RowEdgeFirst1, &ColumnEdgeFirst1, &AmplitudeFirst1,
&RowEdgeSecond1, &ColumnEdgeSecond1, &AmplitudeSecond1,
&IntraDistance1, &InterDistance1);
MeasurePairs(Image, MeasureHandle2, 2, 90, "positive", "all",
&RowEdgeFirst2, &ColumnEdgeFirst2, &AmplitudeFirst2,
&RowEdgeSecond2, &ColumnEdgeSecond2, &AmplitudeSecond2,
&IntraDistance2, &InterDistance2);
// 关闭测量句柄
CloseMeasure(MeasureHandle1);
CloseMeasure(MeasureHandle2);
S2 = HSystem::CountSeconds();
// 显示测量结果
Disp->GetHalconBuffer()->SetColor("red");
Disp->GetHalconBuffer()->DispLine(
RowEdgeFirst1 - RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeFirst1 - RectLength2 * AngleCheck.TupleSin(),
RowEdgeFirst1 + RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeFirst1 + RectLength2 * AngleCheck.TupleSin());
Disp->GetHalconBuffer()->DispLine(
RowEdgeSecond1 - RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeSecond1 - RectLength2 * AngleCheck.TupleSin(),
RowEdgeSecond1 + RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeSecond1 + RectLength2 * AngleCheck.TupleSin());
Disp->GetHalconBuffer()->DispLine(
RowEdgeFirst2 - RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeFirst2 - RectLength2 * AngleCheck.TupleSin(),
RowEdgeFirst2 + RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeFirst2 + RectLength2 * AngleCheck.TupleSin());
Disp->GetHalconBuffer()->DispLine(
RowEdgeSecond2 - RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeSecond2 - RectLength2 * AngleCheck.TupleSin(),
RowEdgeSecond2 + RectLength2 * AngleCheck.TupleCos(),
ColumnEdgeSecond2 + RectLength2 * AngleCheck.TupleSin());
// 更新测量时间标签
sprintf(buf, "%5.2f", (S2 - S1) * 1000);
string = buf;
MeasTimeLabel->setText(string);
// 更新导线数量标签
NumLeads = IntraDistance1.Length() + IntraDistance2.Length();
sprintf(buf, "%2ld", (Hlong)NumLeads[0]);
string = buf;
NumLeadsLabel->setText(string);
// 更新最小导线距离标签
MinDistance = (InterDistance1.TupleConcat(InterDistance2)).TupleMin();
sprintf(buf, "%6.3f", (double)MinDistance[0]);
string = buf;
MinLeadsDistLabel->setText(string);
}
// 刷新 HALCON 缓冲区以应用所有更改并更新显示
Disp->GetHalconBuffer()->FlushBuffer();
}
匹配计时器事件
void Matching::timerEvent(QTimerEvent*)
{
StartMatching();
}
开启计时器
void Matching::Start(void)
{
StartButton->setEnabled(false);
StopButton->setEnabled(true);
// Start Timer -> ::timerEvent() is called continously
Timer = startTimer(20);
}
停止
// Stop continuous matching
void Matching::Stop(void)
{
StartButton->setEnabled(true);
StopButton->setEnabled(false);
// Kill Timer
if (Timer != -1)
{
killTimer(Timer);
Timer = -1;
}
}
析构函数
Matching::~Matching(void)
{
using namespace HalconCpp;
// Close all allocated HALCON resources.
CloseFramegrabber(FGHandle);
if (Timer != -1)
{
killTimer(Timer);
Timer = -1;
}
}