Friday, December 11, 2020

Edge Detection, Segmentation, and Denoising on Images with Python GUI (PyQt): Part 3

This content is powered by Balige PublishingVisit this link

Part 1 Part 2


Tutorial Steps To Implement Image Segmentation Using Multiple Thresholding and K-Means Algorithm

Modify image_proc.ui, put two Radio Button widgets onto form and set their objectName properties to rbMultiple and rbKMeans. Set their text properties to Multiple Thresholding and K-Means Algorithm. Then, save form with the same name. Now, the form looks as shown in Figure below.


Define two new methods, multiple_thresh() and thresh_seg(), to implement multiple thresholding segmentation as follows:


def thresh_seg(self,img):
    img_seg = np.zeros(img.shape, np.float32)

    if len(img.shape) == 2:
       img_seg = self.multiple_thresh(img)
    else:
        img_seg[:, :, 0] = self.multiple_thresh(img[:, :, 0])
        img_seg[:, :, 1] = self.multiple_thresh(img[:, :, 1])
        img_seg[:, :, 2] = self.multiple_thresh(img[:, :, 2])
        cv2.normalize(img_seg, img_seg, 0, 255, \
            cv2.NORM_MINMAX, dtype=-1)
        img_seg = img_seg.astype(np.uint8)
    return img_seg

def multiple_thresh(self, img):
    img_thresh= 255*img.reshape(img.shape[0]*img.shape[1])
    thresh_mean = img_thresh.mean()
        
    for i in range(img_thresh.shape[0]):
        if img_thresh[i] > thresh_mean: 
            img_thresh[i] = 3
        elif img_thresh[i] > 0.5:
            img_thresh[i] = 2
        elif img_thresh[i] > 0.25:
            img_thresh[i] = 1
        else:
            img_thresh[i] = 0
    img_mul = img_thresh.reshape(img.shape[0],img.shape[1])
    return img_mul

Add a new statement to import KMeans algorithm from sklearn package:

from sklearn.cluster import KMeans

Define a new method, kmeans_seg(), to implement K-Means algorithm as follows:

def kmeans_seg(self,img):
    img_seg = np.zeros(img.shape, np.float32)
    img_n = img.reshape(img.shape[0]*img.shape[1], img.shape[2])

    kmeans = KMeans(n_clusters=5, random_state=0).fit(img_n)
    pic2show = kmeans.cluster_centers_[kmeans.labels_]
    img_seg = pic2show.reshape(img.shape[0], img.shape[1], \
        img.shape[2])

    cv2.normalize(img_seg, img_seg, 0, 255, \
        cv2.NORM_MINMAX, dtype=-1)
    img_seg = img_seg.astype(np.uint8)
    return img_seg

Define a new method, seg_output(), to display segmented image and its histogram as follows:

def seg_output(self,output):       
    height, width, channel = output.shape
    bytesPerLine = 3 * width  
    cv2.cvtColor(output, cv2.COLOR_BGR2RGB, output)
    qImg = QImage(output, width, height, \
        bytesPerLine, QImage.Format_RGB888)
    pixmap = QPixmap.fromImage(qImg)

    self.display_image(pixmap, output, self.labelFilter, \
        self.widgetHistFilter, 'Histogram of Segmented Image')

Define rbstate() method to determine which radio button is clicked by user as follows:

def rbstate(self):
    noisy = self.choose_noise(img)
    if self.rbMultiple.isChecked() == True:
        output = self.thresh_seg(noisy)
        self.seg_output(output)

    if self.rbKMeans.isChecked() == True:
        output = self.kmeans_seg(noisy)
        self.seg_output(output)

Connect toggled() event of rbMultiple and rbKMeans to rbState() method and put them inside __init__() method as follows:

self.rbMultiple.toggled.connect(self.rbstate)
self.rbKMeans.toggled.connect(self.rbstate)

Run image_processing.py. Choose test image and click on rbMultiple widget. The result is shown in Figure below.


The click on rbKMeans widget and the result is shown in Figure below.


The following is image_processing.py script so far:

#image_processing.py

import cv2
import numpy as np
from PyQt5.QtWidgets import*
from PyQt5 import QtGui, QtCore
from PyQt5.uic import loadUi
from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as NavigationToolbar)
from PyQt5.QtWidgets import QDialog, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QImage
from PIL import Image
from skimage.util import random_noise
from sklearn.cluster import KMeans
from skimage.segmentation import active_contour
from skimage.filters import gaussian
import skimage.color as color
import numpy as np
fname = ""
     
class FormImageProcessing(QMainWindow):   
    def __init__(self):
        QMainWindow.__init__(self)
        loadUi("image_proc.ui",self)        

        self.setWindowTitle("Image Processing")
        self.pbImage.clicked.connect(self.open_file)
        self.setState('START')
        
        self.hsMean.valueChanged.connect(self.set_hsMean)
        self.hsVar.valueChanged.connect(self.set_hsVar)
        self.hsAmount.valueChanged.connect(self.set_hsAmount)
        self.leMean.textEdited.connect(self.do_noise)
        self.leVar.textEdited.connect(self.do_noise)
        self.leAmount.textEdited.connect(self.do_noise)
        self.cboNoise.currentIndexChanged.connect(self.do_noise)
        self.sbMinVal.valueChanged.connect(self.set_minval)
        self.sbMaxVal.valueChanged.connect(self.set_maxval)
        self.sbKernel.valueChanged.connect(self.set_kernel)
        self.leMinVal.textEdited.connect(self.do_edge)
        self.leMaxVal.textEdited.connect(self.do_edge)
        self.leKernel.textEdited.connect(self.do_edge)
        self.cboEdge.currentIndexChanged.connect(self.do_edge)
        
        self.rbMultiple.toggled.connect(self.rbstate)
        self.rbKMeans.toggled.connect(self.rbstate)

        
    def thresh_seg(self,img):
        img_seg = np.zeros(img.shape, np.float32)
        if len(img.shape) == 2:
           img_seg = self.multiple_thresh(img)
        else:
            img_seg[:, :, 0] = self.multiple_thresh(img[:, :, 0])
            img_seg[:, :, 1] = self.multiple_thresh(img[:, :, 1])
            img_seg[:, :, 2] = self.multiple_thresh(img[:, :, 2])
            cv2.normalize(img_seg, img_seg, 0, 255, cv2.NORM_MINMAX, dtype=-1)
            img_seg = img_seg.astype(np.uint8)
        return img_seg

    def multiple_thresh(self, img):
        img_thresh= 255*img.reshape(img.shape[0]*img.shape[1])
        thresh_mean = img_thresh.mean()
        
        for i in range(img_thresh.shape[0]):
            if img_thresh[i] > thresh_mean: 
                img_thresh[i] = 3
            elif img_thresh[i] > 0.5:
                img_thresh[i] = 2
            elif img_thresh[i] > 0.25:
                img_thresh[i] = 1
            else:
                img_thresh[i] = 0
        img_mul = img_thresh.reshape(img.shape[0],img.shape[1])
        return img_mul

    def kmeans_seg(self,img):
        img_seg = np.zeros(img.shape, np.float32)
        img_n = img.reshape(img.shape[0]*img.shape[1], img.shape[2])

        kmeans = KMeans(n_clusters=5, random_state=0).fit(img_n)
        pic2show = kmeans.cluster_centers_[kmeans.labels_]
        img_seg = pic2show.reshape(img.shape[0], img.shape[1], img.shape[2])

        cv2.normalize(img_seg, img_seg, 0, 255, cv2.NORM_MINMAX, dtype=-1)
        img_seg = img_seg.astype(np.uint8)
        return img_seg
    
    def rbstate(self):
        noisy = self.choose_noise(img)
        if self.rbMultiple.isChecked() == True:
            output = self.thresh_seg(noisy)
            self.seg_output(output)

        if self.rbKMeans.isChecked() == True:
            output = self.kmeans_seg(noisy)
            self.seg_output(output)

    def seg_output(self,output):       
        height, width, channel = output.shape
        bytesPerLine = 3 * width  
        cv2.cvtColor(output, cv2.COLOR_BGR2RGB, output)
        qImg = QImage(output, width, height, \
            bytesPerLine, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qImg)
        self.display_image(pixmap, output, self.labelFilter, self.widgetHistFilter, 'Histogram of Segmented Image')
        
    def open_file(self):
        global img
        self.fname = QFileDialog.getOpenFileName(self, 'Open file', 
           'd:\\',"Image Files (*.jpg *.gif *.bmp *.png)")
        pixmap = QPixmap(self.fname[0])
        img = cv2.imread(self.fname[0], cv2.IMREAD_COLOR)
        self.display_image(pixmap, img, self.labelImage, self.widgetHistIm, 'Histogram of Original Image')
        self.setState('RUN')
        self.hsAmount.setEnabled(False)
        self.leAmount.setEnabled(False)

    def display_image(self, pixmap, img, label, qwidget1, title):       
        label.setPixmap(pixmap)
        label.setScaledContents(True);                
        self.display_histogram(img, qwidget1, title)

    def display_histogram(self, img, qwidget1, title):
        qwidget1.canvas.axes1.clear()
        
        channel = len(img.shape)
        
        if channel == 2: #grayscale image
            histr = cv2.calcHist([img],[0],None,[256],[0,256])
            qwidget1.canvas.axes1.plot(histr,\
                                               color = 'yellow',linewidth=3.0)
            qwidget1.canvas.axes1.set_ylabel('Frequency',\
                                                     color='white')
            qwidget1.canvas.axes1.set_xlabel('Intensity', \
                                                     color='white')
            qwidget1.canvas.axes1.tick_params(axis='x', colors='white')
            qwidget1.canvas.axes1.tick_params(axis='y', colors='white')
            qwidget1.canvas.axes1.set_title(title,color='white')
            qwidget1.canvas.axes1.set_facecolor('xkcd:black')
            qwidget1.canvas.axes1.grid()
            qwidget1.canvas.draw() 
        
        else : #color image
            color = ('b','g','r')
            for i,col in enumerate(color):
                histr = cv2.calcHist([img],[i],None,[256],[0,256])
                qwidget1.canvas.axes1.plot(histr,\
                                               color = col,linewidth=3.0)
                qwidget1.canvas.axes1.set_ylabel('Frequency',\
                                                     color='white')
                qwidget1.canvas.axes1.set_xlabel('Intensity', \
                                                     color='white')
                qwidget1.canvas.axes1.tick_params(axis='x', colors='white')
                qwidget1.canvas.axes1.tick_params(axis='y', colors='white')
                qwidget1.canvas.axes1.set_title(title,color='white')
                qwidget1.canvas.axes1.set_facecolor('xkcd:black')
                qwidget1.canvas.axes1.grid()
            qwidget1.canvas.draw()        

    def setState(self, state):
        if state == 'START':
            self.cboNoise.setEnabled(False)
            self.hsMean.setEnabled(False)
            self.hsVar.setEnabled(False)
            self.hsAmount.setEnabled(False)
            self.leMean.setEnabled(False)
            self.leVar.setEnabled(False)
            self.leAmount.setEnabled(False)
            self.leMinVal.setEnabled(False)
            self.leMaxVal.setEnabled(False)
            self.leKernel.setEnabled(False)
            self.sbMinVal.setEnabled(False)
            self.sbMaxVal.setEnabled(False)
            self.sbKernel.setEnabled(False)
            self.cboEdge.setEnabled(False)
        else:
            self.cboNoise.setEnabled(True)
            self.hsMean.setEnabled(True)
            self.hsVar.setEnabled(True)
            self.hsAmount.setEnabled(True)
            self.leMean.setEnabled(True)
            self.leVar.setEnabled(True)
            self.leAmount.setEnabled(True)
            self.leMinVal.setEnabled(True)
            self.leMaxVal.setEnabled(True)
            self.leKernel.setEnabled(True)
            self.sbMinVal.setEnabled(True)
            self.sbMaxVal.setEnabled(True)
            self.sbKernel.setEnabled(True)
            self.cboEdge.setEnabled(True)

    def set_hsMean(self, value):
        self.leMean.setText(str(round((value/64),2)))
        self.do_noise()
        
    def set_hsVar(self, value):
        self.leVar.setText(str(round((value/64),2)))
        self.do_noise()
        
    def set_hsAmount(self, value):
        self.leAmount.setText(str(round((value/10),2)))
        self.do_noise()
        
    def choose_noise(self,img):        
        strCB = self.cboNoise.currentText()
        mean = float(self.leMean.text())
        var = float(self.leVar.text())
        amount = float(self.leAmount.text())
        sigma = var**0.5
        row,col,ch= img.shape
        
        if strCB == 'Gaussian':  
            self.hsAmount.setEnabled(False)
            self.leAmount.setEnabled(False)
            self.hsMean.setEnabled(True)
            self.leMean.setEnabled(True)
            self.hsVar.setEnabled(True)
            self.leVar.setEnabled(True)
            noisy_image = self.gaussian_noise(img, mean, sigma, row, col)
            return noisy_image
        if strCB == 'Speckle': 
            self.hsAmount.setEnabled(False)
            self.leAmount.setEnabled(False)
            self.hsMean.setEnabled(True)
            self.leMean.setEnabled(True)
            self.hsVar.setEnabled(True)
            self.leVar.setEnabled(True)
            noisy_image = self.speckle_noise(img, mean, sigma, row, col)
            return noisy_image
        if strCB == 'Poisson':  
            self.hsMean.setEnabled(False)
            self.leMean.setEnabled(False)
            self.hsVar.setEnabled(False)
            self.leVar.setEnabled(False)
            self.hsAmount.setEnabled(True)
            self.leAmount.setEnabled(True)
            noisy_image = self.poisson_noise(img, amount)
            return noisy_image
        if strCB == 'Salt & Pepper':  
            self.hsMean.setEnabled(False)
            self.leMean.setEnabled(False)
            self.hsVar.setEnabled(False)
            self.leVar.setEnabled(False)
            self.hsAmount.setEnabled(True)
            self.leAmount.setEnabled(True)
            noisy_image = self.salt_pepper_noise(img, amount)
            return noisy_image

    def gaussian_noise2(self,img, mean, sigma, row, col):
        gaussian = np.random.normal(mean, sigma, (row,col)) #  np.zeros((224, 224), np.float32)       
        noisy_image = np.zeros(img.shape, np.float32)            
        if len(img.shape) == 2:
            noisy_image = img + gaussian
        else:
            noisy_image[:, :, 0] = img[:, :, 0] + gaussian
            noisy_image[:, :, 1] = img[:, :, 1] + gaussian
            noisy_image[:, :, 2] = img[:, :, 2] + gaussian
            cv2.normalize(noisy_image, noisy_image, 0, 255, cv2.NORM_MINMAX, dtype=-1)
            noisy_image = noisy_image.astype(np.uint8)
        return noisy_image

    def speckle_noise2(self,img, mean, sigma, row, col):
        gaussian = np.random.normal(mean, sigma, (row,col)) #  np.zeros((224, 224), np.float32)
        noisy_image = np.zeros(img.shape, np.float32)
            
        if len(img.shape) == 2:
            noisy_image = img + img*gaussian
        else:
            noisy_image[:, :, 0] = img[:, :, 0] + gaussian * img[:, :, 0]
            noisy_image[:, :, 1] = img[:, :, 1] + gaussian * img[:, :, 1]
            noisy_image[:, :, 2] = img[:, :, 2] + gaussian * img[:, :, 2]
            cv2.normalize(noisy_image, noisy_image, 0, 255, cv2.NORM_MINMAX, dtype=-1)
            noisy_image = noisy_image.astype(np.uint8)
        return noisy_image

    def gaussian_noise(self,img, mean, sigma, row, col):
        # Generate Gaussian noise
        gauss = np.random.normal(mean,sigma,img.size)
        gauss = gauss.reshape(img.shape[0],img.shape[1],img.shape[2]).astype('uint8')
        # Add the Gaussian noise to the image
        img_gauss = cv2.add(img,gauss)
        return img_gauss

    def speckle_noise(self,img, mean, sigma, row, col):
        # Generate Gaussian noise
        gauss = np.random.normal(mean,sigma,img.size)
        gauss = gauss.reshape(img.shape[0],img.shape[1],img.shape[2]).astype('uint8')
        # Add the Gaussian noise to the image
        img_sp = cv2.add(img,img*gauss)
        return img_sp

    def salt_pepper_noise(self, img, val):
        # Add salt-and-pepper noise to the image.
        noise_img = random_noise(img, mode='s&p',amount=val)     

        # The above function returns a floating-point image
        # on the range [0, 1], thus we changed it to 'uint8'
        # and from [0,255]
        imgsnp = np.array(255*noise_img, dtype = 'uint8')
        return imgsnp

    def poisson_noise(self, img, peak):
        pois = np.random.poisson(img / 255.0 * peak) / peak * 255
        
        # The above function returns a floating-point image
        # on the range [0, 1], thus we changed it to 'uint8'
        # and from [0,255]
        imgpois = np.array(255*pois, dtype = 'uint8')
        return imgpois
        
    def do_noise(self):
        noisy = self.choose_noise(img)        
        height, width, channel = noisy.shape
        bytesPerLine = 3 * width  
        cv2.cvtColor(noisy, cv2.COLOR_BGR2RGB, noisy)
        qImg = QImage(noisy, width, height, \
            bytesPerLine, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qImg)
        self.display_image(pixmap, noisy, self.labelFilter, self.widgetHistFilter, 'Histogram of Noisy Image')

    def choose_edge(self,img):        
        strCB = self.cboEdge.currentText()
        minVal = float(self.leMinVal.text())
        maxVal = float(self.leMaxVal.text())
        kernel = int(self.leKernel.text())
        noisy = self.choose_noise(img)
        
        if strCB == 'Canny':  
            self.leKernel.setEnabled(False)
            self.sbKernel.setEnabled(False)
            self.leMinVal.setEnabled(True)
            self.sbMinVal.setEnabled(True)
            self.leMaxVal.setEnabled(True)
            self.sbMaxVal.setEnabled(True)
            edge_im = self.canny_edge(noisy, minVal, maxVal)
            return edge_im
        if strCB == 'Sobel X':  
            self.leKernel.setEnabled(True)
            self.sbKernel.setEnabled(True)
            self.leMinVal.setEnabled(False)
            self.sbMinVal.setEnabled(False)
            self.leMaxVal.setEnabled(False)
            self.sbMaxVal.setEnabled(False)
            edge_im = self.sobelx_edge(noisy, 1, 0, kernel)
            return edge_im
        if strCB == 'Sobel Y': 
            self.leKernel.setEnabled(True)
            self.sbKernel.setEnabled(True)
            self.leMinVal.setEnabled(False)
            self.sbMinVal.setEnabled(False)
            self.leMaxVal.setEnabled(False)
            self.sbMaxVal.setEnabled(False)
            edge_im = self.sobelx_edge(noisy, 0, 1, kernel)
            return edge_im
        if strCB == 'Laplacian':  
            self.leKernel.setEnabled(True)
            self.sbKernel.setEnabled(True)
            self.leMinVal.setEnabled(False)
            self.sbMinVal.setEnabled(False)
            self.leMaxVal.setEnabled(False)
            self.sbMaxVal.setEnabled(False)
            edge_im = self.laplacian_edge(noisy, kernel)
            return edge_im
        
    def canny_edge(self, img, minVal, maxVal):      
        edge_im = np.zeros(img.shape, np.float32)
        
        if len(img.shape) == 2:
           edge_im = cv2.Canny(img,minVal, maxVal)
        else:
            edge_im[:, :, 0] = cv2.Canny(img[:, :, 0],minVal, maxVal)
            edge_im[:, :, 1] = cv2.Canny(img[:, :, 1],minVal, maxVal)
            edge_im[:, :, 2] = cv2.Canny(img[:, :, 2],minVal, maxVal)
            cv2.normalize(edge_im, edge_im, 0, 255, cv2.NORM_MINMAX, dtype=-1)
            edge_im = edge_im.astype(np.uint8)
        return edge_im

    def sobelx_edge(self, img, d1, d2, kernel):      
        edge_im = np.zeros(img.shape, np.float32)
        
        if len(img.shape) == 2:
           edge_im = cv2.Sobel(img,cv2.CV_64F, d1, d2, kernel)
        else:
            edge_im[:, :, 0] = cv2.Sobel(img[:, :, 0],cv2.CV_64F, d1, d2, kernel)
            edge_im[:, :, 1] = cv2.Sobel(img[:, :, 1],cv2.CV_64F, d1, d2, kernel)
            edge_im[:, :, 2] = cv2.Sobel(img[:, :, 2],cv2.CV_64F, d1, d2, kernel)
            cv2.normalize(edge_im, edge_im, 0, 255, cv2.NORM_MINMAX, dtype=-1)
            edge_im = edge_im.astype(np.uint8)
        return edge_im

    def laplacian_edge(self, img, kernel):      
        edge_im = np.zeros(img.shape, np.float32)
        
        if len(img.shape) == 2:
           edge_im = cv2.Laplacian(img, cv2.CV_64F, kernel)
        else:
            edge_im[:, :, 0] = cv2.Laplacian(img[:, :, 0], cv2.CV_64F, kernel)
            edge_im[:, :, 1] = cv2.Laplacian(img[:, :, 1], cv2.CV_64F, kernel)
            edge_im[:, :, 2] = cv2.Laplacian(img[:, :, 2], cv2.CV_64F, kernel)
            cv2.normalize(edge_im, edge_im, 0, 255, cv2.NORM_MINMAX, dtype=-1)
            edge_im = edge_im.astype(np.uint8)
        return edge_im
    
    def do_edge(self):
        edges = self.choose_edge(img)        
        height, width, channel = edges.shape
        bytesPerLine = 3 * width  
        cv2.cvtColor(edges, cv2.COLOR_BGR2RGB, edges)
        qImg = QImage(edges, width, height, \
            bytesPerLine, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qImg)
        self.display_image(pixmap, edges, self.labelFilter, self.widgetHistFilter, 'Histogram of Edge Detection')
        
    def set_minval(self):
        self.leMinVal.setText(str(self.sbMinVal.value()))  
        self.do_edge()
        
    def set_maxval(self):
        self.leMaxVal.setText(str(self.sbMaxVal.value())) 
        self.do_edge()

    def set_kernel(self):
        self.leKernel.setText(str(self.sbKernel.value())) 
        self.do_edge()
        
if __name__=="__main__":
    import sys
    app = QApplication(sys.argv)    
    w = FormImageProcessing()
    w.show()
    sys.exit(app.exec_())


Edge Detection, Segmentation, and Denoising on Images with Python GUI (PyQt): Part 4





No comments:

Post a Comment