Thursday, November 19, 2020

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

This content is powered by Balige PublishingVisit this link

In this tutorial, you will learn how to use OpenCV, PIL, NumPy library and other libraries to perform edge detection, segmentation, and denoising on images with Python GUI (PyQt).

Tutorial Steps To Generate And Display Noisy Image
Open Qt Designer. Put two Label widgets dan one Push Button widget onto form. Set objectName property of the two Label widgets by name labelImage and labelFilter. Set objectName property of the Push Button widget to pbImage.

Put two Widget from the Containers panel on the form. Set their objectName property to widgetHistIm and widgetHistFilter.

Put one Combo Box onto form and its objectName property to cboNoise. Double click on this widget and populate it with four items as shown in figure below.


Put three Line Edit widgets onto form and set each objectName property to leMean, leVar, and leAmount. Set its text property of each to 0, 0, and 0.1.

Then, put three Horizontal Slider widgets and set their objectName properties to hsMean, hsVar, and hsAmount. Set minimum property of each widget to 0, and maximum property of hsMean and hsVar to 255 and maximum property of hsAmount to 10. Set singleStep property of each widget to 1.

Save the form as image_proc.ui. Now, the form looks as shown in figure below.


Next, right-click on the two Widget and from the context menu displayed select Promote to ... . Name the Promoted class name as imfilter. Then click Add button and click the Promote button. In the Object Inspector window, you can see that widgetHistIm and widgetHistFilter (imfilter class) along with other widgets are in the centralwidget object (QWidget class) as shown in Figure below.


Write this Python script and set its name as to image_processing.py to open file dialog, display original image, and display its histogram:

#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)
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')
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()
if __name__=="__main__":
import sys
app = QApplication(sys.argv)
w = FormImageProcessing()
w.show()
sys.exit(app.exec_())

Define a new method named 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)
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)

Invoke setState() method from inside __init__() to disable widgets when form starts:

def __init__(self):
QMainWindow.__init__(self)
loadUi("image_thresh.ui",self)
self.setWindowTitle("Image Thresholding")
self.pbImage.clicked.connect(self.open_file)
self.setState('START')

Invoke setState() from open_file() method to enable back the widgets when user click Open Image button as follows:

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)

Run image_processing.py. Make sure some widgets disabled when form first starts as shown in Figure below.

Then open one image, then you will see the image and its histogram as shown in Figure below. Makes sure the widgets enabled back.

Define the two new methods named do_noise() and choose_noise() as follows:
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_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

In choose_noise(), there are four method invoking to generate four different noise:
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

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

self.cboNoise.currentIndexChanged.connect(self.do_noise)

Modify set_hsMean(), set_hsVar(), and set_hsAmount() to invoke do_noise() as follows:
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()

Connect textEdited() event of leMean, leVar, and leAmount widgets to do_noise() and place them 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)

Run image_processing.py. Choose input image and you can choose mean, variance, or amount/peak values to generate noisy image using horizontal sliders as shown in Figure below.


You can choose different noise by selecting different item in combo box as shown in 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)
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)
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)
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_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')
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 2


1 comment:


  1. Thank you for the information you provide, it helped me a lot. I hope to have many more entries or so from you.
    Very interesting blog.
    fullcrackedpc.net
    EarthView Crack
    Qimage Ultimate Crack
    Tenorshare UltData Windows Crack

    ReplyDelete