前言:
==========================================================
分类器相对于深度学习来讲是一种古老传统的图片处理技术。halcon中常见的有四类分类器:
MLP(多层神经网络neural Nets)
SVM(支持向量机)
K-NN(K-最邻近)
GMM(高斯混合类型)
分类器的应用领域主要是下面这些:
image segmentation 图像分割
object recognition 对象识别
quality control 质量控制
novelty detection 缺陷检测
optical character recognition(OCR) 光学字符识别
勇哥第一次见到分类器的视觉项目是锂电池的极片缺陷检测,效果还不错。
这两年深度学习火起来后,发现深度学习完成上面所说的领域的应用更容易,效果也更好。
但深度学习对硬件要求太高,你把IPC加装个一百多W的显卡很多时候是不现实的。
如果你用cpu来跑,会发现速度乎快乎慢,cpu全部内核会100%被占用。
分类器相对于深度学习来讲不吃硬件,所以相对来讲算是轻量级的应用。
==========================================================
这个瓶口检测的示例演示了算子select_feature_set_knn的用法和 *class_train_data的功能。
本例子是检查瓶颈是否有缺陷。
程序基于一组训练图像,选择一组参数集,对缺陷进行最佳判别。
引入“profile”和“profile_change”特性:我们希望自动估计控制极坐标变换分辨率的参数
第3部分:自动选择特征
使用knn分类器选择最佳特征
select_feature_set_knn算子不仅返回最佳功能,还有一个用这些训练出来的分类器。
最后进行好坏分类。
下图为OK品
大缺陷
小缺陷
NG,瓶口特征缺少
read_image (Image, 'bottles/bottle_mouth_01')
dev_update_off ()
dev_close_window ()
dev_close_window ()
dev_open_window_fit_image (Image, 0, 0, 640, 512, WindowHandle)
dev_open_window (0, 650, 448, 160, 'black', WindowHandleFeature)
dev_open_window (220, 650, 448, 512 - 220, 'black', WindowHandlePolar)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
set_display_font (WindowHandleFeature, 14, 'mono', 'true', 'false')
set_display_font (WindowHandlePolar, 14, 'mono', 'true', 'false')
dev_set_window (WindowHandle)
dev_display (Image)
*引入“profile”和“profile_change”特性:我们希望自动估计控制极坐标变换分辨率的参数。
disp_message (WindowHandle, 'Introducing the features "profile" and\n"profile_change":\nWe want to estimate automatically a parameter\nthat controls the resolution of a polar\ntransformation.', 'window', 12, 12, 'black', 'true')
dev_set_draw ('margin')
dev_set_line_width (3)
RingSize := 70
*第一部分:导言
*使用第一张图片解释特征
find_bottle_mouth (Image, Row, Column, Radius)
gen_circle_contour_xld (OuterCircle, Row, Column, Radius, 0, 6.28318, 'positive', 1)
dev_set_color ('green')
dev_display (OuterCircle)
* 创建显示特征的图像(仅用于可视化)
gen_image_const (FeatureImage, 'float', 14, 80)
* 变量初始化
FeatureNames := []
FeatureLength := []
DataSample := []
MinParamValue := 20
MaxParamValue := 80
ParameterIncrement := 10
Change := true
* 创建数据向量并为所有子功能命名
for TestParameter := MinParamValue to MaxParamValue by ParameterIncrement
* 用TestParameter作为极坐标变换分辨率的参数计算特征
compute_gray_value_profile (Image, Row, Column, RingSize, Radius, TestParameter, GrayValueProfile, GrayValueProfileChange)
* 收集结果
FeatureNames := [FeatureNames,'profile_' + TestParameter + '(' + TestParameter + ')','profile_change_' + TestParameter + '(c' + TestParameter + ')']
FeatureLength := [FeatureLength,|GrayValueProfile|,|GrayValueProfileChange|]
DataSample := [DataSample,GrayValueProfile,GrayValueProfileChange]
* 显示特征,首先是轮廓...
disp_gray_profile (FeatureImage, Image, PolarTransImage, GrayValueProfile, TestParameter, MinParamValue, ParameterIncrement, Row, Column, RingSize, Radius, WindowHandlePolar, WindowHandleFeature, Change, Columns, Rows, I)
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* ...然后显示更改的配置文件
disp_gray_profile_change (FeatureImage, PolarTransImage, EdgeAmplitude, GrayValueProfileChange, TestParameter, MinParamValue, ParameterIncrement, WindowHandlePolar, WindowHandleFeature, WindowHandle, Columns, Rows, I)
stop ()
endfor
dev_set_window (WindowHandlePolar)
dev_close_window ()
*第2部分:预训练
*为所有图像生成整个特征集
*为分类器创建训练数据结构
create_class_train_data (|DataSample|, ClassTrainDataHandle)
set_feature_lengths_class_train_data (ClassTrainDataHandle, FeatureLength, FeatureNames)
* 准备分类
ClassNames := ['perfect','small defect','large defect','completely missing']
*
* 训练
*
* 训练数据的类
ClassNumber := [0,2,1,1,2,1,3,0,2,1,1,2,2,2,2,1]
* 计算所有图像的特征
for Index := 1 to 16 by 1
read_image (Image, 'bottles/bottle_mouth_' + Index$'.02')
gen_circle_contour_xld (OuterCircle, Row, Column, Radius, 0, 6.28318, 'positive', 1)
find_bottle_mouth (Image, Row, Column, Radius)
DataSample := []
ShortDescription := []
* 计算整个特征集
for TestParameter := MinParamValue to MaxParamValue by ParameterIncrement
compute_gray_value_profile (Image, Row, Column, RingSize, Radius, TestParameter, GrayValueProfile, GrayValueProfileChange)
DataSample := [DataSample,GrayValueProfile,GrayValueProfileChange]
set_visualization_data (FeatureImage, GrayValueProfile, TestParameter, MinParamValue, ParameterIncrement, GrayValueProfileChange, Columns, Rows)
endfor
* 显示特征
show_all_features (Image, FeatureImage, WindowHandle, ClassNames, ClassNumber, Index, WindowHandleFeature, MinParamValue, MaxParamValue, ParameterIncrement, TestParameter)
if (Index < 16)
disp_message (WindowHandleFeature, 'Features for image ' + Index, 'window', 120, 12, 'black', 'true')
endif
dev_display (OuterCircle)
* 将特征向量添加到训练数据中
add_sample_class_train_data (ClassTrainDataHandle, 'row', DataSample, number(ClassNumber[Index - 1]))
wait_seconds (0.1)
endfor
dev_set_window (WindowHandle)
*
*第3部分:自动选择特征
*
*使用knn分类器选择最佳特征
*select_feature_set_knn算子不仅返回最佳功能,还有一个用这些训练出来的分类器。
select_feature_set_knn (ClassTrainDataHandle, 'greedy', [], [], KNNHandle, SelectedFeatureNames, Score)
dev_display (Image)
Message := 'Selected features:'
Message := [Message,' - ' + SelectedFeatureNames]
disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true')
dev_set_window (WindowHandleFeature)
tuple_regexp_match (SelectedFeatureNames, '\\d+', Matches)
Sizes := number(Matches)
tuple_regexp_match (SelectedFeatureNames, '(c)\\d+', Matches)
Derivative := Matches [==] 'c'
gen_rectangle1 (Rectangle, gen_tuple_const(|Sizes|,0), (Sizes - 20) / 5 + Derivative, Sizes, (Sizes - 20) / 5 + Derivative)
dev_set_color ('green')
dev_set_draw ('margin')
dev_display (Rectangle)
disp_message (WindowHandleFeature, 'Selected best features', 'window', 120, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_set_window (WindowHandleFeature)
dev_close_window ()
* 将新训练的Classifier应用到训练集
for Index := 1 to 16 by 1
dev_set_window (WindowHandle)
read_image (Image, 'bottles/bottle_mouth_' + Index$'.02')
find_bottle_mouth (Image, Row, Column, Radius)
Feature := []
for IndexInner := 0 to |SelectedFeatureNames| - 1 by 1
compute_gray_value_profile (Image, Row, Column, RingSize, Radius, Sizes[IndexInner], GrayValueProfile, GrayValueProfileChange)
if (Derivative[IndexInner])
Feature := [Feature,GrayValueProfileChange]
else
Feature := [Feature,GrayValueProfile]
endif
endfor
* 分类
classify_class_knn (KNNHandle, Feature, Result, Rating)
dev_display (Image)
gen_circle_contour_xld (OuterCircle, Row, Column, Radius, 0, 6.28318, 'positive', 1)
if (ClassNames[Result] == 'completely missing')
dev_set_color ('red')
elseif (ClassNames[Result] == 'large defect')
dev_set_color ('orange')
elseif (ClassNames[Result] == 'small defect')
dev_set_color ('yellow')
else
dev_set_color ('green')
endif
dev_display (OuterCircle)
disp_message (WindowHandle, 'Image ' + Index + ': Classified as ' + ClassNames[Result], 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
endfor
compute_gray_value_profile函数
*此过程计算基于灰度值及其派生到一维特征数组的极坐标投影的特征。
*找到的圆被展开成极坐标图像
polar_trans_image_ext (Image, PolarTransImage, Row, Column, 0, rad(360), RingSize - 5, Radius + 5, ImageSize, ImageSize, 'bilinear')
* 投影灰度值
gray_projections (PolarTransImage, PolarTransImage, 'simple', GrayValueProfile, VertProjection2)
* 计算导数
sobel_amp (PolarTransImage, EdgeAmplitude, 'x_binomial', 3)
abs_image (EdgeAmplitude, ImageAbs)
* Project derivative
gray_projections (ImageAbs, ImageAbs, 'simple', GrayValueProfileChange, VertProjection1)
* 缩放导数到与图像值相似的缩放比例.
GrayValueProfileChange := GrayValueProfileChange * 5
return ()
disp_gray_profile函数代码:
* 显示灰度值配置文件
Columns := gen_tuple_const(|GrayValueProfile|,((TestParameter - MinParamValue) / ParameterIncrement) * 2)
Rows := [0:|GrayValueProfile| - 1]
set_grayval (FeatureImage, Rows, Columns, GrayValueProfile)
polar_trans_image_ext (Image, PolarTransImage, Row, Column, 0, rad(360), RingSize - 5, Radius + 5, TestParameter, TestParameter, 'bilinear')
dev_set_window (WindowHandlePolar)
dev_display (PolarTransImage)
Message := 'Polar transformation of the circle'
Message[1] := 'Resolution: ' + |GrayValueProfile| + ' pixels'
disp_message (WindowHandlePolar, Message, 'window', 12, 12, 'black', 'true')
dev_set_window (WindowHandleFeature)
dev_display (FeatureImage)
Message := 'Feature profile_' + TestParameter + ' (' + TestParameter + ')'
Message[1] := '(Projection of the transformation below)'
disp_message (WindowHandleFeature, Message, 'window', 110, 12, 'black', 'true')
for I := MinParamValue to TestParameter by ParameterIncrement
disp_message (WindowHandleFeature, I, 'window', 1, 6 + 32 * (((I - MinParamValue) / ParameterIncrement) * 2), 'white', 'false')
if (I < TestParameter)
disp_message (WindowHandleFeature, 'c' + I, 'window', 1, 2 + 32 * (((I - MinParamValue) / ParameterIncrement) * 2 + 1), 'white', 'false')
endif
endfor
return ()
disp_gray_profile_change函数
* 显示灰度导数特征
Columns := gen_tuple_const(|GrayValueProfileChange|,((TestParameter - MinParamValue) / ParameterIncrement) * 2 + 1)
Rows := [0:|GrayValueProfileChange| - 1]
set_grayval (FeatureImage, Rows, Columns, GrayValueProfileChange)
sobel_amp (PolarTransImage, EdgeAmplitude, 'x_binomial', 3)
dev_set_window (WindowHandlePolar)
dev_display (EdgeAmplitude)
Message := 'Sobel filtered polar transformation'
Message[1] := 'Resolution: ' + |GrayValueProfileChange| + ' pixels'
disp_message (WindowHandlePolar, Message, 'window', 12, 12, 'black', 'true')
dev_set_window (WindowHandleFeature)
dev_display (FeatureImage)
for I := MinParamValue to TestParameter by ParameterIncrement
disp_message (WindowHandleFeature, I, 'window', 1, 6 + 32 * (((I - MinParamValue) / ParameterIncrement) * 2), 'white', 'false')
disp_message (WindowHandleFeature, 'c' + I, 'window', 1, 2 + 32 * (((I - MinParamValue) / ParameterIncrement) * 2 + 1), 'white', 'false')
endfor
Message := 'Feature profile_change_' + TestParameter + ' (c' + TestParameter + ')'
Message[1] := '(Projection of the transformation below)'
disp_message (WindowHandleFeature, Message, 'window', 110, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
return ()
这个例子采用极坐标方式对瓶口进行缺陷收集,对极坐标图采用不同的分辨率(20-80像素)图像进行采集自适应用knn特征的方式,即决了定最佳分辨率,还直接返回用这些训练出来的knn分类器。
这个例子相当精彩,信息量很大,是缺陷检测的优秀示例。
勇哥分析记录这个例子的时候感觉文字图片乏力,还是觉得应该出一个视频教程才能说清楚。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

