可以用Halcon建立深度学习模型,并用到实际项目中。
流程大概是使用免费的 Halcon Deeplearing Label Tool制作模型后,导出成.hdl
文件,并在应用程序中用ApplyDlClassifier
应用模型。
经过测试,ApplyDlClassifer
在第一次运行时,无论数据集有多大,执行时间在500ms左右(AMD 5800X + Nvidia P2200),能够明显感受到卡顿,而在第二次运行时,就感受不到卡顿。所以我考虑,在应用程序启动时,使用线程对模型进行预热。
预热听着高深,其实就是读取模型(ReadDlClassifier
) 并送入一张空白图。
下面演示我的做法:
- 使用
struct
包装一些Halcon深度学习算子需要用到的参数
// 深度学习上下文
//-----------------------------------------------------------------------------
struct InspectionContext
{
QString modelPath; // 模型路径
HTuple classifierHandle; // Halcon 分类器句柄
HObject sampleBatch; // Batch
HTuple predictedClass; // 推理分类
HTuple confidences; // 置信度
};
- 在
QApplication
初始化完成时,对模型预热。因为QApplication
初始化完成后,就可以使用QApplication::applicationDirPath()
之类的函数了。
2.1 封装一个自己的Application类
// MyApplication.h
class MyApplication : public QApplication
{
Q_OBJECT
public:
MyApplication(int &argc, char **argv);
signals:
void warmUpDeepLearningModels();
};
NebulaApplication::NebulaApplication(int &argc, char **argv)
: QApplication(argc, argv)
{
// [可选]
// 设置最大可执行线程数
// const int maxThreadCount = 5;
// QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
connect(this, &NebulaApplication::warmUpDeepLearningModels,
&getAlgorithmGlobalData(), &AlgorithmGlobalData::slotDoModelWarmUp);
emit warmUpDeepLearningModels();
}
2.2 我将Halcon算子封装成一个单例类,并使用getAlgorithmGlobalData
获得这个单例对象。slotDoModelWarmUp
是这个单例类一个槽函数。
2.3 在slotDoModelWarmUp
中开多线程,对模型进行预热。
void AlgorithmGlobalData::slotDoModelWarmUp()
{
// 设置模型路径
InspectionContext& capacitanceInspectionContext = getAlgorithmGlobalData().capacitanceInspectionContext;
capacitanceInspectionContext.modelPath = "/model/capacitance.hdl";
// 启动子线程
QtConcurrent::run(deeplearningModelWarmUp, std::ref(capacitanceInspectionContext));
}
我将Halcon深度学习算子要用到的变量封装成一个context
结构体,放在单例类中。为了节约开销,我使用引用,直接获取这个单例对象。这样,在向线程中传参数时,需要一个std::ref
。
线程函数如下:
void deeplearningModelWarmUp(InspectionContext& inspectionContext)
{
// 一些变量
HTuple hv_ImageWidth, hv_ImageHeight;
HTuple hv_NumChannels;
HTuple hv_RangeMin, hv_RangeMax;
HTuple& classifierHandle = inspectionContext.classifierHandle;
// 拼出模型路径
QString modelPath = QCoreApplication::applicationDirPath() + inspectionContext.modelPath;
// 读取模型
HDBG(ReadDlClassifier(modelPath.toStdString().c_str(), &classifierHandle);)
// 样本尺寸
HDBG(GetDlClassifierParam(classifierHandle, "image_width", &hv_ImageWidth);)
HDBG(GetDlClassifierParam(classifierHandle, "image_height", &hv_ImageHeight);)
// 样本通道
HDBG(GetDlClassifierParam(classifierHandle, "image_num_channels", &hv_NumChannels);)
// 灰度值范围
HDBG(GetDlClassifierParam(classifierHandle, "image_range_min", &hv_RangeMin);)
HDBG(GetDlClassifierParam(classifierHandle, "image_range_max", &hv_RangeMax);)
// 指定 batch
HTuple warmUpBatch = 1;
HDBG(SetDlClassifierParam(classifierHandle, "batch_size", warmUpBatch);)
// 生成一个 warmUpSampe,灰度值设置为15
HImage warmUpSample;
HImage warmUpSampleR, warmUpSampleG, warmUpSampleB;
HDBG(GenImageConst(&warmUpSample, "real", hv_ImageWidth, hv_ImageHeight);)
HDBG(GenImageProto(warmUpSample, &warmUpSampleR, 15);)
HDBG(GenImageProto(warmUpSample, &warmUpSampleG, 15);)
HDBG(GenImageProto(warmUpSample, &warmUpSampleB, 15);)
HDBG(Compose3(warmUpSampleR, warmUpSampleG, warmUpSampleB, &warmUpSample);)
// 开始推理
HTuple classifierResultHandle;
HDBG(ApplyDlClassifier(warmUpSample, classifierHandle, &classifierResultHandle);)
}
classifierHandle
是一个引用变量,这使得我可以直接操控单例类中的数据成员,而不是使用拷贝。
经过测试,在AMD 5800X + Nvidia P2200下,推理速度达到了11.66ms。