图像基础操作:图像读取、显示、保存、类型转换、ROI定义与裁剪
在Halcon视觉检测框架中,图像基础操作是所有上层算法(定位、测量、识别、缺陷检测)的“地基”。如果图像在进入处理管线之前没有得到正确的读取、转换或裁剪,后续的形态学处理、特征提取和模板匹配都会产生不可预期的偏差。本章将围绕五个核心操作——读取、显示、保存、类型转换、ROI定义与裁剪——展开,重点讲解Halcon中图像数据的内存模型、算子调用规范以及工程落地时的常见陷阱。
一、核心原理:Halcon图像对象与数据流
Halcon中的图像不是简单的二维数组,而是一个包含通道信息、类型信息、区域信息以及像素数据的复合对象。理解这一点是掌握所有图像操作的前提。
- 图像对象(HObject):Halcon使用统一的HObject类型来管理图像、区域、XLD轮廓等数据。图像对象内部包含指向实际像素内存的指针,以及描述图像尺寸、通道数、像素类型(如byte、uint2、real)的元数据。
- 通道(Channel):彩色图像通常包含3个通道(R、G、B),灰度图像只有1个通道。Halcon中可以使用
access_channel和compose3等算子进行通道分离与合并。 - 像素类型(Pixel Type):决定了每个像素占用的内存位数和取值范围。常见类型包括
byte(0-255)、uint2(0-65535,常用于深度图或16位工业相机)、real(浮点型,常用于图像运算中间结果)。 - 图像坐标系:Halcon中图像原点位于左上角,行坐标(Row)向下增长,列坐标(Column)向右增长。ROI定义时使用的行列坐标均基于此约定。
下图展示了从图像读取到ROI裁剪的典型数据流,以及各阶段图像对象状态的变化:
二、方法流程:五大操作的标准实现
2.1 图像读取(read_image)
read_image是Halcon中最基础的图像输入算子,支持BMP、JPEG、PNG、TIFF以及Halcon原生格式(.hobj)等。其标准调用方式如下:
// C++ / C# 语法示例
HObject hoImage;
HTuple hvWidth, hvHeight;
// 读取图像文件
ReadImage(&hoImage, "D:/images/part_01.png");
// 获取图像尺寸
GetImageSize(hoImage, &hvWidth, &hvHeight);
// 输出: hvWidth = 1280, hvHeight = 1024
在批量处理场景中,推荐使用read_image配合循环遍历文件夹,或使用list_files算子先获取文件列表,再逐一读取。对于工业相机采集的图像,通常使用grab_image或grab_image_async算子,它们直接从采集设备获取图像数据,不经过磁盘I/O。
GetImageSize返回的尺寸是否符合预期。如果图像尺寸为0或异常,说明文件路径错误或文件格式不兼容,应提前终止流程并记录日志,避免后续算子因空图像崩溃。
2.2 图像显示(disp_obj)
Halcon的显示操作依赖于窗口句柄。标准流程为:打开窗口 → 显示图像 → 更新窗口。在HDevelop中,窗口句柄通常由dev_open_window自动管理;在C++/C#集成开发中,需要显式获取窗口句柄。
// HDevelop 语法
dev_open_window(0, 0, 512, 512, 'black', WindowHandle)
disp_obj(hoImage, WindowHandle)
// C++ 语法
HTuple hv_WindowHandle;
OpenWindow(0, 0, 512, 512, 0, "visible", "", &hv_WindowHandle);
DispObj(hoImage, hv_WindowHandle);
显示操作本身不改变图像数据,但会影响调试效率。在视觉检测框架中,建议将显示逻辑封装为独立模块,通过开关控制是否启用实时显示,避免在产线运行时因显示刷新导致处理速度下降。
2.3 图像保存(write_image)
保存图像时需指定格式和压缩参数。Halcon的write_image算子支持BMP、JPEG、PNG、TIFF等格式,其中JPEG支持质量参数(0-100),PNG支持压缩级别(0-9)。
// 保存为PNG格式,压缩级别6
WriteImage(hoImage, "png", 6, "D:/output/result_01.png");
// 保存为JPEG格式,质量85
WriteImage(hoImage, "jpeg", 85, "D:/output/result_01.jpg");
2.4 类型转换(convert_image_type)
类型转换是图像预处理中最容易被忽视的环节。Halcon中convert_image_type算子用于将图像从一种像素类型转换为另一种类型,同时支持缩放因子(Mult)和偏移量(Add)。
// 将byte类型图像转换为real类型,用于后续浮点运算
HObject hoRealImage;
ConvertImageType(hoImage, &hoRealImage, "real");
// 将uint2类型图像转换为byte类型,并应用缩放
// 假设原始深度图范围0-4095,映射到0-255
HObject hoByteImage;
ConvertImageType(hoDepthImage, &hoByteImage, "byte", 255.0/4095.0, 0);
类型转换的典型应用场景包括:
- 将16位工业相机图像转换为8位用于显示或保存。
- 将灰度图转换为real类型,避免整数除法带来的精度损失。
- 将彩色图转换为灰度图(使用
rgb1_to_gray算子),减少计算量。
2.5 ROI定义与裁剪(reduce_domain)
ROI(Region of Interest)是视觉检测中缩小处理范围、提升速度、排除干扰的核心手段。Halcon中ROI操作分为两步:定义区域和裁剪图像。
定义区域可以使用多种方式:
// 方式1:矩形ROI
HObject hoRectROI;
GenRectangle1(&hoRectROI, 100, 150, 400, 600); // 行1,列1,行2,列2
// 方式2:圆形ROI
HObject hoCircleROI;
GenCircle(&hoCircleROI, 300, 400, 50); // 圆心行,圆心列,半径
// 方式3:任意多边形ROI
HObject hoPolyROI;
HTuple hvRows = {100, 200, 250, 150};
HTuple hvCols = {100, 120, 300, 280};
GenRegionPolygon(&hoPolyROI, hvRows, hvCols);
裁剪操作使用reduce_domain算子,将图像与ROI区域结合,生成一个只包含ROI范围内像素的新图像对象:
HObject hoROIImage;
ReduceDomain(hoImage, hoRectROI, &hoROIImage);
// 此时hoROIImage的尺寸与原始图像相同,但有效区域仅为ROI内部
// 可以通过crop_domain进一步压缩图像尺寸
HObject hoCroppedImage;
CropDomain(hoROIImage, &hoCroppedImage);
reduce_domain与crop_domain的区别在于:前者保留原始图像坐标系,ROI外部像素为无效区域;后者将ROI区域裁剪为新的独立图像,坐标系从(0,0)重新开始。在模板匹配和测量任务中,通常使用reduce_domain保留坐标信息;在缺陷检测和分类任务中,使用crop_domain可以显著减少数据量。
三、案例/场景:PCB板定位中的ROI应用
以PCB板表面元件定位为例,完整展示图像基础操作在工程中的串联使用。假设输入为500万像素工业相机拍摄的PCB图像,需要定位板上的一个矩形Mark点。
// 步骤1:读取图像
HObject hoPCBImage;
ReadImage(&hoPCBImage, "D:/images/pcb_01.png");
// 步骤2:类型转换(若相机输出为12位,需转为byte)
HObject hoByteImage;
ConvertImageType(hoPCBImage, &hoByteImage, "byte", 255.0/4095.0, 0);
// 步骤3:定义ROI(Mark点大致位于图像中心区域)
HObject hoROI;
GenRectangle1(&hoROI, 800, 1000, 1200, 1400);
// 步骤4:裁剪ROI
HObject hoROIImage;
ReduceDomain(hoByteImage, hoROI, &hoROIImage);
// 步骤5:在ROI内进行阈值分割定位
HObject hoMarkRegion;
Threshold(hoROIImage, &hoMarkRegion, 128, 255);
// 步骤6:计算Mark点中心坐标
HTuple hvRow, hvCol;
AreaCenter(hoMarkRegion, &hvArea, &hvRow, &hvCol);
// 输出坐标基于原始图像坐标系,可直接用于机械手引导
该案例中,ROI将处理范围从500万像素缩小到约16万像素,处理速度提升约30倍,同时排除了PCB边缘反光和相邻元件的干扰。
四、误区与复盘
| 常见误区 | 问题描述 | 正确做法 |
|---|---|---|
| 忽略图像类型检查 | 直接对uint2图像执行阈值分割,导致结果异常 | 先使用GetImageType检查类型,必要时转换 |
| ROI坐标混淆 | 将行列坐标与XY坐标混淆,导致ROI位置偏移 | 明确Halcon坐标系为行(Row)列(Column),原点在左上角 |
| 过度使用crop_domain | 在需要原始坐标系的场景中使用crop_domain,导致坐标丢失 | 需要坐标映射时使用reduce_domain,仅需图像数据时使用crop_domain |
| 保存格式选择错误 | 将16位图像保存为JPEG,导致数据截断 | 16位图像使用PNG或TIFF格式保存 |
| 显示操作拖慢流程 | 在循环中频繁调用disp_obj,导致处理帧率下降 | 使用显示开关控制,或仅在调试模式下启用显示 |
图像基础操作虽然看似简单,但在实际工程中,80%的异常问题(如内存泄漏、坐标偏移、图像花屏)都源于这一层的处理不当。建议在框架设计阶段,将本章内容封装为独立的图像处理基类,提供统一的接口和异常处理机制,为后续的定位、测量、识别等高级功能奠定坚实基础。