Μια σύντομη μελέτη των αλγορίθμων κατωφλίου εικόνας

Εισαγωγή
Αυτό το άρθρο παρουσιάζει εν συντομία το όριο εικόνας και τους αλγόριθμους που χρησιμοποιούνται για τον καθορισμό ορίου εικόνας. Το κατώφλι εικόνας είναι μια απλή τεχνική τμηματοποίησης εικόνας. Χρησιμοποιείται για τη μετατροπή μιας εικόνας σε κλίμακα του γκρι ή μιας εικόνας RGB σε δυαδική εικόνα. Σε αυτό το άρθρο, θα εξετάσουμε αλγόριθμους κατωφλίου όπως το απλό κατώφλι, την τεχνική κατωφλίου otsu και την προσαρμοστική τεχνική κατωφλίου, μαζί με μια σύντομη σημείωση για έναν αλγόριθμο βαθιάς εκμάθησης (U-Net) για τμηματοποίηση εικόνας.
Τι είναι το Image Thresholding;
Πριν κατανοήσουμε τον όρο Image Thresholding, ας κατανοήσουμε πρώτα τον όρο Image Segmentation. Η κατάτμηση εικόνας είναι μια κοινή τεχνική που χρησιμοποιείται για τη διαίρεση μιας εικόνας σε ομάδες pixel με βάση ορισμένα κριτήρια.
Το κατώφλι εικόνας είναι ένας τύπος τμηματοποίησης εικόνας που χωρίζει το προσκήνιο από το φόντο μιας εικόνας. Σε αυτήν την τεχνική, οι τιμές των εικονοστοιχείων εκχωρούνται που αντιστοιχούν στις παρεχόμενες τιμές κατωφλίου. Στην όραση υπολογιστή, ο καθορισμός κατωφλίου γίνεται σε εικόνες σε κλίμακα του γκρι.
Οι παρακάτω εικόνες δείχνουν μια εικόνα σε κλίμακα του γκρι και την εικόνα που λαμβάνεται μετά την εφαρμογή κατωφλίου σε αυτήν.
Γιατί χρειαζόμαστε το Image Thresholding;
Ας καταλάβουμε τη σημασία του κατωφλίου εικόνας με ένα παράδειγμα-
Ρίξτε μια ματιά στις παρακάτω εικόνες,
Συγκρίνοντας την πρώτη εικόνα, η μάσκα στη δεύτερη εικόνα φαίνεται καθαρά. Ας πάρουμε ένα άλλο παράδειγμα,
Η πρώτη εικόνα, η αρχική εικόνα, είναι λίγο παραμορφωμένη από τη δεύτερη εικόνα που λάβαμε μετά την εφαρμογή του κατωφλίου. Έτσι, το thresholding είναι χρήσιμο για την εξαγωγή κειμένου που δεν είναι καθαρό στην εικόνα.
Το κατώφλι εικόνας μας βοηθά να διαιρέσουμε το προσκήνιο και το φόντο μιας εικόνας, κάτι που μπορεί να βοηθήσει στον εντοπισμό των αντικειμένων που δεν είναι καθαρά ορατά στις εικόνες.
Κατανόηση διαφορετικών τεχνικών κατωφλίου
Σε αυτό το άρθρο, θα μάθουμε για διαφορετικές τεχνικές που χρησιμοποιούνται στον καθορισμό ορίου εικόνας και θα εφαρμόσουμε αυτές τις τεχνικές χρησιμοποιώντας το OpenCV.
Απλό κατώφλι
Το Simple Thresholding είναι επίσης γνωστό ως Binary Thresholding. Αυτή η τεχνική ορίζει μια τιμή κατωφλίου και συγκρίνει κάθε pixel με τη συγκεκριμένη τιμή κατωφλίου. Εάν η τιμή του εικονοστοιχείου είναι μικρότερη ή ίση με το εκχωρημένο όριο, τότε η τιμή του εικονοστοιχείου ορίζεται στο μηδέν ή στη μέγιστη τιμή.
Εφαρμογή απλού κατωφλίου χρησιμοποιώντας OpenCV:
Εισαγωγή απαραίτητων βιβλιοθηκών
import cv2 from google.colab.patches import cv2_imshow import matplotlib.pyplot as plt
Μετατροπή έγχρωμης εικόνας σε κλίμακα του γκρι
image = cv2.imread('/content/drive/MyDrive/AV/OpenCV/test.jpg') cv2_imshow(image) # coverting color image into grayscale orig_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Δυαδικό κατώφλι
# Arguments of function cv2.threshold # cv2.threshold(grayscaled image, threshold value, maximum value of pixel, type of threshold) # Output is a tuple containg the threshold value and thresholded image t, thresh = cv2.threshold(orig_img,70,255,cv2.THRESH_BINARY) cv2_imshow(thresh)
Δυαδικό αντίστροφο κατώφλι
t,thresh1 = cv2.threshold(orig_img,70,255,cv2.THRESH_BINARY_INV) cv2_imshow(thresh1)
Περικοπή κατωφλίου
rect,thresh2 = cv2.threshold(orig_img,70,255,cv2.THRESH_TRUNC) cv2_imshow(thresh2)
Όριο στο μηδέν
rect,thresh3 = cv2.threshold(orig_img,70,255,cv2.THRESH_TOZERO) cv2_imshow(thresh3)
Αντίστροφο κατώφλι έως μηδέν
rect,thresh4 = cv2.threshold(orig_img,127,255,cv2.THRESH_TOZERO_INV) cv2_imshow(thresh4)
Η παρακάτω εικόνα λαμβάνεται μετά την εφαρμογή απλού ορίου
Το κατώφλι του Otsu
Ένας από τους τρόπους για να επιτευχθεί ένα βέλτιστο όριο είναι η μέθοδος του Otsu. Σε αυτή τη μέθοδο, βρίσκουμε την εξάπλωση του προσκηνίου και του φόντου των εικόνων για όλες τις πιθανές τιμές κατωφλίου. Ως βέλτιστο όριο λαμβάνεται το όριο με τη μικρότερη διαφορά.
Πώς λειτουργεί το κατώφλι του Otsu;
Η ιδέα στο όριο του Otsu είναι να μεγιστοποιήσει τη διακύμανση μεταξύ των κατηγοριών. Η διακύμανση μεταξύ των κατηγοριών μπορεί να οριστεί ως εξής:
Εδώ, είναι η διακύμανση μεταξύ των κλάσεων δύο κλάσεων – κλάσης προσκηνίου και κλάσης παρασκηνίου.
Αφήνω, είναι ο αριθμός των pixel στις κλάσεις παρασκηνίου και προσκηνίου, αντίστοιχα. n ο συνολικός αριθμός των pixel στην εικόνα τότε,
ΤΟ μέσος όρος της κλάσης υποβάθρου και της κλάσης προσκηνίου αντιπροσωπεύεται ως
Ο αλγόριθμος του Otsu υπολογίζει τη διακύμανση μεταξύ των κλάσεων για όλες τις πιθανές τιμές κατωφλίου. Ως βέλτιστη τιμή κατωφλίου λαμβάνεται το όριο με την υψηλότερη απόκλιση μεταξύ των κατηγοριών. Τιμές μικρότερες από τη βέλτιστη τιμή κατωφλίου εμπίπτουν σε μια κλάση και άλλες τιμές σε άλλη κλάση.
Εφαρμογή του κατωφλίου του Otsu:
Υλοποίηση του κατωφλίου του Otsu με χρήση OpenCV
blur = cv2.GaussianBlur(orig_img,(5,5),0) #Applying Gaussian Blurr on image to get better threshold t,thresh5 = cv2.threshold(blur,128,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) print('Threshold obtained by Otsu Thresholding : ', t) cv2_imshow(thresh5)
Η παρακάτω εικόνα ελήφθη μετά την εφαρμογή του ορίου δυαδοποίησης του Otsu.
Προσαρμοστικό κατώφλι
Τόσο το Simple thresholding όσο και το thresholding του Otsu είναι καθολικές τεχνικές κατωφλίου που χρησιμοποιούν μια ενιαία τιμή κατωφλίου στο όριο εικόνας. Αλλά μια μεμονωμένη τιμή κατωφλίου μπορεί να μην είναι επαρκής επειδή μπορεί να λειτουργεί καλά σε ένα συγκεκριμένο μέρος της εικόνας, αλλά μπορεί να αποτύχει σε ένα άλλο μέρος. Για την επίλυση αυτών των περιορισμών, μπορεί να χρησιμοποιηθεί προσαρμοστικό όριο.
Το Adaptive thresholding είναι μια τοπική τεχνική κατωφλίου. Αυτή η τεχνική λαμβάνει υπόψη κάθε pixel και τη γειτονιά του. Ο αριθμητικός μέσος ή ο Gaussian μέσος όρος της έντασης των εικονοστοιχείων χρησιμοποιείται συνήθως για τον υπολογισμό του ορίου της γειτονιάς. τότε η τιμή κατωφλίου χρησιμοποιείται για την ταξινόμηση του εικονοστοιχείου. Στον Gaussian μέσο όρο, η τιμή pixel μακρύτερα από το κέντρο της περιοχής συμβάλλει λιγότερο στην εύρεση του ορίου της περιοχής, ενώ στον αριθμητικό μέσο όρο, όλες οι τιμές pixel συνεισφέρουν εξίσου.
Εφαρμογή του Προσαρμοστικού ορίου χρησιμοποιώντας το OpenCV:
# Arithmatic Mean Adaptive thresholding thresh6 = cv2.adaptiveThreshold(orig_img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,4) cv2_imshow(thresh6)
# Gaussian Mean Thresholding thresh7 = cv2.adaptiveThreshold(orig_img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,4) cv2_imshow(thresh7)
Οι παρακάτω εικόνες ελήφθησαν μετά την εφαρμογή προσαρμοστικού κατωφλίου:
Εισαγωγή στο UNet: Deep Learning Model for Image Segmentation
Σε αυτό το άρθρο, θα συζητήσουμε την αρχιτεκτονική U-Net για τμηματοποίηση εικόνας. Η αρχιτεκτονική UNet εισήχθη για την τμηματοποίηση της BioMedical Image από τους Olag Ronneberger et al. Με αυτήν την αρχιτεκτονική U-Net, η τμηματοποίηση των εικόνων μπορεί να υπολογιστεί με μια σύγχρονη GPU μέσα σε μικρό χρονικό διάστημα. Το UNet χρησιμοποιεί την έννοια του Fully Convolution Network μαζί με ελάχιστες τροποποιήσεις. Αυτό το μοντέλο βοηθά στον εντοπισμό του αντικειμένου σε μια εικόνα και στην εύρεση της μάσκας αυτού του αντικειμένου.
Αρχιτεκτονική U-Net:
Η παρακάτω εικόνα δείχνει την αρχιτεκτονική του U-Net.
- Αυτό το μοντέλο πήρε το όνομά του από την αρχιτεκτονική σε σχήμα U.
- Όπως μπορούμε να δούμε στην εικόνα, αυτή η αρχιτεκτονική έχει δύο μονοπάτια που δημιουργήθηκαν ως δίκτυο κωδικοποίησης-αποκωδικοποιητή.
- Εφαρμόζουμε δύο στρώματα συνέλιξης και μέγιστες στρώσεις συγκέντρωσης στην αριστερή διαδρομή.
- Η λειτουργία ενεργοποίησης ReLU ακολουθεί κάθε συνέλιξη.
- Στη σωστή διαδρομή, εφαρμόζουμε συνελίξεις μετάθεσης μαζί με δύο κανονικές συνελίξεις
Υλοποίηση UNet χρησιμοποιώντας Keras:
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import tensorflow_datasets as tfds import matplotlib.pyplot as plt import numpy as np
dataset, info = tfds.load('oxford_iiit_pet:3.*.*', with_info=True)
def resize(input_image, input_mask): input_image = tf.image.resize(input_image, (128, 128), method="nearest") input_mask = tf.image.resize(input_mask, (128, 128), method="nearest") return input_image, input_mask
def augment(input_image, input_mask): if tf.random.uniform(()) > 0.5: # Random flipping of the image and mask input_image = tf.image.flip_left_right(input_image) input_mask = tf.image.flip_left_right(input_mask) return input_image, input_mask
def normalize(input_image, input_mask): input_image = tf.cast(input_image, tf.float32) / 255.0 input_mask -= 1 return input_image, input_mask
def load_image_train(datapoint): input_image = datapoint["image"] input_mask = datapoint["segmentation_mask"] input_image, input_mask = resize(input_image, input_mask) input_image, input_mask = augment(input_image, input_mask) input_image, input_mask = normalize(input_image, input_mask) return input_image, input_mask
def load_image_test(datapoint): input_image = datapoint["image"] input_mask = datapoint["segmentation_mask"] input_image, input_mask = resize(input_image, input_mask) input_image, input_mask = normalize(input_image, input_mask) return input_image, input_mask
train_dataset = dataset["train"].map(load_image_train, num_parallel_calls=tf.data.AUTOTUNE) test_dataset = dataset["test"].map(load_image_test, num_parallel_calls=tf.data.AUTOTUNE)
BATCH_SIZE = 64 BUFFER_SIZE = 1000 train_batches = train_dataset.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat() train_batches = train_batches.prefetch(buffer_size=tf.data.experimental.AUTOTUNE) validation_batches = test_dataset.take(3000).batch(BATCH_SIZE) test_batches = test_dataset.skip(3000).take(669).batch(BATCH_SIZE)
def display(display_list): plt.figure(figsize=(15, 15)) title = ["Input Image", "True Mask", "Predicted Mask"] for i in range(len(display_list)): plt.subplot(1, len(display_list), i+1) plt.title(title[i]) plt.imshow(tf.keras.utils.array_to_img(display_list[i])) plt.axis("off") plt.show() sample_batch = next(iter(train_batches)) random_index = np.random.choice(sample_batch[0].shape[0]) #Displaying an image and it's corresponding masked image sample_image, sample_mask = sample_batch[0][random_index], sample_batch[1][random_index] display([sample_image, sample_mask])
#Creating 2 convolution blocks with ReLU activation function def double_conv_block(x, n_filters): # Conv2D then ReLU activation x = layers.Conv2D(n_filters, 3, padding = "same", activation = "relu", kernel_initializer = "he_normal")(x) # Conv2D then ReLU activation x = layers.Conv2D(n_filters, 3, padding = "same", activation = "relu", kernel_initializer = "he_normal")(x) return x
#Creating downsampling or encoder blocks 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
# Creating Upsampling or decoder blocks def upsample_block(x, conv_features, n_filters): # Transpose convolution Layer x = layers.Conv2DTranspose(n_filters, 3, 2, padding="same")(x) # concatenate x = layers.concatenate([x, conv_features]) # dropout x = layers.Dropout(0.3)(x) # Conv2D twice with ReLU activation x = double_conv_block(x, n_filters) return x
def build_unet_model(Image_Size): # Input Layer inputs = layers.Input(shape=Image_Size) # Creating 4 downsampling layers f1, p1 = downsample_block(inputs, 64) f2, p2 = downsample_block(p1, 64*2) f3, p3 = downsample_block(p2, 64*4) f4, p4 = downsample_block(p3, 64*8) # Bottleneck bottleneck = double_conv_block(p4, 1024) # Creating 4 upsampling layers u6 = upsample_block(bottleneck, f4, 512) u7 = upsample_block(u6, f3, 256) u8 = upsample_block(u7, f2, 128) u9 = upsample_block(u8, f1, 64) # Output Layer outputs = layers.Conv2D(3, 1, padding="same", activation = "softmax")(u9) # Creating model with Keras unet_model = tf.keras.Model(inputs, outputs, name="U-Net") return unet_model
# Creating a model with input shape(128, 128, 3) unet_model = build_unet_model((128,128,3)) # Compiling the model # Optimizer - Adam # loss Categorical cross entropy # Metrics - Accuracy unet_model.compile(optimizer=tf.keras.optimizers.Adam(), loss="sparse_categorical_crossentropy", metrics="accuracy")
# Model Training TRAIN_LENGTH = info.splits["train"].num_examples STEPS_PER_EPOCH = TRAIN_LENGTH // BATCH_SIZE VAL_SUBSPLITS = 5 TEST_LENTH = info.splits["test"].num_examples VALIDATION_STEPS = TEST_LENTH // BATCH_SIZE // VAL_SUBSPLITS model_history = unet_model.fit(train_batches,epochs=15,steps_per_epoch=STEPS_PER_EPOCH,validation_steps=VALIDATION_STEPS,validation_data=test_batches)
# Creating mask for predicted class def create_mask(pred_mask): pred_mask = tf.argmax(pred_mask, axis=-1) pred_mask = pred_mask[..., tf.newaxis] return pred_mask[0]
# Prediction def show_predictions(dataset=None, num=1): if dataset: for image, mask in dataset.take(num): pred_mask = unet_model.predict(image) display([image[0], mask[0], create_mask(pred_mask)]) else: display([sample_image, sample_mask, create_mask(model.predict(sample_image[tf.newaxis, ...]))])
import cv2 image = cv2.imread('/content/drive/MyDrive/AV/OpenCV/test.jpg') image1 = cv2.resize(image, (128,128)) cv2_imshow(image1) image1 = tf.expand_dims(image1, axis=0) pred_mask = unet_model.predict(image1) pred_mask1 = tf.expand_dims(pred_mask, axis=0) display(create_mask(pred_mask1))
συμπέρασμα
Ο κύριος στόχος αυτού του άρθρου είναι να μάθετε για το κατώφλι εικόνας. Βρήκαμε μια σύντομη εισαγωγή στο κατώφλι εικόνας και στις τεχνικές που χρησιμοποιούνται για την εκτέλεση του κατωφλίου εικόνας. Οι τεχνικές κατωφλίου που είδαμε σε αυτό το άρθρο είναι οι εξής:
- Η απλή τεχνική κατωφλίου είναι μια συνολική τεχνική κατωφλίου και ο χρήστης παρέχει το όριο εδώ. Είδαμε επίσης διαφορετικούς τύπους απλών τεχνικών κατωφλίου.
- Το κατώφλι του Otsu – Είναι επίσης μια παγκόσμια τεχνική κατωφλίου. Το όριο του Otsu χρησιμοποιείται για να βρεθεί η βέλτιστη τιμή κατωφλίου.
- Το Adaptive thresholding – είναι μια τοπική τεχνική κατωφλίου. Στο άρθρο δόθηκε επίσης μια σύντομη εισαγωγή στον αριθμητικό μέσο όρο και στις προσαρμοστικές τεχνικές μέσης Gaussian.
Οι παραπάνω τεχνικές χρησιμοποιούνται στο κατώφλι εικόνας. μελετήσαμε επίσης το μοντέλο UNet που χρησιμοποιείται στην τμηματοποίηση εικόνων.