• Nazlı As

U-Net ile Görüntü Segmentasyonu

Nazlı As, Tarkan Atak Kan

Görüntü segmentasyonu ile görüntüdeki piksellere etiketler atanır. Etiketler, görüntünün daha fazla işlenmesini sağlayarak karmaşıklığını azaltır. Bu yöntemi uygulayabilmek için geliştirilen evrişimsel sinir ağına U-Net denir. U-Net ilk olarak Freiburg Üniversitesi Bilgisayar Bilimleri Bölümünde biyomedikal görüntü segmentasyonu için geliştirilmiştir. Ancak çatlakların belirlenmesinden, hayvanların segmentasyonuna kadar çok geniş alanda kullanım olacağı bulmaktadır.


U-Net Mimarisi

U-Net, bir köprü aracılığıyla bağlanan dört encoder bloğu ve dört decoder bloğundan oluşan U şeklinde bir encoder-decoder ağ mimarisidir. Encoder ağı, fotoğrafların boyutlarını yarısına indirir ve filtre sayısını iki katına çıkarır. Decoder ağı, fotoğrafların boyutlarını iki katına çıkarır ve filtre sayısını yarıya indirir. Encoder, convolution, maxpooling ve dropout işlemlerini içerir. Decoder, conv2DTranspose, concatenate ve dropout işlemlerini içerir.




Encoder:


1)Fotoğrafların giriş boyutları 572x572'dir.

2)Convolution katmanı ile 64 tane özellik uygulanır. (boyut: 568x568x64)

3)Maxpooling katmanı ile 2x2 matris kullanılarak fotoğraflardaki en büyük piksel değerini alır. (boyut: 284x284x64)

4)Convolution katmanı ile 128 özellik daha uygulanır.(boyut: 280x280x128)

Bu işlemler iki kez daha devam ettikten sonra 64x64x512 boyutlu fotoğraflar oluşur.


Bottleneck:


1)32x32x512 boyutlu fotoğraflara iki kez Convolution katmanı uygulanır.(boyut: 28x28x1024)

Encode ve decode blokları birleştirilirken oluşan elips yapıya “Bottleneck” (darboğaz) denir.


Decoder:


1)Up-Convolution katmanı ile fotoğraflara 2x2 matrisler uygulanır. (boyut: 56x56x1024 )

2)Bu katman ile en son oluşturulmuş olan encoder katmanı birleştirilir ve bu işleme “Concatenate” (birleştirme) denir. Decoder bloğunun her bir katmanında öncesinde o katmanla aynı hizada olan encoder bloğundaki katman birleştirilir ve

3)Convolution katmanı ile 512 tane özellik uygulanır. (boyut:52x52x512)

Dört kez decoder bloğu gerçekleştikten sonra 388x388x2 boyutlu çıkış fotoğrafları ortaya çıkar.

 

Teoriyi pratiğe dökme zamanı… Oxford-IIIT Pet Dataset kullanarak ve U-NET mimarisinden yararlanarak image segmentation yapalım.


Ana işlemlerden biri olan veri hazırlama aşaması ile başlayalım ve Oxford-IIIT Pet Dataset’e bir bakalım. Sette 37 cins evcil hayvan için yaklaşık 200’er tane fotoğraf hazırlanmış. Segmentasyon için de yanında doğruluk maskesi sunulmuş.



Şekil 2

Şekil 2'deki kolonlar soldan sağa sırasıyla orijinal fotoğraf, ilgili çerçeve ve doğruluk maskesini barındırıyor. Görüldüğü gibi doğruluk maskelerimizdeki piksellerin anlamlı renkleri var. Mavi arka planı, kırmızı tanımsız bölgeyi, sarı da hayvanın bulunduğu yerleri belirtiyor. Verimizin yapısını anladığımıza göre artık veriyi işlemeden bahsedebiliriz.


Veriyi işlemekteki amacımız, hem U-Net modelimize girdisini sağlamak hem de verideki değişkenleri modelle uyumlu hale getirmek. Modele veriyi TensorFlow’un Dataset API’ını kullanarak vermeyi amaçladığımızdan da formatını ona göre düzenleyeceğiz.


İlk fonksiyonumuz cleanAndStore. Adından anlaşılacağı gibi öncelikle verimizdeki fotoğrafları temizleyip kaydetmemiz gerekiyor.



def cleanAndStore(path):  
	image_list = sorted(os.listdir(path_to_images)  
	mask_list = sorted(os.listdir(path_to_trimaps)  
	new_image = []  
	new_mask = []
	for imaj in image_list:      
		mock = cv2.imread(path_to_images' + imaj, cv2.IMREAD_COLOR)      
		try:
			mock = cv2.resize(mock(128,128),
			interpolation=cv2.INTER_AREA)      			 
		except:
			continue
		imaj = imaj[0:-3]      
		for maske in mask_list:
			maske = maske[0:-3]          
			if imaj == maske:
				 new_image.append(imaj + 'jpg') 
				 new_mask.append(maske + 'png')   
				 mask_list.pop(0)          
				 break     
	return new_image, new_mask

Fotoğraf ve maske türleri için ayrı ayrı dosya adlarını alfabetik sıralanmış olarak kaydediyoruz ve iki tane boş liste hazırlıyoruz. Bu listelere sağlam gördüklerimizi ekleyeceğiz. For döngümüzde her bir fotoğraf için öncelikle hatasız açılma testi yapıyoruz. Hata barındırmıyorsa ikinci döngümüzde aynı isimdeki dosyayı bulup listelerimize atıyoruz.


def collect(images, mask):    
	image_list = []    
	mask_list = []
	for filename in images:
		image = cv2.imread(path_to_images + filename, cv2.IMREAD_COLOR)
		image = cv2.resize(image, (128, 128))           		 		 				     
		image = tf.cast(image, tf.float32) / 255.0       
 		image_list.append(image)   
	for filename in mask:
		a += 1        
		mask = cv2.imread(path_to_masks + filename, cv2.IMREAD_GRAYSCALE)        	
		mask = cv2.resize(mask, (128, 128))        
		mask -= 1        
		mask_list.append(mask)   
	return image_list, mask_list    			   

Collect fonksiyonuna ise bir önceki fonksiyonda listelere koyduğumuz adresleri sunarak bunları artık birer numpy dizisi haline getiriyoruz. Aynı zamanda fotoğrafı normalize edip maskenin piksellerinden de 1 çıkarıyoruz.


Maske Pikselleri

Maske piksellerinden 1 çıkarmamızın sebebi kullandığımız setteki piksel değerlerinin 1, 2 ve 3 olması. Değerleri 0, 1, 2 yapmamız oransal olarak birbirinden daha uzak değerlere sahip olmamızı sağlıyor.

def tf_dataset(X, Y,  batch=8):
	dataset = tf.data.Dataset.from_tensor_slices((X, Y))    
	dataset = dataset.batch(batch,drop_remainder=True)    
	dataset = dataset.prefetch(10)    
	return dataset


Ardından TensorFlow’un kendi API’ını kullanarak train ve validation datasetlerimizi oluşturuyoruz.


def double_conv_block(x,n_filters):
	x=layers.Conv2D(n_filters,3,padding="same",activation="relu",
	kernel_initializer="he_normal")(x) 
	x=layers.Conv2D(n_filters,3,padding="same",activation="relu", 			
	kernel_initializer="he_normal")(x)  
	return x
	
def downsample_block(x,n_filters):
	f=double_conv_block(x,n_filters)    
	p=layers.MaxPool2D(2)(f)    
	p=layers.Dropout(0.3)(p)    
	return f,p

def upsample_block(x,conv_features,n_filters):
	x=layers.Conv2DTranspose(n_filters,3,2,padding="same")(x)
	x=layers.concatenate([x,conv_features])   		 
	x=layers.Dropout(0.3)(x)  
	x=double_conv_block(x,n_filters)  
	return x

Dataseti kendi ihtiyaçlarımıza göre şekillendirdikten sonra artık U-Net modeli oluşturma kısmına geçebiliriz. Modeli oluşturabilmek için sıklıkla kullanacağımız bazı fonksiyonları önceden yaratıyoruz. Bu fonksiyonlar convolution katmanı için double_conv_block fonksiyonu, modeli encoder mimarisi için downsample_block fonksiyonu ve son olarak decoder mimarisi için upsample_block fonksiyonudur.


Fonksiyonları oluşturduktan sonra U-Net modelini geliştirmeye geçebiliriz.


def build_unet_model():
	f1,p1=downsample_block(inputs,64)  
	f2,p2=downsample_block(p1,128)  
	f3,p3=downsample_block(p2,256)  
	f4,p4=downsample_block(p3,512)
	
	bottleneck=double_conv_block(p4,1024)

	u6=upsample_block(bottleneck,f4,512)  
	u7=upsample_block(u6,f3,256)  
	u8=upsample_block(u7,f2,128)  
	u9=upsample_block(u8,f1,64)
	
	outputs=layers.Conv2D(3,1,padding="same",activation = "softmax")(u9)
	unet_model = tf.keras.Model(inputs, outputs, name="U-Net")  
	return unet_model

İlk olarak, gelen fotoğrafları downsample_block fonksiyonunda encode ediyoruz. Her encode bloğunda iki kez convolution uygulayarak f1, f2 ,f3 ,f4 katmanlarını ve bu katmanları sırasıyla MaxPool2D ve Dropout fonksiyonlarına göndererek p1, p2, p3 ve p4 katmanlarını output olarak elde ediyoruz.


Modelin ikinci kısmında katmanları decode edebilmek için upsample_block fonksiyonuna input olarak encode bloğunda oluşan f1, f2, f3 ve f4 katmanlarını ve decode bloğunda output olarak elde ettiğimiz u6, u7, u8 ve u9 katmanlarını gönderiyoruz. Bu fonksiyonda Conv2dTranspose, concatenate ve Dropout işlemleri gerçekleştikten sonra oluşan output oluşur. Output değerleri activation=”softmax” fonksiyonu ile 0 ve 1 arasında yer alır.


Modeli oluşturduktan sonra compile ve fit fonksiyonlarına göndererek modelin eğitilmeye başlamasını sağlıyoruz.


unet_model = build_unet_model()
unet_model.compile(optimizer=tf.keras.optimizers.Adam(), 
					loss="sparse_categorical_crossentropy",                		
					metrics="accuracy")

NUM_EPOCHS = 20
model_history = unet_model.fit(train_dataset,    
							 epochs=NUM_EPOCHS,                           		                             
							validation_data=test_dataset)

Modeli eğittikten loss ve accuracy değerlerinin epoch'lara bağlı olarak değişimi aşağıdaki grafiklerde görülebilir.



Eğitilmiş modeli herhangi bir kedi fotoğrafına uygulayalım.


Referanslar


1)Şekil 1: University of Freiburg. (2015).U-Net: Convolutional Networks for Biomedical Image Segmentation. https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/

2)Şekil 2: The Oxford-IIIT Pet Dataset. (2012).

https://www.robots.ox.ac.uk/~vgg/data/pets/

30 görüntüleme