Сегментация имеет решающее значение для задач интерпретации изображений. Тогда не отставайте от тренда. Давайте реализуем это, и вы в кратчайшие сроки станете профессионалом!

Что такое семантическая сегментация?

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

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

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

Семантическая сегментация обычно используется для:

  • Автономное вождение
  • Промышленный осмотр
  • Классификация территорий, заметных на спутниковых снимках
  • Обзор медицинской визуализации

Реализация семантической сегментации:

  1. Первый подход - это скользящее окно, когда мы берем наше входное изображение и разбиваем его на множество маленьких, крошечных локальных фрагментов изображения, но я надеюсь, вы уже догадались, что это быть дорогостоящим в вычислительном отношении. Так что мы не используем его на практике.
  2. Другой подход - полностью сверточная сеть, в которой сеть имеет целый гигантский стек сверточных слоев без полностью связанных слоев, что сохраняет пространственный размер входных данных. Это также было бы чрезвычайно затратно в вычислительном отношении.
  3. Третий и самый лучший - это Повышение и Понижение дискретизации изображения. Поэтому вместо того, чтобы выполнять все свертки для полного пространственного разрешения изображения, мы, возможно, пройдем через небольшое количество сверточных слоев с исходным разрешением, затем уменьшим дискретизацию этой карты функций, а затем увеличим ее.
    Здесь мы просто хотите улучшить пространственное разрешение наших прогнозов во второй половине сети, чтобы наше выходное изображение теперь могло иметь то же измерение, что и наше входное изображение. Это намного эффективнее с точки зрения вычислений. потому что вы можете сделать сеть очень глубокой и работать с более дешевым пространственным разрешением.

Итак, давайте реализуем это в коде:

  • Импортируйте необходимые библиотеки, необходимые для обработки, т.е.
    важные функции Pytorch, такие как загрузчик данных, переменные, функции преобразования и оптимизатора,
    Импорт класса набора данных для VOC12 и городских пейзажей, Импорт классов Relabel,
    ToLabel и Colorize из файла transform.py,
    импорт класса iouEval из файла iouEval.py.
#SSCV IIITH 2K19
import random
import time
import numpy as np
import torch
print(torch.__version__)
import math
from PIL import Image, ImageOps
from torch.optim import SGD, Adam, lr_scheduler
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision.transforms import  Resize
from torchvision.transforms import ToTensor, ToPILImage
from dataset import cityscapes
from dataset import idd_lite
import sys
print(sys.executable)
from transform import Relabel, ToLabel, Colorize
import matplotlib
from matplotlib import pyplot as plt
%matplotlib inline
import importlib
from iouEval import iouEval, getColorEntry #importing iouEval class from the iouEval.py file
from shutil import copyfile
  • Определите несколько глобальных параметров:
NUM_CHANNELS = 3 #RGB Images
NUM_CLASSES = 8 #IDD Lite has 8 labels or Level1 hierarchy of labels
USE_CUDA = torch.cuda.is_available() 
IMAGE_HEIGHT = 160
DATA_ROOT = ‘/tmp/school/6-segmentation/user/1/6-segmentation/idd1_lite’
BATCH_SIZE = 2
NUM_WORKERS = 4
NUM_EPOCHS = 100
ENCODER_ONLY = True
device = torch.device(“cuda” )
#device = ‘cuda’
color_transform = Colorize(NUM_CLASSES)
image_transform = ToPILImage()
IOUTRAIN = False
IOUVAL = True
  • Дополнения, то есть разные функции, реализованные для произвольного увеличения как изображения, так и цели:
class MyCoTransform(object):
 def __init__(self, enc, augment=True, height=160):
 self.enc=enc
 self.augment = augment
 self.height = height
 pass
 def __call__(self, input, target):
 # Resizing data to required size
 input = Resize((self.height,320), Image.BILINEAR)(input)
 target = Resize((self.height,320), Image.NEAREST)(target)
if(self.augment):
 # Random horizontal flip
 hflip = random.random()
 if (hflip < 0.5):
 input = input.transpose(Image.FLIP_LEFT_RIGHT)
 target = target.transpose(Image.FLIP_LEFT_RIGHT)
 
 #Random translation 0–2 pixels (fill rest with padding)
 transX = random.randint(0, 2) 
 transY = random.randint(0, 2)
input = ImageOps.expand(input, border=(transX,transY,0,0), fill=0)
 target = ImageOps.expand(target, border=(transX,transY,0,0), fill=7) #pad label filling with 7
 input = input.crop((0, 0, input.size[0]-transX, input.size[1]-transY))
 target = target.crop((0, 0, target.size[0]-transX, target.size[1]-transY))
input = ToTensor()(input)
 
 target = ToLabel()(target)
 
 target = Relabel(255,7)(target)
 return input, target
  • Загрузка данных. Мы будем следовать семантике, рекомендованной pytorch, и использовать загрузчик данных для загрузки данных.
best_acc = 0
co_transform = MyCoTransform(ENCODER_ONLY, augment=True, height=IMAGE_HEIGHT)
co_transform_val = MyCoTransform(ENCODER_ONLY, augment=False, height=IMAGE_HEIGHT)
#train data
dataset_train = idd_lite(DATA_ROOT, co_transform, ‘train’)
print(len(dataset_train))
#test data
dataset_val = idd_lite(DATA_ROOT, co_transform_val, ‘val’)
print(len(dataset_val))
loader_train = DataLoader(dataset_train, num_workers=NUM_WORKERS, batch_size=BATCH_SIZE, shuffle=True)
loader_val = DataLoader(dataset_val, num_workers=NUM_WORKERS, batch_size=BATCH_SIZE, shuffle=False)
  • Поскольку это проблема классификации, мы используем потерю перекрестной энтропии, но почему?

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

criterion = torch.nn.CrossEntropyLoss()
  • Теперь загрузим модель и оптимизируем ее!
model_file = importlib.import_module(‘erfnet’)
model = model_file.Net(NUM_CLASSES).to(device)
optimizer = Adam(model.parameters(), 5e-4, (0.9, 0.999), eps=1e-08, weight_decay=1e-4) 
start_epoch = 1
  • Итак, последняя суть кодирования здесь - обучение!
import os
steps_loss = 50
my_start_time = time.time()
for epoch in range(start_epoch, NUM_EPOCHS+1):
 print(“ — — — TRAINING — EPOCH”, epoch, “ — — -”)
epoch_loss = []
 time_train = []
doIouTrain = IOUTRAIN 
 doIouVal = IOUVAL
if (doIouTrain):
 iouEvalTrain = iouEval(NUM_CLASSES)
model.train()
 for step, (images, labels) in enumerate(loader_train):
start_time = time.time()
 inputs = images.to(device)
 targets = labels.to(device)
 
 outputs = model(inputs, only_encode=ENCODER_ONLY)
# zero the parameter gradients
 optimizer.zero_grad()
 
 # forward + backward + optimize
 loss = criterion(outputs, targets[:, 0])
 loss.backward()
 optimizer.step()
epoch_loss.append(loss.item())
 time_train.append(time.time() — start_time)
if (doIouTrain):
 #start_time_iou = time.time()
 iouEvalTrain.addBatch(outputs.max(1)[1].unsqueeze(1).data, targets.data)
 #print (“Time to add confusion matrix: “, time.time() — start_time_iou)
# print statistics
 if steps_loss > 0 and step % steps_loss == 0:
 average = sum(epoch_loss) / len(epoch_loss)
 print(‘loss: {average:0.4} (epoch: {epoch}, step: {step})’, “// Avg time/img: %.4f s” % (sum(time_train) / len(time_train) / BATCH_SIZE))
average_epoch_loss_train = sum(epoch_loss) / len(epoch_loss)
iouTrain = 0
 if (doIouTrain):
 iouTrain, iou_classes = iouEvalTrain.getIoU()
 iouStr = getColorEntry(iouTrain)+’{:0.2f}’.format(iouTrain*100) + ‘\033[0m’
 print (“EPOCH IoU on TRAIN set: “, iouStr, “%”) 
my_end_time = time.time()
print(my_end_time — my_start_time)

После тренировки в течение 100 эпох вы увидите следующее:

  • Проверка:
#Validate on val images after each epoch of training
print(“ — — — VALIDATING — EPOCH”, epoch, “ — — -”)
model.eval()
epoch_loss_val = []
time_val = []
if (doIouVal):
 iouEvalVal = iouEval(NUM_CLASSES)
for step, (images, labels) in enumerate(loader_val):
 start_time = time.time()
inputs = images.to(device) 
 targets = labels.to(device)
 
 with torch.no_grad():
 outputs = model(inputs, only_encode=ENCODER_ONLY) 
 #outputs = model(inputs)
 loss = criterion(outputs, targets[:, 0])
 epoch_loss_val.append(loss.item())
 time_val.append(time.time() — start_time)
#Add batch to calculate TP, FP and FN for iou estimation
 if (doIouVal):
 #start_time_iou = time.time()
 iouEvalVal.addBatch(outputs.max(1)[1].unsqueeze(1).data, targets.data)
 #print (“Time to add confusion matrix: “, time.time() — start_time_iou)
 
 if steps_loss > 0 and step % steps_loss == 0:
 average = sum(epoch_loss_val) / len(epoch_loss_val)
 print(‘VAL loss: {average:0.4} (epoch: {epoch}, step: {step})’, 
 “// Avg time/img: %.4f s” % (sum(time_val) / len(time_val) / BATCH_SIZE))
average_epoch_loss_val = sum(epoch_loss_val) / len(epoch_loss_val)
iouVal = 0
if (doIouVal):
iouVal, iou_classes = iouEvalVal.getIoU()
 print(iou_classes)
 iouStr = getColorEntry(iouVal)+’{:0.2f}’.format(iouVal*100) + ‘\033[0m’
 print (“EPOCH IoU on VAL set: “, iouStr, “%”)

  • Визуализация вывода:
# Qualitative Analysis
dataiter = iter(loader_val)
images, labels = dataiter.next()
if USE_CUDA:
 images = images.to(device)
inputs = images.to(device)
with torch.no_grad():
 outputs = model(inputs, only_encode=ENCODER_ONLY)
label = outputs[0].max(0)[1].byte().cpu().data
label_color = Colorize()(label.unsqueeze(0))
label_save = ToPILImage()(label_color)
plt.figure()
plt.imshow(ToPILImage()(images[0].cpu()))
plt.figure()
plt.imshow(label_save)

В кратчайшие сроки ваша модель будет готова! Разве это не потрясающе?
Не стесняйтесь играть со своей недавно разработанной моделью! Попробуйте увеличить количество эпох и посмотрите, как ваша модель работает еще лучше!

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

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