Сегментация изображения — это процесс разделения изображения на разные области или сегменты на основе определенных критериев, таких как цвет, текстура или интенсивность. Это фундаментальная задача компьютерного зрения, которая используется в различных приложениях, таких как обнаружение объектов, анализ медицинских изображений и автономное вождение.

TensorFlow — это популярная платформа машинного обучения с открытым исходным кодом, которая предоставляет мощные инструменты для создания и обучения моделей глубокого обучения, в том числе для сегментации изображений.

Цель

В этом проекте мы будем использовать инфраструктуру машинного обучения TensorFlow для обучения и оценки нейронной сети сегментации изображений с использованием набора данных медицинских изображений. Мы выполним семантическую сегментацию, чтобы классифицировать каждый пиксель на МРТ-изображении сердца, независимо от того, является ли пиксель частью левого желудочка (ЛЖ) или нет.

Набор данных

Вам нужно будет зарегистрироваться на Веб-сайте Cardiac MR Left Ventricle Segmentation Challenge, и ссылки для скачивания набора данных будут отправлены вам по электронной почте. Набор данных представляет собой серию изображений сердца (в частности, МРТ-сканов с короткой осью (SAX)) с экспертной маркировкой.

Репрезентативный пример данных показан выше. Слева — МРТ-изображения, а справа — экспертно-сегментированные области (часто называемые контурами). Части изображений, входящие в состав LV, обозначены белым цветом. Извлечение данных из необработанных изображений и последующая подготовка этих изображений в этой статье не рассматриваются.

1. Визуализация набора данных

Мы определим функцию «отображение» для отображения изображения и его метки, а затем запустим ее в нашем обучении.

# function to display an image, it's label 
def display(display_list):
    plt.figure(figsize=(10, 10))
    title = ['Input Image', 'Label']

    for i in range(len(display_list)):
        display_resized = tf.reshape(display_list[i], [256, 256])
        plt.subplot(1, len(display_list), i+1)
        plt.title(title[i])
        plt.imshow(display_resized)
        plt.axis('off')
    plt.show()

# display 3 random images and labels from the training set
for image, label in train.take(3):
    sample_image, sample_label = image, label
    display([sample_image, sample_label])

Вывод:

# an image and label from validation data
for image, label in val.take(1):
    sample_image, sample_label = image, label
    display([sample_image, sample_label])

Вывод:

2. Построение модели

Входными данными будут значения каждого пикселя, и, поскольку изображения черно-белые, мы будем использовать 1 цветовой канал. Слой ожидает векторное представление, а не матрицу, поэтому мы сгладим матричное представление изображений. Скрытый плотный слой будет иметь размер, который вы можете настроить на любое положительное целое число.

Каждый входной пиксель может относиться к двум классам; Левый желудочек (LV) или нет, поэтому это будет вывод. Затем мы изменим форму вектора, чтобы просмотреть его как изображение.

tf.keras.backend.clear_session()

# set up the model architecture
model = tf.keras.models.Sequential([
    Flatten(input_shape=[256, 256, 1]),
    Dense(64, activation='relu'),
    Dense(256*256*2, activation='softmax'),
    Reshape((256, 256, 2))
])

# specify how to train the model with algorithm, the loss function and metrics
model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy'])

# plot the model including the sizes of the model
tf.keras.utils.plot_model(model, show_shapes=True)

Вывод:

3. Обучение модели

EPOCHS = 20
STEPS_PER_EPOCH = len(list(parsed_train_data))
VALIDATION_STEPS = 26

model_history = model.fit(train_dataset, epochs=EPOCHS,    
                          steps_per_epoch=STEPS_PER_EPOCH,
                          validation_steps=VALIDATION_STEPS,
                          validation_data=test_dataset,
                         callbacks=[tensorboard_callback])  

Вывод:

Код будет выводить каждую эпоху и статистику ее модели.

4. Оценка модели

Оценка производительности модели.

# output model statistics
loss = model_history.history['loss']
val_loss = model_history.history['val_loss']
accuracy = model_history.history['accuracy']
val_accuracy = model_history.history['val_accuracy']

epochs = range(EPOCHS)

plt.figure()
plt.plot(epochs, loss, 'r', label='Training loss')
plt.plot(epochs, val_loss, 'bo', label='Validation loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss Value')
plt.ylim([0, 1])
plt.legend()
plt.show()

Вывод:

#model evaluation on the test dataset
model.evaluate(test_dataset)

Модель имела потери 0,6931 и точность 0,986. Модель не работала хорошо, так как функция потерь очень высока.

5. CNN с Dice Metric Loss

Одна метрика, которую мы можем использовать для более точного определения того, насколько хорошо наша сеть сегментирует LV, среди прочего называется метрикой Дайса или коэффициентом Соренсена-Дайса. Это метрика для сравнения сходства двух образцов. В нашем случае мы будем использовать его для сравнения двух областей интереса, то есть площади контура, помеченного экспертом, и площади нашего прогнозируемого контура.

#dice coef function
def dice_coef(y_true, y_pred, smooth=1):
    indices = K.argmax(y_pred, 3)
    indices = K.reshape(indices, [-1, 256, 256, 1])

    true_cast = y_true
    indices_cast = K.cast(indices, dtype='float32')

    axis = [1, 2, 3]
    intersection = K.sum(true_cast * indices_cast, axis=axis)
    union = K.sum(true_cast, axis=axis) + K.sum(indices_cast, axis=axis)
    dice = K.mean((2. * intersection + smooth)/(union + smooth), axis=0)

    return dice

#clear the backend session to free up memory
tf.keras.backend.clear_session()

#define the layers of the model architecture
layers = [
    Conv2D(input_shape=[256, 256, 1],
           filters=100,
           kernel_size=5,
           strides=2,
           padding="same",
           activation=tf.nn.relu,
           name="Conv1"),
    MaxPool2D(pool_size=2, strides=2, padding="same"),
    Conv2D(filters=200,
           kernel_size=5,
           strides=2,
           padding="same",
           activation=tf.nn.relu),
    MaxPool2D(pool_size=2, strides=2, padding="same"),
    Conv2D(filters=300,
           kernel_size=3,
           strides=1,
           padding="same",
           activation=tf.nn.relu),
    Conv2D(filters=300,
           kernel_size=3,
           strides=1,
           padding="same",
           activation=tf.nn.relu),
    Conv2D(filters=2,
           kernel_size=1,
           strides=1,
           padding="same",
           activation=tf.nn.relu),
    Conv2DTranspose(filters=2, kernel_size=31, strides=16, padding="same")
]

#create a sequential model with the defined layers
model = tf.keras.models.Sequential(layers)

#compiling the model
model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy'])
#setting up and running the model
EPOCHS = 20
STEPS_PER_EPOCH = len(list(parsed_train_data))
VALIDATION_STEPS = 26

model_history = model.fit(train_dataset, epochs=EPOCHS,
                          steps_per_epoch=STEPS_PER_EPOCH,
                          validation_steps=VALIDATION_STEPS,
                          validation_data=test_dataset)

Вывод:

Модель имела убыток 0,0871 и точность 0,9830.

Мы снова запустим модель на этот раз с 30 эпохами и измерим потери метрики в кости.

tf.keras.backend.clear_session() 

layers=layers # the layers in our model architecture

model = tf.keras.models.Sequential(layers) # the model

model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[dice_coef,'accuracy'])

# setting up and running the model using 30 passes
EPOCHS = 30
STEPS_PER_EPOCH = len(list(parsed_train_data))

model_history = model.fit(train_dataset, epochs=EPOCHS,
                          steps_per_epoch=STEPS_PER_EPOCH,
                          validation_data=test_dataset)

#evaluating the model using the test dataset
model.evaluate(test_dataset)

Вывод:

Модель улучшилась, дав потери 0,0863, коэффициент кости 0,049 и точность 0,983.