2. 图像处理基础

在进入Halcon的Blob分析与图像预处理实战之前,必须先建立对图像底层数据结构的清晰认知。图像在计算机中并非一张“照片”,而是一个多维数值矩阵。像素、灰度、分辨率与颜色空间构成了描述图像的四根支柱,而Halcon作为工业级机器视觉库,其图像数据类型(HObject)、区域(Region)与像素访问机制均建立在这些概念之上。本章将系统梳理这些核心概念,并重点说明它们在Halcon中的具体表示方式与操作入口,为后续的阈值分割、形态学处理与特征提取打下坚实基础。

图像数据模型与Halcon表示 像素与灰度 f(x,y) = 灰度值 8bit: 0~255 Halcon: byte / uint2 分辨率 空间分辨率: W×H 灰度分辨率: 量化位数 Halcon: get_image_size 颜色空间 RGB / HSV / YUV Halcon: rgb1_to_gray trans_from_rgb Halcon 图像数据模型 HObject (Image) Region (区域) Tuple (数组) HTuple / HImage 像素访问 get_grayval / set_grayval get_image_pointer1 类型转换 convert_image_type change_domain / crop_domain 通道处理 decompose3 / compose3 access_channel

像素与灰度:图像的最小信息单元

核心原理:像素(Pixel)是数字图像的最小不可分割单元,每个像素对应场景中一个微小区域的采样结果。在灰度图像中,像素值称为灰度(Gray Value),通常用8位无符号整数(0~255)表示,0代表纯黑,255代表纯白,中间值为不同深浅的灰色。灰度值的大小反映了该像素位置的光照强度或物体反射率。Halcon中,灰度图像默认使用byte类型存储,即每个像素占用1字节内存;对于需要更高精度(如医学影像、工业X光检测)的场景,可使用uint2类型(0~65535)或real类型(浮点)。

Halcon中的表示:在Halcon中,图像数据封装在HObject对象中,通过read_image读取文件,或通过gen_image1从外部指针创建。访问单个像素灰度值使用get_grayval(Image, Row, Column, Grayval),该算子返回指定行列坐标的灰度值。批量获取像素数据则使用get_image_pointer1(Image, Pointer, Type, Width, Height),直接获取图像数据内存指针,便于与C/C++或Python的NumPy数组互操作。

实战场景:在检测PCB板焊点缺陷时,首先需要确认焊点区域的灰度分布。通过get_grayval遍历焊点ROI内的像素,统计灰度直方图,可快速判断是否存在虚焊(灰度偏低)或桥连(灰度异常偏高)。Halcon的gray_histo算子可直接生成ROI内的灰度直方图,辅助设定阈值。
数据类型位深取值范围Halcon类型标识典型应用
byte8bit0~255'byte'普通工业相机图像
uint216bit0~65535'uint2'高动态范围、X光图像
int432bit-2^31~2^31-1'int4'深度图、差值图
real32bit浮点±3.4e38'real'浮点运算中间结果
常见误区:初学者常混淆byte与uint2的存储差异。当图像为12bit相机输出时,若直接以byte类型读取,高4位数据将被截断,导致灰度信息丢失。正确做法是使用read_image时指定类型,或读取后调用convert_image_type进行转换。

分辨率:空间分辨率与灰度分辨率

核心原理:分辨率包含两个维度:空间分辨率指图像中像素的密度,通常用宽×高(如1920×1080)表示,决定了能分辨的最小物体尺寸;灰度分辨率指灰度量化的位数,8bit可区分256级灰度,16bit可区分65536级。在工业检测中,空间分辨率决定检测精度,灰度分辨率决定对比度敏感度。例如,检测细微划痕时,若灰度分辨率不足,划痕与背景的微小灰度差可能被量化噪声淹没。

Halcon中的操作:获取图像尺寸使用get_image_size(Image, Width, Height)。改变图像尺寸使用zoom_image_factor(按比例缩放)或zoom_image_size(缩放到指定尺寸)。灰度分辨率转换使用convert_image_type(Image, ImageConverted, 'uint2')。值得注意的是,zoom_image_factor默认使用双线性插值,对于边缘检测场景,建议使用zoom_image_factor(Image, ZoomImage, 0.5, 0.5, 'constant')以最近邻插值保留边缘锐度。

经验提醒:在Halcon中,get_image_size返回的是像素行列数,而get_image_pointer1返回的指针指向数据首地址。若需计算图像在内存中的行字节数(即步长),可使用get_image_pointer1返回的Width乘以像素字节数,但注意Halcon图像行数据可能按4字节对齐,实际步长需通过get_image_pointer1的Width参数结合get_image_type推算,或直接使用get_image_pointer1的Width作为行宽。
// 获取图像尺寸与类型
read_image(Image, 'pcb_defect.png');
get_image_size(Image, Width, Height);
get_image_type(Image, Type);
// 将16bit图像转为8bit用于显示
convert_image_type(Image, ImageByte, 'byte');
// 缩放图像至原尺寸的75%
zoom_image_factor(ImageByte, ImageScaled, 0.75, 0.75, 'constant');

颜色空间:从RGB到灰度与HSV

核心原理:彩色图像通常使用RGB(红绿蓝)三通道表示,每个通道独立存储灰度值。但RGB通道间相关性高,且对光照变化敏感,不适合直接用于Blob分析。因此,工业视觉中常将RGB转换到其他颜色空间:HSV(色调、饱和度、亮度)将颜色信息与亮度分离,适合基于颜色的目标分割;YUV(亮度、色度)广泛用于视频压缩,Y分量即灰度图。Halcon提供trans_from_rgb算子,支持RGB到HSV、YUV、Lab等空间的转换。

Halcon中的通道操作:多通道图像在Halcon中仍为HObject,但包含多个通道。使用decompose3(Image, ImageR, ImageG, ImageB)将三通道图像拆分为单通道;compose3则反向合并。提取单通道使用access_channel(Image, ChannelImage, 1)(1为R通道,2为G,3为B)。彩色转灰度使用rgb1_to_gray(Image, GrayImage),其内部按标准公式计算:Gray = 0.299R + 0.587G + 0.114B。

案例拆解:在药片颜色分拣系统中,药片有红、黄、蓝三种颜色。直接使用RGB阈值分割易受环境光影响。处理流程如下:
  1. 采集彩色图像read_image(Image, 'pills.png');
  2. 转换到HSV空间:trans_from_rgb(Image, ImageHSV, 'hsv');
  3. 提取H通道:access_channel(ImageHSV, HChannel, 1);
  4. 在H通道上设定阈值:红色H值约0~10或170~180,黄色约20~30,蓝色约100~120;
  5. 使用threshold分别提取三种颜色区域,再通过connection分割连通域,统计药片数量。
该方案中,H通道对光照变化不敏感,即使亮度波动,色调值仍保持稳定,显著提高了分拣鲁棒性。
颜色空间分量含义Halcon转换算子适用场景
RGB红/绿/蓝—原始采集、显示
HSV色调/饱和度/亮度trans_from_rgb(...,'hsv')颜色分割、光照变化场景
YUV亮度/色度trans_from_rgb(...,'yuv')视频处理、灰度提取
Lab亮度/绿红/蓝黄trans_from_rgb(...,'lab')颜色差异精确测量
风险提示:HSV转换后,H通道在0和360度处存在不连续性(红色跨越0度边界)。在设定红色阈值时,需使用两个区间(如H<10或H>170)并做并集操作,否则会漏检部分红色区域。Halcon中可使用threshold两次后通过union2合并区域。

Halcon图像对象与内存管理

核心原理:Halcon的图像对象(HObject)不仅包含像素数据,还包含域(Domain)信息。域定义了图像中有效处理的区域,默认是整个图像矩形。通过change_domain可设置ROI,后续所有算子仅处理域内像素,大幅提升运算效率。此外,Halcon采用写时复制(Copy-on-Write)机制,多个变量可共享同一图像数据,直到某个变量被修改时才真正复制,因此内存使用高效。

操作流程:创建图像对象的方式包括:read_image读取文件、gen_image1从外部数据创建、gen_image_const生成常量图像。释放图像使用clear_obj,但Halcon的自动垃圾回收机制会管理大部分内存,仅在处理大量图像或长时间运行时需手动清理。检查图像是否有效使用test_obj。

// 创建ROI域并处理
read_image(Image, 'wafer.png');
gen_rectangle1(Rect, 100, 100, 500, 500);
change_domain(Image, Rect, ImageROI);
// 仅对ROI内像素进行阈值分割
threshold(ImageROI, Region, 128, 255);
// 清理不再使用的对象
clear_obj(ImageROI);
经验总结:在Blob分析中,合理设置域可减少90%以上的计算量。例如,检测手机屏幕缺陷时,先通过模板匹配定位屏幕区域,再将该区域设为域,后续的阈值、形态学操作均在此域内执行,既提高速度又避免背景干扰。此外,reduce_domain算子可将图像域缩减为指定区域,效果与change_domain类似,但reduce_domain会保留原始图像数据,适合需要保留全图信息的场景。

像素坐标系统与行列约定

核心原理:Halcon使用行列坐标(Row, Column)而非笛卡尔坐标(X, Y)。Row对应图像垂直方向(从上到下递增),Column对应水平方向(从左到右递增)。原点位于左上角。这一约定与图像存储的行主序一致,但在与机械手坐标交互时需转换。Halcon提供image_points_to_world_plane算子将像素坐标转换为世界坐标,或使用hom_mat2d_translate进行仿射变换。

常见误区:初学者常将Row误认为X坐标,导致ROI位置偏移。例如,gen_rectangle1(Rect, 100, 200, 300, 400)表示左上角坐标为(100, 200),右下角为(300, 400),即Row从100到300,Column从200到400。若需在图像中心(960, 540)处生成100×100的ROI,应写为gen_rectangle1(Rect, 490, 540, 590, 640)。

实战案例:在晶圆缺陷检测中,需将缺陷坐标映射到机械手坐标系。已知相机标定矩阵HomMat2D,缺陷像素坐标(200, 350)通过affine_trans_point_2d(HomMat2D, 200, 350, WorldX, WorldY)转换为世界坐标。注意,Halcon的affine_trans_point_2d输入参数顺序为(Row, Column),输出为(X, Y),与常规数学约定不同,需仔细核对。
操作Halcon算子坐标约定
读取像素值get_grayval(Image, Row, Col, Val)Row先行,Col后列
生成矩形ROIgen_rectangle1(Rect, Row1, Col1, Row2, Col2)左上角(Row1,Col1),右下角(Row2,Col2)
像素转世界image_points_to_world_plane(..., Row, Col, X, Y)输入Row,Col,输出X,Y
仿射变换点affine_trans_point_2d(HomMat, Row, Col, X, Y)输入Row,Col,输出X,Y
风险说明:当图像经过旋转或镜像处理后,坐标映射关系会改变。例如,mirror_image后Row方向反转,若未更新标定矩阵,后续坐标转换将产生系统性偏差。建议在图像预处理流程中,先完成所有几何变换,再进行坐标标定,或使用hom_mat2d_invert求逆矩阵补偿变换。

图像类型转换与内存优化

核心原理:不同图像类型占用内存差异显著。一张1920×1080的8bit灰度图占用约2MB,而16bit图占用4MB,real类型则占用8MB。在内存受限的嵌入式视觉系统中,合理选择图像类型至关重要。Halcon的convert_image_type支持所有类型间转换,但需注意精度损失:从16bit转8bit时,超出255的灰度值将被截断,而非缩放。

优化策略:在Blob分析流程中,建议遵循“采集→转换→处理→转换→显示”的链路。例如,相机输出12bit图像,先以uint2类型读取,进行阈值分割时使用threshold(支持uint2),得到区域后,仅将区域内的图像转为8bit用于显示,避免全图转换。此外,crop_domain可裁剪出ROI区域,减少后续处理的数据量。

// 内存优化示例:仅处理ROI区域
read_image(ImageRaw, 'high_dynamic.tif');
get_image_size(ImageRaw, W, H);
gen_rectangle1(Roi, H/4, W/4, 3*H/4, 3*W/4);
reduce_domain(ImageRaw, Roi, ImageCrop);
// 此时ImageCrop仅包含ROI数据,后续处理速度提升约4倍
threshold(ImageCrop, Region, 100, 500);
经验提醒:使用get_image_pointer1获取指针时,返回的Type参数为字符串,如'byte'、'uint2'。在C++接口中,可通过HImage::GetImagePointer1获取指针并直接操作内存,但需注意Halcon图像行数据可能包含填充字节(Padding),实际行字节数需通过get_image_pointer1的Width参数乘以像素大小,再向上对齐到4的倍数。建议使用get_image_pointer1返回的Width作为步长,而非自行计算。

本章总结与常见误区复盘

本章围绕图像的四项核心属性展开,并映射到Halcon的具体操作。像素与灰度是图像的基本构成,分辨率决定了检测精度与数据量,颜色空间为多通道处理提供灵活手段,而Halcon的HObject对象模型则封装了这些底层细节。以下为常见误区汇总:

  • 误区一:混淆行列坐标与XY坐标,导致ROI定位错误。解决:始终牢记Halcon中Row在前、Column在后。
  • 误区二:忽略图像类型转换的精度损失。解决:16bit转8bit时,先通过min_max_gray获取灰度范围,再使用scale_image进行线性拉伸。
  • 误区三:在彩色图像上直接进行灰度阈值分割。解决:先通过decompose3提取单通道,或转换到HSV后使用H通道。
  • 误区四:未利用图像域(Domain)导致处理速度慢。解决:使用reduce_domain或change_domain限定处理区域。
  • 误区五:内存管理不当,长时间运行导致内存泄漏。解决:在循环中及时clear_obj不再使用的临时对象。
复盘案例:某项目需检测金属表面划痕,初始方案直接对整幅1920×1200的8bit灰度图进行阈值分割,耗时约35ms。优化后,先通过threshold定位高亮金属区域,再reduce_domain将域缩小至金属区域,处理时间降至12ms。进一步将图像转为uint2类型以保留更多灰度细节,划痕检出率从82%提升至95%。该案例说明,理解图像数据模型并合理运用域与类型转换,能同时提升性能与精度。