文章目录

1.引言

1.1 什么是超盒分类

在这里插入图片描述

   超盒分类(Hyperboxes Classification)是 Halcon 机器视觉软件中一种经典且直观的分类方法。在工业视觉检测和图像处理领域,分类就是将一个对象划分到若干预定义类别中的某一个。例如:

  • 在流水线上,将金属零件分为螺母、垫圈和挡圈三类
  • 在农产品分拣中,区分橘子和柠檬
  • 在印刷品检测中,判断印刷图案是否存在缺陷

   Halcon作为业界领先的机器视觉软件,提供了完整的分类工具链,支持多种分类器类型,包括:

分类器 全称 特点
Hyperbox 超盒分类器 简单直观,轴平行边界
GMM 高斯混合模型 概率模型,适合异常检测
MLP 多层感知器 神经网络,表达能力强
SVM 支持向量机 理论扎实,泛化能力强
KNN K 近邻 非参数,支持增量学习
DL 深度学习 处理复杂图像的主流方案

   本文重点介绍 超盒分类器(Hyperbox Classifier)

1.2 超盒分类器的历史定位

   超盒分类器在Halcon的分类器家族中占有特殊位置,是最早提供的分类方法之一,代表了一种最简单直接的分类思想——用轴对齐的超矩形来界定类别边界

   尽管GMM、MLP、SVM等更先进的分类器因其更强的表达能力被广泛使用,但超盒分类器在以下场景中仍具有独特价值:

  • 教学与入门:原理简单清晰,非常适合作为理解机器学习分类思想的入门工具
  • 低维简单分类:特征维度较低、类别边界与坐标轴平行时,能以极低计算成本完成任务
  • 快速原型开发:项目初期需要快速验证分类方案时,可作为快速的 baseline 方法
  • 多通道图像分割:通过 learn_ndim_box / class_ndim_box 直接对多通道图像进行像素级分类
  • 资源受限环境:嵌入式设备或内存有限的场景

1.3 本文的学习目标

   通过阅读本文,你将系统掌握:

  1. 超盒分类器的核心原理与数学基础
  2. Halcon 中所有超盒分类相关算子的功能与参数
  3. 超盒分类器的完整使用流程
  4. 多种场景下的代码示例(HDevelop + C++)
  5. 超盒分类器与其他分类器的对比与选择策略
  6. 实际工程应用中的注意事项与最佳实践

2.分类理论基础

2.1 特征空间与特征向量

   分类器不会直接处理原始图像像素,而是基于从图像中提取的数值特征进行判断。常见的特征包括:

特征类型 示例 Halcon 算子
几何特征 面积、周长、圆度、紧密度 area_center, circularity, compactness
形状特征 不变矩、轮廓复杂度 moments_region_central_invar
灰度特征 平均灰度、灰度方差 intensity, gray_histo
纹理特征 能量、相关性、均匀性、对比度 cooc_feature_image
颜色特征 RGB 各通道均值、标准差 decompose3 + intensity

   将一个目标的所有特征值按固定顺序排列,就形成了该目标的 特征向量(Feature Vector)

f ⃗ = [ f 1 , f 2 , . . . , f n ] \vec{f} = [f_1, f_2, ..., f_n] f =[f1,f2,...,fn]

   将各特征分别作为坐标轴,就构建了一个 n n n维的 特征空间(Feature Space)。每个目标在特征空间中对应一个点,相同类别的目标因为特征相似而聚集在一起,形成"簇(Cluster)"。

        圆度(Circularity)|     ● ● ●     (类别 A)
            |   ●  ●  ●
            |  ●    ●
            | ●        ● ●
            |●   ● ●
            |          ■ ■ ■ (类别 B)
            |       ■   ■
            +------------------------→ 面积(Area)

2.2 分类的基本流程

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  训练阶段    │ ──→ │  模型保存    │ ──→ │  分类阶段    │
│             │     │             │     │             │
│ 提取特征     │     │ write_class │     │ 提取特征     │
│ learn_class  │     │    _box     │     │ enquire_class│
│    _box     │     │             │     │    _box     │
└─────────────┘     └─────────────┘     └─────────────┘

3.超盒分类器的数学原理

3.1 超盒(Hyperbox)的定义

   超盒(Hyperbox) 是超立方体在任意维度上的推广:

维度 几何形状 示例
1 维 线段 [ l 1 , u 1 ] [l_1, u_1] [l1,u1]
2 维 矩形 [ l 1 , u 1 ] × [ l 2 , u 2 ] [l_1, u_1] \times [l_2, u_2] [l1,u1]×[l2,u2]
3 维 长方体 [ l 1 , u 1 ] × [ l 2 , u 2 ] × [ l 3 , u 3 ] [l_1, u_1] \times [l_2, u_2] \times [l_3, u_3] [l1,u1]×[l2,u2]×[l3,u3]
n n n 超矩形 ∏ i = 1 n [ l i , u i ] \prod_{i=1}^{n}[l_i, u_i] i=1n[li,ui]

   形式化定义:在 n n n 维特征空间中,一个超盒 B B B 由两个 n n n 维向量定义:

B = { x ∈ R n : l i ≤ x i ≤ u i , i = 1 , . . . , n } B = \{x \in \mathbb{R}^n : l_i \leq x_i \leq u_i, \quad i = 1,...,n\} B={xRn:lixiui,i=1,...,n}

   其中:

  • L ⃗ = [ l 1 , l 2 , . . . , l n ] \vec{L} = [l_1, l_2, ..., l_n] L =[l1,l2,...,ln]下界向量
  • U ⃗ = [ u 1 , u 2 , . . . , u n ] \vec{U} = [u_1, u_2, ..., u_n] U =[u1,u2,...,un]上界向量
  • l i ≤ u i l_i \leq u_i liui 对所有 i i i 成立

3.2 超盒分类器的决策规则

   对于 C C C个类别,每个类别由一组超盒 { B i 1 , B i 2 , . . . , B i m i } \{B_{i1}, B_{i2}, ..., B_{im_i}\} {Bi1,Bi2,...,Bimi} 表示,其中 m i m_i mi 是第 i i i 类的超盒数量。

分类决策函数:

g i ( x ) = max ⁡ j = 1 , . . . , m i I ( x ∈ B i j ) g_i(x) = \max_{j=1,...,m_i} I(x \in B_{ij}) gi(x)=j=1,...,mimaxI(xBij)

   其中 I ( ⋅ ) I(\cdot) I() 是指示函数,当 x x x 在超盒 B i j B_{ij} Bij 内时为 1,否则为 0。

分类过程:

  1. 检查待分类特征向量是否落在各类别的超盒区域内
  2. 若落在某类别的超盒内,则归入该类别
  3. 若落在多个类别的重叠区域,需定义冲突解决策略
  4. 若不落在任何类别的超盒内,可归入"拒绝类"

3.3 超盒构建算法

   超盒分类器的训练过程本质上是确定每个类别超盒边界的过程:

  1. 初始化:为每个训练样本创建一个最小超盒
  2. 合并:合并同一类别中重叠的超盒
  3. 扩展:扩展超盒以覆盖更多同类样本
  4. 修剪:移除包含过多异类样本的超盒

3.4 决策边界的数学表达

   超盒分类器的决策边界是分段线性的,由多个超平面组成:

⋃ i = 1 m ( ⋂ j = 1 n { x : l i j ≤ x j ≤ u i j } ) \bigcup_{i=1}^{m} \left(\bigcap_{j=1}^{n} \{x: l_{ij} \leq x_j \leq u_{ij}\}\right) i=1m(j=1n{x:lijxjuij})

   其中 m m m是超盒数量, n n n 是特征维度。在二维空间中,决策边界是轴对齐的矩形边界

3.5 泛化能力分析

   超盒分类器的 VC 维 2 n 2n 2n n n n 为特征维度),这意味着:

  • 需要至少 O ( n ) O(n) O(n) 个样本才能保证良好的泛化性能
  • 对高维特征容易过拟合
  • 建议特征维度不超过 20 维

3.6 优势与局限

维度 优势 局限
原理 简单直观,易于理解 只能处理轴平行边界
速度 分类仅需不等式比较, O ( n ) O(n) O(n) 复杂度 对复杂分布表达能力有限
内存 仅存储各维边界值,占用极低 特征维度高时边界粗糙
可解释性 可直接观察各类别特征范围 无法捕捉倾斜决策边界
学习 支持增量学习 对特征尺度敏感,通常需归一化

4.Euclidean距离分类器原理

   Euclidean(欧氏距离)分类器是另一种基础分类方法,常与超盒分类器配合使用。

4.1 欧氏距离的数学定义

   对于两个 n n n 维特征向量 x = ( x 1 , x 2 , . . . , x n ) x = (x_1, x_2, ..., x_n) x=(x1,x2,...,xn) y = ( y 1 , y 2 , . . . , y n ) y = (y_1, y_2, ..., y_n) y=(y1,y2,...,yn)

d ( x , y ) = ∑ i = 1 n ( x i − y i ) 2 d(x,y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2} d(x,y)=i=1n(xiyi)2

4.2 最小距离分类器决策规则

   给定 C C C个类别,每个类别的中心(均值)为 μ 1 , μ 2 , . . . , μ C \mu_1, \mu_2, ..., \mu_C μ1,μ2,...,μC,分类决策函数为:

类别 = arg ⁡ max ⁡ i = 1 , . . . , C ( − d ( x , μ i ) ) \text{类别} = \arg\max_{i=1,...,C} \left(-d(x, \mu_i)\right) 类别=argi=1,...,Cmax(d(x,μi))

   即将样本分到距离最近的类别中心所代表的类别

4.3 决策边界

   对于两类问题,决策边界是连接两个类别中心的垂直平分超平面

w T x + b = 0 w^Tx + b = 0 wTx+b=0

   其中 w = μ 1 − μ 2 w = \mu_1 - \mu_2 w=μ1μ2 b = − 1 2 ( μ 1 T μ 1 − μ 2 T μ 2 ) b = -\frac{1}{2}(\mu_1^T\mu_1 - \mu_2^T\mu_2) b=21(μ1Tμ1μ2Tμ2)

4.4 距离度量族

   欧氏距离是闵可夫斯基距离的特例( p = 2 p=2 p=2):

d p ( x , y ) = ( ∑ i = 1 n ∣ x i − y i ∣ p ) 1 / p d_p(x,y) = \left(\sum_{i=1}^{n}|x_i - y_i|^p\right)^{1/p} dp(x,y)=(i=1nxiyip)1/p

p p p 名称 特点
p = 1 p=1 p=1 曼哈顿距离 对异常值鲁棒
p = 2 p=2 p=2 欧氏距离 最常用,几何意义直观
p → ∞ p \to \infty p 切比雪夫距离 取最大差值

4.5 与超盒分类器的互补关系

特性 Euclidean分类器 Hyperbox分类器
边界形状 垂直平分超平面(倾斜) 轴平行矩形
表达能力 球形分布 轴对齐分布
分类依据 到类别中心的距离 是否在超盒范围内
拒绝类支持 需额外实现 原生支持
适用场景 类别分布近似球形 特征边界清晰、轴对齐

5.Halcon 超盒分类算子详解

5.1 算子速查表

序号 算子名称 功能类别 说明
1 create_class_box 创建管理 创建新的超盒分类器
2 close_class_box 创建管理 关闭并释放指定的分类器
3 close_all_class_box 创建管理 关闭所有分类器
4 learn_class_box 训练 添加训练样本并训练分类器
5 learn_sampset_box 训练 使用数据集训练分类器
6 learn_ndim_box 训练 多通道图像训练
7 enquire_class_box 分类 对特征向量进行分类
8 enquire_reject_class_box 分类 带置信度的分类
9 test_sampset_box 分类 数据集批量分类测试
10 class_ndim_box 分类 多通道图像分类分割
11 descript_class_box 辅助操作 获取分类器描述
12 get_class_box_param 辅助操作 获取分类器参数
13 set_class_box_param 辅助操作 设置分类器参数
14 write_class_box 文件 I/O 保存分类器到文件
15 read_class_box 文件 I/O 从文件加载分类器
16 read_sampset 文件 I/O 读取训练数据集
17 clear_sampset 文件 I/O 释放数据集内存

5.2 创建与管理类算子

5.2.1 create_class_box — 创建超盒分类器

create_class_box( : : : ClassifHandle)

   功能:创建一个新的超盒分类器实例,返回唯一句柄。

参数 类型 方向 说明
ClassifHandle handle output 分类器句柄,用于后续操作
* 创建一个新的超盒分类器
create_class_box(ClassifHandle)

   注意:每个 create_class_box 调用创建独立实例;不再需要时应调用 close_class_box 释放资源。

5.2.2 close_class_box — 关闭分类器

close_class_box( : : ClassifHandle : )

   关闭并释放指定分类器占用的内存资源。

5.2.3 close_all_class_box—关闭所有分类器

close_all_class_box( : : : )

   批量关闭所有已创建但未关闭的分类器。

5.3 训练类算子

5.3.1 learn_class_box — 训练分类器

learn_class_box( : : Features, Class : ClassifHandle)

   功能:将一组特征向量及类别标签添加到分类器进行训练。分类器根据新样本更新各类别的超盒边界。

参数 类型 方向 说明
Features tuple input 特征向量(一维数组)
Class integer input 类别标签(从 0 开始)
ClassifHandle handle input 分类器句柄
* 为类别 0 添加训练样本
Features := [100.5, 0.85, 12.3]
learn_class_box(Features, 0, ClassifHandle)

* 为类别 1 添加训练样本
Features := [50.2, 0.65, 8.7]
learn_class_box(Features, 1, ClassifHandle)

5.3.2 learn_sampset_box — 使用数据集训练

learn_sampset_box( : : SAMPSET, MaxIterations : ClassifHandle, Error, Loop)
参数 类型 方向 说明
SAMPSET object input 训练数据集对象
MaxIterations integer input 最大迭代次数
ClassifHandle handle input 分类器句柄
Error integer output 训练误差
Loop integer output 实际迭代次数

5.3.3 learn_ndim_box — 多通道图像训练

learn_ndim_box( : : Foreground, Background, MultiChannelImage : ClassifHandle)

   功能:专门用于多通道图像的分类训练。直接利用前景/背景区域的像素值作为特征,自动学习分类边界。

参数 类型 方向 说明
Foreground region input 前景训练区域
Background region input 背景训练区域
MultiChannelImage image input 多通道输入图像(如 RGB)
ClassifHandle handle input 分类器句柄
create_class_box(ClassifHandle)
read_image(Image, 'ic')

* 绘制前景训练区域
dev_set_color('green')
gen_rectangle1(Foreground, 360, 198, 369, 226)

* 绘制背景训练区域
dev_set_color('red')
gen_rectangle1(Background, 84, 336, 337, 504)

* 训练分类器
learn_ndim_box(Foreground, Background, Image, ClassifHandle)

5.4 分类与查询类算子

5.4.1 enquire_class_box—分类特征向量

enquire_class_box( : : Features : Class)
参数 类型 方向 说明
Features tuple input 待分类的特征向量
Class integer output 分类结果(类别标签)
TestFeatures := [85.3, 0.78, 10.5]
enquire_class_box(TestFeatures, ClassResult)

5.4.2 enquire_reject_class_box—带拒绝的分类

enquire_reject_class_box( : : Features : Class, Confidence)

   额外返回置信度值(0.0 ~ 1.0),用于判断分类可靠性。

参数 类型 方向 说明
Features tuple input 待分类的特征向量
Class integer output 分类结果
Confidence real output 分类置信度

5.4.3 class_ndim_box—多通道图像分类

class_ndim_box( : : MultiChannelImage : Regions : ClassifHandle)

功能:对多通道图像进行像素级分类,输出分类结果区域。是 learn_ndim_box 的配套分类算子。

参数 类型 方向 说明
MultiChannelImage image input 输入的多通道图像
Regions region output 分类结果区域
ClassifHandle handle input 分类器句柄
class_ndim_box(Image, Regions, ClassifHandle)
dev_display(Image)
dev_set_color('green')
dev_display(Regions)

5.4.3 test_sampset_box—批量测试

test_sampset_box( : : SAMPSET : ClassifHandle, Error, NumCorrect)

5.5 辅助与 I/O 类算子

算子 功能 常用场景
descript_class_box 获取分类器描述信息 调试、查看类别数量
get_class_box_param 获取分类器参数 查询超盒边界等
set_class_box_param 设置分类器参数 调整分类行为
write_class_box 保存分类器到文件(.hcb 模型持久化
read_class_box 从文件加载分类器 模型复用
read_sampset 读取训练数据集 批量训练
clear_sampset 释放数据集内存 资源清理
* 保存分类器
write_class_box(ClassifHandle, 'trained_classifier.hcb')

* 加载分类器
read_class_box('trained_classifier.hcb', ClassifHandle)

6.代码示例详解

6.1 基础示例:最简单的超盒分类

* ==========================================
* Halcon 超盒分类器基础示例
* ==========================================

dev_close_window()
dev_open_window(0, 0, 512, 512, 'black', WindowHandle)
dev_set_color('white')

* 第一步:创建分类器
create_class_box(ClassifHandle)

* 第二步:添加训练样本
* 类别 0 的训练样本
learn_class_box([10.0, 20.0, 30.0], 0, ClassifHandle)
learn_class_box([12.0, 18.0, 32.0], 0, ClassifHandle)
learn_class_box([11.0, 22.0, 28.0], 0, ClassifHandle)

* 类别 1 的训练样本
learn_class_box([50.0, 60.0, 70.0], 1, ClassifHandle)
learn_class_box([52.0, 58.0, 72.0], 1, ClassifHandle)
learn_class_box([48.0, 62.0, 68.0], 1, ClassifHandle)

* 类别 2 的训练样本
learn_class_box([90.0, 80.0, 40.0], 2, ClassifHandle)
learn_class_box([88.0, 82.0, 42.0], 2, ClassifHandle)

* 第三步:显示分类器信息
descript_class_box(ClassifHandle, Info)
disp_message(WindowHandle, 'Classifier created with 3 classes', \
             'window', 20, 20, 'black', 'true')

* 第四步:对测试样本进行分类
enquire_class_box([11.5, 20.0, 30.5], ClassResult)
disp_message(WindowHandle, 'Test1 -> Class ' + ClassResult, \
             'window', 60, 20, 'black', 'true')

enquire_class_box([51.0, 60.0, 71.0], ClassResult)
disp_message(WindowHandle, 'Test2 -> Class ' + ClassResult, \
             'window', 100, 20, 'black', 'true')

enquire_class_box([89.0, 81.0, 41.0], ClassResult)
disp_message(WindowHandle, 'Test3 -> Class ' + ClassResult, \
             'window', 140, 20, 'black', 'true')

* 第五步:关闭分类器
close_class_box(ClassifHandle)

代码解析

  1. create_class_box 创建分类器实例,获取句柄
  2. learn_class_box 逐个添加训练样本(特征向量 + 类别标签)
  3. enquire_class_box 对新特征向量进行分类
  4. close_class_box 释放资源

6.2 官方示例:多通道图像分类(class_ndim_box)

* ==========================================
* HALCON 官方示例:class_ndim_box.hdev
* 基于多通道图像的分类
* ==========================================

* 读取示例图像(多通道电路板图像)
read_image(Image, 'ic')
get_image_size(Image, Width, Height)

dev_close_window()
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
dev_display(Image)

* 绘制前景训练区域(绿色)
dev_set_color('green')
gen_rectangle1(Foreground, 360, 198, 369, 226)

* 绘制背景训练区域(红色)
dev_set_color('red')
gen_rectangle1(Background, 84, 336, 337, 504)

* 创建超盒分类器
create_class_box(ClassifHandle)

* 使用前景和背景区域训练分类器
* learn_ndim_box 自动提取指定区域的像素值作为特征
learn_ndim_box(Foreground, Background, Image, ClassifHandle)

* 对整个图像进行分类
class_ndim_box(Image, Regions, ClassifHandle)

* 显示分类结果
dev_display(Image)
dev_set_color('blue')
dev_display(Regions)

* 统计分类区域数量
count_obj(Regions, NumberOfRegions)
disp_message(WindowHandle, 'Classified regions: ' + NumberOfRegions, \
             'window', 12, 12, 'black', 'true')

* 关闭分类器
close_class_box(ClassifHandle)

   代码解析

  1. 读取多通道图像 'ic'
  2. 用绿色矩形框选择前景区域(目标对象)
  3. 用红色矩形框选择背景区域
  4. learn_ndim_box 自动提取区域像素值进行训练
  5. class_ndim_box 对整幅图像进行像素级分类
  6. 显示分类后的区域

6.3 带置信度的分类示例

* ==========================================
* 带置信度的超盒分类
* ==========================================

dev_close_window()
dev_open_window(0, 0, 768, 512, 'black', WindowHandle)

read_image(Image, 'fruits')
dev_display(Image)

create_class_box(ClassifHandle)

* 为不同水果区域训练
* 类别 0:苹果区域
dev_set_color('red')
gen_rectangle1(Class0Region, 100, 150, 200, 280)
intensity(Class0Region, Image, MeanGray0, Deviation0)
area_center(Class0Region, Area0, Row0, Column0)
learn_class_box([Area0, MeanGray0, Deviation0], 0, ClassifHandle)

* 类别 1:橙子区域
dev_set_color('orange')
gen_rectangle1(Class1Region, 250, 350, 400, 480)
intensity(Class1Region, Image, MeanGray1, Deviation1)
area_center(Class1Region, Area1, Row1, Column1)
learn_class_box([Area1, MeanGray1, Deviation1], 1, ClassifHandle)

* 使用带拒绝的分类
TestFeatures := [Area0 * 0.9, MeanGray0, Deviation0]
enquire_reject_class_box(TestFeatures, ClassResult, Confidence)

disp_message(WindowHandle, 'Class: ' + ClassResult + \
             '  Confidence: ' + Confidence$'.2f', \
             'window', 20, 20, 'white', 'true')

* 保存分类器
write_class_box(ClassifHandle, 'fruit_classifier.hcb')
close_class_box(ClassifHandle)

6.4 完整实战:金属零件分类

* ==========================================
* 实战项目:金属零件分类
* 螺母(Nut) / 垫圈(Washer) / 挡圈(Retainer)
* ==========================================

dev_close_window()
dev_update_off()
dev_open_window(0, 0, 800, 600, 'black', WindowHandle)

create_class_box(ClassifierHandle)

* ---- 螺母训练样本(类别 0)----
* 特征:[面积, 圆度, 紧密度, 灰度均值, 灰度方差, 长宽比]
learn_class_box([5000, 0.95, 0.03, 200, 0.01, 1.2], 0, ClassifierHandle)
learn_class_box([5200, 0.94, 0.03, 210, 0.01, 1.3], 0, ClassifierHandle)
learn_class_box([4800, 0.96, 0.03, 195, 0.01, 1.1], 0, ClassifierHandle)

* ---- 垫圈训练样本(类别 1)----
learn_class_box([3000, 0.85, 0.05, 180, 0.02, 0.8], 1, ClassifierHandle)
learn_class_box([3200, 0.83, 0.05, 175, 0.02, 0.9], 1, ClassifierHandle)
learn_class_box([2800, 0.87, 0.05, 185, 0.02, 0.7], 1, ClassifierHandle)

* ---- 挡圈训练样本(类别 2)----
learn_class_box([1500, 0.75, 0.08, 120, 0.03, 0.4], 2, ClassifierHandle)
learn_class_box([1600, 0.73, 0.08, 125, 0.03, 0.5], 2, ClassifierHandle)
learn_class_box([1400, 0.77, 0.08, 115, 0.03, 0.3], 2, ClassifierHandle)

* 保存分类器
write_class_box(ClassifierHandle, 'metal_parts_classifier.hcb')

* 测试分类
ClassNames := ['Nut', 'Washer', 'Retainer']

* 测试样本 1:应该是螺母
enquire_reject_class_box([5100, 0.95, 0.03, 205, 0.01, 1.2], Class1, Conf1)
disp_message(WindowHandle, 'Test1: ' + ClassNames[Class1] + \
             ' (Conf: ' + Conf1$'.2f' + ')', 'window', 20, 20, 'black', 'true')

* 测试样本 2:应该是垫圈
enquire_reject_class_box([2900, 0.86, 0.05, 182, 0.02, 0.8], Class2, Conf2)
disp_message(WindowHandle, 'Test2: ' + ClassNames[Class2] + \
             ' (Conf: ' + Conf2$'.2f' + ')', 'window', 50, 20, 'black', 'true')

* 测试样本 3:应该是挡圈
enquire_reject_class_box([1550, 0.74, 0.08, 122, 0.03, 0.45], Class3, Conf3)
disp_message(WindowHandle, 'Test3: ' + ClassNames[Class3] + \
             ' (Conf: ' + Conf3$'.2f' + ')', 'window', 80, 20, 'black', 'true')

close_class_box(ClassifierHandle)

6.5 多类别超盒分类(高级)

* ==========================================
* 多类别 Hyperbox 分类器
* 支持多个前景类别和背景类别
* ==========================================

proc train_multi_class_hyperbox(Image, RegionsList, LabelsList)
    * Image: 多通道图像
    * RegionsList: 区域列表
    * LabelsList: 对应类别标签(0=背景,>0=前景类别)

    create_class_box(ClassifierHandle)

    * 获取所有唯一标签(排除背景标签 0)
    UniqueLabels := []
    for i := 0 to |LabelsList| - 1 by 1
        if (LabelsList[i] > 0)
            if (find(UniqueLabels, LabelsList[i]) == -1)
                UniqueLabels := [UniqueLabels, LabelsList[i]]
            endif
        endif
    endfor

    * 为每个类别训练
    for label_idx := 0 to |UniqueLabels| - 1 by 1
        CurrentLabel := UniqueLabels[label_idx]

        * 收集当前类别和背景区域
        CurrentRegions := []
        BackgroundRegions := []

        for i := 0 to |LabelsList| - 1 by 1
            if (LabelsList[i] == CurrentLabel)
                CurrentRegions := [CurrentRegions, RegionsList[i]]
            elseif (LabelsList[i] == 0)
                BackgroundRegions := [BackgroundRegions, RegionsList[i]]
            endif
        endfor

        * 合并同类区域并训练
        if (|CurrentRegions| > 0)
            union_obj(CurrentRegions, UnionRegion)
            if (|BackgroundRegions| > 0)
                union_obj(BackgroundRegions, BackgroundUnion)
                learn_ndim_box(UnionRegion, BackgroundUnion, Image, ClassifierHandle)
            endif
        endif
    endfor

    return(ClassifierHandle)
endproc

* 使用示例
read_image(MultiImage, 'multi_class_image')

gen_rectangle1(Region1, 100, 100, 150, 150)    * 类别 1
gen_rectangle1(Region2, 200, 200, 250, 250)    * 类别 2
gen_rectangle1(Region3, 300, 300, 350, 350)    * 类别 3
gen_rectangle1(Background1, 50, 50, 80, 80)    * 背景

RegionsList := [Region1, Region2, Region3, Background1]
LabelsList  := [1, 2, 3, 0]

* 训练多类别分类器
MultiClassifier := train_multi_class_hyperbox(MultiImage, RegionsList, LabelsList)

* 分类新图像
read_image(TestImage, 'test_multi_class')
class_ndim_box(TestImage, ResultRegions, MultiClassifier)

* 结果分析
connection(ResultRegions, ConnectedRegions)
count_obj(ConnectedRegions, NumObjects)

6.6 C++ 代码示例

#include "HalconCpp.h"
#include <iostream>
#include <vector>

using namespace HalconCpp;
using namespace std;

struct TrainingSample {
    vector<double> features;
    int classLabel;
};

int main() {
    try {
        // 创建超盒分类器
        HClassBox classifier;
        classifier.CreateClassBox();
        cout << "分类器创建成功" << endl;

        // 准备训练样本
        vector<TrainingSample> trainingData = {
            {{10.0, 20.0, 30.0}, 0},
            {{12.0, 18.0, 32.0}, 0},
            {{11.0, 22.0, 28.0}, 0},
            {{50.0, 60.0, 70.0}, 1},
            {{52.0, 58.0, 72.0}, 1},
            {{48.0, 62.0, 68.0}, 1},
            {{90.0, 80.0, 40.0}, 2},
            {{88.0, 82.0, 42.0}, 2}
        };

        // 添加训练样本
        for (const auto& sample : trainingData) {
            HTuple features;
            for (double f : sample.features) {
                features.Append(f);
            }
            classifier.LearnClassBox(features, sample.classLabel);
        }
        cout << "训练完成,共 " << trainingData.size() << " 个样本" << endl;

        // 保存分类器
        classifier.WriteClassBox("trained_classifier.hcb");

        // 分类测试
        vector<HTuple> testFeatures = {
            HTuple(11.5).Append(20.0).Append(30.5),
            HTuple(51.0).Append(60.0).Append(71.0),
            HTuple(89.0).Append(81.0).Append(41.0)
        };

        cout << "\n分类测试结果:" << endl;
        for (size_t i = 0; i < testFeatures.size(); ++i) {
            HTuple classResult, confidence;
            classifier.EnquireRejectClassBox(
                testFeatures[i], &classResult, &confidence);
            cout << "  样本 " << (i+1) << " -> 类别="
                 << classResult[0].I()
                 << ", 置信度=" << confidence[0].D() << endl;
        }

        // 关闭分类器
        classifier.CloseClassBox();

        // 演示加载已保存的分类器
        HClassBox loadedClassifier;
        loadedClassifier.ReadClassBox("trained_classifier.hcb");
        cout << "\n分类器加载成功" << endl;

        loadedClassifier.CloseClassBox();
        return 0;

    } catch (HException& except) {
        cerr << "Halcon异常: " << except.ErrorMessage() << endl;
        return 1;
    }
}

6.7 自定义Euclidean距离分类器实现

* ==========================================
* 自定义 Euclidean 距离分类器
* ==========================================

* 训练阶段:计算各类别中心
proc train_euclidean_classifier(FeaturesList, LabelsList)
    UniqueLabels := []
    for i := 0 to |LabelsList| - 1 by 1
        if (find(UniqueLabels, LabelsList[i]) == -1)
            UniqueLabels := [UniqueLabels, LabelsList[i]]
        endif
    endfor

    ClassCenters := []
    for i := 0 to |UniqueLabels| - 1 by 1
        CurrentLabel := UniqueLabels[i]
        ClassSamples := []
        for j := 0 to |LabelsList| - 1 by 1
            if (LabelsList[j] == CurrentLabel)
                ClassSamples := [ClassSamples, FeaturesList[j]]
            endif
        endfor

        NumSamples := |ClassSamples|
        if (NumSamples > 0)
            Center := []
            NumFeatures := |ClassSamples[0]|
            for k := 0 to NumFeatures - 1 by 1
                SumVal := 0.0
                for m := 0 to NumSamples - 1 by 1
                    SumVal := SumVal + ClassSamples[m][k]
                endfor
                Center := [Center, SumVal / NumSamples]
            endfor
            ClassCenters := [ClassCenters, Center]
        endif
    endfor

    return([UniqueLabels, ClassCenters])
endproc

* 分类阶段:基于欧氏距离
proc classify_euclidean(Features, UniqueLabels, ClassCenters)
    MinDistance := 1e100
    PredictedLabel := -1

    for i := 0 to |UniqueLabels| - 1 by 1
        Center := ClassCenters[i]
        Distance := 0.0
        for j := 0 to |Features| - 1 by 1
            Diff := Features[j] - Center[j]
            Distance := Distance + Diff * Diff
        endfor
        Distance := sqrt(Distance)

        if (Distance < MinDistance)
            MinDistance := Distance
            PredictedLabel := UniqueLabels[i]
        endif
    endfor

    return([PredictedLabel, MinDistance])
endproc

6.8 混合分类器架构(Hyperbox+Euclidean)

* ==========================================
* 混合 Euclidean-Hyperbox 分类器
* 两级分类:快速筛选 + 精细分类
* ==========================================

proc hybrid_classifier(Features, HyperboxHandle, EuclideanCenters, EuclideanLabels, \
                       ThresholdHigh, ThresholdLow)
    * 第一阶段:Hyperbox 快速筛选
    enquire_reject_class_box(Features, HyperboxClass, HyperboxConf)

    if (HyperboxConf > ThresholdHigh)
        * 高置信度,直接返回
        return([HyperboxClass, HyperboxConf])

    elseif (HyperboxConf < ThresholdLow)
        * 低置信度,使用 Euclidean 分类器
        [EuclideanClass, EuclideanDist] := classify_euclidean( \
            Features, EuclideanLabels, EuclideanCenters)
        EuclideanConf := 1.0 / (1.0 + EuclideanDist)
        return([EuclideanClass, EuclideanConf])

    else
        * 中等置信度,融合两个结果
        [EuclideanClass, EuclideanDist] := classify_euclidean( \
            Features, EuclideanLabels, EuclideanCenters)
        EuclideanConf := 1.0 / (1.0 + EuclideanDist)

        if (HyperboxClass == EuclideanClass)
            * 结果一致,取平均置信度
            FinalConf := (HyperboxConf + EuclideanConf) / 2
            return([HyperboxClass, FinalConf])
        else
            * 结果不一致,选择置信度高的
            if (HyperboxConf > EuclideanConf)
                return([HyperboxClass, HyperboxConf])
            else
                return([EuclideanClass, EuclideanConf])
            endif
        endif
    endif
endproc

7.超盒分类器与其他分类器的对比

7.1 Halcon 分类器体系总览

┌──────────────────────────────────────────────────────────────┐
│                     Halcon 分类器体系                         │
├──────────────────────────────────────────────────────────────┤
│                                                               │
│  ┌─────────────────┐    ┌─────────────────┐                 │
│  │  超盒分类器      │    │  GMM 分类器      │                 │
│  │  (Hyperbox)     │    │ (高斯混合模型)   │                 │
│  │  简单/快速/直观  │    │ 概率/异常检测    │                 │
│  └─────────────────┘    └─────────────────┘                 │
│                                                               │
│  ┌─────────────────┐    ┌─────────────────┐                 │
│  │  MLP 分类器      │    │  SVM 分类器      │                 │
│  │ (多层感知器)     │    │ (支持向量机)     │                 │
│  │ 深层模式识别     │    │ 泛化能力强       │                 │
│  └─────────────────┘    └─────────────────┘                 │
│                                                               │
│  ┌─────────────────┐    ┌─────────────────┐                 │
│  │  KNN 分类器      │    │  深度学习分类器  │                 │
│  │ (K 最近邻)       │    │ (DL Classifier) │                 │
│  │ 增量学习         │    │ 最强表达能力     │                 │
│  └─────────────────┘    └─────────────────┘                 │
│                                                               │
└──────────────────────────────────────────────────────────────┘

7.2 分类器特性对比表

特性 Hyperbox GMM MLP SVM KNN 深度学习
训练速度 ⚡ 快 ⚡ 快 🐢 慢 ⚡ 中等 ⚡ 极快 🐢 慢
分类速度 ⚡⚡ 极快 ⚡ 快 ⚡ 快 ⚡ 中等 🐢 慢 ⚡ 中等
内存占用 📦 低 📦 中 📦 低 📦 高 📦 高 📦 高
表达能力 ⭐ 有限 ⭐⭐ 中 ⭐⭐⭐ 强 ⭐⭐⭐ 强 ⭐⭐⭐ 强 ⭐⭐⭐⭐ 最强
拒绝类支持 ✅ 原生 ✅ 支持 ⚠️ 需额外训练 ❌ 不支持 ✅ 支持 ✅ 支持
可解释性 ⭐⭐⭐ 高 ⭐⭐ 中 ⭐ 低 ⭐ 低 ⭐⭐ 中 ⭐ 低
增量学习 ✅ 支持 ❌ 不支持 ❌ 不支持 ❌ 不支持 ✅ 支持 ⚠️ 有限
适用维度 低维 (<20) 中维 高维 高维 中维 极高维

7.3 分类器选择决策树

开始
  │
  ▼
特征维度是否低于 10 维?
  │
  ├── 是 ──► 类别边界是否与坐标轴平行?
  │              │
  │              ├── 是 ──► ✅ 超盒分类器
  │              │
  │              └── 否 ──► 是否需要处理多通道图像?
  │                            │
  │                            ├── 是 ──► ✅ learn_ndim_box
  │                            │
  │                            └── 否 ──► 进入下一步
  │
  └── 否 ──► 进入下一步
  │
  ▼
实时性要求是否极高?
  │
  ├── 是 ──► ✅ GMM 或 MLP
  │
  └── 否 ──► 进入下一步
  │
  ▼
是否有大量训练样本(>1000)?
  │
  ├── 是 ──► ✅ SVM 或 深度学习
  │
  └── 否 ──► 进入下一步
  │
  ▼
是否需要支持拒绝类?
  │
  ├── 是 ──► ✅ GMM 或 KNN
  │
  └── 否 ──► ✅ MLP 或 SVM

7.4 算法复杂度分析

分类器 训练复杂度 分类复杂度 空间复杂度
Euclidean O ( n ⋅ m ) O(n \cdot m) O(nm) O ( C ⋅ m ) O(C \cdot m) O(Cm) O ( C × m ) O(C \times m) O(C×m)
Hyperbox O ( n ⋅ m ⋅ B ) O(n \cdot m \cdot B) O(nmB) O ( B ⋅ m ) O(B \cdot m) O(Bm) O ( B × 2 m ) O(B \times 2m) O(B×2m)
GMM O ( n ⋅ m ⋅ K ⋅ T ) O(n \cdot m \cdot K \cdot T) O(nmKT) O ( K ⋅ m ) O(K \cdot m) O(Km) O ( K × m 2 ) O(K \times m^2) O(K×m2)
MLP O ( n ⋅ E ⋅ W ) O(n \cdot E \cdot W) O(nEW) O ( W ) O(W) O(W) O ( W ) O(W) O(W)

   其中 n n n = 样本数, m m m = 特征维度, C C C = 类别数, B B B = 超盒数量, K K K = 高斯分量数, T T T = 迭代次数, E E E = 训练轮数, W W W = 网络权重数

8.性能优化与高级技巧

8.1 特征归一化

   强烈建议在使用超盒分类器前对特征进行归一化:

* Min-Max 归一化到 [0, 1]
NormalizedArea := (Area - MinArea) / (MaxArea - MinArea)
NormalizedCirc := (Circularity - MinCirc) / (MaxCirc - MinCirc)
NormalizedComp := (Compactness - MinComp) / (MaxComp - MinComp)

NormalizedFeatures := [NormalizedArea, NormalizedCirc, NormalizedComp]

   为什么需要归一化?

  • 面积可能在 1000~10000 范围,而圆度在 0~1 范围
  • 不归一化会导致面积特征主导距离计算
  • 归一化后各特征对分类的贡献均衡

8.2 特征选择原则

原则 说明 示例
归一化 所有特征到相同范围 Min-Max 或 Z-score
互补性 从不同角度描述目标 面积 + 圆度 + 灰度
低冗余 避免高度相关的特征 避免同时使用面积和周长
区分性 特征在类别间差异大 选择类别间方差大的特征

8.3 重叠区域处理策略

* 方案 1:使用置信度阈值
enquire_reject_class_box(TestFeatures, Class, Confidence)
if (Confidence < 0.5)
    Class := -1  * 标记为"未知"
endif

* 方案 2:多次训练调整边界
* 通过调整训练样本的数量和分布来优化边界

8.4 自适应超盒扩展

proc adaptive_hyperbox_expansion(Classifier, Image, ExpansionFactor)
    * 获取当前超盒边界
    get_class_box_param(Classifier, 'box_bounds', Bounds)

    NumBoxes := |Bounds|
    for i := 0 to NumBoxes - 1 by 1
        * 提取当前超盒内的样本
        reduce_domain(Image, BoxRegion, BoxImage)

        * 计算样本分布
        intensity(BoxRegion, BoxImage, Mean, Deviation)

        * 根据分布调整边界
        if (Deviation < Threshold)
            * 样本集中,缩小边界
            NewBounds := shrink_bounds(Bounds[i], ExpansionFactor)
        else
            * 样本分散,保持或轻微扩展
            NewBounds := expand_bounds(Bounds[i], ExpansionFactor * 0.5)
        endif

        set_class_box_param(Classifier, 'box_' + i + '_bounds', NewBounds)
    endfor
endproc

8.5 重叠超盒处理策略

   当多个类别的超盒在特征空间中重叠时,可采用以下策略:

策略 说明 适用场景
优先规则 为重叠区域定义优先级 类别有明确优先级
概率融合 计算属于各超盒的概率,加权平均 需要精细决策
细分处理 将重叠区域细分为新超盒 重叠区域有规律
置信度拒绝 低置信度时标记为拒绝类 宁可漏检不可误检

8.6 特征加权欧氏距离

d w ( x , y ) = ∑ i = 1 n w i ( x i − y i ) 2 d_w(x,y) = \sqrt{\sum_{i=1}^{n} w_i(x_i - y_i)^2} dw(x,y)=i=1nwi(xiyi)2

权重 w i w_i wi 的确定方法:

方法 公式 特点
互信息法 w i = I ( X i ; Y ) w_i = I(X_i; Y) wi=I(Xi;Y) 信息论基础
方差倒数法 w i = 1 / σ i 2 w_i = 1/\sigma_i^2 wi=1/σi2 简单有效
学习权重 通过优化算法学习 最优但计算量大

8.7 性能评估实现

proc evaluate_classifier(Classifier, TestFeatures, TestLabels)
    ConfusionMatrix := []
    NumClasses := |unique(TestLabels)|

    * 初始化混淆矩阵
    for i := 0 to NumClasses - 1 by 1
        Row := []
        for j := 0 to NumClasses - 1 by 1
            Row := [Row, 0]
        endfor
        ConfusionMatrix := [ConfusionMatrix, Row]
    endfor

    * 对每个测试样本分类
    for i := 0 to |TestFeatures| - 1 by 1
        TrueLabel := TestLabels[i]
        enquire_class_box(TestFeatures[i], PredictedLabel)

        * 更新混淆矩阵
        ConfusionMatrix[TrueLabel][PredictedLabel] := \
            ConfusionMatrix[TrueLabel][PredictedLabel] + 1
    endfor

    * 计算准确率
    Correct := 0
    for i := 0 to NumClasses - 1 by 1
        Correct := Correct + ConfusionMatrix[i][i]
    endfor
    Accuracy := Correct / |TestFeatures|

    return([ConfusionMatrix, Accuracy])
endproc

9.实战应用与最佳实践

9.1 完整分类项目模板

* ==========================================
* Halcon 分类项目完整模板
* ==========================================

* ---- 第一部分:数据准备 ----
list_files('training_images', ['files','recursive'], ImageFiles)
tuple_regexp_select(ImageFiles, ['\\.(png|jpg|bmp)$', 'ignore_case'], ImageFiles)

ClassNames := ['Class0', 'Class1', 'Class2']
NumClasses := |ClassNames|

* ---- 第二部分:创建分类器 ----
create_class_box(ClassifierHandle)

* ---- 第三部分:特征提取与训练 ----
for i := 0 to |ImageFiles| - 1 by 1
    read_image(Image, ImageFiles[i])

    * 图像预处理
    binary_threshold(Image, Region, 'max_separability', 'dark', UsedThreshold)
    connection(Region, ConnectedRegions)

    * 遍历所有连通区域
    count_obj(ConnectedRegions, NumRegions)
    for j := 1 to NumRegions by 1
        select_obj(ConnectedRegions, SingleRegion, j)

        * 提取特征
        area_center(SingleRegion, Area, Row, Column)
        circularity(SingleRegion, Circularity)
        compactness(SingleRegion, Compactness)
        intensity(SingleRegion, Image, MeanGray, DeviationGray)

        * 归一化特征
        Features := [Area / 10000, Circularity, Compactness, \
                     MeanGray / 255, DeviationGray / 255]

        * 添加到分类器(需要根据实际场景确定标签)
        * learn_class_box(Features, ClassLabel, ClassifierHandle)
    endfor
endfor

* ---- 第四部分:模型评估 ----
* (使用测试集评估准确率)

* ---- 第五部分:保存模型 ----
write_class_box(ClassifierHandle, 'classifier_model.hcb')

* ---- 第六部分:关闭资源 ----
close_class_box(ClassifierHandle)

9.2 工业视觉检测系统架构

* ==========================================
* 工业零件分类系统(两级架构)
* 第一级:Hyperbox 快速筛选
* 第二级:Euclidean 精细分类
* ==========================================

* 1. 图像采集
grab_image(LiveImage, AcqHandle)
rgb1_to_gray(LiveImage, GrayImage)

* 2. 特征提取
cooc_feature_image(GrayImage, GrayImage, 8, 0, \
                   Energy, Correlation, Homogeneity, Contrast)
circularity(SegmentedRegion, Circularity)
area_center(SegmentedRegion, Area, Row, Column)

decompose3(LiveImage, R, G, B)
intensity(R, R, R_Mean, R_Dev)
intensity(G, G, G_Mean, G_Dev)
intensity(B, B, B_Mean, B_Dev)

* 3. 构建特征向量
Features := [Energy, Correlation, Homogeneity, Contrast, \
             Circularity, Area, R_Mean, G_Mean, B_Mean]

* 4. 两级分类
* 第一级:Hyperbox 快速分类
compose3(FeatureImage1, FeatureImage2, FeatureImage3, MultiChannelFeatures)
class_ndim_box(MultiChannelFeatures, HyperboxResult, HyperboxClassifier)

HyperboxConf := calculate_confidence(HyperboxResult)

if (HyperboxConf > 0.8)
    FinalClass := get_class_from_hyperbox(HyperboxResult)
else
    * 第二级:Euclidean 精细分类
    [EuclideanClass, EuclideanDist] := classify_euclidean( \
        Features, EuclideanCenters, EuclideanLabels)
    EuclideanConf := 1.0 / (1.0 + EuclideanDist)

    * 融合决策
    if (HyperboxConf > EuclideanConf)
        FinalClass := HyperboxClass
    else
        FinalClass := EuclideanClass
    endif
endif

* 5. 输出结果
disp_message(WindowHandle, 'Result: ' + FinalClass, 'window', 20, 20, 'black', 'true')

9.3 推荐的Halcon特征提取算子

* ---- 几何特征 ----
area_center(Region, Area, Row, Column)
circularity(Region, Circularity)
roundness(Region, Distance, Sigma, Roundness, Sides)
compactness(Region, Compactness)
ellipse_axis(Region, Ra, Rb, Phi)

* ---- 矩特征 ----
moments_region_central_invar(Region, PSI1, PSI2, PSI3, PSI4)
moments_region_central(Region, M20, M11, M02, M30, M21, M12, M03)

* ---- 灰度特征 ----
intensity(Regions, Image, Mean, Deviation)
gray_histo(Regions, Image, AbsoluteHisto, RelativeHisto)

* ---- 纹理特征 ----
cooc_feature_image(Image, Image, 8, 0, Energy, Correlation, Homogeneity, Contrast)

10.常见问题与解决方案

Q1:分类结果全是同一个类别

可能原因

  • 训练样本分布不均匀
  • 某些类别的超盒范围过大,包含了其他类别

解决方案

  1. 确保每个类别有足够数量且分布均匀的训练样本
  2. 检查特征归一化是否正确
  3. 使用 enquire_reject_class_box 查看置信度
  4. 增加区分度更高的特征

Q2:边界附近的样本分类不稳定

可能原因:特征向量微小变化导致落在不同类别的超盒边界上

解决方案

  1. 增加边界附近的训练样本密度
  2. 使用 enquire_reject_class_box 的置信度进行二次判断
  3. 考虑使用 GMM 等边界更平滑的分类器

Q3:新增样本导致分类器性能下降

可能原因:新样本改变了各类别的超盒边界

解决方案

  1. 定期使用完整数据集重新训练
  2. 在添加新样本前保存当前分类器
  3. 评估新样本是否为异常值

Q4:多通道图像分类效果不佳

可能原因

  • 前景/背景区域选择不具代表性
  • 图像通道信息不足以区分目标

解决方案

  1. 选择多个有代表性的前景和背景区域
  2. 使用更多通道(如 HSV、Lab 颜色空间)
  3. 结合 learn_ndim_boxlearn_class_box 进行混合训练

Q5:分类器保存后加载失败

可能原因

  • 文件路径错误
  • 文件被损坏
  • Halcon 版本不兼容

解决方案

  1. 使用绝对路径保存和加载
  2. 检查文件权限
  3. 确保保存和加载使用相同版本的 Halcon

11.参考资料

11.1 Halcon 官方资源

11.2 技术博客与教程

11.3 推荐书籍

  • 《Halcon 机器视觉算法原理与实践》
  • 《机器视觉算法与应用》(MVTec 官方推荐)
  • 《数字图像处理》—— 冈萨雷斯著(理论基础)

11.4 进一步学习方向

   掌握超盒分类器后,建议依次学习:

  1. GMM 分类器:最常用的分类器之一,适合工业检测
  2. MLP 神经网络:具有更强的模式表达能力
  3. SVM 支持向量机:理论基础扎实,应用广泛
  4. 深度学习分类:处理复杂图像分类任务的主流方法
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐