Pessoas contando com OpenCV, Python e Ubidots
O processamento digital de imagens (DIP) está crescendo rapidamente, em grande parte graças ao aumento das técnicas de aprendizado de máquina que os desenvolvedores podem acessar através da nuvem. Ser capaz de processar imagens digitais na nuvem ignora quaisquer requisitos de hardware dedicados, tornando o DIP a escolha certa. Sendo o método mais barato e versátil para processamento de imagens, o DIP encontrou aplicações amplas. Uma das mais comuns é a detecção e contagem de pedestres – uma métrica útil para aeroportos, estações ferroviárias, lojas de varejo, estádios, eventos públicos e museus.
Os contadores de pessoas tradicionais e prontos para uso não são apenas caros – os dados que eles geram geralmente estão vinculados a sistemas proprietários que limitam suas opções de extração de dados e otimização de KPI. Por outro lado, um DIP incorporado usando sua própria câmera e SBC não apenas economizará tempo e dinheiro, mas também lhe dará a liberdade de adaptar seu aplicativo aos KPIs de seu interesse e extrair insights da nuvem que de outra forma não seriam possíveis. .
Usar a nuvem para habilitar seu aplicativo DIP IoT permite uma funcionalidade geral aprimorada. Com recursos aumentados na forma de visualizações , relatórios, alertas e referências cruzadas de fontes de dados externas (como clima, preços de fornecedores em tempo real ou sistemas de gerenciamento de negócios), o DIP oferece aos desenvolvedores a liberdade que desejam.
Imagine um dono de mercearia com uma geladeira de sorvetes: ele quer acompanhar a quantidade de pessoas que passam e selecionam um produto, bem como a quantidade de vezes que a porta foi aberta e a temperatura interna da geladeira. A partir apenas destes poucos dados, um retalhista pode executar análises de correlação para melhor compreender e otimizar os preços dos seus produtos e o consumo geral de energia do frigorífico.
Para iniciar sua aplicação de processamento digital de imagens, Ubidots criou o seguinte tutorial do Sistema de Contagem de Pessoas usando OpenCV e Python para analisar o número de pessoas em uma determinada área . Expanda seus aplicativos além da contagem de pessoas com os recursos adicionais da IoT Ubidots . Aqui, você pode ver um dashboard de contagem de pessoas ao vivo construído com Ubidots .
Neste artigo, revisaremos como implementar uma sobreposição DIP simples para criar um contador de pessoas usando OpenCV e Ubidots . Este exemplo funciona melhor com qualquer distribuição baseada em Linux e também em Raspberry Pi, Orange Pi ou sistemas embarcados semelhantes.
Para dúvidas adicionais sobre integração, entre em contato com o suporte Ubidots e saiba como sua empresa pode aproveitar as vantagens dessa tecnologia de valor agregado.
Índice:
- Requisitos de aplicação
- Codificação – 8 subseções
- Teste
- Criando seu Dashboard
- Resultados
1) Requisitos de aplicação
- Qualquer Linux embarcado com uma versão derivada do Ubuntu
- Python 3 ou mais recente instalado em seu sistema operacional.
- OpenCV 3.0 ou superior instalado em seu sistema operacional. Se estiver usando Ubuntu ou seus derivados, siga o tutorial oficial de instalação ou execute o comando abaixo:
pip instalar opencv-contrib-python
- Depois de instalar o Python 3 e o OpenCV com sucesso, verifique sua configuração executando este pequeno trecho de código (basta digitar 'python' em seu terminal):
importar cv2 cv2.__versão__
Você deverá obter uma tela de impressão com sua versão do OpenCV:
- Instale o Numpy seguindo as oficiais ou apenas executando o comando abaixo:
pip instalar numpy
- Instale imutils
pip instalar imutils
- Solicitações de instalação
solicitações de instalação pip
2) Codificação
Toda a rotina de detecção e envio de dados pode ser encontrada aqui . Para uma melhor explicação de nossa codificação, dividiremos o código em oito seções para explicar melhor cada aspecto do código para seu melhor entendimento.
Seção 1:
de imutils.object_detection import non_max_suppression import numpy as np import imutils import cv2 import requests import time import argparse URL_EDUCATIONAL = "http://things. ubidots .com" URL_INDUSTRIAL = "http://industrial.api. ubidots .com" INDUSTRIAL_USER = True # Defina como False se você for um usuário educacional TOKEN = "...." # Coloque aqui seu Ubidots TOKEN DEVICE = "detector" # Dispositivo onde será armazenado o resultado VARIABLE = "people" # Variável onde será armazenado o resultado # Opencv SVM pré-treinado com pessoas HOG apresenta HOGCV = cv2.HOGDescriptor() HOGCV.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
Na Seção 1 importamos as bibliotecas necessárias para implementar nosso detector, imutils é uma ferramenta de biblioteca útil para DIP e nos permitirá realizar diferentes transformações de nossos resultados, cv2 é nosso wrapper OpenCV Python, as solicitações nos permitirão enviar nossos dados/resultados HTTP para Ubidots e argparse nos permitirá ler comandos de nosso terminal de comando dentro de nosso script.
IMPORTANTE: Não se esqueça de atualizar este código com o TOKEN da sua conta Ubidots , e se você for um usuário educacional , não se esqueça de definir o INDUSTRIAL_USER
como FALSE
.
Após a importação da biblioteca, inicializaremos nosso descritor de objetos orientados ao histograma. HOG, resumindo, esta é uma das técnicas mais populares para detecção de objetos e já foi implementada em diversas aplicações com resultados bem-sucedidos e, para nossa sorte, o OpenCV já implementou de forma eficiente a combinação do algoritmo HOG com uma máquina de vetores de suporte , ou SVM, que é uma técnica clássica de aprendizado de máquina para fins de previsão.
Esta declaração: cv2.HOGDescriptor_getDefaultPeopleDetector()
chama o modelo pré-treinado para detecção de pessoas do OpenCV e alimentará nossa função de avaliação de recursos de máquina de vetores de suporte.
Seção 2
def detector(image): ''' @image é um array numpy ''' image = imutils.resize(image, width=min(400, image.shape[1])) clone = image.copy() (rects, pesos) = HOGCV.detectMultiScale(image, winStride=(8, 8), padding=(32, 32), scale=1.05) # Aplica supressão não máxima do pacote imutils para início sobreposto # boxes rects = np.array ([[x, y, x + w, y + h] para (x, y, w, h) em retos]) resultado = non_max_suppression(rects, probs=None,lapThresh=0,65) retornar resultado
A função detector()
Para evitar problemas de desempenho, redimensionamos a imagem usando imutils e depois chamamos o detectMultiScale()
de nosso objeto HOG. O método de detecção multiescala permite-nos então analisar a imagem e saber se existe uma pessoa usando o resultado da classificação do nosso SVM. Os parâmetros deste método estão além do escopo deste tutorial, mas se você quiser saber mais consulte a documentação oficial do OpenCV ou confira a ótima explicação de Adrian Rosebrock .
A análise HOG irá gerar algumas caixas de captura (objetos detectados), mas às vezes essas caixas se sobrepõem causando falsos positivos ou erros de detecção. Para contornar essas confusões, usaremos o utilitário de supressão não-maxima da imutils para remover as caixas sobrepostas – conforme mostrado abaixo:
Imagens reproduzidas de https://www.pyimagesearch.com
Seção 3:
def localDetect(image_path): result = [] image = cv2.imread(image_path) if len(image) <= 0: print("[ERROR] não foi possível ler sua imagem local") return result print("[INFO] Detectando people") result = detector(image) # mostra o resultado para (xA, yA, xB, yB) no resultado: cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0 ), 2) cv2.imshow("resultado", imagem) cv2.waitKey(0) cv2.destroyAllWindows() return (resultado, imagem)
Agora, nesta parte do nosso código, devemos definir uma função para ler uma imagem de um arquivo local e detectar qualquer pessoa nela. Para fazer isso, você verá que simplesmente chamei a função detector() e adicionei um loop simples para pintar as caixas redondas do detector. Retorna o número de caixas detectadas e a imagem com a detecção pintada. Em seguida, basta recriar o resultado em uma nova janela do sistema operacional.
Seção 4:
def cameraDetect(token, dispositivo, variável, sample_time=5): cap = cv2.VideoCapture(0) init = time.time() # O tempo de amostragem permitido para Ubidots é 1 ponto/segundo se sample_time < 1: sample_time = 1 while( True): # Captura quadro a quadro ret, frame = cap.read() frame = imutils.resize(frame, width=min(400, frame.shape[1])) result = detector(frame.copy() ) # mostra o resultado para (xA, yA, xB, yB) em resultado: cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2) cv2.imshow( 'frame', frame) # Envia resultados if time.time() - init >= sample_time: print("[INFO] Enviando resultados reais do frame") # Converte a imagem para base 64 e a adiciona ao contexto b64 = convert_to_base64( frame) context = {"image": b64} sendTo Ubidots (token, dispositivo, variável, len(resultado), context=context) init = time.time() if cv2.waitKey(1) & 0xFF == ord(' q'): break # Quando tudo estiver pronto, libere a captura cap.release() cv2.destroyAllWindows() def convert_to_base64(image): image = imutils.resize(image, width=400) img_str = cv2.imencode('.png ', imagem)[1].tostring() b64 = base64.b64encode(img_str) return b64.decode('utf-8')
Semelhante à função da Seção 3 , esta da Seção 4 chamará o detector() e as caixas de pintura e a imagem será recuperada diretamente da webcam usando o método VideoCapture() Também modificamos ligeiramente o oficial para obter imagens da câmera e enviar os resultados para uma Ubidots a cada “n” segundos (a sendTo
Ubidots
()
será revisada posteriormente neste tutorial). A função convert_to_base64()
irá converter sua imagem para uma string de base 64, esta string é muito importante para observar nossos resultados em Ubidots usando código JavaScript dentro de um widget HTML Canvas.
Seção 5:
def detectPeople(args): image_path = args["image"] camera = True if str(args["camera"]) == 'true' else False # Rotina para ler a imagem local if image_path != Nenhum e não câmera: print ("[INFO] Caminho da imagem fornecido, tentativa de leitura da imagem") (resultado, imagem) = localDetect(image_path) print("[INFO] enviando resultados") # Converte a imagem para base 64 e a adiciona ao contexto b64 = convert_to_base64(image) context = {"image": b64} # Envia o resultado req = sendTo Ubidots (TOKEN, DEVICE, VARIABLE, len(result), context=context) if req.status_code >= 400: print("[ERROR ] Não foi possível enviar dados para Ubidots ") return req # Rotina para ler imagens da webcam if camera: print("[INFO] lendo imagens da câmera") cameraDetect(TOKEN, DEVICE, VARIABLE)
Este método tem como objetivo inserir os argumentos através do seu terminal e acionar uma rotina que procura pessoas em um arquivo de imagem armazenado localmente ou através da sua webcam.
Seção 6:
def buildPayload(variável, valor, contexto): return {variável: {"valor": valor, "contexto": contexto}} def sendTo Ubidots (token, dispositivo, variável, valor, contexto={}, industrial=True): # Constrói o endpoint url = URL_INDUSTRIAL if industrial else URL_EDUCATIONAL url = "{}/api/v1.6/devices/{}".format(url, device) payload = buildPayload(variable, value, context) headers = {"X -Auth-Token": token, "Content-Type": "application/json"} tentativas = 0 status = 400 enquanto status >= 400 e tentativas <= 5: req = requests.post(url=url, headers=headers , json=payload) status = req.status_code tentativas += 1 time.sleep(1) return req
Essas duas funções da Seção 6 são o caminho para enviar seus resultados ao Ubidots para compreensão e visualização de seus dados. A primeira função def buildPayload
cria a carga útil dentro da solicitação, enquanto a segunda função def sendTo
Ubidots
recebe seus Ubidots (TOKEN , a variável e os rótulos do dispositivo) para armazenar os resultados. Que neste caso é o comprimento das caixas redondas detectadas pelo OpenCV. Opcionalmente, um contexto também pode ser enviado para armazenar os resultados como uma imagem base64 para que possa ser recuperada posteriormente.
Seção 7:
def argsParser(): ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default=None, help="caminho para o diretório do arquivo de teste de imagem") ap.add_argument("-c" , "--camera", default=False, help="Definir como verdadeiro se desejar usar a câmera") args = vars(ap.parse_args()) return args
Agora na Seção 7 , estamos chegando ao final de nossa análise de código. A função argsParser()
simplesmente analisa e retorna como um dicionário os argumentos passados através do seu terminal para o nosso script. Haverá dois argumentos dentro do analisador:
- imagem: O caminho para o arquivo de imagem dentro do seu sistema
- camera: Uma variável que se definida como 'true' chamará o método cameraDetect().
Seção 8:
def main(): args = argsParser() detectPeople(args) if __name__ == '__main__': main()
A seção 8 e a parte final do nosso código é a main()
que simplesmente chama os argumentos do console e inicia a rotina especificada.
Não se esqueça, todo o código pode ser extraído do Github aqui .
3) Teste
Abra o seu processador de texto favorito (sublime-text, notepad, nano, etc) e copie e cole o código completo disponível aqui . Atualize o código com seu Ubidots TOKEN e salve seu arquivo como “peopleCounter.py”.
Com seu código salvo corretamente, vamos testar as próximas quatro imagens aleatórias selecionadas do conjunto de dados público Caltech e Pexels:
Para analisar essas imagens, primeiro você deve armazenar as imagens em seu laptop ou PC e rastrear o caminho para analisar as imagens.
python peopleCounter.py PATH_TO_IMAGE_FILE
No meu caso, armazenei as imagens em um caminho denominado ‘conjunto de dados’. Para executar um comando válido, execute o comando abaixo, mas com o caminho da sua imagem.
python peopleCounter.py -i dataset/image_1.png
Se você deseja tirar imagens de sua câmera em vez de um arquivo local, basta executar o comando abaixo:
python peopleCounter.py -c verdadeiro
Resultados do teste:
Além dessas verificações de testes, você também verá, em tempo real, os resultados desses testes armazenados na sua conta Ubidots :
4) Criando seu Dashboard
Usaremos um HTML Canvas para visualizar em tempo real nossos resultados, este tutorial não é destinado a widgets de canvas HTML, então se você não sabe como utilizá-los consulte os artigos abaixo:
- Exemplos de widgets de tela
- Demonstração introdutória do widget Canvas
- Canvas Criando um widget em tempo real
Usaremos o exemplo básico em tempo real com pequenas modificações para visualizar nossas imagens. Abaixo você pode ver o trecho de código do widget
HTML
<img id="img" width="400px" height="auto"/>
JS
soquete var; var srv = " ubidots "; // var srv = "app. ubidots .com:443" // Remova o comentário desta linha se você for um usuário educacional var VAR_ID = "5ab402dabbddbd3476d85967"; // Coloque aqui seu var Id var TOKEN = "" // Coloque aqui seu token $ ( document ).ready(function() { function renderImage(imageBase64){ if (!imageBase64) return; $ ('#img'). attr('src', 'data:image/png;base64, ' + imageBase64); // Função para recuperar o último valor, ela é executada apenas uma vez function getDataFromVariable(variable, token, retorno de chamada) { var url = 'https: ubidots .com/api/v1.6/variables/' + variável + '/valores'; -Type': 'application/json' }; $ .ajax({ url: url, método: 'GET', cabeçalhos: cabeçalhos, dados: { page_size: 1 }, sucesso: function (res) { if (res.results.length > 0){ renderImage(res.results[0].context.image } callback() } }); o soquete do servidor = io.connect("https://"+ srv, {path: '/notifications'}); callback) { // Publica o ID da variável que deseja escutar socket.emit('rt/variables/id/last_value', { variável: variável }); // Escuta alterações socket.on('rt/variables/' + variável + '/last_value', callback); subscribedVars.push(variável); }; // Função para cancelar a assinatura para escuta var unSubscribeVariable = function (variable) { socket.emit('unsub/rt/variables/id/last_value', { variável: variável }); var pst = subscribedVars.indexOf(variável); if (pst !== -1){ subscribedVars.splice(pst, 1); } }; var connectSocket = function (){ // Implementa a conexão do soquete socket.on('connect', function(){ console.log('connect'); socket.emit('authentication', {token: TOKEN}); } ); window.addEventListener('online', function () { console.log('online'); socket.emit('autenticação', {token: TOKEN}); }); socket.on('autenticado', function () { console.log('autenticado'); subscribedVars.forEach(function (variable_id) { socket.emit('rt/variables/id/last_value', { variável: variável_id }) ; } }); } /* Rotina Principal */ getDataFromVariable(VAR_ID, TOKEN, function(){ connectSocket(); }); conectarSocket(); //connectSocket(); // Assine a variável com seu próprio código. subscribeVariable(VAR_ID, function(value){ var parsedValue = JSON.parse(value); console.log(parsedValue); // $ ('#img').attr('src', 'data:image/png;base64 , ' + parsedValue.context.image); renderImage(parsedValue.context.image }) });
Não esqueça de colocar o TOKEN e o ID da variável no início do trecho de código.
BIBLIOTECAS DE TERCEIROS
Adicione as próximas bibliotecas de terceiros:
Depois de salvar seu widget, você deverá obter algo como o mostrado abaixo:
5) Resultados
Você pode assistir aos dashboards com os resultados neste link .
Neste artigo, exploramos como criar um IoT usando DIP (processamento de imagem), OpenCV e Ubidots . Com esses serviços, seu aplicativo DIP é muito mais preciso do que o PIR ou outros sensores ópticos ao detectar e identificar diferenças entre pessoas, lugares ou coisas – proporcionando um contador de pessoas eficiente sem toda a estática da manipulação inicial de dados.
Deixe-nos saber o que você pensa deixando Ubidots em nossos fóruns da comunidade ou conecte-se ao Ubidots simplesmente pelo Facebook , Twitter ou Hackster .
Feliz hacking!