一 图像分割是一种将图像划分为不同区域或对象的过程

通常在像素级别进行,通过将图像中具有相似特征的区域分组或定义对象的边界来完成,这是一种识别和解析图像中不同对象或特征的方法。

试图找到潜在的癌性病变,图像分割发挥作用的地方,分割过程用于识别图像中的不同组织和结构,在区分癌细胞和其他正常组织方面发挥着重要作用。

下面的脑部扫描,分割已经识别出癌性肿瘤并以不同的颜色显示。

尽管U-Net专注于生物医学图像,其灵活的结构允许它有效的用于其他类型的图像数据

input image tile ---->copy crop---> output segmentation map

U-net 的命名是因为结构类似于字母U,我们在输出端得到分割后的输入图像,U-net的架构师独特的,因为它由收缩路径和扩展路径组成。

收缩路径编码器 从输入图像中提取属性图,而扩展路径解码器,将这些属性转换回更高分辨率的形式,跳跃连接允许低级和高级属性结合,从而实现更好的分割性能。

我们将使用U-net架构对Kvasir数据集中的图像进行分割。

import os
import cv2
import random
import numpy as np

import matplotlib.pyplot as plt
%matplotlib inline

from sklearn.model_selection import train_test_dplit
from skimage.io import imread, imshow
from skimage.transform import resize
from skimage.color import rgb2gray

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras impor backend as K
from tensorflow.keras.models import Mode, load_model, save_model
from tensorflow.keras.optimizers import Adam, Adamax
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.layers import Input, Activation, BatchNormalization, Dropout, Lambda, Conv2d, Conv2DTranspose, MaxPooling2D, concatenate

import warnigs
warnings.filterwarnings("ignore")

然后我们u-net 输入图像定义特定大小变量

IMG_CHANNELS = 3

IMG_WIDTH = 256

IMG_HEIGHT = 256

由于我们ziakaggle运行我们程序我们以下方式访问我们数据获取它们id图像名称

images_path = "/kaggle/input/kvasir-dataset-for-classification-and-segmentation/kvasir-seg/Kvasir-SEG/images"

mask_path = "/kaggle/input/kvasir-dataset-for-classification-and-segmentation/kvasir-seg/Kvasir-SEG/masks"

img_ids = next(os.walk(images_path))[2]

mask_ids = next(os.walk(mask_path))[2]

X = np.zeros(len(img_ids), 256, 256, 3), dtype=np.uint8

y = np.zeros(len(mask_ids), 256, 256, 1), dtype = np.bool

这些开头数据结构数据预处理步骤一部分实际的图像标签数据随后填充这些结构用于模型训练这个预处理步骤清洁组织准备数据训练模型过程一部分

输入图像创建一个数组存储输入图像在这个数组中图像大小为256x256像素并且3颜色通道RGB然而最初这些图像内容指定填充0.

目标标签y目标标签代表模型应该学习正确输出这个数组也包含具有256x256像素大小掩码但是只有一个颜色通道 黑白最初这些掩码填充

现在我们创建我们输入图像提供给我们模型

for n,img in enumerate(os.listdir(images_path)):
    file_path = os.path.join(images_path, img)
    image = imread(file_path)
    image = resize(image, (256, 256), mode="constant", preserve_range=True)
    X[in] = image
    
for n, mask in enumerate(os.listdir(mask_path)):
    file_path = os.path.join(mask_path, mask)
    mask = imread(file_path)
    mask = rgb2gray(mask)
    mask = np.expand_dims(resize(mask, (256, 256), mode="constant", preserve_range=True), axis=-1)
    y[n] = mask

这个脚本给定目录images_path 获取图像文件然后每张图像调整给的大小256x256像素保存numpy数组(x)掩码也执行相同过程掩码也被调整相同大小保存numpy数组y结果数组X包含处理图像数组y包含处理后掩码我们看一个示例图像及其掩码

使用train_test_split函数自动化分割拆分用于训练评估性能数据集过程允许模型使用训练数据进行训练然后使用拆分测试数据评估性能

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=32)

print(f"X_train.shape: {X_train.shape}\nY_train.shape: {y_train.shape}")

我们正在编写我们的U-net模型架构

input = tf.keras.layers.Input((256, 256, 3))

# reduce image config
s = tf.keras.layers.Lambda(lambda x: x / 256)(input)

# block 1
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation="relu", kernel_initializer='he_normal', padding='same')(s)
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1)
c1 = tf.keras.layers.Dropout(0.1)(c1)
c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1)
p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1)

# block 2
c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1)
c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2)
c2 = tf.keras.layers.Dropout(0.1)(c2)
c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2)
p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2)

# block 3
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2)
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3)
c3  = tf.keras.layers.Dropout(0.2)(c3)
c3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3)
p3 = tf.keras.layers.MaxPooling2D((2, 2))(c3)

# block 4
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3)
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4)
c4 = tf.keras.layers.Dropout(0.2)(c4)
c4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4)
p4 = tf.keras.layers.MaxPooling2D((2, 2))(c4)

# block 5
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4)
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)
c5 = tf.keras.layers.Dropout(0.3)(c5)
c5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)
p5 = tf.keras.layers.MaxPooling2D((2, 2))(c5)

# block 6
c6 = tf.keras.layers.Conv2D(512, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p5)
c6 = tf.keras.layers.Conv2D(512, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6)
c6 = tf.keras.layers.Dropout(0.3)(c6)
c6 = tf.keras.layers.Conv2D(512, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6)

# block 7 - back 1
u7 = tf.keras.layers.Conv2DTranspose(256, (2, 2), strides=(2, 2), padding='same')(c6)
u7 = tf.keras.layers.concatenate([u7, c5])
c7 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7)
c7 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7)
c7 = tf.keras.layers.Dropout(0.3)(c7)
c7 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7)

# block 8 - back 2
u8 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c7)
u8 = tf.keras.layers.concatenate([u8, c4])
c8 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8)
c8 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8)
c8 = tf.keras.layers.Dropout(0.2)(c8)
c8 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8)

# block 9 - back 3
u9 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c8)
u9 = tf.keras.layers.concatenate([u9, c3])
c9 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9)
c9 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)
c9 = tf.keras.layers.Dropout(0.2)(c9)
c9 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)

# block 10 - back 4
u10 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c9)
u10 = tf.keras.layers.concatenate([u10, c2])
c10 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u10)
c10 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c10)
c10 = tf.keras.layers.Dropout(0.1)(c10)
c10 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c10)

# block 11 - back 5
u11 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c10)
u11 = tf.keras.layers.concatenate([u11, c1])
c11 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u11)
c11 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c11)
c11 = tf.keras.layers.Dropout(0.1)(c11)
c11 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c11)

outputs = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c11)

model = tf.keras.Model(inputs=input, outputs=outputs, name='U-NET')

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy','iou_coef'])

Adam优化器是一种训练期间更新权重优化算法Adam使用自适应估计处理大型数据集复杂模型特别有效

我们使用binary crossentropy作为损失函数Binary crossentropy是一种用于分类问题损失函数这个损失函数通过计算两个类别之间的差异通过比较模型输出实际标签计算误差优化了模型学习过程在这里我们执行像素分割我们检查图像每个像素属于一个类别有两个类别背景对象我们喜欢使用binary cross entropy损失函数

我们评估指标看到iou_coef指标这个指标我们评估我们分割问题成功程度iou_coef也被称为交集并集IOU系数衡量模型预测分割结果实际分割结果匹配程度这个指标越高模型分割性能就越好因此IOU系数确定模型执行分割准确重要指标

现在我们代码包括iou_score:

def iou_coef(y_true, y_pred, smooth=100):

intersection = K.sum(y_true * y_pred)

sum = K.sum(y_true + y_pred)

iou = (intersection + smooth) / (sum - intersection + smooth)

return iou

我们开始训练

model.fit(X_train, y_train, validation_split=0.1, batch_size=8, epochs=100)

iou_coef 的值在 0 和 1 之间,越接近 1,模型的性能越好。0.8496 的 IOU 值意味着模型的预测与实际分割掩码很好地重叠。这意味着模型的预测相当准确地识别了真实图像中的对象。现在,我们进入模型将在测试数据上进行预测的阶段:

ind = random.randint(0, len(X_test))
img = X_test[ind]
predictions = model.predict(np.expand_dims(img, axis=0), verbose=0)
plt.figure(figsize=(15, 12))
plt.ubplot(1, 3, 1)
plt.title("original image")
plt.imshow(np.squeeze(img))
plt.subplot(1, 3, 2)
plt.title("predicted mask")
plt.imshow(np.squeeze(predictions))
plt.subplot(1, 3, 3)
plt.imshow(np.squeeze(img))
plt.imshow(np.squeeze(predictions), alpha=0.5)
plt.show()

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐