Tensorflow U- Net ввод маски сегментации - PullRequest
2 голосов
/ 01 марта 2020

Я новичок в тензорном потоке и сегментации Семанти c.

Я проектирую U- Net для сегмента c. Каждое изображение имеет один объект, который я хочу классифицировать. Но в общей сложности у меня есть изображения 10 различных объектов. Я запутался, как я могу подготовить свой ввод маски? Считается ли это сегментацией с несколькими метками или только для одного класса?

Должен ли я преобразовать свой вход в один код в горячем виде? Должен ли я использовать to_categorical? Я нахожу примеры для мультиклассовой сегментации, но я не знаю, если это так. Потому что на одном изображении у меня есть только один объект для обнаружения / классификации.

Я пытался использовать это в качестве кода для ввода. Но я не уверен, что я делаю правильно или нет.

#Generation of batches of image and mask
class DataGen(keras.utils.Sequence):
    def __init__(self, image_names, path, batch_size, image_size=128):
        self.image_names = image_names
        self.path = path
        self.batch_size = batch_size
        self.image_size = image_size

    def __load__(self, image_name):
        # Path
        image_path = os.path.join(self.path, "images/aug_test", image_name) + ".png"
        mask_path = os.path.join(self.path, "masks/aug_test",image_name) +  ".png"

        # Reading Image
        image = cv2.imread(image_path, 1)
        image = cv2.resize(image, (self.image_size, self.image_size))


        # Reading Mask
        mask = cv2.imread(mask_path, -1)
        mask = cv2.resize(mask, (self.image_size, self.image_size))

        ## Normalizaing 
        image = image/255.0
        mask = mask/255.0

        return image, mask

    def __getitem__(self, index):
        if(index+1)*self.batch_size > len(self.image_names):
            self.batch_size = len(self.image_names) - index*self.batch_size

        image_batch = self.image_names[index*self.batch_size : (index+1)*self.batch_size]

        image = []
        mask  = []

        for image_name in image_batch:
            _img, _mask = self.__load__(image_name)
            image.append(_img)
            mask.append(_mask)

        #This is where I am defining my input
        image = np.array(image)
        mask  = np.array(mask)
        mask = tf.keras.utils.to_categorical(mask, num_classes=10, dtype='float32') #Is this true?


        return image, mask

    def __len__(self):
        return int(np.ceil(len(self.image_names)/float(self.batch_size)))

Это правда? Если это так, чтобы получить метку / класс как вывод, что я должен изменить в своем вводе? Должен ли я изменить значение пикселя моей маски в соответствии с моим классом?

Вот моя архитектура U- Net.

# Convolution and deconvolution Blocks

def down_scaling_block(x, filters, kernel_size=(3, 3), padding="same", strides=1):
    conv = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(x)
    conv = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(conv)
    pool = keras.layers.MaxPool2D((2, 2), (2, 2))(conv)
    return conv, pool

def up_scaling_block(x, skip, filters, kernel_size=(3, 3), padding="same", strides=1):
    conv_t = keras.layers.UpSampling2D((2, 2))(x)
    concat = keras.layers.Concatenate()([conv_t, skip])
    conv = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(concat)
    conv = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(conv)
    return conv

def bottleneck(x, filters, kernel_size=(3, 3), padding="same", strides=1):
    conv = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(x)
    conv = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(conv)
    return conv

def UNet():
    filters = [16, 32, 64, 128, 256]
    inputs = keras.layers.Input((image_size, image_size, 3))

    '''inputs2 = keras.layers.Input((image_size, image_size, 1))
       conv1_2, pool1_2 = down_scaling_block(inputs2, filters[0])'''

    Input = inputs
    conv1, pool1 = down_scaling_block(Input, filters[0])
    conv2, pool2 = down_scaling_block(pool1, filters[1])
    conv3, pool3 = down_scaling_block(pool2, filters[2])
    '''conv3 = keras.layers.Conv2D(filters[2], kernel_size=(3,3), padding="same", strides=1, activation="relu")(pool2)
    conv3 = keras.layers.Conv2D(filters[2], kernel_size=(3,3), padding="same", strides=1, activation="relu")(conv3)
    drop3 = keras.layers.Dropout(0.5)(conv3)
    pool3 = keras.layers.MaxPooling2D((2,2), (2,2))(drop3)'''
    conv4, pool4 = down_scaling_block(pool3, filters[3])

    bn = bottleneck(pool4, filters[4])

    deConv1 = up_scaling_block(bn, conv4, filters[3]) #8 -> 16
    deConv2 = up_scaling_block(deConv1, conv3, filters[2]) #16 -> 32
    deConv3 = up_scaling_block(deConv2, conv2, filters[1]) #32 -> 64
    deConv4 = up_scaling_block(deConv3, conv1, filters[0]) #64 -> 128

    outputs = keras.layers.Conv2D(10, (1, 1), padding="same", activation="softmax")(deConv4)
    model = keras.models.Model(inputs, outputs)
    return model

model = UNet()
model.compile(optimizer='adam', loss="categorical_crossentropy", metrics=["acc"])

train_gen = DataGen(train_img, train_path, image_size=image_size, batch_size=batch_size)
valid_gen = DataGen(valid_img, train_path, image_size=image_size, batch_size=batch_size)
test_gen = DataGen(test_img, test_path, image_size=image_size, batch_size=batch_size)

train_steps = len(train_img)//batch_size
valid_steps = len(valid_img)//batch_size

model.fit_generator(train_gen, validation_data=valid_gen, steps_per_epoch=train_steps, validation_steps=valid_steps, 
                    epochs=epochs)

Надеюсь, я правильно объяснил свой вопрос. Любая помощь оценена!

ОБНОВЛЕНИЕ: Я изменил значение каждого пикселя в маске в соответствии с классом объекта. (Если изображение содержит объект, который я хочу классифицировать как объект № 2, то я изменил значение пикселя маски на 2. Весь массив маски будет содержать 0 (bg) и 2 (объект). Соответственно для каждого объекта, маска будет содержать 0 и 3, 0 и 10 и т. д. c.)

Здесь я сначала изменил маску на двоичную, а затем, если значение пикселя больше 1, я изменил его на 1 или 2 или 3. (согласно номеру объекта / класса)

Затем я преобразовал их в one_hot с помощью to_categorical, как показано в моем коде. обучение идет, но сеть ничего не изучает. Точность и потеря продолжают колебаться между двумя значениями. В чем моя ошибка здесь? Я делаю ошибку при создании маски (изменяя значение в пикселях?) Или в функции to_categorical?

ПРОБЛЕМА НАЙДЕНА: Я делал ошибку при создании маски ... Я читал изображение с помощью cv2, который читает image as heightxwidth ... Я создавал маску со значениями пикселей в соответствии с классом, после того, как учел размер моего изображения как widthxheight ... Что вызывало проблему и заставляло сеть ничего не изучать .. Это работает сейчас ..

1 Ответ

1 голос
/ 04 марта 2020

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

Если ваш набор данных имеет N различных меток (т. Е. 0 - фон, 1 - собаки, 2 - кошки ...), вы есть проблема с несколькими классами, даже если ваши изображения содержат только вид объекта.

Должен ли я преобразовать свой ввод в один код в горячем виде? Должен ли я использовать to_categorical?

Да, вы должны в горячем виде кодировать свои метки. Использование to_categorical сводится к исходному формату ваших ярлыков. Скажем, у вас есть N классов и ваши метки (высота, ширина, 1), где каждый пиксель имеет значение в диапазоне [0, N). В этом случае keras.utils.to_categorical (label, N) предоставит метку с плавающей запятой (высота, ширина, N), где каждый пиксель равен 0 или 1. И вам не нужно делить на 255 .

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

Проверьте этот репозиторий (не моя работа): keras- unet. Папка notebooks содержит два примера для обучения u- net на небольших наборах данных. Они не мультиклассы, но легко go шаг за шагом использовать свой собственный набор данных. Начните с загрузки ваших ярлыков следующим образом:

im = Image.open(mask).resize((512,512))
im = to_categorical(im,NCLASSES)

измените форму и нормализуйте ее следующим образом:

x = np.asarray(imgs_np, dtype=np.float32)/255
y = np.asarray(masks_np, dtype=np.float32)
y = y.reshape(y.shape[0], y.shape[1], y.shape[2], NCLASSES)
x = x.reshape(x.shape[0], x.shape[1], x.shape[2], 3)

адаптируйте вашу модель к NCLASSES

model = custom_unet(
input_shape,
use_batch_norm=False,
num_classes=NCLASSES,
filters=64,
dropout=0.2,
output_activation='softmax')

выберите правильный убыток:

from keras.losses import categorical_crossentropy
model.compile(    
   optimizer=SGD(lr=0.01, momentum=0.99),
   loss='categorical_crossentropy',    
   metrics=[iou, iou_thresholded])

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...