[PYTHON] Bildsegmentierung mit U-Net

Die Umgebung verwendet Python3.7 und Tensorflow 2.1.1 mit den folgenden Inhalten.

Was ist Bildsegmentierung?

VOC2012 Schauen wir uns ein Beispiel für die Bildsegmentierung in einem Datensatz an (siehe unten). Screen Shot 2020-10-29 at 22.17.07.png In diesem Beispiel werden die Pixel im Bild als Fahrrad, Fahrer oder Hintergrund klassifiziert. Diese Art der Klassifizierung von Bildern ** in Pixeleinheiten ** wird als Bildsegmentierung (oder semantische Segmentierung) bezeichnet.

Wenn Sie mit dem Studium neuronaler Netze beginnen, implementieren Sie häufig die handschriftliche Zeichenerkennung von MNIST in Tutorials. In diesem Fall wird das Bild eingegeben und die Wahrscheinlichkeit, dass das Bild eine Zahl von 0 bis 9 ist, wird ausgegeben, so dass die letzte Ausgabeschicht eindimensionale 10 Knoten sind.

Andererseits möchten wir im Fall der Bildsegmentierung jedes Pixel des Bildes klassifizieren, damit die endgültige Ausgabeebene die Abmessungen hat (vertikale Größe des Bildes, horizontale Größe des Bildes, Anzahl der Klassen). U-net, das ich dieses Mal vorstellen werde, kann eine hervorragende Genauigkeit für solche Bildsegmentierungsprobleme liefern.

U-net U-net ist ein Modell mit CNN und Sprungverbindung, das in der Veröffentlichung "U-Net: Faltungsnetzwerke für die Segmentierung biomedizinischer Bilder" veröffentlicht wurde. (Abbildung unten).

Screen Shot 2020-10-29 at 22.44.45.png

Die Grundidee von U-net ist ・ Merkmalsextraktion beim Aufrauen des Bildes mit der Falt- und Poolebene

U-Net-Implementierung

Schreiben wir zunächst den ersten Teil von U-net (die oberen drei Ebenen in der obigen Abbildung). In der folgenden Implementierung wird die Größe des Eingabebildes auf (256, 256) eingestellt.

from tensorflow import keras
from tensorflow.keras import layers

inputs = keras.Input(shape=(256, 256, 1), name="img")
x = layers.Conv2D(64, 3, activation="relu", padding="same", kernel_initializer='he_normal')(inputs)
block_1_output = layers.Conv2D(64, 3, activation="relu", padding="same")(x)

Ein Bild der Größe (256, 256, 1) wird mit einem Convolutional Neural Network (CNN) mit 64 Kanälen und einer Filtergröße von 3 gefaltet. Da die Aktivierungsfunktion ein tiefes Modell ist, verwenden wir ReLU. Behalten Sie die Ausgabe mit dem Namen block_1_output für eine spätere Sprungverbindung bei. Die nächste Schicht von U-net wäre wie folgt.

x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_1_output)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_2_output = layers.Conv2D(128, 3, activation="relu", padding="same")(x)

Es empfängt "block_1_output", vergröbert es mit Max Pooling, erhöht die Anzahl der Kanäle mit CNN und faltet es zusammen. Speichern Sie "block_2_output" für das Überspringen der Verbindung wie zuvor. Wenn Sie in diesem Zustand U-net schreiben, sieht das ganze Bild wie folgt aus.

from tensorflow import keras
from tensorflow.keras import layers

inputs = keras.Input(shape=(256, 256, 1), name="img")
x = layers.Conv2D(64, 3, activation="relu", padding="same", kernel_initializer='he_normal')(inputs)
block_1_output = layers.Conv2D(64, 3, activation="relu", padding="same")(x)

x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_1_output)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_2_output = layers.Conv2D(128, 3, activation="relu", padding="same")(x)

x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_2_output)
x = layers.Conv2D(256, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_3_output = layers.Conv2D(256, 3, activation="relu", padding="same")(x)

x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_3_output)
x = layers.Conv2D(512, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_4_output = layers.Conv2D(512, 3, activation="relu", padding="same")(x)

x = layers.Dropout(0.5)(block_4_output)
x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(x)
x = layers.Conv2D(1024, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_5_output = layers.Conv2D(1024, 3, activation="relu", padding="same")(x)

x = layers.Dropout(0.5)(block_5_output)
x = layers.UpSampling2D(size=(2,2))(x)
x = layers.Conv2D(512, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_4_output], axis=3)
x = layers.Conv2D(512, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_6_output = layers.Conv2D(512, 3, activation="relu", padding="same")(x)

x = layers.UpSampling2D(size=(2,2))(block_6_output)
x = layers.Conv2D(256, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_3_output], axis=3)
x = layers.Conv2D(256, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_7_output = layers.Conv2D(256, 3, activation="relu", padding="same")(x)

x = layers.UpSampling2D(size=(2,2))(block_7_output)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_2_output], axis=3)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_8_output = layers.Conv2D(128, 3, activation="relu", padding="same")(x)

x = layers.UpSampling2D(size=(2,2))(block_8_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_1_output], axis=3)
x = layers.Conv2D(64, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = layers.Conv2D(64, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
outputs = layers.Conv2D(1, 1, activation="sigmoid", padding="same")(x)

model = keras.Model(inputs, outputs, name="u-net")

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

Es ist lange her, dass U-Net kompliziert ist, aber ich wiederhole ähnliche Dinge. Verwenden Sie tf.concat, um eine Verbindung mit dem Kanalteil als Achse herzustellen. Das Bild des U-Net-Netzwerks enthält nicht die Dropout-Ebene, aber wenn ich den Text des Papiers lese,

Drop-out layers at the end of the contracting path perform further >implicit data augmentation

Da dies der Fall ist, ist die Dropout-Schicht auch im obigen Netzwerk enthalten. Unter der Annahme einer Segmentierung in zwei Klassen wird binary_crossentropy verwendet, wobei die Anzahl der Ausgangskanäle auf 1 gesetzt ist. Sie können sich auch über die Ausgabe informieren, indem Sie die Anzahl der Kanäle auf 2 setzen und sparse_categorical_crossentropy verwenden.

Lassen Sie uns überprüfen, ob das Modell korrekt erstellt wurde.

tf.keras.utils.plot_model(model)

Wenn Sie das Modell mit ausgeben, sieht es wie folgt aus. Unknown-4.png Es ist sehr lang, aber Sie können sehen, dass die erste halbe Schicht und die zweite halbe Schicht durch Überspringen der Verbindung richtig verbunden sind. Wenn Sie das Modell mit model.summary () ausgeben, sieht es so aus.

model.summary()

Model: "u-net"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
img (InputLayer)                [(None, 256, 256, 1) 0                                            
__________________________________________________________________________________________________
conv2d (Conv2D)                 (None, 256, 256, 64) 640         img[0][0]                        
__________________________________________________________________________________________________
conv2d_1 (Conv2D)               (None, 256, 256, 64) 36928       conv2d[0][0]                     
__________________________________________________________________________________________________
max_pooling2d (MaxPooling2D)    (None, 128, 128, 64) 0           conv2d_1[0][0]                   
__________________________________________________________________________________________________
conv2d_2 (Conv2D)               (None, 128, 128, 128 73856       max_pooling2d[0][0]              
__________________________________________________________________________________________________
conv2d_3 (Conv2D)               (None, 128, 128, 128 147584      conv2d_2[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)  (None, 64, 64, 128)  0           conv2d_3[0][0]                   
__________________________________________________________________________________________________
conv2d_4 (Conv2D)               (None, 64, 64, 256)  295168      max_pooling2d_1[0][0]            
__________________________________________________________________________________________________
conv2d_5 (Conv2D)               (None, 64, 64, 256)  590080      conv2d_4[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)  (None, 32, 32, 256)  0           conv2d_5[0][0]                   
__________________________________________________________________________________________________
conv2d_6 (Conv2D)               (None, 32, 32, 512)  1180160     max_pooling2d_2[0][0]            
__________________________________________________________________________________________________
conv2d_7 (Conv2D)               (None, 32, 32, 512)  2359808     conv2d_6[0][0]                   
__________________________________________________________________________________________________
dropout (Dropout)               (None, 32, 32, 512)  0           conv2d_7[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D)  (None, 16, 16, 512)  0           dropout[0][0]                    
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 16, 16, 1024) 4719616     max_pooling2d_3[0][0]            
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 16, 16, 1024) 9438208     conv2d_8[0][0]                   
__________________________________________________________________________________________________
dropout_1 (Dropout)             (None, 16, 16, 1024) 0           conv2d_9[0][0]                   
__________________________________________________________________________________________________
up_sampling2d (UpSampling2D)    (None, 32, 32, 1024) 0           dropout_1[0][0]                  
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 32, 32, 512)  4719104     up_sampling2d[0][0]              
__________________________________________________________________________________________________
tf_op_layer_concat (TensorFlowO [(None, 32, 32, 1024 0           conv2d_10[0][0]                  
                                                                 conv2d_7[0][0]                   
__________________________________________________________________________________________________
conv2d_11 (Conv2D)              (None, 32, 32, 512)  4719104     tf_op_layer_concat[0][0]         
__________________________________________________________________________________________________
conv2d_12 (Conv2D)              (None, 32, 32, 512)  2359808     conv2d_11[0][0]                  
__________________________________________________________________________________________________
up_sampling2d_1 (UpSampling2D)  (None, 64, 64, 512)  0           conv2d_12[0][0]                  
__________________________________________________________________________________________________
conv2d_13 (Conv2D)              (None, 64, 64, 256)  1179904     up_sampling2d_1[0][0]            
__________________________________________________________________________________________________
tf_op_layer_concat_1 (TensorFlo [(None, 64, 64, 512) 0           conv2d_13[0][0]                  
                                                                 conv2d_5[0][0]                   
__________________________________________________________________________________________________
conv2d_14 (Conv2D)              (None, 64, 64, 256)  1179904     tf_op_layer_concat_1[0][0]       
__________________________________________________________________________________________________
conv2d_15 (Conv2D)              (None, 64, 64, 256)  590080      conv2d_14[0][0]                  
__________________________________________________________________________________________________
up_sampling2d_2 (UpSampling2D)  (None, 128, 128, 256 0           conv2d_15[0][0]                  
__________________________________________________________________________________________________
conv2d_16 (Conv2D)              (None, 128, 128, 128 295040      up_sampling2d_2[0][0]            
__________________________________________________________________________________________________
tf_op_layer_concat_2 (TensorFlo [(None, 128, 128, 25 0           conv2d_16[0][0]                  
                                                                 conv2d_3[0][0]                   
__________________________________________________________________________________________________
conv2d_17 (Conv2D)              (None, 128, 128, 128 295040      tf_op_layer_concat_2[0][0]       
__________________________________________________________________________________________________
conv2d_18 (Conv2D)              (None, 128, 128, 128 147584      conv2d_17[0][0]                  
__________________________________________________________________________________________________
up_sampling2d_3 (UpSampling2D)  (None, 256, 256, 128 0           conv2d_18[0][0]                  
__________________________________________________________________________________________________
conv2d_19 (Conv2D)              (None, 256, 256, 64) 73792       up_sampling2d_3[0][0]            
__________________________________________________________________________________________________
tf_op_layer_concat_3 (TensorFlo [(None, 256, 256, 12 0           conv2d_19[0][0]                  
                                                                 conv2d_1[0][0]                   
__________________________________________________________________________________________________
conv2d_20 (Conv2D)              (None, 256, 256, 64) 73792       tf_op_layer_concat_3[0][0]       
__________________________________________________________________________________________________
conv2d_21 (Conv2D)              (None, 256, 256, 64) 36928       conv2d_20[0][0]                  
__________________________________________________________________________________________________
conv2d_22 (Conv2D)              (None, 256, 256, 1)  65          conv2d_21[0][0]                  
==================================================================================================
Total params: 34,512,193
Trainable params: 34,512,193
Non-trainable params: 0
__________________________________________________________________________________________________

Die Gesamtzahl der Parameter beträgt 34.512.193!, Was eine ziemliche Zahl ist.

Lernen

Informationen zur Vorbereitung von Eingabedaten, z. B. zum Auffüllen von Daten, finden Sie in Vorheriger Artikel. Schneiden Sie die Bilddaten von ISBI Challenge 2012 (Segmentierung neuronaler Strukturen in EM-Stapeln) in Patches von (256, 256) und führen Sie eine Datenerweiterung mit ImageDataGenerator durch. Es gibt.

Lassen Sie uns nun wie folgt lernen. my_generator ist ein Generator, der Trainingsbilddaten übergibt, und my_val_gen ist ein Generator, der Verifizierungsbilddaten übergibt.

EPOCHS = 200
STEPS_PER_EPOCH = 300
VALIDATION_STEPS = STEPS_PER_EPOCH//10
model_history = model.fit(my_generator, epochs=EPOCHS,
                          steps_per_epoch=STEPS_PER_EPOCH,
                          validation_steps=VALIDATION_STEPS,
                          validation_data=my_val_gen)

Der Lernzustand ist wie folgt. Die horizontale Achse ist die Anzahl der EPOCHS und die vertikale Achse ist die Genauigkeit oder der Verlust. Der Trainingsverlust und die Trainingsgenauigkeit nehmen monoton ab, was darauf hinweist, dass das Lernen voranschreitet. Die Validierungsgenauigkeit liegt ebenfalls bei über 90%, und es scheint eine prädiktive Leistung zu geben. Wenn Sie genau hinschauen, ist die Validierungsgenauigkeit / der Validierungsverlust das Maximum / Minimum pro 20 bis 30 Schritte. Dies deutet darauf hin, dass es mit zunehmender Anzahl von EPOCHS überanpasst. history.png

Betrachten wir die Segmentierung von Testdaten anhand eines Modells gemäß EPOCHS Nummer 30.

def create_mask(pred_img):
  pred_img = tf.math.greater(pred_img, 0.5)
  return pred_img[0]

plt.figure(dpi=150)
plt.subplot(1, 2, 1)
plt.imshow(test_image[:256, :256], cmap="gray")

plt.subplot(1, 2, 2)
test_image = test_image[:256,:256,tf.newaxis]
test_image = np.expand_dims(test_image, axis=0)
pred_img = model.predict(test_image)
masked_img = create_mask(pred_img)
plt.imshow(masked_img[:,:,0])

1.jpg Andererseits war die Segmentierung der Trainingsdaten wie folgt. 2.jpg Sie können sehen, dass die Segmentierung sowohl für die Trainingsdaten als auch für die Testdaten gut funktioniert. Gibt es jedoch im Vergleich zu den Trainingsdaten ein Rauschen in den Segmentierungsergebnissen für die Testdaten? Ich denke. In diesem Zusammenhang kann es erforderlich sein, etwas mehr über das Modell und die Datenerweiterungsmethode zu erfahren.

Am Ende

Wir haben die Implementierung mit Tensorflow für die Bildsegmentierung mit U-Net eingeführt. Ich habe das U-Net-Modell des Originalpapiers so implementiert, wie es ist, aber es gibt verschiedene Techniken, die verwendet werden können, wie z. B. die Chargennormalisierung. Wenn ich eine Chance habe, werde ich erneut eine Einführung schreiben.

Recommended Posts

Bildsegmentierung mit U-Net
Versuchen Sie es mit Jupyters Docker-Image
Vorhersage von Wolkenbildern mit convLSTM
Generieren Sie ein Docker-Image mit Fabric
SLIC Superpixel-Segmentierung im Scikit-Bild
Implementierte Bildsegmentierung in Python (Union-Find)
Beurteilung des hintergrundbeleuchteten Bildes mit OpenCV
[Python] Verwenden von OpenCV mit Python (Bildtransformation)
Bildsegmentierung mit Scikit-Image und Scikit-Learn
Umweltfreundliches Scraping mit Bildverarbeitung
[FSL] Bildmessung mit ROI (VOI)
Binarisierung von Bildern mittels linearer Diskriminanzanalyse
Bilderkennung von Früchten mit VGG16
Python: Grundlagen der Bilderkennung mit CNN
Pokemon-Symbolbildunterscheidung mithilfe von HOG-Funktionen
Kategorieschätzung mit der Bilderkennungs-API von docomo
Python: Anwendung der Bilderkennung mit CNN
Gesichtsbildinferenz mit Flask und TensorFlow
Bilderkennung mit CNN Pferden und Hirschen
Bildsegment mit Oxford_iiit_pet in Google Colab
Bildersammlung mit der benutzerdefinierten Such-API von Google
Bildsegmentierung mit CaDIS: ein Katarakt-Datensatz
(Lesen des Papiers) Instanzbewusste Bildfärbung (Regionsteilung: Farbabbildung mithilfe der Instanzsegmentierung)