Já imaginou poder criar um sistema de vigilância aérea usando um drone comprado no Mercado Livre, um notebook e técnicas de Deep Learning aplicadas a problemas de Visão Computacional?
Neste vídeo do YouTube, eu quero mostrar a você não apenas como implementei essa arquitetura, mas também mostrar os vídeos desse projeto na prática. E, para combinar com um sistema de vigilância aérea, nada melhor que realizar alguns voos dentro de uma Base Aérea da Força Aérea Brasileira.
Assista ao vídeo para aprender como que treinei uma rede neural profunda no TensorFlow e construir um pipeline capaz de detectar objetos a partir de um vídeo transmitido em tempo real, com um drone em pleno voo.
Aplicações possíveis para drones
O drone é um veículo aéreo não tripulado (ou VANT) controlado remotamente, que pode ser utilizado para vários fins, como defesa, segurança, lazer e até entrega de mercadorias. Fato é que, conforme as opções no mercado se tornam cada vez mais acessíveis, aumenta-se a gama de aplicações possíveis.
Filmagens de casamentos, flagras de famosos por paparazzo, manutenção de linhas de alta tensão, vigilância aérea, sem contar o voo meramente recreativo (o famoso “voar pelo prazer de voar”): são muitas as aplicações do drone.
Esse objeto vem ganhando importância por causa de suas características: pouca massa (feito de materiais de baixa densidade), tamanho pequeno (fácil acessar muitos ambientes), grande alcance e, principalmente, alta autonomia. Uma vez que eu era piloto e tinha um drone novinho para testar, pensei por que não utilizar Deep Learning para implementar um caso prático?
Como Major Aviador da Reserva e piloto da Força Aérea por 16 anos, já realizei diversas missões conhecidas como Vigilância Aérea Perimetral.
Especificamente, algumas das milhares de horas de voo da minha vida operacional foram realizando missões de alerta em determinadas localidades, como Base Aérea de Natal (BANT), Centro de Lançamento da Barreira do Inferno (CLBI), Centro de Lançamento de Alcantra (CLA), Campo de Provas Brigadeiro Velloso e Academia da Força Aérea (AFA). Por serem áreas restritas, esse tipo de missão tinha como fim verificar se havia a presença de pessoas não autorizadas naquele local.
E o que um drone tem a ver com tudo isso?
Eu não sei se você sabe, mas o custo da hora de voo de uma aeronave militar é extremamente elevado. Se você somar a essa equação todos os profissionais envolvidos, o tamanho das áreas a serem vigiadas e a quantidade de missões que a FAB tem que cumprir constantemente, fica fácil entender a minha motivação ao desenvolver uma solução baseada em drones comerciais.
Características do DJI Mavic Air 2
O modelo de drone que eu possuo é o DJI Mavic Air 2, um equipamento fantástico para se pilotar. Pesando apenas 570 gramas, o Mavic Air 2 consegue voar por até 34 minutos, alcança mais de 18 quilômetros de distância e um teto máximo de voo (altura máxima) de 5 mil metros.
Além disso, pode atingir uma velocidade de quase 70 km/h nas condições ideais, o equipamento grava vídeos na resolução de 4k (memória interna ou cartão de memória) e ainda é capaz de realizar a transmissão de vídeo com qualidade 720p a 30 fps (quadros por segundo).
E outra coisa fantástica do modelo é a possibilidade de acoplar seu celular ao controle (joystick) e receber as informações do voo diretamente na sua tela.
Visão geral do projeto
O pipeline de dados, os aplicativos e a escolha da arquitetura foram escolhidas por atenderem os equipamentos que eu dispunha naquele momento. Caso você deseje replicar o projeto, eu recomendo fazer uma pesquisa para ver se eles também seriam as opções mais adequadas para você.
Por exemplo, eu sei que existem drones capazes de transmitir as imagens diretamente para o computador, sem utilizar o celular como intermediário. Entretanto, esse não foi o meu caso, já que a funcionalidade RTMP não está presente no software nativo do drone Mavic Air 2.
Então, para resolver esse problema, uma vez que as imagens da câmera do drone eram exibidas na tela do meu celular enquanto eu pilotava, instalei o app “Prism Live Studio” (disponível gratuitamente para iPhone e Android). Com esse aplicativo, eu conseguiria transmitir a própria tela do celular (por meio do protocolo RTMP) para o computador.
Porém, antes de qualquer coisa, eu precisava que todos equipamentos estivessem conectados em uma mesma rede, com IPs locais atribuídos a cada um deles. Isso foi fácil, pois bastou criar um hotspot a partir do celular e conectar o Macbook nesse Wi-Fi.
Porém, o fato de todos estarem conectados na mesma rede não significa que você consiga acessar cada um deles.
Para conseguir receber a transmissão da tela do meu celular, instalei NGINX no Macbook a partir do Homebrew (gerenciador de pacotes do Mac, assim como o APT ou YUM, do Linux), o que permitiu criar um RTMP server na máquina.
Após essas etapas, provavelmente você deve conseguir enxergar todo mundo na mesma rede. Para verificar se o firewall não está bloqueando ou estão em hosts diferentes, faça um script simples e tente receber a transmissão da tela do celular com o OpenCV.
Implementando YOLO com TensorFlow
A arquitetura utilizada para o projeto foi a conhecida YOLO, treinada em cima do COCO dataset. Dependendo da sua aplicação, é possível escolher se deseja manter todas as classes possíveis ou selecionar apenas algumas delas (carros, pessoas, motos etc.).
# REFERÊNCIAS # # Implementação da arquitetura YOLO baseada no artigo YOLO object detection with OpenCV # do Adrian Rosebrock, autor do livro Deep Learning for Computer Vision # importar os pacotes necessários import numpy as np import argparse import os import cv2 import time from imutils.video import VideoStream from imutils.video import FPS # constantes do modelo CONFIDENCE_MIN = 0.4 NMS_THRESHOLD = 0.2 MODEL_BASE_PATH = "yolo-coco" # receber os argumentos para o script ap = argparse.ArgumentParser() ap.add_argument("-i", "--input", required=True, help="Endereço do streaming do drone") streaming_path = vars(ap.parse_args())['input']
A primeira parte do código apenas importa os pacotes usados no detector de objeto (baseado na API do TensorFlow) e define constantes relativas à sensibilidade do modelo.
Como o que se busca aqui é o processamento real-time do streaming, e não o carregamento de um arquivo estático de algum vídeo, importamos os objetos VideoStream
e FPS
dessa biblioteca fantástica que é o imutils
.
# extrair os nomes das classes a partir do arquivo print("[+] Carregando labels das classes treinadas...") with open(os.path.sep.join([MODEL_BASE_PATH, 'coco.names'])) as f: labels = f.read().strip().split("\n") # gerar cores únicas para cada label np.random.seed(42) colors = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8") # carregar o modelo treinado YOLO (c/ COCO dataset) print("[+] Carregando o modelo YOLO treinado no COCO dataset...") net = cv2.dnn.readNetFromDarknet( os.path.sep.join([MODEL_BASE_PATH, 'yolov3.cfg']), os.path.sep.join([MODEL_BASE_PATH, 'yolov3.weights'])) # extrair layers não conectados da arquitetura YOLO ln = net.getLayerNames() ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
Após executado, o script irá carregar o próprio modelo treinado da YOLO, com seus pesos e configurações passadas dentro do arquivo yolov3.cfg
, os nomes (labels) que forem definidos e extrair as camadas não conectadas da arquitetura.
Vale lembrar que no meu repositório do Github, contendo o código completo, eu não disponibilizei o modelo devido ao seu tamanho. No entanto, como você pode perceber, você consegue baixá-lo chamando o método cv2.dnn.readNetFromDarknet()
.
Se você já implementou algum modelo Deep Learning para fazer classificação a partir de arquivos locais, você provavelmente manipulou cada um dos quadros carregados pelo OpenCV. Na prática, isso significa que um vídeo com taxa de 30 fps possui 30 imagens por segundo e que todas elas passarão pelos neurônios do modelo.
Porém, quando o que buscamos é uma análise em tempo-real, provavelmente não conseguiríamos acompanhar essa frame rate sem um equipamento poderoso. É um trade-off, claro, onde iremos sacrificar a taxa fps para ganhar agilidade.
# iniciar a recepção do streaming vs = VideoStream(streaming_path).start() time.sleep(1) fps = FPS().start() print("[+] Iniciando a recepção do streaming via RTMP...")
E para conseguir esse processamento, instanciamos um objeto VideoStream(streaming_path)
, sendo que o argumento passado será o endereço do drone via protocolo RTMP.
Uma vez que estaremos adquirindo os quadros a partir do endereço de rede informado, basta iterarmos sobre esses. Dependendo da máquina que você tenha, poderá conseguir uma fps maior ou menor.
# iterar sobre os frames do streaming while True: frame = vs.read() # caso se deseje redimensionar os frames # frame = cv2.resize(frame, None, fx=0.2, fy=0.2) # capturar a largura e altura do frame (H, W) = frame.shape[:2] # construir um container blob e fazer uma passagem (forward) na YOLO blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False) net.setInput(blob) layer_outputs = net.forward(ln) # criar listas com boxes, nível de confiança e ids das classes boxes = [] confidences = [] class_ids = [] for output in layer_outputs: for detection in output: scores = detection[5:] class_id = np.argmax(scores) confidence = scores[class_id] # filtrar pelo threshold da confiança if confidence > CONFIDENCE_MIN and class_id in [0, 1, 2, 3]: box = detection[0:4] * np.array([W, H, W, H]) (center_x, center_y, width, height) = box.astype("int") x = int(center_x - (width / 2)) y = int(center_y - (height / 2)) boxes.append([x, y, int(width), int(height)]) confidences.append(float(confidence)) class_ids.append(class_id)
De maneira resumida e dentro daquilo que se é possível explicar em um artigo de blog, o código acima identifica as dimensões dos quadros transmitidos, constrói um “container blob” e realiza uma passagem (forward) pela YOLO já carregada com pesos treinados no COCO dataset.
Caso você queira entender mais a fundo ou aprender Deep Learning do zero, clique aqui e conheça a Escola de Data Science.
Para armazenar as possíveis detecções, são criadas listas para armazenar as coordenadas dos retângulos, a confiança da previsão e os identificadores das classes associadas a cada uma das previsões.
# eliminar ruído e redundâncias aplicando non-maxima suppression new_ids = cv2.dnn.NMSBoxes(boxes, confidences,CONFIDENCE_MIN, NMS_THRESHOLD) if len(new_ids) > 0: for i in new_ids.flatten(): (x, y) = (boxes[i][0], boxes[i][1]) (w, h) = (boxes[i][2], boxes[i][3]) # plotar retângulo e texto das classes detectadas no frame atual color_picked = [int(c) for c in colors[class_ids[i]]] cv2.rectangle(frame, (x, y), (x + w, y + h), color_picked, 2) text = "{}: {:.4f}".format(labels[class_ids[i]], confidences[i]) cv2.putText(frame, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color_picked, 2) # exibir o frame atual cv2.imshow('frame', frame) # sair, caso seja pressionada a tecla ESC c = cv2.waitKey(1) if c == 27: break # atualiza o fps fps.update() # eliminar processos e janelas fps.stop() cv2.destroyAllWindows() vs.stop()
Nessa última parte, eu tento eliminar o ruído e as redundâncias com um filtro adequado e desenho um retângulo para cada previsão que tenha ficado acima do threshold CONFIDENCE_MIN, NMS_THRESHOLD
Enquanto o usuário não cancelar o processamento, nosso script continuará iterando pelos quadros que estão sendo transmitidos pelo drone.
Modelo treinado, decolagem autorizada!
Com o modelo treinado e carregado, não esqueça de iniciar o servidor baseado no NGINX (caso ele não esteja ativo por padrão).
Você precisará ainda do endereço do seu celular na rede para informar como argumento na hora que for executar o script (não esqueça que, como o meu drone não tem a capacidade de transmitir diretamente pela rede, estamos capturando a visão do drone a partir da tela do celular).
Após começar a monitorar a porta 1935 e capturar os pacotes RTMP com o OpenCV, os frames começarão a passar pela YOLO, e você verá os primeiros objetos aparecerem na tela, com suas bounding boxes correspondentes.
Recapitulando as principais etapas do projeto, seguimos a sequência descrita abaixo.
- Criei um hotspot a partir do celular e conectei meu notebook nele, mesma rede.
- Instalei NGINX no notebook para criar um RTMP Server.
- Fiz streaming da tela do celular para o notebook, usando o app de celular PRISM Live.
- Executei um script Python para monitorar a porta 1935 e capturar os pacotes RTMP com o OpenCV.
- Passei os frames na arquitetura deep learning YOLO, treinada no COCO dataset.
Caso você deseje, o código-fonte está disponível nesse meu repositório do Github.
Inscreva-se na Escola de Data Science
Obviamente, não seria possível explicar um projeto desses em apenas um post. Mas caso você queira aprender Deep Learning e Visão Computacional de verdade, eu o convido a se inscrever na Escola de Data Science.
E eu não estou falando apenas de aprender teoria; estou falando sobre aprender com projetos práticos (como o deste artigo) e exemplos do mundo real.
Na Escola de Data Science, além de 150 horas de aulas exclusivas, você ganhará acesso um curso completo de Visão Computacional e aulas detalhadas sobre Deep Learning, e vai aprender como criar esse mesmo modelo de hoje.
Ah, e você tem 7 dias de garantia incondicional, tendo acesso a 100% do conteúdo e poderá, então, conhecer a qualidade do ensino Sigmoidal.
Clique no link abaixo e se inscreva na Escola de Data Science.