Sunday, November 29, 2020

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

This content is powered by Balige PublishingVisit this link

Part 1


Modify image_proc.ui, put one Combo Box onto form and its objectName property to cboEdge. Double click on this widget and populate it with four items as shown in Figure below.



Then, put three Spin Box widgets and set their objectName properties to sbMinVal, sbMaxVal, and sbKernel. Set minimum property of each widget to 0, 0, and 3. Set maximum property of each widget to 255. Set singleStep property of each widget to 1, 1, and 2.



Put three Line Edit widgets onto form and set each objectName property to leMinVal, leMaxVal, and leKernel. Set its text property of each to 0, 0, and 3.

Save form with the same name. Now, the form looks as shown in Figure above.

Modify setState() to set state’s application as follows:

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)

Define the two new methods named do_edge() and choose_edge() as follows:

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 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')

In choose_edge(), there are four methods invoking to perform edge detection on image:

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

Connect currentIndexChanged() of cboEdge widget to do_edge() and place it inside __init__() method as follows:

self.cboEdge.currentIndexChanged.connect(self.do_edge)

Define set_minval(), set_maxval(), and set_kernel() to invoke do_edge() as follows:

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()

Connect textEdited() event of leMinVal, MaxVal and leKernel widgets to do_edge(). Connect valueChanged() signal of sbMinVal to set_minval() method. Connect valueChanged() signal of sbMaxVal to set_maxval().  Connect valueChanged() signal of sbKernel to set_kernel().

place them all inside __init__() method as follows:

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)

Run image_processing.py. Choose input image and you can choose mean, variance, or amount/peak values to generate noisy image using horizontal sliders. Then, you can choose edge detector by selecting different item in cboEdge as shown in both figures 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.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

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)
        
    def open_file(self):
        global img
        fname = QFileDialog.getOpenFileName(self, 'Open file', 
           'd:\\',"Image Files (*.jpg *.gif *.bmp *.png)")
        pixmap = QPixmap(fname[0])
        img = cv2.imread(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))
        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))
        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 3