1.1 语义分割和边缘提取介绍
通过语义分割,我们使用深度学习(DL)网络将输入图像的每个像素分配给一个类。
图(1)语义分割示例
在图(1)中,输入图像的每个像素都被分配给一个类,但是'苹果'的三个不同实例和'橘子'的两个不同实例都不是可区分的对象。
语义分割的结果是输出图像,其中像素值表示输入图像中对应像素的分配类。因此,在HALCON中,输出图像与输入图像具有相同的大小。对于一般的深度学习网络,表示更复杂特征的深层特征图通常比输入图像小。为了获得与输入相同大小的输出,HALCON使用具有两个组件的分割网络:编码器和解码器。编码器确定输入图像的特征,例如,用于基于深度学习的分类。由于这些信息是以压缩格式“编码”的,因此需要解码器将信息重构为期望的结果,在这种情况下,就是将每个像素分配给一个类。注意,在对像素进行分类时,不会将同一类的重叠实例区分为不同的。
边缘提取是语义分割的一种特殊情况,其中模型被训练以区分两类:边缘和背景。
图(2)边缘提取示例
在图(2)中,语义分割的一个特殊情况,其中输入图像的每个像素被分配到两个类中的一个:“边缘”或“背景”。
1.2 语义分割和边缘提取通用工作流程
本小节描述了基于深度学习的语义分割任务的一般工作流程。具体分为数据预处理、模型训练、训练后模型的评价和对新图像的推断四个部分。HDevelop示例segment_pill_defects_deep_learning显示了语义分割应用程序的完整工作流程;示例segment_edges_deep_learning_with_retraining显示了边缘提取应用程序的完整工作流程。
(1) 数据预处理
①通过read_dl_dataset_segmentation()函数来获取训练数据集要找到的数据信息。这样就创建了一个字典DLDataset,它充当数据库,存储有关数据的所有必要信息。
②通过split_dl_dataset()函数对字典DLDataset表示的数据集进行拆分,并结果将保存在DLDataset的每个样本的split键上。
③使用preprocess_dl_dataset()函数进行数据集预处理。使用create_dl_preprocess_param()函数将所有参数及其值存储在字典DLPreprocessParam中。
(2) 模型训练
①通过read_dl_model()算子读取网络模型。
②通过set_dl_model_param()算子设置模型参数,例如'image_dimensions'和'class_ids'等;可以通过get_dl_model_param()算子检索当前参数值。
③使用create_dl_train_param()函数来设置训练参数并存储在字典TrainParam中。这些参数包括:超参数、训练时评估的参数、用于训练结果可视化的参数、和用于序列化的参数。
④使用train_dl_model()来训练模型。该函数的参数有模型句柄DLModelHandle、包含数据信息DLDataset的字典、带有训练参数'TrainParam'的字典信息和训练需要运行多少epochs。在使用过程train_dl_model的情况下,总损失以及可选的评估度量将被可视化。
(3) 模型评估
①使用set_dl_model_param()算子设置可能影响计算的模型参数,例如,'batch_size'。
②使用evaluate_dl_model()函数进行评估。
③字典EvaluationResult保存了所要求的评估度量。
可以使用dev_display_segmentation_evaluation()函数过程可视化评估结果。
(4) 模型推理
①使用set_dl_model_param()算子设置参数,例如'batch_size'。
②可以使用gen_dl_samples_from_images()函数来为每个图像生成数据字典DLSample。
③使用preprocess_dl_samples()函数对训练图像进行预处理。在预处理步骤中保存字典DLPreprocessParam时,可以直接使用它作为输入来指定所有参数值。
④使用apply_dl_model()算子进行模型推理。
⑤从字典'DLResultBatch'中检索结果。可以在分割图像上使用threshold 算子来选择特定类的区域。
1.3 语义分割和边缘提取补充说明
(1)用于训练和推理的数据(Data for training and evaluation)
①Classes
不同的类是由网络划分的集合或类别。它们在字典DLDataset中设置,并通过set_dl_model_param()算子传递给模型。
在语义分割中,注意有两个特殊情况:类 'background'和声明为'ignore'的类
'background'类:网络像对待其他类一样对待背景类。也没有必要有背景类,但如果你的数据集中有不同的类,你不感兴趣,尽管它们必须由网络学习,你可以把它们都设置为'background' 。因此,背景类将更加多样化。有关更多信息,请参阅过程preprocess_dl_samples()函数。
'ignore'类:有可能将一个或多个类声明为'ignore'。分配给'ignore'类的像素将被损失以及所有测量和评估忽略。网络不会将任何像素分类为'ignore'类。同样,被标记为属于此类的像素将被网络分类为非'ignore'类,就像其他像素一样。在下图给出的示例中,网络也会对'border'类的像素进行分类,但是它不会将任何像素分类到'border'类中。你可以使用set_dl_model_param()算子将参数'ignore_class_ids' 声明为'ignore' 类。
在边缘提取中,只区分两类:'edge'和'background'。'edge'类的标签与普通类一样。因此,只有一个类被标记,这个类被称为'edge'。
②DLDataset
这个字典作为一个数据库,这意味着,它存储了网络所需的所有数据信息,例如,图像的名称、路径和类等。通过键segmentation_dir和segmentation_file_name,可以提供它们的命名方式和保存位置的信息。
③segmentation_image
为了让网络能够学习,不同类的成员看起来是什么样子,你可以告诉训练数据集中每个图像的每个像素它属于哪个类。这是通过在相应的segmentation_image中为输入图像的每个像素存储编码为像素值的类来实现的。
图(3)segmentation_image模式
在图(3)中为了提高可见度,灰度值被用来表示数字。图(3-1)为输入图像;图(3-2)为相应的segmentation_image注释,0:背景(白色),1:橙子,2:柠檬,3:苹果和4:边界(黑色)作为一个单独的类,所以我们可以将其声明为“忽略”。
你需要足够的训练数据来将其分成三个子集,一个用于训练,一个用于验证,一个用于测试网络。这些子集最好是独立且相同分布的。对于数据集,可以使用split_dl_data_set()函数。
(2)网络输出( Network output)
网络输出取决于任务:
培训:作为输出,将返回一个字典DLTrainResult,其中包含总损失的当前值以及模型中包含的所有其他损失值。
推理与评价:作为输出,网络将为每个样本返回一个字典DLResult。
对于语义分割,该字典将为每个输入图像包括以下两个图像句柄。
segmentation_image:图像中每个像素都有一个值,对应于它对应的像素被分配给的类(见下图)。
segmentation_confidence:图像中每个像素都具有输入图像中对应像素分类的置信度值(见下图)。
图(4)不同数据在图像上的模式
在图(4)中,为了提高可见度,灰度值被用来表示数字。图(4-1) 为segmentation_image:被声明为'ignore'的类的像素(见上图)被分类。图(4-2)为 segmentation_confidence。
(3)模型参数和超参数(Model Parameters and Hyperparameters)
语义分割相关的超参数:'class weights';对于语义分割模型,模型参数以及超参数('class weights')使用set_dl_model_param()算子设置。注意,由于内存使用量很大,通常只能使用小批处理进行训练。因此,训练相当缓慢,我们建议使用比例如更高的动量进行分类。
①'class weights'
使用超参数'class weights',您可以为每个类分配其像素在训练期间获得的权重。给独特的类赋予不同的权重,就有可能迫使网络学习不同重要性的类。这在类控制图像的情况下是有用的,例如,缺陷检测,缺陷只占图像中的一小部分。在这种情况下,将每个像素分类为背景(因此,“非缺陷”)的网络通常会获得良好的损失结果。为不同的类分配不同的权重有助于重新平衡分布。简而言之,可以将损失集中在训练上,特别是那些被认为是重要的像素。
网络在为每个训练样本创建的图像上获得这些权重。每个像素值为输入图像对应像素在训练过程中获得的权重。
Calculate_class_weights_segmentation()函数用来创建类权重。
Gen_dl_segmentation_weight_images()函数使用类权重并生成weight_image。
这个超参数在程序中被称为class_weights或ClassWeights。下图显示了不同权重的图像。注意,给图像的特定部分赋予权重0.0,这些像素不会导致损失。
图(5)weight_image模式。
图(5-1) segmentation_image定义图像中每个像素的类,0:背景(白色),1:橙子,2:柠檬,3:苹果,4:边界(黑色),声明为“忽略”。图(5-2)对应的weight_image提供类权重,背景:1.0,橙子:30.0,柠檬:75.0,声明为“ignore”的类的像素,这里是类边界,将被忽略并获得权重0.0。
(4) 语义分割数据评估方法(Evaluation measures for the Data from Semantic Segmentation)
①pixel_accuracy
像素精确率就是用正确预测的所有像素与像素总数的比率。
图(6)pixel_accuracy示例
在图(6-1) segmentation_image定义每个像素为ground truth 类。声明为 'ignore' 的类的像素被画成黑色。在图(6-2)为输出图像,以及声明为 'ignore' 的类的像素也会被分类;(3)像素准确率为总橙色面积之比。注意,标记为 'ignore' 的类的一部分的像素将被忽略。
②class_pixel_accuracy
每类像素准确率只考虑单个类的像素。每类像素准确率被定义为该类正确预测的像素/用该类标记的像素总数。如果没有出现类,它将获得class_pixel_accuracy值-1,并且不贡献平均值mean_accuracy。
③mean_accuracy
每个类像素准确率(class_pixel_accuracy)的平均值。
④class_iou
每类IoU(class_iou)为特定类提供正确预测像素与ground truth的交集与它们并集的比率。
如果一个类没有出现,它的class_iou值为-1,并且不会对mean_iou产生影响。
⑤mean_iou
求出每个类iou(class_iou)取平均值。
⑥frequency_weighted_iou
对于mean_iou首先计算每个类的IoU。但是,每个发生类对该度量的贡献是由属于该类的像素的所占比例加权求出的。