图像语义分割
传统的语义分割
大多都是基于像素的分割,由于是对像素变化敏感,所以分割出来的效果往往与人眼所判断出来的效果有很大的不同。
SuperPixel
将像素按特性(如颜色或纹理等)划分到不同的块中,这个块就叫超像素。
通过对超像素的特征提取进而能实现识别。同时降低了运行时间。
传统图像分割问题
基于图像的语义分割
思路:“编码——解码”
编码:使用CNN将图像映射到特征空间,激活情况标志着“这里是什么”
解码:将低分辨率的特征图映射回原图尺寸,为每个像素标记一个类别
典型方法:U-Net
U-net网络由一个收缩路径和一个扩张路径组成。其中,收缩路径遵循典型的卷积网络结构,其由两个重复的33卷积核组成,都使用激活函数ReLU和一个用于下采样的步长为2的22最大池化操作,以及在每一个下采样的步骤中,特征通道数量都加倍。在扩张路径中,每一步都包含对特征图进行上采样;然后用22的卷积核进行卷积运算,用于减少一半的特征通道数量;接着级联收缩路径中相应的裁剪后的特征图;再用两个33的卷积核进行卷积运算,且均使用ReLU激活函数。由于在每次卷积操作中,边界像素存在缺失问题,因此有必要对特征图进行裁剪。在最后一层,利用1*1的卷积核进行卷积运算,将每个64维的特征向量映射网络的输出层。总而言之,该网络有23个卷积层。
案例:利用U-Net进行语义分割
数据集:Oxford-IIIT Pets dataset数据集的一个子集
7390张图片,内容还是猫猫狗狗
跟上次不同的是,这次我们希望找出猫猫狗狗的轮廓
参考教程( U-Net 变种,不是原版架构):https://keras.io/examples/vision/oxford_pets_image_segmentation/
import os
input_dir = "ex06/images/images/"
target_dir = "ex06/annotations/annotations/trimaps/"
img_size = (160, 160)
num_classes = 4
batch_size = 32
input_img_paths = sorted(
[
os.path.join(input_dir, fname)
for fname in os.listdir(input_dir)
if fname.endswith(".jpg")
]
)
target_img_paths = sorted(
[
os.path.join(target_dir, fname)
for fname in os.listdir(target_dir)
if fname.endswith(".png") and not fname.startswith(".")
]
)
print("Number of samples:", len(input_img_paths))
for input_path, target_path in zip(input_img_paths[:10], target_img_paths[:10]):
print(input_path, "|", target_path) #将图像和对应的分割蒙版的文件名一起输出
from IPython.display import Image, display
from tensorflow.keras.preprocessing.image import load_img
import PIL
from PIL import ImageOps
# Display input image #7
display(Image(filename=input_img_paths[9])) #展示图像
# Display auto-contrast version of corresponding target (per-pixel categories)
#ImageOps.autocontrast最大图像对比度。这个函数计算一个输入图像的直方图,从这个直方图中去除最亮和最暗的百分之cutoff,然后重新映射图像,以便保留的最暗像素变为黑色,即0,最亮的变为白色,即255。
img = PIL.ImageOps