Write a beautiful face recognition client with PYQT5 of Python

If you don't say much, see the effect first
Below is the effect of not wearing a mask (cover it with an idol first)

Here is the effect of wearing a mask

Let's describe this effect again. In fact, it's not a picture, but read by the camera in real time. First of all, thank AIZOOTech's open source project - FaceMaskDetection. At the same time, I also refer to the following blogs. Thank you one by one:
(https://blog.csdn.net/qq_41204464/article/details/104596777)
(https://blog.csdn.net/qq_41204464/article/details/106026650)
(https://www.freesion.com/article/8339842034/)
(https://zmister.com/archives/477.html)
The equipped environment is pycharm (free version) + Anaconda. I won't talk about the installation of editor and environment. Several great gods mentioned above. Go and see for yourself. The screenshot of pip installation in conda environment is as follows (my own machine can operate normally in these environments, and I have tried to use it on my friend's machine). As shown in the figure below, just install pip yourself. Finally, don't forget conda install tensorflow. My understanding is to load the library installed by pip into conda (please correct the error). Finally, conda list checks whether it is consistent with my screenshot version. If it is inconsistent, uninstall a non compliant version, and then reinstall and specify the version. For example, pip uninstall keras first, and then pip install keras==2.4.3

It should be noted that I installed tensorflow version 2.2.0, which corresponds to keras version 2.4.3. Before I installed keras version 2.2, an error will be reported. In addition, several other major third-party libraries have been marked with red lines.

The following is the main program code. For convenience, I wrote the UI design code and the main program code together.

import sys 
from PyQt5 import QtCore, QtGui, QtWidgets
import qtawesome
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import cv2
import tensorflow_infer as tsf
class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(800, 511)
        self.verticalLayoutWidget = QtWidgets.QWidget(Form)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 60, 131, 441))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")

        self.label1 = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.label1.setObjectName("labeltitle")
        self.verticalLayout.addWidget(self.label1)
        self.pushButton1 = QtWidgets.QPushButton(qtawesome.icon('fa.camera',color='white'),"") 
        self.pushButton1.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton1)
        self.pushButton2 = QtWidgets.QPushButton(qtawesome.icon('fa.medkit',color='white'),"") 
        self.pushButton2.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton2)
        self.pushButton3 = QtWidgets.QPushButton(qtawesome.icon('fa.smile-o',color='white'),"") 
        self.pushButton3.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton3)
        self.label2 = QtWidgets.QPushButton(self.verticalLayoutWidget)
        self.label2.setObjectName("labeltitle")
        self.verticalLayout.addWidget(self.label2)
        self.pushButton4 = QtWidgets.QPushButton(qtawesome.icon('fa.hourglass-start',color='white'),"") 
        self.pushButton4.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton4)
        self.pushButton5 = QtWidgets.QPushButton(qtawesome.icon('fa.hourglass-end',color='white'),"") 
        self.pushButton5.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton5)
        self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 30, 131, 25))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")

        self.verticalLayoutWidget.setStyleSheet('''     
        QPushButton{border:none;color:white;}     
        QPushButton#labeltitle{         
        border:none;         
        border-bottom:1px solid white;         
        font-size:18px;         
        font-weight:700;         
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;     }     
        QPushButton#pushButton:hover{border-left:4px solid red;font-weight:700;}
        QWidget#verticalLayoutWidget{     
        background:gray;     
        border-top:1px solid white;     
        border-bottom:1px solid white;     
        border-left:1px solid white; 
        border-top-left-radius:10px;
        border-top-right-radius:10px; 
        border-bottom-right-radius:10px;
        border-bottom-left-radius:10px; }
        ''')

        self.pushButton8 = QtWidgets.QPushButton(qtawesome.icon('fa.minus-square',color='white'),"")  # Minimize button
        self.pushButton8.setObjectName("pushButton8")
        self.horizontalLayout.addWidget(self.pushButton8)

        self.pushButton9 = QtWidgets.QPushButton(qtawesome.icon('fa.window-close',color='white'),"") # close button 
        self.pushButton9.setObjectName("pushButton9")
        self.horizontalLayout.addWidget(self.pushButton9)

        self.pushButton8.setStyleSheet('''QPushButton{background:#F76677;border-radius:5px;}QPushButton:hover{background:red;}''') 
        self.pushButton9.setStyleSheet('''QPushButton{background:#6DDF6D;border-radius:5px;}QPushButton:hover{background:green;}''')  
        self.pushButton8.clicked.connect(Form.showMinimized)
        self.pushButton9.clicked.connect(Form.closeEvent)


        self.verticalLayoutWidget_2 = QtWidgets.QWidget(Form)
        self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(150, 30, 631, 471))
        self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.label = QtWidgets.QLabel(self.verticalLayoutWidget_2)
        self.label.setObjectName("label")
        self.label.setFixedSize(641,481)    #Set the size of the Label for video display to 641x481
        self.verticalLayout_2.addWidget(self.label)

        self.verticalLayoutWidget_2.setStyleSheet('''     
        QWidget#verticalLayoutWidget_2{     
        background:gray;     
        border-top:1px solid white;     
        border-bottom:1px solid white;     
        border-left:1px solid white; 
        border-top-left-radius:10px;
        border-top-right-radius:10px; 
        border-bottom-right-radius:10px;
        border-bottom-left-radius:10px; }
        ''')

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

        self.label1.setText( "Mask detection system")
        self.pushButton1.setText("Turn on the camera")
        self.pushButton2.setText("Open mask detection")
        self.pushButton3.setText("Close mask detection")
        self.label2.setText("Other functions")
        self.pushButton4.setText("Coming soon")
        self.pushButton5.setText("Coming soon")
        
        self.label.setText("The camera will be slow in the process of opening. Please wait a moment and don't mess up to avoid jamming.")

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
    
    def setNoTittle(self,Form):
        Form.setWindowFlags(Qt.FramelessWindowHint)
        Form.setWindowOpacity(1) # Set window transparency 
        Form.setAttribute(Qt.WA_TranslucentBackground) # Set window background transparency
    


class MainUi(QtWidgets.QMainWindow):  
    
    def mouseMoveEvent(self, e: QMouseEvent):  # Override move event    
        if self._tracking:
            self._endPos = e.pos() - self._startPos
            self.move(self.pos() + self._endPos)

    def mousePressEvent(self, e: QMouseEvent):  
        if e.button() == Qt.LeftButton:
            self._startPos = QPoint(e.x(), e.y())
            self._tracking = True      

    def mouseReleaseEvent(self, e: QMouseEvent):
        if e.button() == Qt.LeftButton:
            self._tracking = False
            self._startPos = None
            self._endPos = None

    def closeEvent(self):
        reply = QMessageBox.question(self, 'Prompt information', 'Are you sure to exit?', 
        QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            QCoreApplication.instance().quit()
        else:
            pass

class MyMainForm(QtWidgets.QWidget,Ui_Form):
    def __init__(self,parent=None):  
        super().__init__(parent) #Constructor of parent class
        self.timer_camera = QtCore.QTimer() #Define a timer to control the frame rate of the displayed video
        self.cap = cv2.VideoCapture()       #Video stream
        self.CAM_NUM = 0                    #0 indicates that the video stream comes from the built-in camera of the notebook
        self.openkz = False
        
    def setupUi(self,Form):
        Ui_Form.setupUi(self,Form)
        
    def slot_init(self):
        self.pushButton1.clicked.connect(self.open_camera_clicked)    #If the key is clicked, the button is called_ open_ camera_ clicked()
        self.timer_camera.timeout.connect(self.show_camera) #If the timer ends, call show_camera()

        self.pushButton2.clicked.connect(self.openkouzhao)
        self.pushButton3.clicked.connect(self.closekouzhao)
    def openkouzhao(self):
        self.openkz = True
    def closekouzhao(self):
        self.openkz = False

    def open_camera_clicked(self):
        self.label.setText("")
        if self.timer_camera.isActive() == False:   #If the timer does not start
            
            flag = self.cap.open(self.CAM_NUM) #If the parameter is 0, it means to open the built-in camera of the notebook. If the parameter is the path of the video file, it means to open the video
            if flag == False:       #flag indicates whether open() is successful
                msg = QtWidgets.QMessageBox.warning(self,'warning',"Please check whether the camera is connected to the computer correctly",buttons=QtWidgets.QMessageBox.Ok)
            else:
                self.timer_camera.start(30)  #The timer starts to count for 30ms, and the result is to take a frame from the camera for display every 30ms
                self.pushButton1.setText('Turn off the camera')
        else:
            self.timer_camera.stop()  #off timer 
            self.cap.release()        #Release video stream
            self.label.clear()  #Clear video display area
            self.pushButton1.setText('Turn on the camera')
            self.label.setText("The camera will be slow in the process of opening. Please wait a moment and don't mess up to avoid jamming.")
    def show_camera(self):
        flag,self.image = self.cap.read()  #Read from video stream
        #
        
        if self.openkz:
            img_raw = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
            tsf.inference(img_raw,
                        conf_thresh=0.5,
                        iou_thresh=0.5,
                        target_shape=(260, 260),
                        draw_result=True,
                        show_result=False)
            show = img_raw[:, :, ::-1]
        else:
            show = cv2.resize(self.image,(640,480))     #Reset the size of the read frame to 640x480
        #
        
        
        show = cv2.cvtColor(show,cv2.COLOR_BGR2RGB) #The video color is converted back to RGB, which is the real color
        showImage = QtGui.QImage(show.data,show.shape[1],show.shape[0],QtGui.QImage.Format_RGB888) #Convert the read video data into QImage form
        self.label.setPixmap(QtGui.QPixmap.fromImage(showImage))  #Display QImage in the Label displaying the video

if __name__ =='__main__':
    app = QtWidgets.QApplication(sys.argv)
    Form = MainUi() 
    ui = MyMainForm()
    ui.setupUi(Form)
    ui.setNoTittle(Form)
    ui.slot_init()
    Form.show()
    sys.exit(app.exec_())

Someone should be able to find that my QT is made with designer and then transferred to the source code. Yes, so it can be written in a simpler way, but I'm lazy, so I keep inheriting and inheriting. QT here mainly uses several key methods, one is to rewrite the move function, the other is to close the window and design the zoom out and close buttons by yourself, and the other is the style. Then the video stream is transmitted to lable, so that the video can be turned on and off.
Open source tensorflow_infer.py's code has been modified. In fact, it is just to comment out a few lines, and then take the video stream as a sample for comparison, which is verified by TSF The information method checks, returns the result, and finally assigns a value to label.

Here is the tensorflow_infer.py code (if you want to download the model and source code, go to the address below)

# -*- coding:utf-8 -*-
import cv2
import time
import argparse

import numpy as np
from PIL import Image
from keras.models import model_from_json
from utils.anchor_generator import generate_anchors
from utils.anchor_decode import decode_bbox
from utils.nms import single_class_non_max_suppression
from load_model.tensorflow_loader import load_tf_model, tf_inference

sess, graph = load_tf_model('models/face_mask_detection.pb')

# anchor configuration
feature_map_sizes = [[33, 33], [17, 17], [9, 9], [5, 5], [3, 3]]
anchor_sizes = [[0.04, 0.056], [0.08, 0.11], [0.16, 0.22], [0.32, 0.45], [0.64, 0.72]]
anchor_ratios = [[1, 0.62, 0.42]] * 5

# generate anchors
anchors = generate_anchors(feature_map_sizes, anchor_sizes, anchor_ratios)

# for inference , the batch size is 1, the model output shape is [1, N, 4],
# so we expand dim for anchors to [1, anchor_num, 4]
anchors_exp = np.expand_dims(anchors, axis=0)

id2class = {0: 'YES', 1: 'NO'}


def inference(image,
              conf_thresh=0.5,
              iou_thresh=0.4,
              target_shape=(160, 160),
              draw_result=True,
              show_result=True
              ):
    '''
    Main function of detection inference
    :param image: 3D numpy array of image
    :param conf_thresh: the min threshold of classification probabity.
    :param iou_thresh: the IOU threshold of NMS
    :param target_shape: the model input size.
    :param draw_result: whether to daw bounding box to the image.
    :param show_result: whether to display the image.
    :return:
    '''
    # image = np.copy(image)
    output_info = []
    height, width, _ = image.shape
    image_resized = cv2.resize(image, target_shape)
    image_np = image_resized / 255.0  # Normalized to 0 ~ 1
    image_exp = np.expand_dims(image_np, axis=0)
    y_bboxes_output, y_cls_output = tf_inference(sess, graph, image_exp)

    # remove the batch dimension, for batch is always 1 for inference.
    y_bboxes = decode_bbox(anchors_exp, y_bboxes_output)[0]
    y_cls = y_cls_output[0]
    # To speed up, do single class NMS, not multiple classes NMS.
    bbox_max_scores = np.max(y_cls, axis=1)
    bbox_max_score_classes = np.argmax(y_cls, axis=1)

    # keep_idx is the alive bounding box after nms.
    keep_idxs = single_class_non_max_suppression(y_bboxes,
                                                 bbox_max_scores,
                                                 conf_thresh=conf_thresh,
                                                 iou_thresh=iou_thresh,
                                                 )

    for idx in keep_idxs:
        conf = float(bbox_max_scores[idx])
        class_id = bbox_max_score_classes[idx]
        bbox = y_bboxes[idx]
        # clip the coordinate, avoid the value exceed the image boundary.
        xmin = max(0, int(bbox[0] * width))
        ymin = max(0, int(bbox[1] * height))
        xmax = min(int(bbox[2] * width), width)
        ymax = min(int(bbox[3] * height), height)

        if draw_result:
            if class_id == 0:
                color = (0, 255, 0)
            else:
                color = (255, 0, 0)
            cv2.rectangle(image, (xmin, ymin), (xmax, ymax), color, 2)
            cv2.putText(image, "%s: %.2f" % (id2class[class_id], conf), (xmin + 2, ymin - 2),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, color)
        output_info.append([class_id, conf, xmin, ymin, xmax, ymax])

    if show_result:
        Image.fromarray(image).show()
    return output_info


def run_on_video(video_path, output_video_name, conf_thresh):
    cap = cv2.VideoCapture(video_path)
    height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    fps = cap.get(cv2.CAP_PROP_FPS)
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    # writer = cv2.VideoWriter(output_video_name, fourcc, int(fps), (int(width), int(height)))
    total_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
    if not cap.isOpened():
        raise ValueError("Video open failed.")
        return
    status = True
    idx = 0
    while status:
        start_stamp = time.time()
        status, img_raw = cap.read()
        img_raw = cv2.cvtColor(img_raw, cv2.COLOR_BGR2RGB)
        read_frame_stamp = time.time()
        if (status):
            inference(img_raw,
                      conf_thresh,
                      iou_thresh=0.5,
                      target_shape=(260, 260),
                      draw_result=True,
                      show_result=False)
            cv2.imshow('image', img_raw[:, :, ::-1])
            cv2.waitKey(1)
            inference_stamp = time.time()
            # writer.write(img_raw)
            write_frame_stamp = time.time()
            idx += 1
            # print("%d of %d" % (idx, total_frames))
            # print("read_frame:%f, infer time:%f, write time:%f" % (read_frame_stamp - start_stamp,
            #                                                        inference_stamp - read_frame_stamp,
            #                                                        write_frame_stamp - inference_stamp))
    # writer.release()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Face Mask Detection")
    parser.add_argument('--img-mode', type=int, default=0, help='set 1 to run on image, 0 to run on video.')
    parser.add_argument('--img-path', type=str, help='path to your image.')
    parser.add_argument('--video-path', type=str, default='0', help='path to your video, `0` means to use camera.')
    # parser.add_argument('--hdf5', type=str, help='keras hdf5 file')
    args = parser.parse_args()
    if args.img_mode:
        imgPath = args.img_path
        img = cv2.imread(imgPath)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        inference(img, show_result=True, target_shape=(260, 260))
    else:
        video_path = args.video_path
        if args.video_path == '0':
            video_path = 0
        run_on_video(video_path, '', conf_thresh=0.5)

Tensorflow2 is required Friends of version 2 project can get it from the online disk:
Link:
https://pan.baidu.com/s/10IQ1uscONZOYkgdtjcx2pQ
Extraction code: g8g9
--------
After all, I took it from others, so I took the link of [a small tree x] for everyone to download. Welcome to communicate.

Keywords: TensorFlow Deep Learning

Added by obesechicken13 on Mon, 07 Mar 2022 19:20:45 +0200