Detector de distanciamento social usando OpenCV e Raspberry Pi
Detector-de-distanciamento-social-usando-OpenCV-e-Raspberry-Piblog_image_banner

Detector de distanciamento social usando OpenCV e Raspberry Pi


Na época da Covid-19, o distanciamento social era uma forma eficaz de desacelerar a transmissão de vírus infecciosos. As pessoas são aconselhadas a minimizar o contato umas com as outras para minimizar o risco da doença ser transmitida por contato direto. Manter uma distância segura é um desafio para muitos lugares, como fábricas, bancos, ônibus ou estações ferroviárias, etc.


Portanto, na continuação de nossos projetos de segurança Corona anteriores, como máquina desinfetante automática e monitoramento de temperatura sem contato, aqui vamos construir um sistema de Detector de Distanciamento Social usando OpenCV e Raspberry Pi. Estaremos usando os pesos do Algoritmo de Detecção de Objetos YOLO v3 com o módulo Rede Neural Profunda.


Raspberry Pi é sempre uma boa escolha para projetos de processamento de imagem, pois tem mais memória e velocidade do que outros controladores. Anteriormente, usamos o Raspberry Pi para alguns projetos complexos de processamento de imagem, como detecção facial de pontos de referência e aplicativo de reconhecimento facial.


Componentes necessários


* Raspberry Pi 4


Aqui, só precisamos do RPi 4 com OpenCV instalado. OpenCV é usado aqui para processamento de imagem digital. As aplicações mais comuns de processamento digital de imagens são detecção de objetos, reconhecimento facial e contador de pessoas.


YOLO


YOLO (You Only Look Once [Você só olha uma vez]) é uma rede neural de convolução (CNN) inteligente para detecção de objetos em tempo real. YOLOv3, a última variante do algoritmo de detecção de objetos, YOLO pode reconhecer 80 objetos diferentes em imagens e vídeos, e é super rápido e tem excelente precisão. O algoritmo aplica uma única rede neural a toda a imagem e, em seguida, separa a imagem em regiões e calcula as caixas de limite e as probabilidades para cada área. O modelo base YOLO pode processar imagens em tempo real a 45 quadros por segundo. O modelo YOLO supera todos os outros métodos de detecção, como SSD e R-CNN.



O modelo YOLOV3 que vamos usar neste projeto pode ser baixado aqui.



Instalando OpenCV no Raspberry Pi



Antes de instalar o OpenCV e outras dependências, o Raspberry Pi precisa ser totalmente atualizado. Use os comandos abaixo para atualizar o Raspberry Pi para sua versão mais recente:



sudo apt-get update


Em seguida, use os seguintes comandos para instalar as dependências necessárias para instalar o OpenCV em seu Raspberry Pi.


sudo apt-get install libhdf5-dev -y 

sudo apt-get install libhdf5-serial-dev –y 

sudo apt-get install libatlas-base-dev –y 

sudo apt-get install libjasper-dev -y 

sudo apt-get install libqtgui4 –y

sudo apt-get install libqt4-test –y


Finalmente, instale o OpenCV no Raspberry Pi usando os comandos abaixo.


pip3 install opencv-contrib-python==4.1.0.25


Se você é novo no OpenCV, verifique nossos tutoriais anteriores do OpenCV com Raspberry pi:



* Instalando OpenCV no Raspberry Pi usando CMake

* Reconhecimento facial em tempo real com Raspberry Pi e OpenCV

* Reconhecimento de matrículas usando Raspberry Pi e OpenCV

* Estimativa do tamanho da multidão usando OpenCV e Raspberry Pi



Também criamos uma série de tutoriais OpenCV começando no nível iniciante.


Instalando outros pacotes necessários no Raspberry Pi



Antes de programar o detector de distância Raspberry Pi for Social, vamos instalar os outros pacotes necessários.



Instalando o imutils: o imutils é usado para facilitar as funções essenciais de processamento de imagem, como translação, rotação, redimensionamento, esqueletização e exibição de imagens Matplotlib com OpenCV. Use o comando abaixo para instalar os imutils:



pip3 install imutils



Explicação do programa


O código completo é fornecido no final da página. Aqui estamos explicando as seções importantes do código para uma melhor explicação.



Portanto, no início do código, importe todas as bibliotecas necessárias que serão usadas neste projeto.


import numpy as np

import cv2

import imutils

import os

import time


A função Check() é usada para calcular a distância entre dois objetos ou dois pontos em um quadro de vídeo. Os pontos a e b denotam os dois objetos no quadro. Esses dois pontos são usados para calcular a distância euclidiana entre os objetos.


def Check(a,  b):

    dist = ((a[0] - b[0]) ** 2 + 550 / ((a[1] + b[1]) / 2) * (a[1] - b[1]) ** 2) ** 0.5

    calibration = (a[1] + b[1]) / 2     

    if 0 < dist < 0.25 * calibration:

        return True

    else:

        return False


A função de configuração é usada para definir os caminhos para os pesos YOLO, arquivo cfg, arquivo de nomes COCO. O módulo os.path é usado para manipulação de nomes de caminho comuns. O módulo os.path.join() é um submódulo do os.path e é usado para unir um ou mais componentes do caminho de forma inteligente. O método cv2.dnn.readNetFromDarknet() é usado para carregar os pesos salvos na rede. Depois de carregar os pesos, extraia a lista de todas as camadas usadas em uma rede usando um modelo net.getLayerNames.


def Setup(yolo):

    global neural_net, ln, LABELS

    weights = os.path.sep.join([yolo, "yolov3.weights"])

    config = os.path.sep.join([yolo, "yolov3.cfg"])

    labelsPath = os.path.sep.join([yolo, "coco.names"])

    LABELS = open(labelsPath).read().strip().split("\n") 

    neural_net = cv2.dnn.readNetFromDarknet(config, weights)

    ln = neural_net.getLayerNames()

    ln = [ln[i[0] - 1] for i in neural_net.getUnconnectedOutLayers()]


Dentro da função de processamento de imagem, pegamos um único quadro de vídeo e o processamos para detecção de distanciamento social entre cada pessoa na multidão. Nas primeiras duas linhas da função, definimos as dimensões do quadro de vídeo (W, H) como (None, None) inicialmente. Na próxima linha, usamos o método cv2.dnn.blobFromImage() para carregar frames em um lote e executá-los pela rede. A função blob realiza subtração média, escala e troca de canal em um quadro.


(H, W) = (None, None)

    frame = image.copy()

    if W is None or H is None:

        (H, W) = frame.shape[:2]

    blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)

    neural_net.setInput(blob)

    starttime = time.time()

    layerOutputs = neural_net.forward(ln)


As saídas da camada do YOLO consistem em um conjunto de valores. Esses valores nos ajudam a definir qual objeto pertence a qual classe. Fazemos um loop em cada saída em layerOutputs e, conforme detectamos pessoas, definimos o rótulo da classe como "pessoa". De cada detecção, obtemos uma caixa delimitadora que nos dá o centro X, o centro Y, a largura e a altura da caixa para detecção na saída:


scores = detection[5:]

            maxi_class = np.argmax(scores)

            confidence = scores[maxi_class]

            if LABELS[maxi_class] == "person":

                if confidence > 0.5:

                    box = detection[0:4] * np.array([W, H, W, H])

                    (centerX, centerY, width, height) = box.astype("int")

                    x = int(centerX - (width / 2))

                    y = int(centerY - (height / 2))

                    outline.append([x, y, int(width), int(height)])

                    confidences.append(float(confidence))


Depois disso, calcule a distância entre o centro da caixa atual com todas as outras caixas detectadas. Se as caixas delimitadoras estiverem fechadas, altere o status para verdadeiro.


for i in range(len(center)):

            for j in range(len(center)):

                close = Check(center[i], center[j])

                if close:

                    pairs.append([center[i], center[j]])

                    status[i] = True

                    status[j] = True

        index = 0


Nas próximas linhas, desenhe um retângulo ao redor da pessoa usando as dimensões da caixa que recebemos do modelo e, em seguida, verifique se a caixa é segura ou insegura. Se a distância entre as caixas for próxima, a cor da caixa será vermelha, caso contrário, a cor da caixa será verde.


(x, y) = (outline[i][0], outline[i][1])

            (w, h) = (outline[i][2], outline[i][3])

            if status[index] == True:

                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 150), 2)

            elif status[index] == False:

                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)


Agora, dentro da função de loop, estamos lendo cada quadro do vídeo e, em seguida, processando cada quadro para calcular a distância entre as pessoas.


ret, frame = cap.read()

    if not ret:

        break

    current_img = frame.copy()

    current_img = imutils.resize(current_img, width=480)

    video = current_img.shape

    frameno += 1

    if(frameno%2 == 0 or frameno == 1):

        Setup(yolo)

        ImageProcess(current_img)

        Frame = processedImg


Nas próximas linhas, use a função cv2.VideoWriter() para armazenar o vídeo de saída no local especificado por opname que definimos anteriormente.


if create is None:

            fourcc = cv2.VideoWriter_fourcc(*'XVID')

            create = cv2.VideoWriter(opname, fourcc, 30, (Frame.shape[1], Frame.shape[0]), True)

    create.write(Frame)



Testando o Projeto Detector de Distância Social



Assim que seu código estiver pronto, abra um terminal Pi e navegue até o diretório do projeto. O código, o modelo Yolo e o vídeo de demonstração devem estar na mesma pasta conforme mostrado abaixo.


Você pode baixar o diretório YoloV3 daqui, vídeos de Pexels e copiar o código Python fornecido abaixo, e colocá-los no mesmo diretório como mostrado acima.



Quando estiver no diretório do projeto, execute o seguinte comando para iniciar o código:



python3 detector.py


Tentei esse código em um vídeo de exemplo obtido na Pexels. Para mim, o FPS era muito lento e demorava cerca de 10 a 11 minutos para processar o vídeo inteiro.


Em vez de usar um vídeo, você pode até mesmo testar este código com uma câmera Raspberry Pi substituindo cv2.VideoCapture(input) por cv2.VideoCapture(0) na 98ª linha do código. Saiba mais sobre como usar PiCamera com Raspberry Pi seguindo o link.



É assim que você pode usar o OpenCV com Raspberry Pi para detectar as violações de distanciamento social. O vídeo de saída e o código são fornecidos abaixo:



Código:



import numpy as np

import cv2

import imutils

import os

import time

def Check(a,  b):

    dist = ((a[0] - b[0]) ** 2 + 550 / ((a[1] + b[1]) / 2) * (a[1] - b[1]) ** 2) ** 0.5

    calibration = (a[1] + b[1]) / 2      

    if 0 < dist < 0.25 * calibration:

        return True

    else:

        return False

def Setup(yolo):

    global net, ln, LABELS

    weights = os.path.sep.join([yolo, "yolov3.weights"])

    config = os.path.sep.join([yolo, "yolov3.cfg"])

    labelsPath = os.path.sep.join([yolo, "coco.names"])

    LABELS = open(labelsPath).read().strip().split("\n") 

    net = cv2.dnn.readNetFromDarknet(config, weights)

    ln = net.getLayerNames()

    ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]

def ImageProcess(image):

    global processedImg

    (H, W) = (None, None)

    frame = image.copy()

    if W is None or H is None:

        (H, W) = frame.shape[:2]

    blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)

    net.setInput(blob)

    starttime = time.time()

    layerOutputs = net.forward(ln)

    stoptime = time.time()

    print("Video is Getting Processed at {:.4f} seconds per frame".format((stoptime-starttime)))

    confidences = []

    outline = []

    for output in layerOutputs:

        for detection in output:

            scores = detection[5:]

            maxi_class = np.argmax(scores)

            confidence = scores[maxi_class]

            if LABELS[maxi_class] == "person":

                if confidence > 0.5:

                    box = detection[0:4] * np.array([W, H, W, H])

                    (centerX, centerY, width, height) = box.astype("int")

                    x = int(centerX - (width / 2))

                    y = int(centerY - (height / 2))

                    outline.append([x, y, int(width), int(height)])

                    confidences.append(float(confidence))

    box_line = cv2.dnn.NMSBoxes(outline, confidences, 0.5, 0.3)

    if len(box_line) > 0:

        flat_box = box_line.flatten()

        pairs = []

        center = []

        status = []

        for i in flat_box:

            (x, y) = (outline[i][0], outline[i][1])

            (w, h) = (outline[i][2], outline[i][3])

            center.append([int(x + w / 2), int(y + h / 2)])

            status.append(False)

        for i in range(len(center)):

            for j in range(len(center)):

                close = Check(center[i], center[j])

                if close:

                    pairs.append([center[i], center[j]])

                    status[i] = True

                    status[j] = True

        index = 0

        for i in flat_box:

            (x, y) = (outline[i][0], outline[i][1])

            (w, h) = (outline[i][2], outline[i][3])

            if status[index] == True:

                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 150), 2)

            elif status[index] == False:

                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

            index += 1

        for h in pairs:

            cv2.line(frame, tuple(h[0]), tuple(h[1]), (0, 0, 255), 2)

    processedImg = frame.copy()

create = None

frameno = 0

filename = "newVideo.mp4"

yolo = "yolov3/"

opname = "output2.avi"

cap = cv2.VideoCapture(filename)

time1 = time.time()

while(True):

    ret, frame = cap.read()

    if not ret:

        break

    current_img = frame.copy()

    current_img = imutils.resize(current_img, width=480)

    video = current_img.shape

    frameno += 1

    if(frameno%2 == 0 or frameno == 1):

        Setup(yolo)

        ImageProcess(current_img)

        Frame = processedImg

        cv2.imshow("Image", Frame)

        if create is None:

            fourcc = cv2.VideoWriter_fourcc(*'XVID')

            create = cv2.VideoWriter(opname, fourcc, 30, (Frame.shape[1], Frame.shape[0]), True)

    create.write(Frame)

    if cv2.waitKey(1) & 0xFF == ord('s'):

        break

time2 = time.time()

print("Completed. Total Time Taken: {} minutes".format((time2-time1)/60))

cap.release()

cv2.destroyAllWindows()



ENTRE EM CONTATO COM A LOJAMUNDI.

Assine nossa Newsletter! É gratuito!

Cadastre seu nome e email para receber novidades e materiais gratuitos da Lojamundi.