Perbandingan Hasil Segmentasi Otsu vs UNet Deep Learning

By | July 27, 2023
Print Friendly, PDF & Email
319 Views

Perbandingan Hasil Segmentasi Otsu vs UNet Deep Learning – Banyak pembahasan dari tugas computer vision, salah hal yang sederhana bagi manusia tapi belum tentu mudah bagi komputer mengingat cara kerjanya berbeda, dimana otak manusia bekerja secara kontinue dan mesin bekerja secara diskrit.

Salah satunya yaitu proses segmentasi yaitu memisahkan objek yang dianggap foreground dan background. Seringkali hal yang sulit dari proses segmentasi yaitu beragamnya warna dan bahkan mengandung 3 layer seperti RGB sehingga mencari nilai Thresholding menjadi pekerjaan yang cukup rumit. Dalam beberapa case selalu menggunakan pendekatan mode warna grayscale daripada RGB.

Berikut ini saya akan sajikan hasil dari ngoprek deep learning untuk mengetahui bagaimana tugas computer vision dalam hal segmentasi menggunakan 2 algoritma yaitu

  • algoritma konvensional : metode otsu
  • algoritma deep learning: model UNet

Berikut adalah gambar testing yang akan digunakan untuk menguji performa 2 algoritma diatas

 

gambar asli

 

Metode Otsu

Metode otsu seringkali digunakan untuk mencari nilai Thresholding yang dinamis melalui input histogram warna berupa grayscale. Perhatikan pada kasus contoh berikut ini

Grayscale dan Histogram nya

Untuk mendapatkan nilai T cukup mudah dengan menggunakan otsu di OpenCV

T, bin_img = cv2.threshold(img,
                             0, 255, 
                             cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

Nilai T yaitu 69.0 sehingga bila diterapkan pada gambar diatas akan menghasilkan citra berikut dibawah ini

Hasil Segmentasi dengan metode Otsu

Bila kita lihat sejenak hasilnya sangat bagus dan rapi walaupun begitu, saya akan mencoba menggunakan deep learning menggunakan model UNET

UNet Deep Learning

Model UNet berhasil menjadi model machine learning untuk melakukan tugas segmentasi objek bahkan dengan beragam warna sekalipun, saya menggunakan model dari https://pytorch.org/hub/mateuszbuda_brain-segmentation-pytorch_unet/  yang memang bisa di customize untuk menerima input beragam ukuran, mulai dari 256×256; 512×512 bahkan sampai 2048 x 2048 tentu harus berhati dengan RAM yang kalian gunakan nanti!

See also  Menyingkap Rahasia Machine Learning untuk Mengenali Objek dengan akurasi tinggi

Sedikit penjelasan mengenai UNet Deep Learning: Model U-Net ini terdiri dari empat level blok yang berisi dua lapisan konvolusional dengan normalisasi batch dan fungsi aktivasi ReLU, dan satu lapisan penyatuan maks di bagian pengkodean dan lapisan konvolusional di bagian decoding. Jumlah convolutional filter pada setiap blok adalah 32, 64, 128, dan 256. Layer bottleneck memiliki 512 convolutional filter. Dari lapisan penyandian, koneksi lewati digunakan ke lapisan yang sesuai di bagian decoding.

from collections import OrderedDict
import torch
import torch.nn as nn


class UNet(nn.Module):
    def __init__(self, in_channels=3, out_channels=1, init_features=32):
        super(UNet, self).__init__()
        features            = init_features
        self.encoder1       = UNet._block(in_channels, features, name="enc1")
        self.pool1          = nn.MaxPool2d(kernel_size=2, stride=2)
        self.encoder2       = UNet._block(features, features * 2, name="enc2")
        self.pool2          = nn.MaxPool2d(kernel_size=2, stride=2)
        self.encoder3       = UNet._block(features * 2, features * 4, name="enc3")
        self.pool3          = nn.MaxPool2d(kernel_size=2, stride=2)
        self.encoder4       = UNet._block(features * 4, features * 8, name="enc4")
        self.pool4          = nn.MaxPool2d(kernel_size=2, stride=2)

        self.bottleneck = UNet._block(features * 8, features * 16, name="bottleneck")

        self.upconv4 = nn.ConvTranspose2d(
            features * 16, features * 8, kernel_size=2, stride=2
        )
        self.decoder4 = UNet._block((features * 8) * 2, features * 8, name="dec4")
        self.upconv3 = nn.ConvTranspose2d(
            features * 8, features * 4, kernel_size=2, stride=2
        )
        self.decoder3 = UNet._block((features * 4) * 2, features * 4, name="dec3")
        self.upconv2 = nn.ConvTranspose2d(
            features * 4, features * 2, kernel_size=2, stride=2
        )
        self.decoder2 = UNet._block((features * 2) * 2, features * 2, name="dec2")
        self.upconv1 = nn.ConvTranspose2d(
            features * 2, features, kernel_size=2, stride=2
        )
        self.decoder1 = UNet._block(features * 2, features, name="dec1")

        self.conv = nn.Conv2d(
            in_channels=features, out_channels=out_channels, kernel_size=1
        )

    def forward(self, x):
        enc1 = self.encoder1(x)
        enc2 = self.encoder2(self.pool1(enc1))
        enc3 = self.encoder3(self.pool2(enc2))
        enc4 = self.encoder4(self.pool3(enc3))

        bottleneck = self.bottleneck(self.pool4(enc4))

        dec4 = self.upconv4(bottleneck)
        dec4 = torch.cat((dec4, enc4), dim=1)
        dec4 = self.decoder4(dec4)
        dec3 = self.upconv3(dec4)
        dec3 = torch.cat((dec3, enc3), dim=1)
        dec3 = self.decoder3(dec3)
        dec2 = self.upconv2(dec3)
        dec2 = torch.cat((dec2, enc2), dim=1)
        dec2 = self.decoder2(dec2)
        dec1 = self.upconv1(dec2)
        dec1 = torch.cat((dec1, enc1), dim=1)
        dec1 = self.decoder1(dec1)
        return torch.sigmoid(self.conv(dec1))

    @staticmethod
    def _block(in_channels, features, name):
        return nn.Sequential(
            OrderedDict(
                [
                    (
                        name + "conv1",
                        nn.Conv2d(
                            in_channels=in_channels,
                            out_channels=features,
                            kernel_size=3,
                            padding=1,
                            bias=False,
                        ),
                    ),
                    (name + "norm1", nn.BatchNorm2d(num_features=features)),
                    (name + "relu1", nn.ReLU(inplace=True)),
                    (
                        name + "conv2",
                        nn.Conv2d(
                            in_channels=features,
                            out_channels=features,
                            kernel_size=3,
                            padding=1,
                            bias=False,
                        ),
                    ),
                    (name + "norm2", nn.BatchNorm2d(num_features=features)),
                    (name + "relu2", nn.ReLU(inplace=True)),
                ]
            )
        )

Atau menggunakan model yang lain (bisa kalian pelajari sendiri) https://www.kaggle.com/code/heiswicked/pytorch-lightning-unet-segmentation-tumour

See also  Cara Membuat ANN dengan Multi Target

Loss Function

Setelah saya coba2 untuk melakukan training dataset, ternyata saya lebih suka menggunakan loss function berikut ini yang diambil dari https://www.kaggle.com/code/bonhart/brain-mri-data-visualization-unet-fpn

# diambil dari 
# https://www.kaggle.com/code/bonhart/brain-mri-data-visualization-unet-fpn
def dice_coef_loss(inputs, target):
    smooth = 1.0
    intersection = 2.0 * ((target * inputs).sum()) + smooth
    union = target.sum() + inputs.sum() + smooth
    return 1 - (intersection / union)


def bce_dice_loss(inputs, target):
    dicescore = dice_coef_loss(inputs, target)
    bcescore = nn.BCELoss()
    bceloss = bcescore(inputs, target)
    return bceloss + dicescore

dengan penggunaannya sebagai berikut loss = bce_dice_loss(out.float(),labels.float())

karena grafik loss function stabil turun dan sengaja saya break/hentikan ketika sudah mencapai 0.2

Grafik Loss pada tiap epoch

Contoh dataset Training

Untuk melatih deep learning, saya menggunakan beberapa gambar berikut untuk memisahkan objek yang saling berdempet juga

gambar training 1

gambar training 2

dengan target objek

target 1

target 2

Nah berikut dibawah ini hasil segmentasi gambar menggunakan UNet Deep Learning dengan hasil yang lebih rapi dan beberapa objek yang berhimpitan akan terpisah  sehingga jumlah objeknya menjadi lebih banyak yaitu 608 (walaupun masih ada saja objek yang berdempetan)

Terkait mengenai segmentasi, kalian bisa baca

Leave a Reply