Tuesday, November 17, 2020

Image Thresholding with Python GUI (PyQt): Part 2

This content is powered by Balige PublishingVisit this link
See Part 1 

Tutorial Steps To Implement Adaptive Image Thresholding
Adaptive thresholding has three ‘special’ input params and only one output argument. It decides how thresholding value is calculated:



Modify cboThresholding and add two new items as shown in figure below.


Put two more Line Edit widgets on form and set their objectName properties to leBlockSize and leConst. Set their text properties to 11 and 2.

Then, put two Spin Box widgets and set their objectName properties to sbBlockSize and sbConst. Set their minimum properties to 1, set their value properties to 11 and 2, and set their singleStep properties to 2 and 1.

Modify setState() as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def setState(self, state):
    if state == 'START':
        self.cboThresholding.setEnabled(False)
        self.hsMinVal.setEnabled(False)
        self.hsMaxVal.setEnabled(False)
        self.leMinVal.setEnabled(False)
        self.leMaxVal.setEnabled(False)
        self.lwChannel.setEnabled(False)
        self.twIntensity.setEnabled(False)
        self.leBlockSize.setEnabled(False)
        self.sbBlockSize.setEnabled(False)
        self.leConst.setEnabled(False)
        self.sbConst.setEnabled(False)
    else:
        self.cboThresholding.setEnabled(True)
        self.hsMinVal.setEnabled(True)
        self.hsMaxVal.setEnabled(True)
        self.leMinVal.setEnabled(True)
        self.leMaxVal.setEnabled(True)
        self.lwChannel.setEnabled(True)
        self.twIntensity.setEnabled(True)
        self.leBlockSize.setEnabled(True)
        self.sbBlockSize.setEnabled(True)
        self.leConst.setEnabled(True)
        self.sbConst.setEnabled(True)

Define two new methods, set_blocksize() and set_const(), to display value property of both spin box widgets on line edit widgets. Both methods also invoke image_thresh() method:

1
2
3
4
5
6
7
def set_blockSize(self):
    self.leBlockSize.setText(str(self.sbBlockSize.value()))
    self.image_thresh()

def set_const(self):
    self.leConst.setText(str(self.sbConst.value()))
    self.image_thresh()

Inside __init__(), connect valueChanged() signal of sbBlockSize with set_blockSize() method, connect valueChanged() signal of sbConst with set_const() method, connect textEdited() signal of leBlockSize with image_thresh() method, and connect textEdited() signal of leConst with image_thresh() method.

1
2
3
4
self.sbBlockSize.valueChanged.connect(self.set_blockSize)
self.sbConst.valueChanged.connect(self.set_const)
self.leBlockSize.textEdited.connect(self.image_thresh)
self.leConst.textEdited.connect(self.image_thresh)

Define new method named do_adapt_thresh() to implement adaptive image thresholding as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def do_adapt_thresh(self, img, maxVal, blockSize, Const, \
method1, method2,title):
    channel = len(img.shape)
        
    if channel == 2: #grayscale image
        img_blur = cv2.medianBlur(img,blockSize)
        thresh = cv2.adaptiveThreshold(img_blur,maxVal,method1, \
            method2, blockSize,Const)
        qmap = QImage(thresh.data, thresh.shape[1], \
            thresh.shape[0], QImage.Format_Grayscale8)
        pixmap = QPixmap.fromImage(qmap)
            
    else: # color image
        img_blur = cv2.medianBlur(img,blockSize)
        b, g, r = cv2.split(img_blur)
        threshb = cv2.adaptiveThreshold(b,maxVal,method1, \
            method2, blockSize,Const)
        threshg = cv2.adaptiveThreshold(g,maxVal,method1, \
            method2, blockSize,Const)
        threshr = cv2.adaptiveThreshold(r,maxVal,method1, \
            method2, blockSize,Const)
        thresh = cv2.merge((threshb, threshg, threshr))
        height, width, channel = thresh.shape
        bytesPerLine = 3 * width
            
        # To convert back from BGR to RGB space color     
        cv2.cvtColor(thresh, cv2.COLOR_BGR2RGB, thresh)
        qImg = QImage(thresh.data, width, height, \
            bytesPerLine, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qImg)
        
    self.display_image(pixmap, thresh, self.labelThresh, \
        self.widgetHistThresh, title)

Modify thresholding_tech() method by adding code in line 5-6 and 24-31 as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def thresholding_tech(self,img):        
    strCB = self.cboThresholding.currentText()
    minVal = int(self.leMinVal.text())
    maxVal = int(self.leMaxVal.text())
    blockSize = int(self.leBlockSize.text())
    Const = int(self.leConst.text())
    
    if strCB == 'THRESH_BINARY':       
        self.do_thresh(img, minVal, maxVal, cv2.THRESH_BINARY, \
            'Histogram of Thresholded Image with ' + strCB)
    if strCB == 'THRESH_BINARY_INV':
        self.do_thresh(img, minVal, maxVal, cv2.THRESH_BINARY_INV, \
            'Histogram of Thresholded Image with '  + strCB)
    if strCB == 'THRESH_TRUNC':
        self.do_thresh(img, minVal, maxVal, cv2.THRESH_TRUNC, \
            'Histogram of Thresholded Image with ' + strCB)
    if strCB == 'THRESH_TOZERO':
        self.do_thresh(img, minVal, maxVal, \
            cv2.THRESH_TOZERO,\
            'Histogram of Thresholded Image with ' + strCB)
    if strCB == 'THRESH_TOZERO_INV':
        self.do_thresh(img, minVal, maxVal, cv2.THRESH_TOZERO_INV, \
            'Histogram of Thresholded Image with ' + strCB)
    if strCB == 'ADAPTIVE_THRESH_MEAN_C':
        self.do_adapt_thresh(img, 255, blockSize, Const, \
            cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, \
            'Histogram of Thresholded Image with ' + strCB)  
    if strCB == 'ADAPTIVE_THRESH_GAUSSIAN_C':
        self.do_adapt_thresh(img, 255, blockSize, Const, \
            cv2.cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, \
            'Histogram of Thresholded Image with ' + strCB)  

Run image_thresholding.py and see the results of applying adaptive image thresholding as shown in figures below.



The following is the final version of image_thresholding.py:

#image_thresholding.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
import numpy as np
fname = ""
class FormThresholding(QMainWindow):
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')
self.hsMinVal.valueChanged.connect(self.set_hsMinVal)
self.hsMaxVal.valueChanged.connect(self.set_hsMaxVal)
self.cboThresholding.currentIndexChanged.connect(self.image_thresh)
self.leMinVal.textEdited.connect(self.image_thresh)
self.leMaxVal.textEdited.connect(self.image_thresh)
self.lwChannel.clicked.connect(self.channel_thresh)
self.sbBlockSize.valueChanged.connect(self.set_blockSize)
self.sbConst.valueChanged.connect(self.set_const)
self.leBlockSize.textEdited.connect(self.image_thresh)
self.leConst.textEdited.connect(self.image_thresh)
def open_file(self):
global fname
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()
def set_hsMinVal(self, value):
self.leMinVal.setText(str(value))
self.image_thresh()
def set_hsMaxVal(self, value):
self.leMaxVal.setText(str(value))
self.image_thresh()
def image_thresh(self):
img = cv2.imread(fname[0], cv2.IMREAD_COLOR)
self.thresholding_tech(img)
def thresholding_tech(self,img):
strCB = self.cboThresholding.currentText()
minVal = int(self.leMinVal.text())
maxVal = int(self.leMaxVal.text())
blockSize = int(self.leBlockSize.text())
Const = int(self.leConst.text())
if strCB == 'THRESH_BINARY':
self.do_thresh(img, minVal, maxVal, cv2.THRESH_BINARY, \
'Histogram of Thresholded Image with ' + strCB)
if strCB == 'THRESH_BINARY_INV':
self.do_thresh(img, minVal, maxVal, cv2.THRESH_BINARY_INV, \
'Histogram of Thresholded Image with ' + strCB)
if strCB == 'THRESH_TRUNC':
self.do_thresh(img, minVal, maxVal, cv2.THRESH_TRUNC, \
'Histogram of Thresholded Image with ' + strCB)
if strCB == 'THRESH_TOZERO':
self.do_thresh(img, minVal, maxVal, cv2.THRESH_TOZERO,\
'Histogram of Thresholded Image with ' + strCB)
if strCB == 'THRESH_TOZERO_INV':
self.do_thresh(img, minVal, maxVal, cv2.THRESH_TOZERO_INV, \
'Histogram of Thresholded Image with ' + strCB)
if strCB == 'ADAPTIVE_THRESH_MEAN_C':
self.do_adapt_thresh(img, 255, blockSize, Const, \
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, \
'Histogram of Thresholded Image with ' + strCB)
if strCB == 'ADAPTIVE_THRESH_GAUSSIAN_C':
self.do_adapt_thresh(img, 255, blockSize, Const, \
cv2.cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, \
'Histogram of Thresholded Image with ' + strCB)
def do_thresh(self, img, minVal, maxVal, method, title):
channel = len(img.shape)
if channel == 2: #grayscale image
ret, thresh = cv2.threshold(img, minVal, maxVal, method)
qmap = QImage(thresh.data, thresh.shape[1], thresh.shape[0], \
QImage.Format_Grayscale8)
pixmap = QPixmap.fromImage(qmap)
else: # color image
ret, thresh = cv2.threshold(img, minVal, maxVal, method)
height, width, channel = thresh.shape
bytesPerLine = 3 * width
# To convert back from BGR to RGB space color
cv2.cvtColor(thresh, cv2.COLOR_BGR2RGB, thresh)
qImg = QImage(thresh.data, width, height, \
bytesPerLine, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qImg)
self.display_image(pixmap, thresh, self.labelThresh, \
self.widgetHistThresh, title)
def do_adapt_thresh(self, img, maxVal, blockSize, Const, method1, \
method2,title):
channel = len(img.shape)
if channel == 2: #grayscale image
img_blur = cv2.medianBlur(img,blockSize)
thresh = cv2.adaptiveThreshold(img_blur,maxVal,method1, \
method2, blockSize,Const)
qmap = QImage(thresh.data, thresh.shape[1], \
thresh.shape[0], QImage.Format_Grayscale8)
pixmap = QPixmap.fromImage(qmap)
else: # color image
img_blur = cv2.medianBlur(img,blockSize)
b, g, r = cv2.split(img_blur)
threshb = cv2.adaptiveThreshold(b,maxVal,method1, \
method2, blockSize,Const)
threshg = cv2.adaptiveThreshold(g,maxVal,method1, \
method2, blockSize,Const)
threshr = cv2.adaptiveThreshold(r,maxVal,method1, \
method2, blockSize,Const)
thresh = cv2.merge((threshb, threshg, threshr))
height, width, channel = thresh.shape
bytesPerLine = 3 * width
# To convert back from BGR to RGB space color
cv2.cvtColor(thresh, cv2.COLOR_BGR2RGB, thresh)
qImg = QImage(thresh.data, width, height, \
bytesPerLine, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qImg)
self.display_image(pixmap, thresh, self.labelThresh, \
self.widgetHistThresh, title)
def setState(self, state):
if state == 'START':
self.cboThresholding.setEnabled(False)
self.hsMinVal.setEnabled(False)
self.hsMaxVal.setEnabled(False)
self.leMinVal.setEnabled(False)
self.leMaxVal.setEnabled(False)
self.lwChannel.setEnabled(False)
self.twIntensity.setEnabled(False)
self.leBlockSize.setEnabled(False)
self.sbBlockSize.setEnabled(False)
self.leConst.setEnabled(False)
self.sbConst.setEnabled(False)
else:
self.cboThresholding.setEnabled(True)
self.hsMinVal.setEnabled(True)
self.hsMaxVal.setEnabled(True)
self.leMinVal.setEnabled(True)
self.leMaxVal.setEnabled(True)
self.lwChannel.setEnabled(True)
self.twIntensity.setEnabled(True)
self.leBlockSize.setEnabled(True)
self.sbBlockSize.setEnabled(True)
self.leConst.setEnabled(True)
self.sbConst.setEnabled(True)
def channel_thresh(self):
img = cv2.imread(fname[0], cv2.IMREAD_COLOR)
# splitting into individual colors
b, g, r = cv2.split(img)
item = self.lwChannel.currentItem()
channel = item.text()
if channel == 'Blue':
self.thresholding_tech(b)
self.labelTableTitle.setText(\
'Image intensities of '+channel+' channel')
self.populate_table(b)
if channel == 'Green':
self.labelTableTitle.setText(\
'Image intensities of '+channel+' channel')
self.thresholding_tech(g)
if channel == 'Red':
self.labelTableTitle.setText(\
'Image intensities of '+channel+' channel')
self.thresholding_tech(r)
def populate_table(self, matrix):
self.twIntensity.setRowCount(len(matrix))
self.twIntensity.setColumnCount(len(matrix[0]))
for i,row in enumerate(matrix):
for j,val in enumerate(row):
self.twIntensity.setItem(i,j,QTableWidgetItem(str(val)))
def set_blockSize(self):
self.leBlockSize.setText(str(self.sbBlockSize.value()))
self.image_thresh()
def set_const(self):
self.leConst.setText(str(self.sbConst.value()))
self.image_thresh()
if __name__=="__main__":
import sys
app = QApplication(sys.argv)
w = FormThresholding()
w.show()
sys.exit(app.exec_())




No comments:

Post a Comment