3. 图像采集与读取

在机器视觉项目中,图像采集与读取是连接物理世界与算法处理的第一道桥梁。无论后续的定位、测量、缺陷检测算法多么精妙,如果图像采集环节出现丢帧、格式错乱、灰度失真或噪声过大,整个视觉系统的稳定性都将无从谈起。本章围绕 Halcon 环境下的图像获取链路,系统讲解相机连接方式、文件读取策略、格式转换规则以及预处理的基本方法,帮助学习者建立从“硬件触发”到“内存图像”再到“可分析图像”的完整认知。

一、核心原理:图像数据在 Halcon 中的表示与流转

Halcon 使用 HImage 对象(对应底层 Hobject)来承载图像数据。一个图像对象不仅包含像素矩阵,还携带通道数、位深、宽高、区域信息等元数据。理解图像在内存中的组织方式是后续所有操作的基础。

图像采集的核心原理涉及三个层面:

  • 物理层:相机通过 GigE Vision、USB3 Vision、Camera Link 或 GenICam 协议将光信号转换为数字信号,并传输到主机内存。
  • 驱动层:Halcon 通过接口算子(如 open_framegrabber)加载对应厂商的采集驱动,建立与相机的通信会话。
  • 应用层:开发者通过 grab_image 或 grab_image_async 从采集队列中获取一帧图像,并将其转换为 Halcon 内部格式。

对于图像文件读取,Halcon 支持 BMP、JPEG、PNG、TIFF、JPEG 2000 等常见格式,也支持多页 TIFF 和图像序列。读取时需注意:JPEG 是有损压缩,会引入块效应;PNG 支持无损压缩和 Alpha 通道;TIFF 可保存多帧或 16 位深度数据。Halcon 的 read_image 算子会自动识别文件头并解析格式,但开发者仍需关注位深和通道数的隐式转换。

关键认知:Halcon 中图像坐标系原点位于左上角,行坐标(Row)向下增长,列坐标(Column)向右增长。这与许多数学坐标系不同,在后续 ROI 选取和结果显示时需时刻注意。

二、方法流程:从相机连接到图像预处理的标准路径

一个完整的图像获取与预处理流程通常包含以下步骤。下面以 GigE 工业相机为例,给出标准流程。

2.1 相机连接与参数配置

Halcon 通过 open_framegrabber 建立与相机的连接。该算子需要指定采集接口、设备编号、色彩模式、位深、触发模式等参数。以下为典型代码:

* 打开 GigE 相机,设备编号 0,彩色 RGB,8 位深度
open_framegrabber ('GigEVision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'cam0', 0, -1, AcqHandle)

* 设置曝光时间(微秒)
set_framegrabber_param (AcqHandle, 'ExposureTime', 5000.0)

* 设置增益
set_framegrabber_param (AcqHandle, 'Gain', 1.5)

* 设置触发模式为软触发
set_framegrabber_param (AcqHandle, 'TriggerMode', 'On')
set_framegrabber_param (AcqHandle, 'TriggerSource', 'Software')

连接成功后,使用 grab_image 同步采集一帧,或使用 grab_image_async 启动异步连续采集。异步采集可避免因图像处理耗时导致的丢帧,适合高速产线。

2.2 图像文件读取与格式转换

当相机不可用或需要离线调试时,从文件读取图像是常用手段。Halcon 提供 read_image 算子,支持单张和多张读取:

* 读取单张图像
read_image (Image, 'D:/images/part_001.png')

* 读取多页 TIFF 中的第 3 帧
read_image (ImageTiff, 'D:/images/stack.tiff', 3)

* 读取图像序列(文件名带数字编号)
read_image (ImageSeq, 'D:/images/part_00' + Index + '.png')

格式转换是图像处理中高频操作。Halcon 提供 convert_image_type 用于位深转换,change_domain 用于改变图像域,rgb1_to_gray 用于彩色转灰度。以下为常见转换场景:

* 彩色转灰度(加权法:0.299R + 0.587G + 0.114B)
rgb1_to_gray (ImageColor, ImageGray)

* 将 16 位图像转为 8 位(自动缩放或截断)
convert_image_type (Image16, Image8, 'byte')

* 将图像从 RGB 转为 HSV 色彩空间
trans_from_rgb (ImageColor, ImageH, ImageS, ImageV, 'hsv')

经验提示:当从 16 位转 8 位时,默认采用截断方式,即超过 255 的像素值被置为 255。若图像动态范围较窄,建议先使用 scale_image 或 min_max_gray 获取实际灰度范围,再做线性拉伸,避免信息丢失。

2.3 预处理基础操作

预处理的目标是改善图像质量,为后续特征提取创造有利条件。常用操作包括:

  • 去噪:median_image(中值滤波)去除椒盐噪声,gauss_filter(高斯滤波)平滑图像。
  • 对比度增强:scale_image(灰度拉伸)、equ_histo_image(直方图均衡化)。
  • 形态学操作:gray_opening、gray_closing 用于去除亮/暗细节。
  • ROI 裁剪:reduce_domain 将处理区域限定在感兴趣区域,减少计算量。
* 中值滤波去除噪声(掩膜大小 5x5)
median_image (ImageGray, ImageMedian, 'circle', 5, 'mirrored')

* 高斯滤波平滑
gauss_filter (ImageMedian, ImageSmooth, 3)

* 灰度线性拉伸,增强对比度
min_max_gray (ImageSmooth, ImageSmooth, 0, 10, Min, Max, Range)
scale_image (ImageSmooth, ImageScaled, 255.0 / (Max - Min), -255.0 * Min / (Max - Min))

* 裁剪 ROI
gen_rectangle1 (ROI, 100, 200, 400, 600)
reduce_domain (ImageScaled, ROI, ImageROI)

三、案例场景:产线相机连接与离线图像批处理

以下通过两个典型场景,展示上述方法的综合应用。

3.1 场景一:在线相机实时采集与显示

某电子元件装配线需要检测引脚是否缺失。相机安装于传送带上方,采用软触发模式,每次到位信号触发一次采集。代码如下:

open_framegrabber ('GigEVision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'cam_pin', 0, -1, AcqHandle)
set_framegrabber_param (AcqHandle, 'ExposureTime', 8000.0)
set_framegrabber_param (AcqHandle, 'Gain', 1.0)

for i := 1 to 100 by 1
    * 等待外部触发信号
    set_framegrabber_param (AcqHandle, 'TriggerSoftware', 1)
    grab_image (Image, AcqHandle)
    
    * 实时显示
    dev_display (Image)
    
    * 预处理:灰度化 + 中值滤波
    rgb1_to_gray (Image, ImageGray)
    median_image (ImageGray, ImageDenoised, 'circle', 3, 'mirrored')
    
    * 后续检测步骤(略)
endfor

close_framegrabber (AcqHandle)

该场景中,grab_image 为阻塞式采集,若图像处理耗时超过帧间隔,会丢失触发信号。此时应改用 grab_image_async 并设置合理的队列长度。

3.2 场景二:离线批量读取与格式统一

在算法开发阶段,工程师常需要处理一批来自不同相机的历史图像。这些图像可能包含 8 位灰度、24 位彩色、16 位深度等不同格式。统一格式是保证算法稳定性的前提。

list_files ('D:/defect_samples', 'files', ImageFiles)
tuple_regexp_select (ImageFiles, '.*\\.(png|bmp|tif)$', SelectedFiles)

for i := 0 to |SelectedFiles| - 1 by 1
    read_image (Image, SelectedFiles[i])
    
    * 获取图像通道数和位深
    get_image_pointer1 (Image, Pointer, Type, Width, Height)
    
    * 统一转为 8 位灰度
    if (Type == 'uint2')
        convert_image_type (Image, ImageByte, 'byte')
    endif
    if (Type == 'real')
        scale_image (Image, ImageScaled, 1.0, 0)
        convert_image_type (ImageScaled, ImageByte, 'byte')
    endif
    count_channels (Image, Channels)
    if (Channels == 3)
        rgb1_to_gray (Image, ImageByte)
    endif
    
    * 保存统一格式
    write_image (ImageByte, 'png', 0, 'D:/unified/' + i + '.png')
endfor

常见陷阱:read_image 读取 JPEG 文件时,Halcon 会自动解码为 8 位灰度或 RGB,但不会提示原始压缩质量。若后续进行亚像素边缘提取,JPEG 的压缩伪影会被误识别为真实边缘。建议在精度要求高的场景使用 PNG 或 TIFF 格式。

四、误区与复盘:图像获取阶段的典型错误

根据多年项目经验,以下问题在初学者和工程现场中反复出现,需要特别警惕。

4.1 误区一:忽略相机参数与环境的匹配

曝光时间、增益、白平衡等参数并非越大越好。曝光过长会导致运动模糊,增益过大会放大噪声。正确做法是:先固定光照,再调整曝光使灰度直方图峰值位于 180-220 之间,最后微调增益。

4.2 误区二:混淆图像位深与显示范围

16 位图像的灰度范围是 0-65535,但显示器只能显示 0-255。直接显示 16 位图像会看到全黑或全白。必须使用 dev_display 前先做 scale_image 映射到 8 位范围,或使用 dev_set_lut 设置显示查找表。

4.3 误区三:格式转换时丢失关键信息

从彩色转灰度时,Halcon 默认使用 ITU-R BT.601 加权系数。若图像中目标颜色与背景颜色在灰度上接近,直接转换会导致对比度不足。此时应分析 RGB 三个通道的直方图,选择分离度最好的通道,或使用 decompose3 分解后单独处理。

4.4 误区四:预处理过度导致特征失真

预处理并非越强越好。过大的高斯核会模糊边缘,影响亚像素精度;多次中值滤波会改变区域形状。建议遵循“最小必要干预”原则:先做轻度去噪,验证效果后再逐步增强。

4.5 复盘清单

检查项 推荐做法 常见错误
相机连接 使用 info_framegrabber 验证设备枚举 设备编号写死,换相机后无法连接
触发模式 产线用硬件触发,实验室用软触发 触发信号未接导致一直超时
图像格式 统一为 8 位灰度或 24 位 RGB 16 位图像直接参与 8 位算法
预处理顺序 先裁剪 ROI,再滤波,最后增强 先全局增强导致 ROI 外噪声放大
内存管理 循环中及时 clear_obj 释放对象 长时间运行内存持续增长

图像采集与读取是视觉系统的“入口”,其质量直接决定后续算法的上限。建议学习者在实际项目中建立标准化的采集配置模板,并针对不同光照、不同材质工件,记录一组经过验证的参数组合。这样既能保证开发效率,也能在产线调试时快速定位问题根源。通过本章的学习,应能独立完成从相机连接、图像读取、格式转换到基础预处理的完整链路搭建,为后续的定位、测量与缺陷检测打下坚实基础。