Kithara使用Halcon + QT 进行二维码实时识别
目录
- Kithara使用Halcon + QT 进行二维码实时识别
- Halcon 简介以及二维码检测的简要说明
- Halcon 简介
- Halcon的二维码检测功能
- Qt应用框架简介
- 项目说明
- 关键代码
- 抖动测试
- 测试平台:
- 测试结果:
- 开源源码
Halcon 简介以及二维码检测的简要说明
Halcon 简介
Halcon 是一款由德国MVTec Software GmbH开发的机器视觉软件包,它为工业自动化提供了强大的图像分析和处理工具。Halcon 软件广泛应用于质量控制、定位、测量、识别和引导机器人等场景,其核心优势在于高性能的图像处理算法、丰富的功能库以及跨平台的兼容性。
Halcon 提供了基于C++和.NET的API,支持Windows、Linux和macOS操作系统,并且能够集成到各种开发环境中,如Visual Studio、Eclipse等。它不仅适用于PC环境,还支持嵌入式系统,使得Halcon成为工业4.0和物联网(IoT)应用的理想选择。
Halcon的二维码检测功能
二维码检测是Halcon中一个重要的应用领域,尤其在物流、制造业和零售业中,二维码的快速准确读取对于追踪和管理物品至关重要。Halcon提供了专门的工具和算子来高效地检测和解码二维码。
常见的二维码检测流程
-
创建二维码阅读器模板
使用create_data_code_2d_model算子初始化一个二维码检测模型。这一步会根据预期的二维码类型和环境条件设定初始参数。 -
设置二维码阅读器参数
通过set_data_code_2d_param算子调整二维码检测模型的参数。这些参数可能包括二维码的大小范围、旋转角度、噪声容忍度等,以适应不同的应用场景。 -
检测和读取二维码
应用find_data_code_2d算子在输入图像中查找二维码。此算子会返回二维码的位置和方向信息。 -
解码二维码
read_data_code_2d算子用于从检测到的二维码中提取编码信息。这一步骤将二维码转换为可读的文本或数据。 -
结果后处理
解码后的数据通常需要进一步处理,比如验证、存储或发送给其他系统。 -
二维码检测的关键点
图像预处理:在检测之前,可能需要对图像进行预处理,例如去噪、增强对比度或校正图像失真,以提高检测的准确性。
参数调整:适当的参数设置对于提高二维码检测的速度和可靠性至关重要。这可能需要根据具体的应用场景和环境条件进行实验和优化。
多二维码识别:在复杂场景中,可能需要同时检测多个二维码。Halcon的算子支持这一需求,能够处理密集或重叠的二维码。
总之,Halcon的二维码检测功能结合了先进的图像处理技术和直观的编程接口,使得用户能够在各种工业环境中实现高精度和高效率的二维码识别。
关于 Halcon在Kithara中使用可以查阅此文章Kithara中使用Halcon (一),更多关于学习Halcon的方法可以去 Halcon官网关于二维码检测的介绍。
Qt应用框架简介
- Qt 应用框架简介
Qt 是一个跨平台的C++图形用户界面应用程序开发框架,由挪威公司 Trolltech(现为 The Qt Company)于1991年开发。Qt 不仅支持 Windows、Linux 和 macOS 等桌面操作系统,还支持 Android、iOS 等移动平台,甚至可以用于嵌入式系统开发,如 QNX 和 Linux for Devices。
核心特性
-
跨平台性:Qt 最显著的特点就是其强大的跨平台能力,允许开发者编写一次代码即可在多个平台上运行,极大地提高了开发效率和降低了维护成本。
-
GUI 构建:Qt 提供了一套丰富的组件库,包括按钮、列表、表格、对话框等,用于构建美观且功能完备的用户界面。此外,Qt Creator 集成开发环境中的设计器工具允许开发者通过拖放方式构建界面。
-
信号与槽机制:这是 Qt 中一种独特的事件处理机制,用于对象之间的通信。信号(Signal)是在对象中定义的,当特定事件发生时被自动触发;槽(Slot)则是可以响应信号的函数。这种机制简化了事件驱动程序的编写。
-
国际化与本地化:Qt 支持多语言界面,可以轻松地为应用程序添加多种语言支持,满足全球用户的需求。
-
网络功能:Qt 内置了对网络编程的支持,包括 HTTP、FTP、TCP/IP、UDP 等协议,使得开发网络应用变得简单。
数据库集成:Qt 提供了数据库访问模块,支持 SQLite、MySQL、PostgreSQL 等多种数据库,便于开发数据驱动的应用。 -
多媒体支持:通过 QtMultimedia 模块,开发者可以轻松地在应用中集成音频和视频功能。
-
性能优化:Qt 提供了多种工具和技术,如 QML 和 OpenGL,用于构建高性能的图形界面和动画效果。
开发工具
Qt Creator:官方提供的集成开发环境,集成了代码编辑器、调试器、项目管理工具和 UI 设计器,大大提高了开发效率。
Qt Designer:一个用于设计和编辑用户界面的工具,支持拖放操作,可以生成 Qt 的 UI 代码。
Qt Assistant:一个帮助文档浏览器,包含了 Qt 的所有 API 文档和教程,是学习 Qt 的重要资源。
社区与支持
Qt 拥有庞大的开发者社区,提供了丰富的资源、示例代码和第三方库。无论是初学者还是经验丰富的开发者,都能在社区中找到所需的支持和解决方案。
总之,Qt 是一个功能全面、易于使用的开发框架,特别适合那些希望快速开发高质量跨平台应用的开发者。无论是桌面应用、移动应用还是嵌入式设备上的应用,Qt 都能提供强大的支持。
项目说明
使用Kithara Windows实时套件和Haclon的组合,可以进行二维码检测,并结合Qt图形化应用框架实时显示检测图像,实现选择不同网口的摄像头并实时调节检测圆的参数,监控测试抖动。
编写流程:
- 导入Kithara Windows实时套件和Halcon库。
- 使用Qt图形化应用框架创建一个界面,包括一个图像显示区域和解析数据展示。
- 初始化Kithara并打开摄像头,开始实时获取图像。
- 在任务处理中,将实时获取的图像传递给Halcon任务进行二维码检测。
- 根据检测结果,在图像上绘制二维码加检测区域,并通过共享内存将数据回传到应用层将图像实时显示在界面的图像显示区域中以及显示检测结果。
- 可以添加抖动检测功能,进行抖动测试。
- 结束时,释放资源和关闭摄像头。
关键代码
// 创建Halcon对象
HObject CreateImage(const uint type, const Hlong width, const Hlong height, void* data)
{
KS_printK("CreateImage");
HObject ho_image;
switch (type)
{
case KS_CAMERA_PIXEL_MONO_8:
{
const HImage image("byte", width, height, data);
ho_image = image;
break;
}
case KS_CAMERA_PIXEL_RGB_8:
{
HImage image;
image.GenImageInterleaved(data, "rgb", width, height, 0, "byte", width, height, 0, 0, 8, 0);
Rgb1ToGray(image,&ho_image);
break;
}
default:
break;
}
return ho_image;
}
// 识别二维码
HTuple QCoderCheck(const HTuple& hv_data_code_handle,const HObject& ho_image, SharedData *krenel_data_ptr)
{
HObject ho_symbol_xl_ds;
HTuple hv_result_handles;
HTuple hv_decoded_data_strings;
HTuple hv_row{}, hv_col{};
//“二维码”运算符将句柄返回给二维数据代码模型,可用于所有进一步的用途对数据代码的操作。
FindDataCode2d(ho_image, &ho_symbol_xl_ds, hv_data_code_handle, HTuple(), HTuple(), &hv_result_handles, &hv_decoded_data_strings);
GetContourXld(ho_symbol_xl_ds, &hv_row, &hv_col);
if ( hv_row.Length() <= QCODE_XLD_SIZE)
{
for (int i = 0; i < hv_row.Length(); i++)
{
const double xld_row = hv_row.ToDArr()[i];
const double xld_col = hv_col.ToDArr()[i];
krenel_data_ptr->check_qcode_param.qcode_xld_row[i] = xld_row;
krenel_data_ptr->check_qcode_param.qcode_xld_col[i] = xld_col;
}
}
return hv_decoded_data_strings;
}
// 这是实时任务将运行的函数,并对接收到的图像执行 Halcon 操作。只有实时任务才应调用 Halcon 函数。
KSError __stdcall HalconCallback(void * /*pArgs*/, void * /*pContext*/)
{
KS_printK("HalconCallback called!\n");
// 表示已准备好处理图像。
krenel_data_ptr_->ready = 1;
// 图形抖动性测试
int64 last_diff_time {0};
int is_valid_time = 0; // 时间是否有效 0 无效 1 时间有效
int count = 0; // 计数器
int64 jitter_time_sum {0}; // 抖动总时间
// 设置halcon编码格式
//SetSystem("filename_encoding", "utf8");
HTuple hv_data_code_handle;
CreateDataCode2dModel("QR Code", HTuple(), HTuple(), &hv_data_code_handle);
//SetDataCode2dParam (hv_data_code_handle, "string_encoding", "utf8");
// 处理循环,此循环仅在发出中止信号时停止。
for (;;)
{
// 等待图像接收或停止的通知。
KSError error = KS_waitForEvent(krenel_data_ptr_->image_received_event_handle, KSF_NO_FLAGS, 0);
if (error != KS_OK) { KS_printK("KS_waitForEvent failed! \n"); }
if (krenel_data_ptr_->abort != 0) { break; }
// 计数器
count++;
// 获取当前时间,用于计算图像处理的抖动时间
int64 last_time {0};
error = KS_getClock(&last_time, KS_CLOCK_MEASURE_HIGHEST);
if (error != KS_OK) { return error; }
// 获取接收到的图像数据的缓冲区
KSCameraBlock *camera_block;
void *image_data;
error = KS_recvCameraImage(krenel_data_ptr_->stream_handle, &image_data, &camera_block,KSF_NO_FLAGS);
if (error != KS_OK)
{
krenel_data_ptr_->ready = 1;
continue;
}
//如果接收到的块类型不是图像,我们跳过。在任何情况下,如果 KS_recvCameraImage() 成功接收到的缓冲区必须使用 KS_releaseCameraImage() 释放。
if (camera_block->blockType != KS_CAMERA_BLOCKTYPE_IMAGE)
{
KS_releaseCameraImage(krenel_data_ptr_->stream_handle, image_data, KSF_NO_FLAGS);
break;
}
// 在构建图形之前,请检查接收到的图像是否具有正确的像素格式。
const auto *image_block = reinterpret_cast<KSCameraImage *>(camera_block);
// 现在,我们将GigE Vision图像转换为Halcon图像。有几种方法可以实现此目的。
// 请参阅 Halcon 文档,了解从指针到像素创建图像的正确函数数据。
// 这里我们使用 HImage 类的构造函数之一,这等于调用运算符 gen_image1。
// 它将复制像素数据。尽管如此,像素的格式必须得到 Halcon 的支持,
// 否则,必须执行转换。
// 二维码识别
HObject ho_image = CreateImage(image_block->pixelFormat, image_block->width, image_block->height, image_data);
HTuple ret = QCoderCheck(hv_data_code_handle, ho_image, krenel_data_ptr_);
const char *ret_char = ret.ToString();
if (const size_t ret_size = ret.ToString().Length(); ret_size < QCODE_VALUE_SIZE - 1)
{
KSRTL_strncpy(krenel_data_ptr_->check_qcode_param.qcode_value, ret_char, QCODE_VALUE_SIZE);
}
else
{
KSRTL_strncpy(krenel_data_ptr_->check_qcode_param.qcode_value, "", QCODE_VALUE_SIZE);
}
// 填充图像信息到共享内存中
krenel_data_ptr_->image_info.image_height = image_block->height;
krenel_data_ptr_->image_info.image_width = image_block->width;
krenel_data_ptr_->image_info.pixel_format = image_block->pixelFormat;
if (image_block->pixelFormat == KS_CAMERA_PIXEL_MONO_8)
{
KS_memCpy(pixel_buffer_, image_data, image_block->width * image_block->height, KSF_NO_FLAGS);
}
else if (image_block->pixelFormat == KS_CAMERA_PIXEL_BGR_8 || image_block->pixelFormat == KS_CAMERA_PIXEL_RGB_8)
{
KS_memCpy(pixel_buffer_, image_data, image_block->width * image_block->height * 3, KSF_NO_FLAGS);
}
// 释放图像数据
error = KS_releaseCameraImage(krenel_data_ptr_->stream_handle, image_data, KSF_NO_FLAGS);
if (error != KS_OK) { KS_printK("KS_releaseCameraImage failed! \n"); }
// 图形处理完成后,减去上次处理完成的时间
int64 time {0};
error = KS_getClock(&time, KS_CLOCK_MEASURE_HIGHEST);
if (error != KS_OK) { return error; }
// 检测圆处理时间
const int64 diff_time = time - last_time;
int64 time_cyc = diff_time;
KS_convertClock(&time_cyc, KS_CLOCK_MEASURE_HIGHEST, KS_CLOCK_MACHINE_TIME, KSF_NO_FLAGS);
krenel_data_ptr_->jitter_value.time_cyc = time_cyc;
if (is_valid_time == 0)
{
last_diff_time = diff_time;
is_valid_time = 1;
}
else
{
// 处理时间的抖动
const int64 jitter_time = diff_time - last_diff_time;
int64 single_time = jitter_time;
KS_convertClock(&single_time, KS_CLOCK_MEASURE_HIGHEST, KS_CLOCK_MACHINE_TIME, KSF_NO_FLAGS); // 100 ns 为单位
last_diff_time = diff_time;
jitter_time_sum += single_time;
if (krenel_data_ptr_->jitter_value.lat_min > single_time)
{
krenel_data_ptr_->jitter_value.lat_min = single_time;
}
if (krenel_data_ptr_->jitter_value.lat_max < single_time)
{
krenel_data_ptr_->jitter_value.lat_max = single_time;
}
krenel_data_ptr_->jitter_value.lat_avg = jitter_time_sum / count;
krenel_data_ptr_->jitter_value.cur_val = single_time;
}
krenel_data_ptr_->ready = 1;
}
return KS_OK;
}
抖动测试
测试平台:
测试结果:
-
CPU GPU空载测试
-
CPU满载运行,GPU空载运行
-
CPU满载运行,GPU满载运行
-
CPU空载运行,GPU满载运行
Kithara Windows实时套件得益于其CPU独占技术,即使在Windows cpu负载较高的情况下,也能够稳定地处理Haclon对图像中二维码的检测任务并输出结果,但是GPU负载对图像处理响应较大,在实际项目中应避免GPU存在过高负载。 这种稳定性确保了在各种工作负载下,检测任务都能保持一致的性能表现。此外,我们还对不同平台对检测任务的影响进行了测试,进一步验证了Kithara Windows实时套件在多种环境中的优越性能。这些测试结果表明,无论平台和负载如何,该套件都能提供可靠、高效的图像处理能力。
在进行相同的检测任务时,我们发现抖动现象存在显著差异,这表明用于性能检测的CPU在图像处理方面具有一定的影响。此外,在测试过程中我们还注意到,不同的算法和图像的复杂度也会对结果产生影响。这些因素综合作用,揭示了在图像处理任务中,硬件和软件的选择对性能的最终表现有着重要的决定性作用。通过深入分析这些变量,我们可以更好地优化检测系统,从而提高整体的处理效率和准确性。
开源源码
测试源码现已开源,项目Demo均是测试代码,请勿用于实际项目!