Обнаружение лица Opencv в прямом эфире с использованием Qt Gui работает медленно

У меня есть проект, в котором мне нужно спроектировать gui в qt. Этот дизайн содержит виджет, в котором live video feed будет отображаться с веб-камеры USB с помощью opencv. Этот проект обнаружит faces, а также распознает их, что означает, что для каждого кадра будет выполняться большая обработка.

Для этого я создал поток, который инициализирует камеру и берет с нее кадры с помощью opencv. Затем он помещает весь кадр в очередь, и эта очередь затем считывается функцией update_frame, которая в основном отображает кадр в виджете qt. Это работает нормально, без задержек.

Внутри функции update_frame я добавил face detection, из-за чего она работала очень медленно. Поэтому я создал еще один поток start_inferencing, который в основном читает кадр из queue, и после обнаружения лица он снова помещает кадр в другую очередь q2, которая затем читается update_frame и отображается, но по-прежнему отвечает очень медленно. Ниже приведен код:

q = queue.Queue()
q2 = queue.Queue()

def grab(cam, qu, width, height):
    global running
    capture = cv2.VideoCapture(cam)
    capture.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    capture.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    while running:
        frame = {}
        capture.grab()
        ret_val, img = capture.retrieve(0)
        frame["img"] = img

        if qu.qsize() < 100:
            qu.put(frame)
        else:
            print(qu.qsize())

class Logic(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setupUi(self)

        set_initial_alert_temp()

        self.window_width = self.ImgWidget.frameSize().width()
        self.window_height = self.ImgWidget.frameSize().height()
        self.ImgWidget = OwnImageWidget(self.ImgWidget)

        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.update_frame)
        self.timer.start(1)

        self.outside_temp_text_box.setText(str(curr_temp_cel))

    def update_frame(self):
        if not q2.empty():
            frame1 = q2.get()
            img = frame1["img"]

            img_height, img_width, img_colors = img.shape
            scale_w = float(self.window_width) / float(img_width)
            scale_h = float(self.window_height) / float(img_height)
            scale = min([scale_w, scale_h])

            if scale == 0:
                scale = 1

            img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            height, width, bpc = img.shape
            bpl = bpc * width
            image = QtGui.QImage(img.data, width, height, bpl, QtGui.QImage.Format_RGB888)
            self.ImgWidget.setImage(image)

def start_inferencing():
    while True:
        if not q.empty():
            frame = q.get()
            img = frame["img"]
            face_bbox = face.detect_face(img)
            if face_bbox is not None:
                (f_startX, f_startY, f_endX, f_endY) = face_bbox.astype("int")
                f_startX = f_startX + 10
                f_startY = f_startY + 10
                f_endX = f_endX - 10
                f_endY = f_endY - 10
                cv2.rectangle(img, (f_startX, f_startY), (f_endX, f_endY), (0, 255, 0), 2)

                frame1 = {"img": img}

                if q2.qsize() < 100:
                    q2.put(frame1)
                else:
                    print(q2.qsize())

def main():

    capture_thread = threading.Thread(target=grab, args=(0, q, 640, 480))
    capture_thread.start()

    infer_thread = threading.Thread(target=start_inferencing)
    infer_thread.start()

    app = QtWidgets.QApplication(sys.argv)
    w = Logic(None)
    w.setWindowTitle('Test')
    w.show()
    app.exec_()


main()

Ниже приведено краткое описание того, что происходит в коде:

camera -> frame -> queue.put                     # (reading frame from camera and putting it in queue)
queue.get -> frame -> detect face -> queue2.put  # (getting frame from queue, detecting face in it and putting the updated frames in queue2)
queue2.get -> frame -> display it on qt widget   # (getting frame from queue2 and display it on qt widget)

Основная причина медленного видеопотока в реальном времени заключается в том, что кадр, который считывается в функции grab, не может обрабатываться быстрее, и поэтому размер queue продолжает сильно увеличиваться, и поэтому в целом он становится очень медленным. Есть ли хороший подход, который я могу использовать, который может обнаруживать лицо, а также показывать его без каких-либо задержек. Пожалуйста помоги. Спасибо


person S Andrew    schedule 30.03.2020    source источник
comment
Можно ли использовать события (например, «onFrame», «onFaceDetected» и т. д.)?   -  person Andrey Smorodov    schedule 30.03.2020
comment
@AndreySmorodov Можете ли вы добавить больше описания   -  person S Andrew    schedule 30.03.2020
comment
Ваш базовый дизайн выглядит для серии производителей/потребителей (или, возможно, должен рассматриваться как таковой). Так почему же вы запускаете Logic::update_frame из миллисекундного таймера, а не просто блокируете до тех пор, пока в соответствующей очереди не появятся данные?   -  person G.M.    schedule 30.03.2020


Ответы (2)


Очередь накапливает кадры, которые поток не успевает обработать. Таким образом, нет шансов обработать их вообще. Поэтому очередь здесь бесполезна. Рабочие часы здесь определяются поступающими кадрами, каждый кадр генерирует событие, которое может работать в своем собственном потоке (скажем, в потоке обработки) после завершения обработки кадра, поток обработки генерирует другое событие и обрабатывается в другом потоке, скажем, в потоке GUI. и показывает результат пользователю.

Если вам обязательно нужен какой-то буфер, проверьте, что кольцевой буфер имеет конечную длину.

person Andrey Smorodov    schedule 30.03.2020
comment
Спасибо, я плохо разбираюсь в кольцевом буфере или любом другом буфере, поэтому пока буду придерживаться очереди, но обязательно попробую в будущем. - person S Andrew; 30.03.2020

У вас есть последовательность производитель/потребитель...

  1. захватить кадр и нажать на очередь 1
  2. удалить кадр из очереди1, обработать и поставить результаты в очередь2
  3. удалить результаты из очереди2 и отобразить

Из того, что вы сказали, этап 2 является узким местом. В этом случае вы можете попробовать назначить больше ресурсов (т.е. потоков) на этот этап, чтобы 2. иметь несколько потоков, читающих из очереди 1, обрабатывающих и помещающих результаты в очередь 2. Вам просто нужно убедиться, что обработанные данные, извлеченные из очереди 2, упорядочиваются правильно — предположительно, путем присвоения каждому начальному кадру порядкового номера или идентификатора.

person G.M.    schedule 30.03.2020
comment
Можете ли вы привести пример очереди или исключения из очереди с идентификатором, как вы упомянули в своем ответе. Также я думаю, что то, что вы упомянули в своем комментарии, стоит попробовать, потому что нет смысла запускать кадр обновления каждые миллисекунды. Я постараюсь обновить, но как вы думаете, этот конвейер может дать хорошие результаты? - person S Andrew; 30.03.2020