Sunday, December 20, 2020

Object Detection with PyQt: Part 1

This content is powered by Balige Publishing Visit this link (collaboration with Rismon Hasiholan Sianipar)


Tutorial Steps To Detect Face, Eye, and Mouth Using Haar Cascades.

Face detection using the Haar cascade is a machine learning-based approach in which cascade function is trained with a set of input data. OpenCV already contains many trained classifiers for face, eyes, smile, etc. 

You will use cv2.CascadeClassifier.detectMultiScale:

objects = CascadeClassifier.detectMultiScale(\
    image[, scaleFactor[, minNeighbors[, \
    flags[, minSize[, \
    maxSize]]]]]

Below is its parameters:

As noted, a sample usage is available from the OpenCV source code. You can pass in each documented parameter as a keyword.
 
objects = cascade.detectMultiScale(img, 
                         scaleFactor=1.5, 
                         minNeighbors=4, 
                         minSize=(30, 30),
                         flags=cv2.CASCADE_SCALE_IMAGE)

Below is Python script to detect faces and eyes in an image:

import numpy as np
import cv2

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + \
    'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + \
    'haarcascade_eye.xml')

if face_cascade.empty():
  raise IOError('Unable to load the face cascade classifier xml file')

if eye_cascade.empty():
  raise IOError('Unable to load the eye cascade classifier xml file')

# Reads input image  
img = cv2.imread('abba.jpg')

# Converts from BGR space color into Gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

faces = face_cascade.detectMultiScale(img, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(30, 30), \
                         flags=cv2.CASCADE_SCALE_IMAGE)

for (x,y,w,h) in faces:
    img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),3)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    
    eyes = eye_cascade.detectMultiScale(roi_gray, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(10, 10), \
                         flags=cv2.CASCADE_SCALE_IMAGE)
    
    for (ex,ey,ew,eh) in eyes:
        cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,0,255),3)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

First you need to load the required XML classifiers. Then load our input image (or video) in grayscale mode. Then, you find the faces in the image. If faces are found, it returns the positions of detected faces as Rect(x,y,w,h). Once you get these locations, we can create a ROI for the face and apply eye detection on this ROI (since eyes are always on the face).

The result is shown in Figure below.


Now, you can develop the code to detect mouth in every face as follows:

import numpy as np
import cv2

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')

#https://github.com/dasaanand/OpenCV/blob/master/EmotionDet/build/Debug/EmotionDet.app/Contents/Resources/haarcascade_mouth.xml
mouth_cascade = cv2.CascadeClassifier('haarcascade_mouth.xml')

if face_cascade.empty():
  raise IOError('Unable to load the face cascade classifier xml file')

if eye_cascade.empty():
  raise IOError('Unable to load the eye cascade classifier xml file')

if mouth_cascade.empty():
  raise IOError('Unable to load the mouth cascade classifier xml file')
  
# Reads input image  
img = cv2.imread('abba.jpg')

# Converts from BGR space color into Gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# detecting face
faces = face_cascade.detectMultiScale(img, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(30, 30), \
                         flags=cv2.CASCADE_SCALE_IMAGE)

for (x,y,w,h) in faces:
    img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),3)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    
    #detecting eyes
    eyes = eye_cascade.detectMultiScale(roi_gray, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(10, 10), \
                         flags=cv2.CASCADE_SCALE_IMAGE)
    
    for (ex,ey,ew,eh) in eyes:
        cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,0,255),3)

    #detecting mouth
    mouth = mouth_cascade.detectMultiScale(roi_gray, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(29, 29), \
                         flags=cv2.CASCADE_SCALE_IMAGE)
    
    for (mx,my,mw,mh) in mouth:
        cv2.rectangle(roi_color,(mx,my),\
            (mx+mw,my+mh),(255,255,255),3)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Run the code. The result is given in Figure below.



Tutorial Steps To Detect Face Using Haar Cascades with PyQt
Create a new form using Qt Designer with Main Window template. Set its name as object_detection.ui.

Add two new Label widgets and set their objectName properties as labelImage and labelResult. Then, add two more Label widgets and set their text properties as Original Image and Resulting Image.

Add one Push Button widget onto forma, set its ObjectName property as pbReadImage, and set its text property as Read Image. The form now looks as shown in Figure below.


Create a new Python script and name it as object_detection.py. Write the basic content of the script as follows:

#object_detection.py
import sys
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 PyQt5.uic import loadUi

class FormObjectDetection(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        loadUi("object_detection.ui",self)
        self.setWindowTitle("Object Detection")


if __name__=="__main__":
    app = QApplication(sys.argv)    
    w = FormObjectDetection()
    w.show()
    sys.exit(app.exec_())

In __init__() method, add a statement to read cascade classifier of Haar as follows:

self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + \
    'haarcascade_frontalface_default.xml')
if self.face_cascade.empty():
    raise IOError('Unable to load the face cascade classifier xml file')

Define a new method, read_image(), to open file dialog, read file name, and display it in labelImage widget as follows:

def read_image(self):
    self.fname = QFileDialog.getOpenFileName(self, 'Open file', 
       'd:\\',"Image Files (*.jpg *.gif *.bmp *.png)")
    self.pixmap = QPixmap(self.fname[0])        
    self.labelImage.setPixmap(self.pixmap)
    self.labelImage.setScaledContents(True)
    self.img = cv2.imread(self.fname[0], cv2.IMREAD_COLOR)  

Connect the clicked event of pbReadImage widget to readImage() method and put it inside __init_() method:


self.pbReadImage.clicked.connect(self.read_image)

Define a new method, display_resulting_image(), to display resulting image in labelResult widget as follows:

def display_resulting_image(self, img):
    height, width, channel = img.shape
    bytesPerLine = 3 * width  
    cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img)
    qImg = QImage(img, width, height, \
        bytesPerLine, QImage.Format_RGB888)
    pixmap = QPixmap.fromImage(qImg)
    self.labelResult.setPixmap(pixmap)
    self.labelResult.setScaledContents(True)

Define two methods, object_detection() and do_detection(), to perform face detection using Haar cascades as follows:

def object_detection(self,img):
    # Converts from BGR space color into Gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    faces = self.face_cascade.detectMultiScale(img, \
                     scaleFactor=1.1, \
                     minNeighbors=5, \
                     minSize=(20, 20), \
                     flags=cv2.CASCADE_SCALE_IMAGE)

    for (x,y,w,h) in faces:
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]    
     
    self.display_resulting_image(img)
        
def do_detection(self):
    test = self.img
    self.object_detection(test)

Modify read_image() to invoke do_detection() as follows:

def read_image(self):
    self.fname = QFileDialog.getOpenFileName(self, 'Open file', 
       'd:\\',"Image Files (*.jpg *.gif *.bmp *.png)")
    self.pixmap = QPixmap(self.fname[0])        
    self.labelImage.setPixmap(self.pixmap)
    self.labelImage.setScaledContents(True)
    self.img = cv2.imread(self.fname[0], cv2.IMREAD_COLOR) 
    self.do_detection()

Run object_detection.py and click Read Image button to se the result as shown in Figure below.


Tutorial Steps To Detect Eye, and Mouth Using Haar Cascades with PyQt
In __init__() method, add two statements to read cascade classifier of Haar for eye and mouth objects as follows:

self.eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + \
    'haarcascade_eye.xml')
if self.eye_cascade.empty():
    raise IOError('Unable to load the eye cascade classifier xml file')

# Downloaded from https://github.com/dasaanand/OpenCV/blob/master/EmotionDet/build/Debug/EmotionDet.app/Contents/Resources/haarcascade_mouth.xml
self.mouth_cascade = cv2.CascadeClassifier('haarcascade_mouth.xml')
if self.mouth_cascade.empty():
    raise IOError('Unable to load mouth cascade classifier xml file')

Modify object_detection() method in object_detection.py to detect eyes and mouths in test image as follows:

def object_detection(self,img):
    # Converts from BGR space color into Gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    faces = self.face_cascade.detectMultiScale(img, \
                     scaleFactor=1.1, \
                     minNeighbors=5, \
                     minSize=(20, 20), \
                     flags=cv2.CASCADE_SCALE_IMAGE)

    for (x,y,w,h) in faces:
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]   
            
        #detecting eyes
        eyes = self.eye_cascade.detectMultiScale(roi_gray, \
                     scaleFactor=1.1, \
                     minNeighbors=5, \
                     minSize=(10, 10), \
                     flags=cv2.CASCADE_SCALE_IMAGE)
    
        for (ex,ey,ew,eh) in eyes:
            cv2.rectangle(roi_color,(ex,ey),\
               (ex+ew,ey+eh),(255,0,0),3)

        #detecting mouth
        mouth = self.mouth_cascade.detectMultiScale(roi_gray, \
                     scaleFactor=1.1, \
                     minNeighbors=5, \
                     minSize=(29, 29), \
                     flags=cv2.CASCADE_SCALE_IMAGE)
    
        for (mx,my,mw,mh) in mouth:
            cv2.rectangle(roi_color,(mx,my),\
                (mx+mw,my+mh),(255,255,255),3)
     
    self.display_resulting_image(img)

Run object_detection.py and click Read Image button to se the result as shown in Figure below.

Below is the full script of object_detection.py so far:

#object_detection.py
import sys
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 PyQt5.uic import loadUi

class FormObjectDetection(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        loadUi("object_detection.ui",self)
        self.setWindowTitle("Object Detection")
        self.face_cascade = cv2.CascadeClassifier(\
           cv2.data.haarcascades + \
           'haarcascade_frontalface_default.xml')
        if self.face_cascade.empty():
            raise IOError(\
               'Unable to load the face cascade classifier xml file')

        self.eye_cascade = cv2.CascadeClassifier(\
            cv2.data.haarcascades + 'haarcascade_eye.xml')
        if self.eye_cascade.empty():
            raise IOError(\
               'Unable to load the eye cascade classifier xml file')

        # Downloaded from https://github.com/dasaanand/OpenCV/blob/master/EmotionDet/build/Debug/EmotionDet.app/Contents/Resources/haarcascade_mouth.xml
        self.mouth_cascade = cv2.CascadeClassifier(\
            'haarcascade_mouth.xml')
        if self.mouth_cascade.empty():
            raise IOError(\
               'Unable to load the mouth cascade classifier xml file')
        
        self.pbReadImage.clicked.connect(self.read_image)
           
    def read_image(self):
        self.fname = QFileDialog.getOpenFileName(self, 'Open file', 
           'd:\\',"Image Files (*.jpg *.gif *.bmp *.png)")
        self.pixmap = QPixmap(self.fname[0])        
        self.labelImage.setPixmap(self.pixmap)
        self.labelImage.setScaledContents(True)
        self.img = cv2.imread(self.fname[0], cv2.IMREAD_COLOR)       
        self.do_detection()

    def display_resulting_image(self, img):
        height, width, channel = img.shape
        bytesPerLine = 3 * width  
        cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img)
        qImg = QImage(img, width, height, \
            bytesPerLine, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qImg)
        self.labelResult.setPixmap(pixmap)
        self.labelResult.setScaledContents(True)

    def object_detection(self,img):
        # Converts from BGR space color into Gray
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        faces = self.face_cascade.detectMultiScale(img, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(20, 20), \
                         flags=cv2.CASCADE_SCALE_IMAGE)

        for (x,y,w,h) in faces:
            img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
            roi_gray = gray[y:y+h, x:x+w]
            roi_color = img[y:y+h, x:x+w]   
            
            #detecting eyes
            eyes = self.eye_cascade.detectMultiScale(roi_gray, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(10, 10), \
                         flags=cv2.CASCADE_SCALE_IMAGE)
    
            for (ex,ey,ew,eh) in eyes:
                cv2.rectangle(roi_color,\
                    (ex,ey),(ex+ew,ey+eh),(255,0,0),3)

            #detecting mouth
            mouth = self.mouth_cascade.detectMultiScale(roi_gray, \
                         scaleFactor=1.1, \
                         minNeighbors=5, \
                         minSize=(29, 29), \
                         flags=cv2.CASCADE_SCALE_IMAGE)
    
            for (mx,my,mw,mh) in mouth:
                cv2.rectangle(roi_color,(mx,my),\
                              (mx+mw,my+mh),(255,255,255),3)
     
        self.display_resulting_image(img)
        
    def do_detection(self):
        test = self.img
        self.object_detection(test)
        
if __name__=="__main__":
    app = QApplication(sys.argv)    
    w = FormObjectDetection()
    w.show()
    sys.exit(app.exec_())


Object Detection with PyQt: Part 2


No comments:

Post a Comment