首页 > 代码库 > Halcon学习笔记之支持向量机(二)

Halcon学习笔记之支持向量机(二)

例程:classify_halogen_bulbs.hdev

在Halcon中模式匹配最成熟最常用的方式该署支持向量机了,在本例程中展示了使用支持向量机对卤素灯的质量检测方法。通过这个案例,相信大家可以对支持向量机的使用有一个更加清晰的了解。在相当多的检测和识别的应用中,都可以使用相同的方法来解决分类问题。

image

图1. 卤素灯图像

大致原理:

一、准备阶段:描述样本

1. 准备好两组卤素灯图像样本,好坏的各若干张图像;

2. 对样本图像进行分割,获取卤素灯关键部位区域;

3. 选择合适的对图像的描述,作为识别的特征;

一、准备阶段:构造分类器

1. 构造支持向量机:初始化支持向量机分类器所需的特征数量、核函数、类型数等参数;

2. 将样本添加到分类器中;

3. 进行训练,使分类器收敛;

二、识别阶段:分类识别

1. 提取待测目标区域;

2. 提取分类所需的特征:与准备阶段3中进行的操作相同;

3. 代入分类器进行分类。

上述在实际的操作过程中,准备阶段样本描述和构造分类器同时进行,即:将样本图像的特征提取和样本添加到分类器中这两步放在一个循环中完成。整个流程如下图所示:

image

图2. 支持向量机分类识别流程

图中左边部分是准备阶段所做的工作。如图所示,在使用SVM进行分类之前,首先需要构造分类器;构造完分类器之后,样本通过SVM样本描述循环体,被逐个进行特征提取后加入到待训练的SVM分类器中;所有训练样本按照各自的类型添加结束后就进行训练,使SVM收敛。

训练之后的SVM就可以用于分类了,下面就进入识别阶段。如图右侧所示,将待检测的样品图像经过相同的特征提取过程后代入SVM分类器即可得到分类结果。由于SVM本质上是对提取的特征向量的特征空间进行划分来区别特征的类别,因此在识别阶段使用的特征需要和准备阶段的完全相同。这样SVM在对待测样本中提取的特征向量进行划分时才知道它具体落入哪一个类型所在的空间,也就知道该样本的类型了。

对于使用,我们只要知道SVM是一个分类器,可以根据先验知识将特征向量进行分类。如果想深入了解SVM是如何分类的,里面涉及到的原理可以通过下面的链接来查看:

http://files.cnblogs.com/sleepwalker/Ch12_pres%28SVM%29.pdf

几乎所有使用SVM进行分类的流程大框架都遵循上图,在不同的应用中使用SVM的区别就在于图像描述的区别,如:Halcon学习笔记之支持向量机(一)中,样本是每一个像素点,而特征向量是像素点的坐标。在本案例中,样本是两组图像,而特征是分割后区域的若干个形态特征。

主流程代码

首先黏贴出源代码,这是Halcon当中例程中完整的代码:

* This program uses an SVM classifier
* to detect bad halogen bulbs.
* 
* The training images have to be stored in sub directories
* which are named after their class names.
* 
* The procedure calculate_features may be edited to select different
* features for the classification (if the number of features is changed,
* you have to change the first parameter of create_class_svm as well)
* 
get_system (image_dir, HalconImages)
get_system (operating_system, OS)
if (OS = Windows NT)
    tuple_split (HalconImages, ;, HalconImages)
else
    tuple_split (HalconImages, :, HalconImages)
endif
ReadOK := false
dev_get_preferences (suppress_handled_exceptions_dlg, SaveMode)
dev_set_preferences (suppress_handled_exceptions_dlg, true)
for k := 0 to |HalconImages|-1 by 1
    try
        read_image (Image, HalconImages[k] + /halogen_bulb/halogen_bulb_01.png)
        ReadPath := HalconImages[k] + /halogen_bulb/
        ReadOK := true
        break
    catch (Exception)
    endtry
endfor
if (not ReadOK)
    disp_message (WindowHandle, Could not find the images in $HALCONIMAGES, window, -1, -1, black, true)
    stop ()
endif
dev_set_preferences (suppress_handled_exceptions_dlg, SaveMode)
read_image (Image, halogen_bulb/halogen_bulb_01.png)
get_image_pointer1 (Image, Pointer, Type, Width, Height)
dev_close_window ()
dev_open_window (0, 0, Width/2, Height/2, black, WindowHandle)
set_display_font (WindowHandle, 14, courier, true, false)
* 
ClassNames := [good,bad,none]
Colors := [forest green,red,red]
Nu := [0.05]
KernelParam := [0.02]
* 
* Create an SVM classifier
create_class_svm (7, rbf, KernelParam, Nu, |ClassNames|, one-versus-one, principal_components, 5, SVMHandle)
* 
* Add samples
add_samples_to_svm (ClassNames, SVMHandle, WindowHandle, ReadPath)
dev_clear_window ()
* 
* Train the classifier
disp_message (WindowHandle, Training..., window, -1, -1, black, true)
train_class_svm (SVMHandle, 0.001, default)
disp_message (WindowHandle, Training completed, window, -1, -1, black, true)
disp_continue_message (WindowHandle, black, true)
stop ()
* 
* Classify halogen bulbs
classify_regions_with_svm (SVMHandle, Colors, ClassNames, ReadPath)
* 
* Clear the classifier from memory
clear_class_svm (SVMHandle)

以上这是Halcon中案例的源代码,代码稍微有点长,但是其中很大篇幅代码与算法关系不大,下面进行详细介绍。

程序详解

初始化系统

首先我们可以看到截图所示部分的代码,所占篇幅较大。这部分代码的目的仅仅是初始化环境,并对样本图像的目录进行检查,这里不再对内容进行过多介绍。值得注意的是,其中对try-catch的使用在其它例程中不多见,但是实际应用中还是很有必要的,大家在写代码的时候不妨可以参考一下。

image

准备阶段

初始化

下面截图所示代码为准备阶段所做的事情:构造分类器、循环添加样本、训练分类器。

第一行定义了分类器所要分辨的类型:good, bad, none;

第二行定义了在输出时候不同结果显示的颜色;

第三和第四行是构造的分类器参数;

第五行,如注释所示,是构造SVM分类器;

对于前两行定义的元组,实质是一个枚举类型。因为在Halcon中,类型输出实际是一个整数,即诸如:0,1,2……的整数。因此如果输出是0,则对应的类型是good;输出1对应是bad;输出2对应的类型是none。而在显示的时候,同样的,可以使用Colors元组中相应位置的数据作为显示参数,用于显示不同颜色的字符或者区域。

image

 

通过create_class_svm构造SVM分类器之后需要添加样本进行训练,下面就进入外部过程add_sample_to_svm。如下图所示,通过鼠标右键,可以查看外部过程详细定义,该外部过程定义如下:

add_sample_to_svm:

image

这是一个循环,每次循环完成的任务为:

1. 读取下一帧样本图像(Image);

2. 使用固定阈值分割后提取待测区域(Region);

3. 计算特征向量(Features);

4. 将特征向量按照类型加入SVM中;

image

图3. 分割后的区域

calculate_features同样也是一个外部过程,该外部过程定义如下:

calculate_features:

image

从定义中,我们可以知道特征向量的选取,包括:面积(Area)、密实度(Compactness)、四个不变矩特征(PSI1,PSI2,PSI3,PSI4)和凸度(Convexity)。这些特征都属于区域的形态特征,最后将这些特征合并成一个实数型的特征向量Features,作为本过程的最终运算结果:

image

图4. 特征示例

训练SVM

添加样本后就开始训练SVM分类器,这一步Halcon仅通过一个tran_class_svm就能够完成,训练过程实质就是使用构造SVM时定义的核函数(此处为rbf)在高维特征空间寻找最优的分界面,将特征空间划分成不同类型(此处为good、bad、none三类)。训练过程是SVM的核心,也是需要进行大量运算的过程,因此在这一个算子上,可能会耗费比较长的时间。

具体如何划分,原理感兴趣的朋友可以下载上面的链接中的pdf文件进行研究。

识别阶段

到了识别阶段,就是对待测样本进行分类了,分类结果本案例中共有三类:good、bad、none。整个识别过程被封装在外部过程classify_regions_with_svm中:

classify_regions_with_svm:

image

从程序中可以看到,识别过程经历了与样本添加过程中相同的分割和特征提取。将提取到的特征向量代入SVM分类器就就能得出分类结果(Class)了。代入分类器的算子为classify_class_svm。从这里我们也可以看到,对SVM分类器实质是在对提取到的特征向量进行分类。

image

图5. 识别结果图

总结

整体来说,SVM分类到这里基本上已经结束,其它部分显示的代码就不在进行介绍了,相信大家也看得懂。这里我没有对参数的配置进行介绍,只介绍了整个流程。对于参数的解释,使用Halcon的朋友通过查询手册要来得更加方便,也更加准确。