Эта часть является продолжением части 1 сегментации изображений https://medium.com/p/ba934d61f91c

Оглавление:

  • Краткий обзор того, что такое сегментация изображений
  • Код для реализации архитектуры Unet с нуля
  • Объяснение кода
  • Чем сегментация изображения отличается от классификации изображений и обнаружения объектов
  • Рекомендации

Что такое сегментация изображений?

Я объяснил, что такое сегментация изображений, в предыдущей части, но я бы все же обсудил это здесь, чтобы у людей, читающих блог, была преемственность. Что касается сегментации изображения, то изображения, которые у нас есть, состоят из нескольких пикселей, теперь, используя сегментацию, мы пытаемся классифицировать пиксели этого изображения по некоторым классам. Например рассмотрим изображение, на котором есть дерево, озеро (любой водоем), дороги, и давайте пока рассмотрим все остальные объекты, присутствующие на изображении, которые нам не нужны. Здесь может быть 4 класса, и это дерево, водоем, дорога и другие объекты, которые нам не нужны, могут быть отнесены к классу нежелательных объектов. Идеальная модель сегментации изображения будет правильно предсказывать каждый пиксель изображения в соответствующих классах. Таким образом, здесь наряду с классом объекта мы также получаем понимание того, откуда и где объект присутствует на данном изображении, и это понимание может быть очень полезным в таких приложениях, как самоуправляемые автомобили.

Код для реализации архитектуры Unet с нуля и объяснение кода —

Кодировщик

В этой части я попытаюсь реализовать Unet с использованием библиотеки Keras с нуля. Архитектура Unet состоит из двух частей: кодировщик и декодер. Кодировщик может быть создан либо с нуля, либо мы можем использовать модель переноса обучения. Здесь, в этой части, которая является частью 2 сегментации, я буду объяснять, как кодировать Unet с нуля.

Сначала мы начнем с кодирующей части Unet. Пожалуйста, обратитесь к изображению выше для части кодировщика. Обратитесь к приведенному ниже коду для архитектуры кодировщика с использованием Keras.

Прежде всего импортируйте все необходимые библиотеки, как показано ниже.

from keras.layers import Conv2D, MaxPooling2D, UpSampling2D
from keras.layers import Input, add, concatenate
from keras.models import Model
from keras.optimizers import *

Архитектура модели для Encoder с нуля

skip_connections = []

input_img_shape = Input(shape = (512, 512))
en = Conv2D(filters = 8, kernel_size = (3, 3), activation = 'relu', padding = 'same')(input_img_shape)
en = Conv2D(filters = 8, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
skip_connections.append(en)
en = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(en)

en = Conv2D(filters = 16, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
en = Conv2D(filters = 16, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
skip_connections.append(en)
en = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(en)

en = Conv2D(filters = 32, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
en = Conv2D(filters = 32, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
skip_connections.append(en)
en = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(en)

en = Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
en = Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same')(en)
skip_connections.append(en)
en = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(en)

Приведенный выше фрагмент кода предназначен для кодировщика для архитектуры Unet. В кодировщике мы используем

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

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

Декодер

Декодер в Unet всегда нужно создавать с нуля, даже если мы пытаемся использовать предварительно обученную модель в части кодировщика, в зависимости от фильтров и слоев, используемых в кодировщике, мы пытаемся создать декодер с нуля. Декодер в основном пытается восстановить замаскированное изображение из признаков, полученных с помощью кодировщика. Теперь вы можете подумать, что такое замаскированное изображение, поэтому для этого вопроса я прикреплю изображение того, как обычно выглядит замаскированное изображение или маска.

Исходное изображение

Маска изображения

Архитектура модели для декодера

de = Upsampling2D(size = (2, 2))(en)
de = Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = concatenate([skip_connections[3], de])
de = Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)

de = Upsampling2D(size = (2, 2))(de)
de = Conv2D(filters = 32, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = concatenate([skip_connections[2], de])
de = Conv2D(filters = 32, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = Conv2D(filters = 32, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)

de = Upsampling2D(size = (2, 2))(de)
de = Conv2D(filters = 16, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = concatenate([skip_connections[1], de])
de = Conv2D(filters = 16, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = Conv2D(filters = 16, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)

de = Upsampling2D(size = (2, 2))(de)
de = Conv2D(filters = 8, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = concatenate([skip_connections[0], de])
de = Conv2D(filters = 8, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)
de = Conv2D(filters = 8, kernel_size = (3, 3), activation = 'relu', padding = 'same')(de)

Декодер использует

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

Чем сегментация изображения отличается от классификации изображений и обнаружения объектов?

  • В классификации изображений мы просто пытаемся идентифицировать объект, присутствующий на изображении.
  • В Обнаружении объектов мы пытаемся классифицировать объекты, присутствующие на изображении, а также локализовать объекты на изображении.
  • В сегментации изображения мы пытаемся классифицировать каждый пиксель, присутствующий в изображении.

Ссылки

  1. https://medium.com/@hanrelan/a-non-experts-guide-to-image-segmentation-using-deep-neural-nets-dda5022f6282
  2. https://github.com/petrosgk/Kaggle-Carvana-Image-Masking-Challenge/
  3. https://github.com/petrosgk/Kaggle-Carvana-Image-Masking-Challenge/blob/master/model/u_net.py

Спасибо всем за чтение этого блога. Я только новичок в написании блогов, поэтому прошу всех вас оставлять комментарии, отзывы и предложения, если таковые имеются, а также исправлять меня в комментариях, если вы обнаружите какие-либо ошибки. Надеюсь, что этот блог поможет вам получить общее представление об архитектуре unet.

Это часть 2 серии «Сегментация изображений», в которой я объяснил фрагменты кода модели или, в частности, модели unet, в следующих частях этой серии я объясню, как подавать входные данные в эту модель, как создавать входные данные и многое другое, так что следите за обновлениями ;)