• 目标检测—安全帽检测实践


    目录

    数据集部分

    后端部分

    前端部分

    前后端连接

    数据集部分:

    在该项目当中我们采用的是传统的开源数据集SHWD(Safety helmet (hardhat) wearing detect dataset)

    数据集标签为hat,person

    数据集的一些想法

    数据集的收集,标记对于后面的的程序设计预计模型的准确度有十分重要的影响

    1.首先,我暂时对于数据集的标签并不满意,在我的想法中设置三个标签更加合理:
    对于一个来说,我们应该对整个身体画出真实框并标记person,对于头部画出真实框标记hat,head或者hat,no hat。而我们暂时只是对头部进行预测如果带了安全帽,则显示标签hat,如果未带帽子我们则显示person。这一点来说首先发现如下弊端
    1)对于yolo的目标检测来说,如果当人影较小时而人的头部更小,对头部的扫描并不准确,此时对全身扫描来说更能准确识别人,而且当如果图中显示的是后脑勺的话识别效果尤其的不好,而对整体扫描可以大大减少这样的问题出现。

    2)针对后面的目标跟踪问题来说使用对整体作为目标相比较使用头部来说追踪更加准确,也更加好处理。现在如果你选择对person进行追踪的话,则deepsort不会追踪带安全帽的人不符合我们的设计预期,无所以暂时我们的做法是对帽子和人同时追踪并计数勉强合格,但仍然存在实际情况中的一些问题,比如视频中只有一个安全帽没人带他仍会产生技术并不符合我们的预期,对于这一问题我们暂时想到了两个方法:

    Ⅰ.
    第一种就是上面我们提到的对数据集的改进如果这样改进数据集以后我们就可以对识别整个身体的标签person作为跟踪对象,从而避免该问题。而且显示更加清楚否则追踪框和识别框会只出现在头部显示会导致看不清,改变之后也会更清晰。
    Ⅱ .
    如果让保持现在的数据集,我们可以使用训练好可以识别20种物体的训练好的yolo4模型,我们在入来两个yolo模型,一个模型参数是我们根据现在并不满意的数据集训练好的识别头和帽子的模型我们用于yolo的目标检测,另一个模型载入下载好的模型用于识别整个人体用作目标跟踪的对象这样也可以实现上述内容。我们需要将每一帧的图片最为数据依次使用sess.run()跑两个模型,对图片上的物体进行预测,再画框实现分离。不过暂时我们的还不能实现使用sess.run()用相同的数据跑不同的图。

    2.对于给定视频的清晰度过低的问题我们也可以使用数据集的扩充来提高模型对模糊图片的识别能力,我们可以找一些模糊的安全帽图片,或者找一个模糊的安全帽是频将一帧一帧的抽出来图片,然后对该图片进行标注,最后训练。以我看来这种发法有利于我们模型对于模糊视频中物体的识别能力。

    3.我们对识别安全帽这个问题要考虑全面,考虑安全帽容易出现的情况也要充入数据集,从而可以提高我们模型针对各种极端情况的识别效果
    1)第一,我们应该参考国家安全帽标准要求,对基本的安全帽样式,颜色,形状有基本的了解,要是我们的数据集中的涵盖所有安全帽可能的样子,这样对我们识别安全帽这一领域加强了专业性

    2)第二,我们我们要考虑人的活动随意性,对于正面可能容易识别,但是对于人和安全帽的侧面和后面我们也要考虑在内,这有利于提高模型识别的准确度。

    4.我们是否可以使用数据集实现更高级的功能呢,我们能否训练出不仅包含我们需要检测的是否佩戴安全帽和人以外,能识别工地常见的物品,当有人未佩戴安全帽时,我们可以识别该人附近的物品,并且提示处于某物品附近的人未佩戴安全帽,请及时佩戴,以免发生危险。

    综上所述,整个项目过半,给我的一大感受数据集的好坏直接影响我们整个系统的质量以及后续的一系列工作,所以在整个软件杯的过程中不断收集广泛的数据集并不断训练出更好的模型应该是我们一直的任务。

    后端部分

    后端部分以我来看分为两个部分

    第一个是YOLO

    第二个部分是deepsort

    最后是二者的连接。

    YOLO

    模型的训练文件在 G:/软件杯/project

    小技巧的设置

    在train.py和train_eager.py文件下:
    1、mosaic参数可用于控制是否实现Mosaic数据增强。
    2、Cosine_scheduler可用于控制是否使用学习率余弦退火衰减。
    3、label_smoothing可用于控制是否Label Smoothing平滑。

    在train_eager.py文件下:
    1、regularization参数可用于控制是否实现正则化损失。

    文件下载

    训练所需的yolo4_weights.h5可在百度网盘中下载。
    链接: https://pan.baidu.com/s/1DNv71lDkeWff2BmnVpgHeg 提取码: myz8
    yolo4_weights.h5是coco数据集的权重。
    yolo4_voc_weights.h5是voc数据集的权重。

    预测步骤

    1、使用预训练权重

    a、下载完库后解压,在百度网盘下载yolo4_weights.h5或者yolo4_voc_weights.h5,放入model_data,运行predict.py,输入

    img/street.jpg
    
    • 1

    可完成预测。
    b、利用video.py可进行摄像头检测。

    2、使用自己训练的权重

    a、按照训练步骤训练。
    b、在yolo.py文件里面,在如下部分修改model_path和classes_path使其对应训练好的文件;model_path对应logs文件夹下面的权值文件,classes_path是model_path对应分的类

    _defaults = {
        "model_path": 'model_data/yolo4_weight.h5',
        "anchors_path": 'model_data/yolo_anchors.txt',
        "classes_path": 'model_data/coco_classes.txt,
        "score" : 0.5,
        "iou" : 0.3,
        # 显存比较小可以使用416x416
        # 显存比较大可以使用608x608
        "model_image_size" : (416, 416)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    c、运行predict.py,输入

    img/street.jpg
    
    • 1

    可完成预测。
    d、利用video.py可进行摄像头检测。

    训练步骤

    1、本文使用VOC格式进行训练。
    2、训练前将标签文件放在VOCdevkit文件夹下的VOC2007文件夹下的Annotation中。
    3、训练前将图片文件放在VOCdevkit文件夹下的VOC2007文件夹下的JPEGImages中。
    4、在训练前利用voc2yolo4.py文件生成对应的txt。
    5、再运行根目录下的voc_annotation.py,运行前需要将classes改成你自己的classes。注意不要使用中文标签,文件夹中不要有空格!

    classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
    
    • 1

    6、此时会生成对应的2007_train.txt,每一行对应其图片位置及其真实框的位置和对应真实框当中的类别(且该类别是由数字编号,比如有两个类别可能hat用数字0代替,person用数字1代替)。

    7、在训练前需要务必在model_data下新建一个txt文档,文档中输入需要分的类,在train.py中将classes_path指向该文件,示例如下:

    classes_path = 'model_data/new_classes.txt'    
    
    • 1

    model_data/new_classes.txt文件内容为:

    cat
    dog
    ...
    
    • 1
    • 2
    • 3

    8、运行train.py即可开始训练。

    deepsort就是直接调用的具体内部算法尚不清楚·

    两者结合(yolo+deepsort)

    第一部分将训练好的模型载入

    import colorsys
    import numpy as np
    # from keras import backend as K
    import tensorflow as tf
    tf.compat.v1.disable_v2_behavior()
    tf.compat.v1.disable_eager_execution()
    from timeit import default_timer as timer
    from PIL import Image, ImageFont, ImageDraw
    from tensorflow.keras import backend as K
    from tensorflow.keras.models import load_model
    from tensorflow.keras.layers import Input
    # from tensorflow.keras.models import load_weights
    from yolo4.model import yolo_eval, Mish
    from yolo4.utils import letterbox_image
    import os
    from keras.utils import multi_gpu_model
    from yolo4.model import yolo4_body
    os.environ["CUDA_VISIBLE_DEVICES"] = "1"
    
    class YOLO(object):
        def __init__(self):
            #这一模型载入是为了实现deepsort和yolo识别模型不同权重,而暂时未实现
            self.weights_path = './model_data/yolo4_weight.h5'
            #载入训练好的模型
            self.model_path = './model_data/yolo4_model.h5'
            #载入真实框尺寸
            self.anchors_path = './model_data/yolo_anchors.txt'
            #载入自定义识别类别
            self.classes_path = './model_data/coco_classes01.txt'
            self.gpu_num = 1
            self.score = 0.5
            self.iou = 0.5
            self.eager = False
            #读取txt设定的类别读取并组织成列表形式
            self.class_names = self._get_class()
            #获得真实框尺寸组织成列表
            self.anchors = self._get_anchors()
            #获得跑图工具用在跑载入的模型
            self.sess = tf.compat.v1.keras.backend.get_session()
        
            self.model_image_size = (608, 608)  # fixed size or (None, None)
            self.is_fixed_size = self.model_image_size != (None, None)
            self.boxes, self.scores, self.classes = self.generate()
    
        def _get_class(self):
            classes_path = os.path.expanduser(self.classes_path)
            with open(classes_path) as f:
                class_names = f.readlines()
            class_names = [c.strip() for c in class_names]
            return class_names
    
        def _get_anchors(self):
            anchors_path = os.path.expanduser(self.anchors_path)
            with open(anchors_path) as f:
                anchors = f.readline()
                anchors = [float(x) for x in anchors.split(',')]
                anchors = np.array(anchors).reshape(-1, 2)
            return anchors
    
        def generate(self):
            model_path = os.path.expanduser(self.model_path)
            assert model_path.endswith('.h5'), 'Keras model or weights must be a .h5 file.'
            # self.yolo_model= load_model(model_path, custom_objects={'Mish': Mish}, compile=False)
            # # self.yolo_model.load_weights(self.weights_path)
            num_anchors = len(self.anchors)
            num_classes = len(self.class_names)
            print(num_classes)
            #载入yolo4模型,如果你之前训练模型的时候存的是模型则可以直接使用load载但是如果你只
            是保存参数,则需要将原来模型实例化一个后再载入参数,load_weights。
            self.yolo_model = yolo4_body(Input(shape=(None, None, 3)), num_anchors // 3, num_classes)
            self.yolo_model.load_weights(self.weights_path)
            # self.dect_model = load_model(model_path, custom_objects={'Mish': Mish}, compile=False)
            print('{} model, anchors, and classes loaded.'.format(model_path))
    
            # Generate colors for drawing bounding boxes.
            hsv_tuples = [(x / len(self.class_names), 1., 1.)
                          for x in range(len(self.class_names))]
            self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
            self.colors = list(
                map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),
                    self.colors))
            np.random.seed(10101)  # Fixed seed for consistent colors across runs.
            np.random.shuffle(self.colors)  # Shuffle colors to decorrelate adjacent classes.
            np.random.seed(None)  # Reset seed to default.
    
            # Generate output tensor targets for filtered bounding boxes.
            self.input_image_shape = K.placeholder(shape=(2, ))
            if self.gpu_num>=2:
                self.yolo_model = multi_gpu_model(self.yolo_model, gpus=self.gpu_num)
            boxes, scores, classes = yolo_eval(self.yolo_model.output, self.anchors,
                    len(self.class_names), self.input_image_shape,
                    score_threshold=self.score, iou_threshold=self.iou)
            return boxes, scores, classes
            
            
        #该函数会在main函数当中调用,我们会在主函数中将输入的视频提取出每一帧为图片
        作为参数输入该函数当中,我们使用训练好的模型,将图片作为输入数据,对图中的
        目标物体做预测,产生以下信息,预测框位置,预测类别,预测置信度,根据这些数
        我们对图片进画框并目标跟踪
        
        
        def detect_image(self, image):
            start = timer()
            #
            # 调整图片使其符合输入要求
            new_image_size = (self.model_image_size[1], self.model_image_size[0])
            boxed_image = letterbox_image(image, new_image_size)
            image_data = np.array(boxed_image, dtype='float32')
            image_data /= 255.
            image_data = np.expand_dims(image_data, 0)  # Add batch dimension.
    
            if self.eager:
                # 预测结果
                input_image_shape = np.expand_dims(np.array([image.size[1], image.size[0]], dtype='float32'), 0)
                out_boxes, out_scores, out_classes = self.yolo_model.predict([image_data, input_image_shape])
            else:
            #使用模型进行预测
                out_boxes, out_scores, out_classes = self.sess.run(
                            [self.boxes, self.scores, self.classes],
                            feed_dict={
                                self.yolo_model.input: image_data,
                                self.input_image_shape: [image.size[1], image.size[0]],
                                K.learning_phase(): 0
                        })
    
            print('Found {} boxes for {}'.format(len(out_boxes), 'img'))
            # 设置字体
            font = ImageFont.truetype(font='font/simhei.ttf',
                                      size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))
            thickness = (image.size[0] + image.size[1]) // 300
    
            small_pic = []
            for i, c in list(enumerate(out_classes)):
                predicted_class = self.class_names[c]
                box = out_boxes[i]
                score = out_scores[i]
    
                top, left, bottom, right = box
                top = top - 5
                left = left - 5
                bottom = bottom + 5
                right = right + 5
                top = max(0, np.floor(top + 0.5).astype('int32'))
                left = max(0, np.floor(left + 0.5).astype('int32'))
                bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))
                right = min(image.size[0], np.floor(right + 0.5).astype('int32'))
    
                # 画框框
                label = '{} {:.2f}'.format(predicted_class, score)
                draw = ImageDraw.Draw(image)
                label_size = draw.textsize(label, font)
                label = label.encode('utf-8')
                print(label)
    
                if top - label_size[1] >= 0:
                    text_origin = np.array([left, top - label_size[1]])
                else:
                    text_origin = np.array([left, top + 1])
    
                for i in range(thickness):
                    draw.rectangle(
                        [left + i, top + i, right - i, bottom - i],
                        outline=self.colors[c])
                draw.rectangle(
                    [tuple(text_origin), tuple(text_origin + label_size)],
                    fill=self.colors[c])
                draw.text(text_origin, str(label, 'UTF-8'), fill=(0, 0, 0), font=font)
                del draw
            end = timer()
            print(end - start)
    
            #进行追踪
            return_boxes = []
            return_scores = []
            return_class_names = []
            count = 0
            # with graph.as_default():
            for i, c in reversed(list(enumerate(out_classes))):
                predicted_class = self.class_names[c]
                # if predicted_class != 'person':  # 确定追踪类别,追啥就是啥
                #     continue
                #显示当前屏幕内未带人数
                if predicted_class == 'person':  # Modify to detect other classes.
                    count = count + 1
                box = out_boxes[i]
                score = out_scores[i]
                x = int(box[1])
                y = int(box[0])
                w = int(box[3] - box[1])
                h = int(box[2] - box[0])
                if x < 0:
                    w = w + x
                    x = 0
                if y < 0:
                    h = h + y
                    y = 0
                return_boxes.append([x, y, w, h])
                return_scores.append(score)
                return_class_names.append(predicted_class)
    
            return return_boxes, return_scores, return_class_names,image,count
    
        def close_session(self):
            self.sess.close()
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204

    第二部分 main函数部分

    from __future__ import division, print_function, absolute_import
    import sys
    #sys.path.remove('/opt/ros/kinetic/lib/python2.7/dist-packages')
    import os
    import datetime
    from timeit import time
    import warnings
    import cv2
    import numpy as np
    import argparse
    from PIL import Image
    from yolo import YOLO
    
    from deep_sort import preprocessing
    from deep_sort import nn_matching
    from deep_sort.detection import Detection
    from deep_sort.tracker import Tracker
    from tools import generate_detections as gdet
    from deep_sort.detection import Detection as ddet
    from collections import deque
    from keras import backend
    import tensorflow as tf
    from tensorflow.compat.v1 import InteractiveSession
    # config = tf.ConfigProto()
    config = tf.compat.v1.ConfigProto()
    config.gpu_options.allow_growth = True
    session = InteractiveSession(config=config)
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    from PyQt5.QtWidgets import QInputDialog, QLineEdit, QDialog
    from PyQt5.QtWidgets import QApplication
    from probar import *
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--input",help="path to input video", default = "./test_video/test.mp4")
    ap.add_argument("-c", "--class",help="name of class", default = "person")
    args = vars(ap.parse_args())
    
    pts = [deque(maxlen=30) for _ in range(9999)]
    warnings.filterwarnings('ignore')
    
    # initialize a list of colors to represent each possible class label
    np.random.seed(100)
    COLORS = np.random.randint(0, 255, size=(200, 3),
    	dtype="uint8")
    #list = [[] for _ in range(100)]
    
    def mainback(self,input,yolo):
        # QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
        # app = QApplication(sys.argv)
        #展开等待界面
        wait()
        #开始计时
        start = time.time()
        max_cosine_distance = 0.3
        nn_budget = None
        nms_max_overlap = 1.0
    
        counter = []
        #deep_sort
        #载入deepsort所用模型
        model_filename = 'model_data/market1501.pb'
        encoder = gdet.create_box_encoder(model_filename,batch_size=1)
        
        #设置查找目标
        find_objects = ['person']
        #调用追踪函数
        metric = nn_matching.NearestNeighborDistanceMetric("cosine", max_cosine_distance, nn_budget)
        tracker = Tracker(metric)
        
        #可以从设置参数中获得input,也可以从函数调用时直接传入
        writeVideo_flag = True
        #处理视频成一帧一帧
        video_capture = cv2.VideoCapture(input)
    
    
        if writeVideo_flag:
        # Define the codec and create VideoWriter object
            w = int(video_capture.get(3))
            h = int(video_capture.get(4))
            fourcc = cv2.VideoWriter_fourcc(*'MJPG')
            out = cv2.VideoWriter('./output/output.avi', fourcc, 15, (w, h))
            list_file = open('detection_rslt.txt', 'w')
            frame_index = -1
    
        fps = 0.0
        #循环处理每一帧的图片
        while True:
            #获取每一帧图片为三维列表的形式
            ret, frame = video_capture.read()  # frame shape 640*480*3
            if ret != True:
                break
            t1 = time.time()
            image = Image.fromarray(frame[..., ::-1])  # bgr to rgb
            #将获取的每一帧图片进行YOLO识别检测并画出框框,并使用deepsort对目标对象进行追踪
            boxs, confidence, class_names,frame,record= yolo.detect_image(image)
            frame = np.array(frame)
            # RGBtoBGR满足opencv显示格式
            frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
    
            fps = (fps + (1. / (time.time() - t1))) / 100
            print("fps= %.2f" % (fps))
            #处理在图片上显示帧数
            frame = cv2.putText(frame, "fps= %.2f" % (fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    
            # cv2.imshow("video", frame)
            c = cv2.waitKey(1) & 0xff
            if c == 27:
                capture.release()
                break
            features = encoder(frame,boxs)
            # score to 1.0 here).
            detections = [Detection(bbox, 1.0, feature) for bbox, feature in zip(boxs, features)]
            # Run non-maxima suppression.
            boxes = np.array([d.tlwh for d in detections])
            scores = np.array([d.confidence for d in detections])
            indices = preprocessing.non_max_suppression(boxes, nms_max_overlap, scores)
            detections = [detections[i] for i in indices]
    
            # Call the tracker
            tracker.predict()
            tracker.update(detections)
    
            i = int(0)
            indexIDs = []
            c = []
            boxes = []
    
            for det in detections:
                bbox = det.to_tlbr()
                cv2.rectangle(frame,(int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])),(255,255,255), 2)
                #print(class_names)
                #print(class_names[p])
    
            for track in tracker.tracks:
                if not track.is_confirmed() or track.time_since_update > 1:
                    continue
                #boxes.append([track[0], track[1], track[2], track[3]])
                indexIDs.append(int(track.track_id))
                counter.append(int(track.track_id))
                bbox = track.to_tlbr()
                color = [int(c) for c in COLORS[indexIDs[i] % len(COLORS)]]
                #print(frame_index)
                list_file.write(str(frame_index)+',')
                list_file.write(str(track.track_id)+',')
                cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])),(color), 3)
                b0 = str(bbox[0])#.split('.')[0] + '.' + str(bbox[0]).split('.')[0][:1]
                b1 = str(bbox[1])#.split('.')[0] + '.' + str(bbox[1]).split('.')[0][:1]
                b2 = str(bbox[2]-bbox[0])#.split('.')[0] + '.' + str(bbox[3]).split('.')[0][:1]
                b3 = str(bbox[3]-bbox[1])
    
                list_file.write(str(b0) + ','+str(b1) + ','+str(b2) + ','+str(b3))
                #print(str(track.track_id))
                list_file.write('\n')
                #list_file.write(str(track.track_id)+',')
                cv2.putText(frame,str(track.track_id),(int(bbox[0]), int(bbox[1] -50)),0, 5e-3 * 150, (color),2)
                if len(class_names) > 0:
                   class_name = class_names[0]
                   cv2.putText(frame, str(class_names[0]),(int(bbox[0]), int(bbox[1] -20)),0, 5e-3 * 150, (color),2)
    
                i += 1
                #bbox_center_point(x,y)
                center = (int(((bbox[0])+(bbox[2]))/2),int(((bbox[1])+(bbox[3]))/2))
                #track_id[center]
    
                pts[track.track_id].append(center)
    
                thickness = 5
                #center point
                cv2.circle(frame,  (center), 1, color, thickness)
    
    			# draw motion path
                for j in range(1, len(pts[track.track_id])):
                    if pts[track.track_id][j - 1] is None or pts[track.track_id][j] is None:
                       continue
                    thickness = int(np.sqrt(64 / float(j + 1)) * 2)
                    cv2.line(frame,(pts[track.track_id][j-1]), (pts[track.track_id][j]),(color),thickness)
                    #cv2.putText(frame, str(class_names[j]),(int(bbox[0]), int(bbox[1] -20)),0, 5e-3 * 150, (255,255,255),2)
    
    
            count = len(set(counter))
            #将所有行人数和和当前行人数和帧数显示在图片之上
            cv2.putText(frame, "Total Pedestrian Counter: "+str(count),(int(20), int(120)),0, 5e-3 * 200, (0,255,0),2)
            cv2.putText(frame, "Current Pedestrian Counter: "+str(i),(int(20), int(80)),0, 5e-3 * 200, (0,255,0),2)
            cv2.putText(frame, "FPS: %f"%(fps),(int(20), int(40)),0, 5e-3 * 200, (0,255,0),3)
            # cv2.namedWindow("YOLO4_Deep_SORT", 0);
            # cv2.resizeWindow('YOLO4_Deep_SORT', 1024, 768);
            # cv2.imshow('YOLO4_Deep_SORT', frame)
            #将所有行人数和和当前行人数和帧数显示在前端面板之上
            self.lcdNumber.setProperty("intValue", count)
            self.lcdNumber_2.setProperty("intValue", record)
            #将处理后的图片显示在面板上
            img = QImage(frame, frame.shape[1], frame.shape[0], frame.shape[1]*3,QImage.Format_RGB888)
            pix = QPixmap.fromImage(img)
            pix = pix.scaled(691,501, Qt.KeepAspectRatio, Qt.SmoothTransformation)
            self.label_4.setPixmap(pix)
    
            # show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            #
            # showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
            #
            # self.label_4.setPixmap(QPixmap.fromImage(showImage))
    
            if writeVideo_flag:
                # save a frame
                out.write(frame)
                frame_index = frame_index + 1
    
    
            fps  = ( fps + (1./(time.time()-t1)) ) / 2
            out.write(frame)
            frame_index = frame_index + 1
    
            # Press Q to stop!
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
    
        print(" ")
        print("[Finish]")
        end = time.time()
        video_capture.release()
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221

    前端部分(前端部分并不是本人设计)

    页面展示

    登录界面

    在这里插入图片描述

    主页面

    在这里插入图片描述

    设计

    基本控件对应

    页面对应

    Ui_MainWindow() 主界面

    Ui_Dialog1 警告界面

    Ui_Dialog2 登录界面

    Ui_Dialog3 设置界面

    主页面控件设置

    基本格式
    self.label = QtWidgets.QLabel(self.centralwidget)#初始化一个控件对象
    self.label.setGeometry(QtCore.QRect(160, 80, 691, 501))#设置对象面板中位置,标签位置定义方式是(x,y,w,h)
    font = QtGui.QFont()#创建格式对象
    font.setFamily("黑体")#设置格式对象
    font.setPointSize(9)
    self.label.setFont(font)#设置字体大小
    self.label.setText("") #标签上写字
    self.label.setPixmap(QtGui.QPixmap("image/off.png"))#标签上插图
    self.label.setScaledContents(True)#使图根据标签大小缩放
    self.label.setStyleSheet("color: rgb(88, 101, 208);")
    self.label.setObjectName("label")#设置标签控件名称
    self.label.raise_() #在面板上显示设置好的标签
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    控件对应
    控件控件类型控件标号
    开始pushButtonself.pushButton
    结束labelself.label_11
    警告级别labelself.label_a
    共检测人数labelself.label_6
    当前人数labelself.label_65
    未佩戴人数labelself.label_7
    安全帽智能检测系统labelself.label_5
    共检测人数框lcdNumberself.lcdNumber
    当前人数框lcdNumberself.lcdNumber_1
    未佩戴人数框lcdNumberself.lcdNumber_2
    一级警报labelself.label_b
    二级警报labelself.label_c
    三级警报labelself.label_d
    日期显示框dateTimeEditself.dateTimeEdit
    时间显示框dateTimeEditself.dateTimeEdit1

    登录设计

    初始化登录界面的类之后使用登录界面的登录按钮点击触发主界面展示

    class parentWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.main_ui = Ui_MainWindow()
            self.main_ui.setupUi(self)
     
    class childWindow1(QDialog):
        def __init__(self):
            QDialog.__init__(self)
            self.child1=Ui_Dialog1()
            self.child1.setupUi(self)
    
    class childWindow2(QDialog):
        def __init__(self):
            QDialog.__init__(self)
            self.child2=Ui_Dialog2()
            self.child2.setupUi(self)
    
    class childWindow3(QDialog):
        def __init__(self):
            QDialog.__init__(self)
            self.child3=Ui_Dialog3()
            self.child3.setupUi(self)
            
    if __name__=='__main__':
        app=QApplication(sys.argv)
        #初始化各个窗口
        child2 = childWindow2()
        window=parentWindow()
        child1=childWindow1()
        child3= childWindow3()
    
        #获得登录界面登录按钮
        btn = child2.child2.pushButton
        #将登录按钮点击触发和主界面显示事件连接
        btn.clicked.connect(window.show)
        #通过toolButton将两个窗体关联
        btn=window.main_ui.pushButton_3
        btn.clicked.connect(child1.show)
    
        btn = window.main_ui.pushButton_8
        btn.clicked.connect(child2.show)
    
        btn = window.main_ui.pushButton_5
        btn.clicked.connect(child3.show)
        
        #显示登录界面
        child2.show()
        sys.exit(app.exec_())
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    时间设计

    #面板类初始化
    #开始周期工作
    self.timer = QTimer()
    ##这个通过调用槽函数来刷新时间
    self.timer.timeout.connect(self.showTime)
    #每隔一秒刷新一次,这里设置为1000ms
    self.timer.start(1000)
    #接下来是设置面板一打开是所显示的时间
    #设置日期牌
    self.dateTimeEdit = QtWidgets.QDateTimeEdit(self.centralwidget)
    #设置时间牌
    self.dateTimeEdit1 = QtWidgets.QDateTimeEdit(self.centralwidget)
    #设置日期牌位置
    self.dateTimeEdit.setGeometry(QtCore.QRect(870, 50, 111, 21))
    #设置日期牌获得当前时间
    self.dateTimeEdit.setDateTime(QtCore.QDateTime.currentDateTime())
    #设置日期牌的显示格式只要当前时间的日期部分
    self.dateTimeEdit.setDisplayFormat("yyyy/MM/dd")
    #设置日期牌的名称
    self.dateTimeEdit.setObjectName("dateTimeEdit")
    #设置时间牌位置
    self.dateTimeEdit1.setGeometry(QtCore.QRect(870, 70, 111, 21))
    #设置时间牌获得当前时间
    self.dateTimeEdit1.setDateTime(QtCore.QDateTime.currentDateTime())
    #设置时间牌的显示格式只要当前时间的时间部分
    self.dateTimeEdit1.setDisplayFormat("hh:mm:ss")
    #设置时间牌的名称
    self.dateTimeEdit1.setObjectName("dateTimeEdit1")
    #在面板上显示时间牌和日期牌
    self.dateTimeEdit.raise_()
    self.dateTimeEdit1.raise_()
    
    #面板类内的函数
    def showTime(self):
        #与上述一致初始化的在面板上的时间一致,不过是每一秒调用一次,产生动态显示
        time = QDateTime.currentDateTime()
        self.dateTimeEdit.setGeometry(QtCore.QRect(870, 50, 111, 21))
        self.dateTimeEdit.setDateTime(QtCore.QDateTime.currentDateTime())
        self.dateTimeEdit.setDisplayFormat("yyyy/MM/dd")
        self.dateTimeEdit.setObjectName("dateTimeEdit")
        self.dateTimeEdit1.setGeometry(QtCore.QRect(870, 70, 111, 21))
        self.dateTimeEdit1.setDateTime(QtCore.QDateTime.currentDateTime())
        self.dateTimeEdit1.setDisplayFormat("hh:mm:ss")
        self.dateTimeEdit1.setObjectName("dateTimeEdit1")
        self.dateTimeEdit.raise_()
        self.dateTimeEdit1.raise_()
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    警告设计

    再次采用了在面板上设置不同图片的方式实现报警
    在这里插入图片描述

     非警报状态                警报状态
    
    • 1

    我们在主面板上设置了label_a,label_b,label_c作为三个警报灯,初始的时候的都设置为非警报状态的图片

    #设置“警告级别”的标题
    self.label_a = QtWidgets.QLabel(self.centralwidget)
    self.label_a.setGeometry(QtCore.QRect(880,100, 80, 20))
    self.label_a.setFont(font)
    self.label_a.setStyleSheet("color: rgb(88, 101, 208);")
    self.label_a.setObjectName("label_a")
    
    #设置标签并贴上非警报状态的标签
    #设置居中
    self.label_b = QtWidgets.QLabel(self.centralwidget)
    #设置标签位置
    self.label_b.setGeometry(QtCore.QRect(900, 130, 40, 50))
    #设置标签字体
    self.label_b.setFont(font)
    #设置标签图片
    self.label_b.setPixmap(QtGui.QPixmap("image/off.png"))
    #设置图片自适应标签大小缩放
    self.label_b.setScaledContents(True)
    self.label_b.setStyleSheet("color: rgb(88, 101, 208);")
    #设置标签名称
    self.label_b.setObjectName("label_b")
    
    self.label_c = QtWidgets.QLabel(self.centralwidget)
    self.label_c.setGeometry(QtCore.QRect(880, 190, 40, 50))
    self.label_c.setFont(font)
    self.label_c.setPixmap(QtGui.QPixmap("image/off.png"))
    self.label_c.setScaledContents(True)
    self.label_c.setStyleSheet("color: rgb(88, 101, 208);")
    self.label_c.setObjectName("label_c")
    
    self.label_d = QtWidgets.QLabel(self.centralwidget)
    self.label_d.setGeometry(QtCore.QRect(930, 190, 40, 50))
    self.label_d.setFont(font)
    self.label_d.setPixmap(QtGui.QPixmap("image/off.png"))
    self.label_d.setScaledContents(True)
    self.label_d.setStyleSheet("color: rgb(88, 101, 208);")
    self.label_d.setObjectName("label_d")
    
    • 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
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述
    然后我们通过前后端的连接,通过判断为佩戴帽子人数对其三个灯设置不同亮的转态在相同标签处贴上警报状态的图片

    当人数达到警报级别时灯的改变

    if(record > 0 and record <= 5):
        #当未佩戴帽子的人数为(0,5]时,一级警报只有第一个为警报状态,其余为非警报状态
        self.label_b.setPixmap(QtGui.QPixmap("image/on.png"))
        self.label_c.setPixmap(QtGui.QPixmap("image/off.png"))
        self.label_d.setPixmap(QtGui.QPixmap("image/off.png"))
    if(record > 5 and record <= 10):
        #当未佩戴帽子的人数为(5,10]时,二级警报前两个为警报状态,最后一个为非警报状态
        self.label_b.setPixmap(QtGui.QPixmap("image/on.png"))
        self.label_c.setPixmap(QtGui.QPixmap("image/on.png"))
        self.label_d.setPixmap(QtGui.QPixmap("image/off.png"))
    if (record > 10):
        #当未佩戴帽子的人数为十个以上时,三级警报级警报全为警报状态
        self.label_b.setPixmap(QtGui.QPixmap("image/on.png"))
        self.label_c.setPixmap(QtGui.QPixmap("image/on.png"))
        self.label_d.setPixmap(QtGui.QPixmap("image/on.png"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    要注意的是这里边每一种情况都都需要对每一个灯都需要重置转态,即重贴预想的标签,对应等级只调整对应级数的灯,会导致从高级向低级状态改变时灯无法关闭

    对于灯的问题,多整几张不同状态下的图,换图就行了,自己绘制肯定是没有问题,但如果用PS做好图片当然是最简单的办法,而且效率肯定比自己绘制高,因为绘制是需要大量的计算,没有太复杂的计算,就是内个层加渐变。

    暂时界面如此还需更多的设计

    但是从点击开始之后的文件导入知道现实在面板上是本人实现

        #在面板上设置一个按钮,并将按钮的点击事件和打开文件想连
        self.pushButton.clicked.connect(self.openfile)
         
        def openfile(self):
            #设置打开文件页面的初始化位置
            path = "G:\\软件杯"
            #设置打开文件页面,并返回带有文件路径的结果
            #(['G:/软件杯/deep_sort_yolov4_fix/test_video/cut.mp4'], 'All Files (*)')
            openfile_name = QFileDialog.getOpenFileNames(None, "请选择要添加的文件", path, "Text Files (*.xls);;All Files (*)")
            #获取返回结果中的路径字符串形式
            path_list = openfile_name[0]
            path_name = path_list[0]
            #调用函数,产生等待界面,进行目标检测并追踪并且返回到主面板上
            mainback(self,path_name,YOLO())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    前后端的连接问题:

    作为我进行前后端的连接也遇到了许多的问题,我的基本思路有两种:

    1.第一个是把各种从后端处理到的数据输出来然后贴在前端的面板上,我发现得返回的结果是一帧一帧的图片和每个图片对应的检测结果再循环当中一轮一轮输出结果传到前端比较费劲,所谓以我转移将后端结果输出到前端显示的方法,选用第二种方法

    2.第二种方法就是我将设置好样式的面板实例化后输入到后端,直接在后端实时的将结果直接贴在面板之上就实现了将后端结果在前端展示,从而实现前后端的连接

    连接函数如下:

    mainback(self,path_name,YOLO())
    
    • 1

    1)参数:YOLO()
    原本这是后端的main.py的主函数,直接使用主函数调用,需要传入一个YOLO对象所以我们mainback函数仍需该参数YOLO()

     if __name__ == '__main__':
         main(YOLO())
    
    • 1
    • 2

    2)参数: self

    我们需要把主面板传入到后端当中,所以需要一个传入主面板的对象就主面板类的引用,而我们的mainback函数正好位于按钮的点击触发时间当中,二点击触犯函数位于主面板的类当中,所以此时self就是对主面板的引用,所以具有该类的一切属性,所以我我们可以直接将self最为主面板对象传入后端,并且在该面板对象上添加我们需要输出的后端结果

    3)参数:path_name
    G:\软件杯\deep_sort_yolov4_fix\main.py
    作为后端的起始程序,正常直接调用的的时候我们需要输入追踪类别,视频位置等信息
    即如下:

    python main.py -c person -i test_vedio/test.avi
    
    • 1

    我们可以直接在命令行输入上述语句运行程序,并且可以在前端使用下面语句调用后端程序(str=上面的命令行语句组成字符串):

    os.system(str)
    subprocess.run(str)
    
    • 1
    • 2

    这种调用只能实现在前端运行后端程序,但是并不能实现前后端的连接,如果想进行数据连接需要使用函数调用的方式,如果仅仅是视频地址信息可以传入,但是如何传入面板和yolo对象暂时不会实现所以采用函数的方式实现相同的目的。所以需要传入我们想识别的视频,而视频的地址在我们选择文件夹函数的结果中可以提取到,所以我们需要将它通过函数参数的形式传入后端。

  • 相关阅读:
    OpenBox(一个高效通用的黑盒优化系统)安装与使用
    c语言编程 结构(struct)
    蓝桥杯每日一题2032.10.24
    Zookeeper整理
    【idea】解决idea 执行maven build总下载 Downloading maven-metadata.xml文件
    Linux 系统编程,Binder 学习,文件访问相关的接口
    千访 | “霸总”人设揽粉近十万!小红书企业号还能这么玩?
    车联网安全集智联盟正式成立
    火山引擎、腾讯们,看透了大模型这场“猫鼠游戏”
    金仓数据库KingbaseES客户端编程接口指南-Perl DBI(4. DBI 数据库句柄对象)
  • 原文地址:https://blog.csdn.net/lockhou/article/details/126193883