一、引言
在工业视觉项目中,我们经常面临两个矛盾:
- 相机采集线程(如海康 SDK 的回调函数)需要高速、连续获取图像;
- 图像处理逻辑往往较复杂(如 Halcon 识别、定位、测量),处理耗时较长。
若两者直接耦合,容易出现卡顿、数据丢失、资源争用。因此,我们采用一种典型的并发编程方案——生产者-消费者模型,并结合 Qt 多线程与 Halcon 图像接口,实现高性能、高稳定性的视觉系统架构。
容器类源码下载
📦 ImageData 类源码(含 .h 和 .cpp 文件)
通过网盘分享的文件:Qt 与Halcon联合开发七: 多线程图像处理架构设计
链接: https://pan.baidu.com/s/1PRRBlfkZEapBLTYTFrlAJA?pwd=jkcf 提取码: jkcf
二、系统架构概览
模块划分
- 海康 SDK 采集回调线程(生产者)
- ImageData 图像缓存容器(缓存缓冲区)
- 图像处理线程(Halcon算法)(消费者)
- Qt GUI 主界面(控制、展示)
数据流动图
[海康回调线程]
↓ addImageData()
[ImageData 缓存队列]
↑ getImageData()
[Halcon 图像处理线程]
↑
[Qt 控制/展示界面]
三、图像容器类 ImageData 设计
ImageData
类是图像采集和处理之间的中转站,设计目标如下:
设计需求 | 实现方式 |
---|---|
线程安全 | 每个队列绑定 QMutex |
多缓存队列 | 支持多个相机或并行任务 |
等待/唤醒机制 | 使用 QWaitCondition |
状态反馈 | 支持处理慢、失败等情况返回 |
异常信息追踪 | getLastErrorMsg() 提供错误信息 |
结构定义
class ImageData {
public:
enum class ImageDataDistributionMode { ByOrder, ByUser };
enum ObtainImageDataResultStatus {
NoImageData,
ExistDataButObtainFailed,
ExistDataButObtainWithProblem,
ExistDataAndObtainSucced
};
void setImageDataQueneNumber(int num = 1);
bool addImageData(int index, const HalconCpp::HObject& ho_Image);
int getImageData(int index, HalconCpp::HObject* ho_Image);
int distributeImageDataQuene(ImageDataDistributionMode mode = ImageDataDistributionMode::ByOrder);
void clearImageData();
void wakeUp(int index);
void wait(int index);
QString getLastErrorMsg() const;
private:
QList<QList<HalconCpp::HObject>> m_images;
QList<QMutex*> m_mutexs;
QList<QWaitCondition*> m_waits;
QList<QMutex*> m_waitMutexs;
int m_num;
unsigned int m_flag;
QString m_lastErrorMsg;
};
四、图像采集线程(生产者)实现
海康工业相机使用回调机制采集图像数据,以下为图像采集回调函数 ImageCallBack
的核心代码:
void ImageCallBack(unsigned char* pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser) {
HikCamera* camera = static_cast<HikCamera*>(pUser);
try {
int ImageWidth = pFrameInfo->nWidth;
int ImageHeight = pFrameInfo->nHeight;
HalconCpp::HObject ho_Image;
switch (pFrameInfo->enPixelType) {
case PixelType_Gvsp_Mono8:
HalconCpp::GenImage1(&ho_Image, "byte", ImageWidth, ImageHeight, reinterpret_cast<Hlong>(pData));
break;
case PixelType_Gvsp_RGB8_Packed:
HalconCpp::GenImageInterleaved(&ho_Image, reinterpret_cast<Hlong>(pData), "rgb",
ImageWidth, ImageHeight, -1, "byte", 0, 0, 0, 0, -1, 0);
break;
case PixelType_Gvsp_BayerRG8:
HalconCpp::GenImage1(&ho_Image, "byte", ImageWidth, ImageHeight, reinterpret_cast<Hlong>(pData));
HalconCpp::CfaToRgb(ho_Image, &ho_Image, "bayer_rg", "bilinear");
break;
default:
HalconCpp::GenImageConst(&ho_Image, "byte", ImageWidth, ImageHeight);
break;
}
if (!camera->getImageData()->addImageData(0, ho_Image))
camera->sendErrorsMsgs(camera->getImageData()->getLastErrorMsg());
} catch (const HalconCpp::HException&) {
camera->sendErrorsMsgs(tc("C25: 图像数据转换失败"));
}
}
说明:
- 根据不同像素格式构建 Halcon 图像;
- 通过
addImageData()
存入图像容器; - 支持 Bayer 格式自动去马赛克;
- 回调函数中不执行耗时逻辑,快速返回。
五、图像处理线程(消费者)实现
图像处理线程不断尝试从 ImageData 中获取图像:
while (m_isRun) {
HalconCpp::HObject ho_Image;
switch (m_imgData->getImageData(m_imgIndex, &ho_Image)) {
case ImageData::NoImageData:
m_imgData->wait(m_imgIndex);
continue;
case ImageData::ExistDataButObtainFailed:
emit errors(m_cameraIndex, m_imgData->getLastErrorMsg());
m_isRun = false;
continue;
case ImageData::ExistDataButObtainWithProblem:
emit warnings(m_cameraIndex, m_imgData->getLastErrorMsg());
break;
case ImageData::ExistDataAndObtainSucced:
break;
}
processWithHalcon(ho_Image); // 图像算法处理
}
说明:
- 无图像时调用
wait()
,自动挂起,节省 CPU; - 获取失败时可输出错误,便于故障诊断;
- 获取成功后调用图像处理函数;
- 支持线程终止中断(通过
m_isRun
控制)。
六、状态码说明(接口健壮性)
状态值 | 含义 | 处理建议 |
---|---|---|
NoImageData | 缓存为空 | 可等待或跳过 |
ExistDataButObtainFailed | 图像出错 | 输出日志,跳出线程 |
ExistDataButObtainWithProblem | 图像堆积 | 处理慢,可能性能瓶颈 |
ExistDataAndObtainSucced | 成功获取图像 | 正常处理 |