(完结篇)什么是语义分割?原理+手写代码实现?

Unet语义分割

目录

Unet语义分割

1. 如何理解“语义”“分割”。

2. 语义分割原理(重点)

3. 语义分割意义

4. 语义分割应用场景

5. Unet的优势(医学领域)

6. 先行知识储备

7. 语义分割流程

8. 项目结构及介绍

9. 安装环境(python=3.8,pytorch

10. 实现流程(重点)

11. 损失函数

12. 评估指标

13. UNet论文

14. 源码地址(永久、免费)

15. 如何修改成自己的任务

16. 个人成长心得

17. 项目落地应用

  1. 如何理解“语义”“分割”。

        语义分割定位于人工智能的深度学习的计算机视觉领域,其相关任务还有目标检测、图像分类、实例分割、姿态估计等。

        计算机视觉中关于图像识别有四大类任务:

                (1)分类-Classification:解决“是什么?”的问题,即给定一张图片或一段视频判断里面包含什么类别的目标。

                (2)定位-Location:解决“在哪里?”的问题,即定位出这个目标的的位置。

                (3)检测-Detection:解决“在哪里?是什么?”的问题,即定位出这个目标的位置并且知道目标物是什么。

                (4)分割-Segmentation:分为实例的分割(Instance-level)和场景分割(Scene-level),解决“每一个像素属于哪个目标物或场景”的问题。

        语义指具有人们可用语言探讨的意义,分割指图像分割。语义分割即能够将整张图的每个部分分割开,使每个部分都有一定类别意义。和目标检测不同的是,目标检测只需要找到图片中目标,打上框然后分出类别。语义分割是以描边的形式,将整张图不留缝隙的分割成每个区域,每个区域是一个类别,没有类别的默认为背景background。

        总之,语义分割旨在将图像中的每个像素分配给预定义的语义类别之一。与传统的对象检测任务不同,语义分割不仅需要标识图像中存在的对象,还需要为每个像素赋予一个语义标签。可以帮助计算机实现对图像中对象的更精细和准确的理解,以及对视觉信息进行更好的利用,为各种领域提供更多智能化的应用。

2.语义分割原理

        要识别出整张图片的每个部分,就意味着要精确到像素点,所以语义分割实际上是对图像中每一个像素点进行分类,确定每个点的类别(如属于背景、人、汽车、马等),从而进行区域划分。

        那如何做到将像素点上色呢?

        语义分割的输出和图像分类网络类似,图像分类类别数是一个一维的one hot 矩阵。例如图:五分类的[0,1,0,0,0]。语义分割任务最后的输出特征图是一个三维结构,大小与原图类似,通道数就是类别数。其中通道数是类别数,每个通道所标记的像素点,是该类别在图像中的位置,最后通过argmax 取每个通道有用像素合成一张图像,用不同颜色表示不同类别。如下图:

        输入:彩色的原始图[3,256,256]。输出:灰度图[256,256]

        注意:本项目输出结果y_true是采用灰度图分类而非彩色图像分类,类别列表为[0,1,2,3....class]。0代表背景色黑色,后面每一个整数代表一个类别,项目的watch_result.py脚本即可高亮显示各个类别。

3. 语义分割意义

       CNN 的强大之处在于它的多层结构能自动学习特征,并且可以学习到多个层次的特征。如,较浅的卷积层感知域较小,学习到一些局部区域的特征;较深的卷积层具有较大的感知域,能够学习到更加抽象一些的特征。这些抽象的特征对分类很有帮助,有助于分类性能的提高。可以很好地判断出一幅图像中包含什么类别的物体。但是劣势很明显,这些抽象特征对物体的大小、位置和方向等敏感性更低,这就需要语义分割来判断图像每个像素点的类别,进行精确分割,分割是像素级别的。

4.语义分割应用场景

自动驾驶:自动驾驶汽车有「环境感知」的能力,以便其可以安全行驶。

医疗影像诊断:机器可以智能地对医疗影像进行分析,降低医生的工作负担,大大减少了运行诊断测试所需的时间,如细胞的分割识别,肺部形状诊断,识别肿瘤、病变和异常组织。

无人机落点判定:无人机落地前,对地面空地图片进行识别分割,根据大小形状判断是否能安全降落。

5.Unet的优势

Unet是一种流行的语义分割模型,与其他许多语义分割算法相比具有以下优势:

  1. 高精度:Unet在许多公开数据集上取得了令人印象深刻的结果,在许多情况下可以实现高精度的分割。

  2. 小样本学习:Unet具有较少的参数和可训练变量,这使得它对小样本学习更加鲁棒,适用于数据集较小或样本不均衡的情况。

  3. 多任务学习:Unet可以同时处理多个任务,例如分割、检测和分类等,从而将多个任务融合到单个模型中。

  4. 数据增强:由于Unet考虑到了被分割物体的形状和上下文信息,因此它能够通过旋转、镜像等数据增强技术进行有效的数据扩充,从而使模型更加健壮。

  5. 快速推理:Unet具有简单、直观的架构和参数共享,这使得它在推理时具有较快的速度。此外,Unet还可以使用GPU加速来进一步提高推理速度。

     总的来说,Unet在精度、小样本学习、多任务学习、数据增强和快速推理等方面具有优势,使       其成为一种流行的语义分割算法。

在医疗领域的语义分割任务中,Unet网络优点格外显著:

        较少的训练数据:医疗图像数据通常难以获取,且标注困难。Unet网络通过使用跳跃连接(skip connection)来将编码器中的低级特征与解码器中的高级特征相结合,从而可以在较少的训练数据下获得更好的性能。

        良好的泛化性能:医疗图像数据通常存在噪声、伪影和不同的成像条件等问题。Unet网络通过使用反卷积层(transpose convolution)进行上采样操作,避免了精细的空间信息的损失,从而可以获得更好的泛化性能。

        可处理大尺寸图像:医疗图像通常具有较高的分辨率和尺寸,需要能够处理大尺寸图像的模型。Unet网络通过使用池化操作进行下采样,并使用反卷积层进行上采样操作,从而可以有效地处理大尺寸图像。

        多尺度分割:医疗图像中的结构大小和位置差异较大,需要考虑多尺度的特征表达。Unet网络通过使用跳跃连接和上采样操作,可以从多个尺度的特征图中提取并融合信息,从而实现多尺度分割。

6.先行知识储备

(1)encoder-decoder:,encoder为分类网络,用于提取特征,而decoder则是将encoder的先前丢失的空间信息逐渐恢复,该类方法虽然有一定的效果,能恢复部分信息,但毕竟信息已经丢失了,不可能完全恢复。典型算法结构还有segnet/refineNet。

(2)上采样:在卷积神经网络中,由于输入图像通过卷积神经网络(CNN)提取特征后,输出的尺寸往往会变小,而有时我们需要将图像恢复到原来的尺寸以便进行进一步的计算,这个使图像由小分辨率映射到大分辨率的操作。Unet中就需要将下采样的特征图,放大到原图大小提高分辨率,基于原图像素点做分类。卷积是改变通道数如(1*128*40*40)=>(1*64*40*40),上采样是改变尺寸如(1*128*40*40)=>(1*128*80*80)。方法:双线性插值法,转置卷积。

(3)特征融合:由于 CNN 在进行 convolution 和 pooling 过程中丢失了图像细节,即 feature map size 逐渐变小,所以不能很好地指出物体的具体轮廓、指出每个像素具体属于哪个物体,无法做到精确的分割。目的是把从图像中提取的特征,合并成一个比输入特征更具有判别能力的特征。如何正确融合特征是一个难题。融合不同尺度的特征是提高分割性能的一个重要手段。低层特征分辨率更高,包含更多位置、细节信息,但是由于经过的卷积更少,其语义性更低,噪声更多。高层特征具有更强的语义信息,但是分辨率很低,对细节的感知能力较差。特征融合综合利用多种图像特征,实现多特征的优势互补,获得更加鲁棒和准确性的识别结果。在Unet中上采样同时时就是将下采样的高层特征进行融合。两个经典的特征融合的方法

     a.)concat:系列特征融合,直接将连个特征进行连接。两个输入特征x和y的维数若为p和q,输出特征z的维数为p+q。保证俩者high+width一致。能完整保留图片所有信息但通道数C会增加(*2),因此会导致后面卷积的参数变多。如(1*64*40*40)add(1*64*40*40)=>(1*128*40*40)

     b.)add:并行策略,将这两个特征向量组合成复合向量,对于输入特征x和y,z=x+iy,其中i是虚数单位。保证俩者通道数C+high+width一致。没有concant信息多,但是融合后C+high+width不变。如(1*64*40*40)add(1*64*40*40)=>(1*64*40*40)

(4)FPN特征化图像金字塔

        先是对原始图像进行缩放,获得不同尺寸的图像,然后基于每种尺寸的图像生成不同尺寸的特征图,最后基于多尺寸特征图进行预测。这种方法需要针对每种尺寸的图片生成特征图,会消耗较多的计算和内存资源。

(5)什么是8位图?什么是单通道图?什么是灰度图?它们之间的关系?

       8位图是一种数字图像,其中每个像素使用8位二进制数(即一个字节)表示其颜色或亮度值。每个二进制数可以有256种可能的取值,从0到255,表示不同的颜色或灰度级别。在标准的RGB彩色空间中,8位图包含红、绿、蓝三个通道,每个通道都用一个8位二进制数表示每个像素的颜色强度,因此一个像素可以有256x256x256种不同的颜色,约为1670万种。8位图是一种广泛使用的图像格式,例如在Web设计、摄影和图像编辑等领域都有应用。它具有小文件大小、简单处理和良好的兼容性等优点,同时也可以通过调整像素的颜色和对比度来进行一定的图像处理和改善。需要注意的是,虽然8位图具有丰富的颜色信息,但由于其每个通道的每个像素只使用了8位二进制数,因此其所能表示的颜色范围和细节相比较高位深度的图像会更受限制。彩色图和8位图之间存在着一定的关系。通常,在RGB色彩空间下,彩色图像由三个颜色通道(红、绿、蓝)组成,每个通道使用8位二进制数表示每个像素的颜色强度。因此,一个像素可以有256x256x256种不同的颜色,约为1670万种。这种色彩模式的彩色图也被称为24位彩色图或真彩色图。然而,当将一个彩色图像转换为8位图时,每个像素用8位二进制数来表示其颜色信息,从而将图片压缩到更小的尺寸。在这种情况下,图像中的每个像素只有256种可用的颜色,因此该图像的颜色精度相对较低,但文件大小也相对较小。8位图还可以通过使用索引颜色技术将每个像素值映射到颜色表中的某个颜色,从而在保持文件大小较小的同时提供适当的颜色精度。总的来说,彩色图和8位图都是数字图像,它们之间存在着转换和压缩的关系,以满足不同的应用需求。彩色图能够提供更高的颜色精度和更丰富的视觉效果,而8位图则更适合于需要小文件大小和较低颜色要求的应用场景。

       单通道图片是指在彩色模式下,仅包含一种颜色通道的图像。例如,在灰度图像中,每个像素只有一个通道表示亮度值;在索引颜色图像中,每个像素只有一个通道表示颜色索引;在RGB颜色空间中,将其中两个颜色通道设为0,可以得到只有红色、绿色或蓝色通道的单通道图像等。由于单通道图像只需要保存一个通道的信息,因此具有较小的文件大小和更快的处理速度。同时,它们还具有一些特殊的应用场景,例如:1.灰度图像常用于医学影像、文本识别和计算机视觉领域。2.单色图像可以用于印刷或针织物品的设计,或者在某些情况下,用于创建黑白艺术作品。3.颜色索引图像则广泛用于受限资源的环境中,如低分辨率的屏幕、古老的计算机游戏和嵌入式系统等。总的来说,单通道图片是一种有用的图像类型,能够在不失去重要信息的情况下提供更高效率的图像处理。

        灰度图(Grayscale image),也称为灰阶图或单通道图像,是一种没有色彩信息的数字图像。在灰度图中,每个像素的亮度值表示其对应的区域中的强度或亮度水平,通常用于表示黑白照片或其他缺乏颜色信息的图像。灰度图像由单个通道组成,该通道中的每个像素都只有一个灰度级别。 在标准8位灰度图像中,每个像素使用8位表示,因此可以有256个不同的灰度级别,从0代表最暗的黑色到255代表最亮的白色。但是,还可以使用16位或更高的位深度来表示灰度图像以获得更高的精度。灰度图广泛应用于许多领域,例如医学影像、计算机视觉、图像处理和计算机图形学等。由于其简单性和低要求,灰度图像也常用于测试和验证计算机视觉算法。

        8位图和灰度图有一定的关系,因为它们都使用8位二进制数(即一个字节)表示每个像素的亮度值。但是,它们之间也存在一些区别。8位图包含三个颜色通道(红、绿、蓝),每个通道使用8位二进制数表示每个像素的颜色强度。这意味着一个像素可以有256x256x256种不同的颜色,约为1670万种。在标准的8位灰度图像中,每个像素用8位二进制数表示其亮度值,只有256种可能的亮度级别,从0代表最暗的黑色到255代表最亮的白色。因此,灰度图像只有黑白两种颜色,没有彩色信息。虽然8位图和灰度图都使用相同的位深度(8位),但在数据表示方式和颜色信息方面有所不同。8位图具有更丰富的色彩信息,适用于需要显示彩色图片的场景,如网络设计、摄影和图像编辑等。而灰度图由于只包含一个通道的亮度信息,通常适用于医学图像、计算机视觉和图像处理等方面。

       单通道图像和灰度图是密切相关的概念。灰度图是一种单通道图像,其中每个像素只有一个灰度值,表示亮度水平。换言之,灰度图是一种单通道图像,它在彩色空间中只使用了亮度通道。在标准的8位灰度图像中,每个像素使用8位二进制数表示其亮度值,可以有256种不同的灰度级别,从0代表最暗的黑色到255代表最亮的白色。这些灰度级别描述了灰度图像中每个像素的亮度值,而不会包含与颜色相关的信息。因此,灰度图像是一种单通道图像,只包含一个通道的灰度信息。然而,并非所有的单通道图像都是灰度图像。例如,在索引颜色模式下,每个像素的值表示一个颜色索引,该索引对应于颜色表中的具体颜色。这种索引颜色技术使得单通道图像能够在保持相对小文件大小的同时提供适当的颜色精度。因此,单通道图像和灰度图虽然相关,但并不完全等同。

本项目是采用将原始RGB彩色图转换成灰度图处理

(6)什么是混淆矩阵?什么是iou?

 如图,左边为标签图(y_true)右边为预测图(y_pred)。

       对于通过预测出来的结果,除了肉眼观察模型分割的好坏。我们如何用更精确地数值来判断模型的好坏呢?这就需要评价指标。对于深度学习中的计算机视觉领域,往往有诸如语义分割、目标检测、实例分割、行人重识别等多种图像任务,需要对预测的图片中目标区域的分类结果与原始标签图片做吻合度,以语义分割来说,这就需要统计对比两张图片(y_true,y_pred),找到有多少是正例的像素点预测对了、有多少本该是正例的像素点却预测错了、有多少是负例的像素点预测对了、有多少本该是负例的像素点却预测错了。

TP

TruePositive(A):它告诉实际值和预测值相同。A 类的TP只不过是实际值和预测值相同,这意味着单元格 1 的值为 15。

FP

FalsePositive(A):它告诉实际值是负的,在我们的例子中它是 B 类和 C 类,但模型预测它是正的,即 A 类。它是除了 TP 值之外的相应列的值的相加。

FalsePositive(A) = (单元格 4 + 单元格 7):7+2=9

TN

TrueNegative(A):实际值和预测值的含义相同,对于 A:B 类和 C 类是负分类。它是所有非A行和列的值相加。

TrueNegative(A) = (单元格 5 + 单元格 6 + 单元格 8 + 单元格 9):15 + 8 +3 + 45= 71

FN

FalseNegative(A):实际值在我们的例子中是正的,它是 A 类,但模型预测它是负的,即 B 类和 C 类。可以通过除 TP 值之外的相邻行来计算的。

FalseNegative(A) = (单元格 2 + 单元格 3):2 + 3= 5

         然后我们将这些数据汇聚成如上表格,再将算出正确的预测占总数(正确+错误)的比例,就能通过比例进行数值化判断图片中分类结果的好坏。其中表格叫做混淆矩阵,通过混淆矩阵可以计算出很多统计指标分别是:

    准确率(Accuracy)—— 针对整个模型
    精确率(Precision)
    灵敏度(Sensitivity):就是召回率(Recall)
    特异度(Specificity)

 特殊性评价指标——IOU:

IOU(Intersection Over Union)是一种用于衡量目标检测或分割模型性能的指标,而准确率(Accuracy)、召回率(Recall)和精确率(Precision)等则是广泛应用于机器学习分类任务中的指标,它们共同点是都要计算出两张图片的混淆矩阵。它们之间的区别如下:

  1. 计算方式:IOU通常用于目标检测或分割任务中,计算模型预测结果和真实标签之间的交集面积与并集面积之比。而准确率、召回率和精确率则是在二元或多元分类任务中,连续地比较模型预测结果和真实标签之间的相似度。

  2. IOU = TP/(FP+TP+FN)=15/(7+2+15+2+3)= 0.5172

  3. 对不平衡数据的敏感性:由于IOU计算的是交并比,因此在数据不平衡时可能会存在问题,因为模型可以通过更好地捕捉大类别的细节来获得更高的IOU值。而准确率、召回率和精确率可以更好地处理不平衡数据,因为它们只关注正确分类和错误分类的数量。

  4. 指标意义:IOU反映了目标检测或分割任务中模型预测结果和真实标签的相似程度,可以帮助我们评估模型对不同类别的分割效果。而准确率、召回率和精确率则更多地关注分类任务中的正确性和错误性,可以帮助我们评估模型对不同类别的分类效果。

  5. miou即各个类别iou的平均值,本项目是name_classes=1+1,iou/name_classes,如果是一个类别,name_classes=2(背景默认算做一个类别),如果是两个类别,name_classes=3。在计算每个类别单独的iou时,其他的类别像素视为负例。

7.语义分割流程

8.项目结构及介绍

 ./datasets:存放原始图片,打标签的json图片,转换后的y_true灰度图

./evaluation:评价指标脚本handle_evaluation.py,输入:y_ture路径+y_pred路径+类别列表[0,1,2....num_classes]。输出【miou,recall,precision】。内容:将每张图的每个类别单独算iou,单独算iou时,其他的杂类别一律视为背景色,全都转换成0。这样就能整个数

./GPU:如果项目环境需要gpu配置时,存放torch_gpu的离线下载包,没有也可以根据后续配置环境步骤自行配置。用cpu运行则不需使用

./params:模型文件存放地址

./result:预测的结果图(y_pred灰度图)的存放地

./trainning_image:训练过程中,取每一批次的第一张图片存放到此,以便肉眼查看训练效果

./rename.py:将原始数据集按顺序1.png,2.png,3.png,4.png...重命名,输入数据集所在路径,执行即可得出结果。

./make_mask.py(重点):将原始图和打完标签的json文件一并转化成y_ture,并存放到SegmentationClass,根据前面y_ture为灰度图,为了区分y_true中的各类别,通过代码第12行,CLASS_NAMES=['tongue'],以默认命名背景黑色为0,类别一tongue命名为1,类别二命名为2,类别三命名为3.....。本项目只有一个类别tongue。至于为什么要从0开始命名,请查看6.先行知识储备中灰度图的理解,对于一张图片最多可以命名为255种类,一般都用不上255各类别,一定够用。代码内容:通过解析json文件内容规律,找到label标签和points,创建一张空白图,在图中按照points的点坐标进行标记,内容填充为label。

./data.py:将图片处理成torch.tensor格式,还可以查看数据集的数量

./util.py:配合data.py调用,功能是统一图片大小[256*256]。创造一个[256,256]的空白矩形,将图片粘贴到空白矩形上返回。

./net.py:构造Unet网络结构

./train.py:调用data.py,util.py,net.py脚本,通过原始数据集路径+y_true路径,导入数据集。默认第16行设置一次使用一个批次大小为1张图片,默认第29行执行200个批次,每50轮保存一次模型结果到./params指定文件路径。同时将每一批次的第一张图片保存到./trainning_image文件路径下。优化器用Adam,损失函数用CrossEntropyLoss多分类交叉熵。输入:训练集+测试集。输出:训练过程中效果图+unet.pth模型

./test.py:输入:正常彩色图片路径,输出:y_pred灰度图预测结果并保存到./result中。输入原始图片后,将图片转化成tensor格式,然后加载模型,然后送入网络,然后得出结果。

./watch_result.py:查看./result中y_pred预测的灰度图高亮显示,同时此脚本还可以查看任何灰度图的高亮显示结果,如:y_true。内容: p = np.where(p == 1, 1, 0).astype(float)当预测图有多种类别时,留下标签颜色,其他类别颜色视为干扰色全部归为背景色0;

p = np.where(p == 1, 1, 0).astype(float)  

./number_classes.py: 查看(y_true)标签图和(y_pred)预测图类别:执行number_classes.py,输入标签图和预测图路径,将图片转换成数组,然后reshape(-1)展平,然后去重,就能查看图片有哪些类别了

9.安装环境

a.)将虚拟环境创建到你想指定的路径下(我的环境路径是D:\project\tongue_separate

conda create --prefix D:\envs\handle_Unet python==3.8

b.)激活虚拟环境

conda activate D:/envs/handle_Unet

c.)进入项目下路径(我的项目路径是D:\project\handle_UNet)

cd D:\project\handle_UNet

d.)安装gpu运行环境(如果你不想用gpu或者没有gpu,程序会自动识别使用cpu)

三个缺一不可:CUDA配置,cuDNN,torch-gpu版本

安装教程参考:

https://zhuanlan.zhihu.com/p/586913250

CUDA11.3以及PyTorch-GPU版本安装(笔记)_Johngo学长

torch-gpu版本:torch-1.13.1+cu117-cp38-cp38-win_amd64.whl

e.)进入虚拟环境,进入项目路径,安装我封装好的项目用到的所以环境包

pip install requirements.txt

如有报错,请百度搜索安装教程

f.)执行任何脚本没有问题,完成

10.实现流程

a.)

input输入:一般彩色图,大小不限

output输出:预测的灰度图,大小[256*256]

图片变化流程(高清图见项目文档):输入->输出

 b.)UNet介绍:简单实用,主要在医学领域

红色方框中是下采样,特征提取部分,和其他卷积神经网络一样,都是通过堆叠卷积提取图像特征,通过池化来压缩特征图,特征提取部分可以使用优秀的网络,例如:Resnet50,VGG等。蓝色方框中为上采样,图像还原部分。

第一部分是主干特征提取部分,我们可以利用主干部分获得一个又一个的特征层,Unet的主干特征提取部分与VGG相似,为卷积和最大池化的堆叠。利用主干特征提取部分我们可以获得五个初步有效特征层,在第二步中,我们会利用这五个有效特征层可以进行特征融合。

第二部分是加强特征提取部分,我们可以利用主干部分获取到的五个初步有效特征层进行上采样,并且进行特征融合(对上采样得到的结果进行通道的堆叠),获得一个最终的,融合了所有特征的有效特征层。

第三部分是预测部分,我们会利用最终获得的最后一个有效特征层对每一个特征点进行分类,相当于对每一个像素点进行分类。

其他算法结构:条件随机场:无向概率图模型,增加像素点与像素点之间的关联性;GAN对抗网络;DeepLabv1-v3;Dilation10; PSPNet (2016);FCN

 c.)总体流程图(高清图在项目文件夹的相关文档中,单独打开图片放大即可清晰):

  • 选择合适的算法

        本项目是舌像识别,即用户输入一张舌像图片,识别找到舌像区域。分析可知,该任务用计算机视觉领域的语义分割最为合适,查找资料可知,相比于语义分割的其他算法,在医疗领域UNet算法最为合适。于是采用UNet网络作为模型架构。接下来就开始研究UNet论文思想,熟悉论文后,就计划确定整个项目流程。

  • 数据集准备

         将原始图片放入./datasets/JPEGImages中,执行rename.py进行1.png,2.png,3.png.....按顺序重命名。

  • 数据预处理

         打标签:打标签的目的是将原始图做成y_true,以便训练过程中生成的y_pred预测图有真实值做损失,从而进一步降低损失,调整参数。也方便模型优化好后,算iou时有y_true和y_pred。

pip install labelme==3.16.7 -i https://mirrors.aliyun.com/pypi/simple/
labelme

         9.b,9.c进入虚拟环境,进入项目路径,下载labelme,并启动labelme.exe。对每一张图片的目标区域进行标记,然后保存到D:\project\handle_UNet\datasets\before,打完标签后的文件是.json格式。我们需要将.json对应的原始图片一起放入D:\project\handle_UNet\datasets\before。

       然后执行脚本make_mask.py,我们将原始图和打完标签的json文件一并转化成y_ture,并存放到SegmentationClass,根据前面y_ture为灰度图,为了区分y_true中的各类别,通过代码第12行,CLASS_NAMES=['tongue'],以默认命名背景黑色为0,类别一tongue命名为1,类别二命名为2,类别三命名为3.....。本项目只有一个类别tongue。至于为什么要从0开始命名,请查看6.先行知识储备中灰度图的理解,对于一张图片最多可以命名为255种类,一般都用不上255各类别,一定够用。处理完所有将y_true自动保存到指定./datasets/SegmentationClass.make_mask,此时的y_true为灰度图肉眼只能看见全黑。

         书写data.py,utils.py脚本,内容为将x,y_true转换为torch.Tensor格式【3,256,256】【256,256】,因为计算机视觉任务中都需要将图像转换成计算机可以识别的数值,才能计算。

 

 

  • 构建网络架构

根据UNet网络结构构造,上采样+卷积+下采样,封装成UNet类

self.out=nn.Conv2d(64,num_classes,3,1,1)的内容:

  • nn.Conv2d:表示使用二维卷积操作,即在二维空间上对输入进行卷积操作。
  • 64:表示该卷积层输入通道数为64。
  • num_classes:表示该卷积层输出通道数为num_classes。
  • 3:表示卷积核的大小为3x3。
  • 1:表示卷积操作的步长为1。在这里,步长指卷积核每次滑动的距离。
  • 1:表示卷积核周围的padding值为1。在这里,padding指在输入数据周围填充一圈值为0的像素点,以便于卷积核能够处理边缘像素点。

因此,这行代码的作用是创建一个包含64个输入通道和5个输出通道的卷积层,卷积核大小为3x3,步长为1,padding值为1。该卷积层可以被用于深度学习模型中,例如图像分类、物体检测或图像分割等任务。

  • 训练

        输入:训练集+测试集。输出:训练过程中效果图+unet.pth模型       

        调用data.py,util.py,net.py脚本,通过原始数据集路径+y_true路径,导入数据集。默认第16行设置一次使用一个批次大小为1张图片,默认第29行执行200个批次,每一轮打印一次损失,每50轮保存一次模型结果到./params指定文件路径。优化器用Adam,损失函数用CrossEntropyLoss多分类交叉熵。

        同时将每一批次的第一张图片保存到./trainning_image文件路径下

  • 测试

在Unet预测时,为了获取最终的像素级别的分割结果,需要进行如下几步操作:

将输入图像转换为PyTorch张量,并将其移动到GPU上(如果需要);
使用训练好的Unet模型对输入张量进行前向传播计算,得到输出预测张量 out;
对输出预测张量 out 沿着通道数的方向求出每个像素点的预测概率最大的类别编号,即使用 torch.argmax() 函数沿着第二个维度(即通道数)取最大值,得到形状为 (H, W) 的二维整数张量。
降低输出预测张量 out 的维度,以去掉不必要的批次大小维度和通道数维度。这里需要注意,在训练阶段中,由于输入数据通常是按批次加载(batch size),因此训练图像的形状通常是 (B, C, H, W),其中 B 表示批次大小,C 表示通道数(即类别数),H 和 W 分别表示图像的高度和宽度。但是在测试阶段中,一般只处理单张图像,因此需要通过 torch.unsqueeze() 方法添加一个新的批次大小维度,使输入张量形状变为 (1, C, H, W)。通过 torch.squeeze() 方法可以去掉多余的批次大小维度和通道数维度,从而得到形状为 (H, W) 的二维整数张量。但是为了满足后续可视化函数的要求,还需要再次添加一个批次大小维度(即使用 torch.unsqueeze() 方法)。
最终的输出预测结果 out 是一个形状为 (1, H, W) 的三维整数张量,表示模型对输入图像进行像素级别的分割结果。

        加载模型,输入:正常彩色图片路径,输出:y_pred灰度图预测结果并保存到./result中。输入原始图片后,将图片转化成tensor格式,然后加载模型,然后将[3,256,256]送入网络,然后得出结果。

        模型可视化,将灰度图*255得到高亮图。肉眼判断预测和真实值的准确度。内容: p = np.where(p == 1, 1, 0).astype(float)当预测图有多种类别时,留下标签颜色,其他类别颜色视为干扰色全部归为背景色0;

  • 评估模型

输入:y_ture路径+y_pred路径+类别列表[0,1,2....num_classes]。

输出【miou,recall,precision】

算两者混淆矩阵,计算MIOU

 

  • 部署

 11.损失函数

       和IOU不同的是,虽同为评估指标,但是多分类交叉熵和iou用到的地方不同。多分类交叉熵,用在train.py第25行,训练过程中,生成的y_pred图,每个像素点都有自己的类别。多分类交叉熵的作用是,矫正y_pred每一个像素点的分类结果和y_true真实值像素点类别,不断的调整优化模型参数。而IOU是用在模型训练好了,生成的y_pred和y_true的评估,来判断模型的好坏程度。

(1) Class entropy loss二分类或多分类交叉熵损失,对每个像素点做分类的损失。其中二分类交叉熵损失为:BCELOSS: binary_cross_entropy

(2) Focal loss,考虑到class像素点不均衡的情况,如:天空和人在一张图片中占比相差太大。可以给更大的权重某一类别,提高它的重要性。

 12.评估指标

(1) 平均交并比MIoU(Mean Intersection over Union), 类似于目标检测,计算两个集合的交并比,两个集合分别是真实值(Ground truth)和预测值(predicted segmentation),是一个交集与并集的比值,平均交并比为对各类的交并比进行平均。这样的评价指标可以判断目标的捕获程度(使预测标签与标注尽可能重合),也可以判断模型的精确程度(使并集尽可能重合)。本项目用到

 

IOU(Intersection Over Union)是一种用于衡量目标检测或分割模型性能的指标,而准确率(Accuracy)、召回率(Recall)和精确率(Precision)等则是广泛应用于机器学习分类任务中的指标。它们之间的区别如下:

  1. 计算方式:IOU通常用于目标检测或分割任务中,计算模型预测结果和真实标签之间的交集面积与并集面积之比。而准确率、召回率和精确率则是在二元或多元分类任务中,连续地比较模型预测结果和真实标签之间的相似度。

  2. IOU = TP/(FP+TP+FN)=15/(7+2+15+2+3)= 0.5172

  3. 对不平衡数据的敏感性:由于IOU计算的是交并比,因此在数据不平衡时可能会存在问题,因为模型可以通过更好地捕捉大类别的细节来获得更高的IOU值。而准确率、召回率和精确率可以更好地处理不平衡数据,因为它们只关注正确分类和错误分类的数量。

  4. 指标意义:IOU反映了目标检测或分割任务中模型预测结果和真实标签的相似程度,可以帮助我们评估模型对不同类别的分割效果。而准确率、召回率和精确率则更多地关注分类任务中的正确性和错误性,可以帮助我们评估模型对不同类别的分类效果。

  5. miou即各个类别iou的平均值,本项目是name_classes=1+1,iou/name_classes,如果是一个类别,name_classes=2(背景默认算做一个类别),如果是两个类别,name_classes=3。

(2) 像素准确率(Pixel Accuracy): 正确分类的像素数量与所有像素数量的比值.

(3) 像素准确率平均值(MPA):PA的变体,每个类内正确分类的像素数量和该类的所有像素点数 (Ground truth)的比值,之后求所有类的平均。

13.UNet论文

知网搜索:

                 U-Net: Convolutional Networks for Biomedical Image Segmentation

14.源码地址

免费永久链接:https://download.csdn.net/download/weixin_46412999/87690385

15.如何修改成自己的任务

a.)make_mask.py,第12行CLASS_NAMES修改成自己的类别(一定是打标签当时命名的类别),第27行如果你的原始图是jpg就将png修改成jpg

b.)train.py,第15行num_classes=n+1,n修改为你的类别数。第16行batch_size修改为你一轮想训练多少张图片。第 29行epoch修改为你想训练的轮次;第38行%1修改为每轮隔多少张图片打印一次损失,第47行%50修改为每多少轮保存一次模型

c.)handel_evaluation.py,第42行[0,1]修改为你的类别列表,如:三个类别就是[0,1,2,3],五个类别就是[0,1,2,3,4,5]

16.个人心得

从零开始:

了解语义分割在深度学习中定位

了解语义分割的原理

了解评估指标

了解并选择合适的语义分割UNet算法模型并研究论文

了解上采样、下采样、特征融合、encoder-decoder、图像处理等原理

了解gpu环境配置

了解到了计算机怎么理解处理图片数据(不同通道表示,计算一张图片中每个类别数:对所有像素去重)

手写所有脚本包括{{{{

手写make_mask.py,将打完标签的json格式转化y_ture

手写data.py,utils.py,rename.py等,数据预处理代码,

手写UNnet网络构造

手写训练代码

手写测试代码

手写MIOU评价指标代码}}}}

并将所有知识记录下来做成如您所看的博客

参考博主链接:

图像分割UNet硬核讲解(带你手撸unet代码)_哔哩哔哩_bilibili

17.项目落地应用

 

  • 104
    点赞
  • 778
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的使用OpenCV实现MNIST手写数字识别的C++代码示例: ``` #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <iostream> using namespace cv; using namespace cv::dnn; using namespace std; int main() { // 读取训练集和测试集图像数据和标签数据 Mat train_images, train_labels, test_images, test_labels; FileStorage fs_train_images("train-images-idx3-ubyte", FileStorage::READ); FileStorage fs_train_labels("train-labels-idx1-ubyte", FileStorage::READ); FileStorage fs_test_images("t10k-images-idx3-ubyte", FileStorage::READ); FileStorage fs_test_labels("t10k-labels-idx1-ubyte", FileStorage::READ); fs_train_images["images"] >> train_images; fs_train_labels["labels"] >> train_labels; fs_test_images["images"] >> test_images; fs_test_labels["labels"] >> test_labels; // 数据预处理:将像素值归一化到[0,1]之间 train_images.convertTo(train_images, CV_32F, 1.0 / 255); test_images.convertTo(test_images, CV_32F, 1.0 / 255); // 数据预处理:将标签数据转化为独热编码 Mat train_labels_onehot = Mat::zeros(train_labels.rows, 10, CV_32F); for (int i = 0; i < train_labels.rows; i++) { train_labels_onehot.at<float>(i, train_labels.at<uchar>(i, 0)) = 1.0; } // 构建神经网络模型 Net net = readNetFromTensorflow("mnist_model.pb"); // 训练神经网络模型 net.setPreferableBackend(DNN_BACKEND_OPENCV); net.setPreferableTarget(DNN_TARGET_CPU); net.setInput(train_images); net.forward(); Mat output = net.getLayer("dense_2")->output; Mat loss = Mat::zeros(1, 1, CV_32F); for (int i = 0; i < train_labels.rows; i++) { Mat label = train_labels_onehot.row(i); Mat pred = output.row(i); loss += -label.dot(Mat(pred.t()).log()) - (1 - label).dot(Mat(1 - pred.t()).log()); } cout << "Training loss: " << loss.at<float>(0, 0) / train_labels.rows << endl; // 对测试集进行预测,并计算准确率 int correct = 0; for (int i = 0; i < test_images.rows; i++) { Mat input = test_images.row(i); net.setInput(input); Mat pred = net.forward(); int label = test_labels.at<uchar>(i, 0); if (label == pred.at<float>(0, 0)) { correct++; } } float accuracy = (float)correct / test_images.rows; cout << "Test accuracy: " << accuracy << endl; return 0; } ``` 需要注意的是,上述代码中使用了一个已经训练好的神经网络模型,并将其保存为TensorFlow的pb文件,读取模型时使用了OpenCV中的readNetFromTensorflow()函数。如果想要自己训练模型,则需要使用其他的神经网络框架进行训练,并将训练好的模型保存为TensorFlow的pb文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值