ÍNDICE DE CONTEÚDO
Durante o 1° IoT RoadShow 2015 da Intel em São Paulo, participei com a equipe do Embarcados na realização do projeto MonitorAKI, que fez uso primordialmente de Visão Computacional por meio da biblioteca OpenCV. Além, é claro, de enviar mensagens via WhatsApp, conforme demonstrado em outro artigo meu.
Uma das questões que mais me pediram foi acerca de como configurar a Intel Edison para operar com OpenCV, e ainda mais… Como trabalhar com Visão Computacional com uma placa que nem sequer possui saída de vídeo? Isso é realmente uma limitação…? Não! Se não tem saída de vídeo, que tal usar Rede? Ainda mais…Wi-Fi!
Quando se trabalha com Visão Computacional é muito comum a necessidade de realizar ajustes em algoritmos e parâmetros utilizados, sem contar a quantidade de erros que ocorrem até você acertar o ponto no seu programa. Sendo assim, é crucial verificar o resultado do processo, que normalmente se dá na forma de imagem, ou vídeo.
Em uma placa que não possui saída de vídeo ou ambiente desktop, normalmente chamada na literatura de headless, a técnica mais comum era programar o algoritmo para salvar a imagem ou vídeo no sistema de arquivos da placa, e depois remotamente fazer o download dos arquivos para verificar o resultado. Nem preciso dizer que esse processo era demorado, né?
Não somente em se tratando de placas que não possuem saída de vídeo fica o ponto do meu trabalho. Ao se encerrar a interface gráfica e eliminar alguns processos correlacionados, o sistema como um todo fica com mais recursos de memória e processador disponíveis para a execução de algoritmos de Visão Computacional, o que pode levar a um melhor desempenho da placa como um todo.
E se você quiser fazer um robô com câmera? E quiser ver o que o robô… vê? Pela rede?
Pensando nesses detalhes apresentados, escrevi em coautoria com o Thiago Lima e o Prof. Dr. Evandro Luis Linhari Rodrigues o artigo A low cost embedded system with computer vision and video streaming (Um sistema embarcado de baixo custo com visão computacional e transmissão de vídeo), cujo pôster foi apresentado no Workshop de Visão Computacional 2015 (WVC2015) em São Carlos – SP. E agora, demonstro aqui a implementação prática do trabalho, fazendo uso da linguagem Python.
Um pouco de Visão Computacional
Visão Computacional compreende todo um conjunto de técnicas capazes de fazer um computador interpretar imagens de uma maneira tão próxima quanto um ser humano é capaz. Dessa forma, com a aplicação de tais técnicas é possível então fazer um computador reconhecer suas mãos, reconhecer seus gestos, reconhecer seu rosto, realizar sua identificação por impressão digital, etc.
Já usou uma câmera digital? Viu que ela normalmente identifica os rostos presentes na imagem? Com certeza ela implementa o algoritmo de Viola-Jones, que detecta padrões numa imagem correspondentes ao rosto humano. Como exemplo, veja a imagem abaixo, demonstrando um exemplo de detecção de faces.
Viola-Jones é um dos muitos algoritmos existentes, e a cada dia surgem mais, frutos de intensa pesquisa na área.
Não somente em câmeras digitais estão presentes tais algoritmos. Celulares SmartPhones usam câmeras frontais para acompanhar os olhos do usuário e controlar eventos para leitura e exibição de vídeo, televisores SmartTVs já reconhecem gestos do usuário e temos inclusive o famoso Kinect, usado nos consoles Xbox para controle por gestos e movimentos.
Você também pode escrever programas que façam uso de visão computacional. Para isso, uma das principais bibliotecas disponíveis é a OpenCV, originalmente criada pela Intel em 1999, e agora mantida por toda uma comunidade de desenvolvedores, com suporte a linguagens tais como C, C++, Python, Java, dentre outras.
Basicamente, toda imagem é uma matriz, cujas linhas e colunas endereçam os pixels da referida imagem. Por sua vez, como estes pixels serão referenciados irá depender se a imagem está em escala de cinza, colorida, etc.
Podemos unicamente realizar algum processo sobre a imagem capturada, e não necessariamente algo de maior inteligência. Dessa forma, estamos na área de Processamento Digital de Imagens. É nesta área que entram técnicas para remover ruídos de imagens, alterar propriedades de brilho, contraste, etc. Ou seja, você aperfeiçoa a imagem ou alguma propriedade importante nela. Se você usa o Instagram e fica brincando com filtros de imagem, sem saber, está usando de técnicas de Processamento Digital de Imagens.
OpenCV pode ser facilmente instalado e configurado em SBCs tais como a Intel Edison, e juntamente com uma Webcam, por exemplo, torna-se possível criar programas capazes de controlar um robô seguidor de linha por imagem, ou um sistema que reconhece gestos e movimentos, ou até mesmo um sistema de segurança que detecta movimento na imagem, tira foto e envia por e-mail, por exemplo. As possibilidades… são infinitas. Mãos à obra?
Configuração da Intel Edison
Antes de tudo, tomarei por base uma placa Intel Edison previamente configurada com Poky Linux da Intel. Caso você queira reconfigurar a sua, pode ver este guia do Diego Sueiro ou o Guia da Intel (inglês).
Agora, certifique-se de duas coisas:
- Sua Intel Edison possui acesso à rede local e à Internet;
- Sua Intel Edison possui acesso remoto via SSH.
Para o devido funcionamento do sistema, iremos precisar da biblioteca OpenCV devidamente instalada, e da biblioteca NumPy, específica para Python. Em se tratando da Intel Edison, é possível instalar a biblioteca OpenCV para Python somente. Caso queira usar com alguma linguagem tal como C ou C++, será necessário compilar dos fontes.
Para instalar as bibliotecas necessárias, realize os seguintes comandos em um terminal de console (com acesso local ou remoto):
1 2 |
# opkg install python-opencv # opkg install python-numpy |
Feito o processo, vamos conferir se as bibliotecas foram devidamente instaladas. Para isso, inicie o console Python por meio do comando:
1 |
# python |
E agora verifique se as bibliotecas foram devidamente instaladas digitando:
1 2 |
>> import cv2 >> import numpy |
E como saber se está tudo OK?! Simples: Se não aparecer nenhuma mensagem de erro, então tá OK!
Para sair, digite quit() no console Python. Veja o resultado na imagem adiante.
No meu caso, estou usando uma Câmera USB. Certifique-se que sua Intel Edison esteja reconhecendo devidamente a sua câmera USB. Para verificar isso, digite o seguinte comando no console Linux:
1 |
# ls /dev/video* |
Se aparecer “/dev/video0”, então está tudo OK. A princípio… Veja como ficou a minha tela:
Caso a sua câmera esteja conectada na USB da placa-base da Intel Edison, e mesmo assim não esteja aparecendo, certifique-se que a USB da Intel Edison esteja configurada para Host.
Não tem a Intel Edison? Todos os passos aqui mostrados se aplicam também na Intel Galileo, tanto Gen1 quanto Gen2, como demais SBCs com Linux Embarcado, tal como a Raspberry Pi. Bastando, é claro, ter Python 2.7 instalado, com OpenCV e Numpy.
Arquitetura do Sistema
Como a Intel Edison não possui saída de vídeo, e muito menos um ambiente gráfico em execução, para visualizar em “tempo real” a execução de algoritmos de Visão Computacional iremos então bolar um sistema com as seguintes propriedades:
- Linguagem de programação utilizada: Python;
- Forma de comunicação: Socket TCP via Rede Ethernet ou Wi-Fi;
- Abordagem Cliente-Servidor:
- Intel Edison: Servidor;
- Aplicação Desktop: Cliente;
- Dispositivo de captura: Câmera USB – WebCam.
Então, a título de exemplo, irei demonstrar uma aplicação escrita em Python executando na Intel Edison, a qual fará a leitura de quadros de uma Webcam conectada em sua porta USB, irá processar (ou não…) estes quadros e os transmitirá pela rede para a aplicação cliente, tal como mostrado na figura a seguir.
Dessa forma, teremos um stream de quadros sendo transmitido, possibilitando ver o resultado do processo realizado na Intel Edison, por exemplo.
Codificação da Aplicação Servidora
Para entender fundamentalmente a aplicação, além de OpenCV também é importante ter noções básicas de Sockets de comunicação. Em termos de Python, alguns conceitos muito breves são apresentados aqui.
A comunicação via rede utilizando sockets precisa de dados “serializados”, ou seja, dados transformados em uma sequência de informações. O que acontece é que uma imagem é uma matriz. Como transformar uma matriz em uma sequência? Basta usarmos o método .array() da biblioteca NumPy e o método .tostring(), que, em sequência, irão serializar um objeto a ser transferido via rede usando Socket.
Na nossa sequência de código para a aplicação servidora, teremos a seguinte lógica:
- Criação de objeto de Socket;
- Vínculo com IP e Porta de comunicação;
- Habilitação de escuta de conexões;
- Criação de objeto de captura de imagem;
- Parametrização da codificação da imagem;
- Aguarda conexão de cliente;
- Loop infinito:
- Codificação da imagem;
- Serialização da imagem;
- Transmissão da imagem;
- Captura de nova imagem;
- Encerramento de objetos de captura e socket.
Adiante, segue o código-fonte desenvolvido para este artigo. Como de costume, está muito bem comentado, apresentando detalhes e informações acerca de cada trecho e rotina. Leia com atenção.
Como base, crie um arquivo chamado openCV-Server.py em sua Intel Edison, e cole o seguinte trecho de código mostrado abaixo. Salve o arquivo.
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 |
#!/usr/bin/python import socket import cv2 import numpy # Variaveis para armazenar IP e porta de comunicacao TCP_IP = '' # campo vario - vincula a todos os ips que a maquina possui na rede TCP_PORT = 5052 # Criacao do socket de comunicacao para servidor serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Vincula o socket de servidor com o IP e Porta. IP = '' - rede serverSocket.bind((TCP_IP,TCP_PORT)) # Habilita que socket de servidor ira escutar requisicoes serverSocket.listen(True) # Cria objeto de captura vinculado a device 0 - webcam capture = cv2.VideoCapture(0) # Captura um quadro para verificar conexao ret, frame = capture.read() # Parametrizacao da codificacao de imagem a ser transmitirda encode_param = [int(cv2.IMWRITE_JPEG_QUALITY),90] print 'Aguardando conexoes...' # Aguarda conexao com cliente e cria objeto "conn" - socket conn, add = serverSocket.accept() while ret: # codifica o quadro de imagem em formato jpg e grava em imgencode result, imgencode = cv2.imencode('.jpg', frame, encode_param) # tranforma imgencode em vetor - serializacao data = numpy.array(imgencode) # converte o vetor em string stringData = data.tostring() # Envia comprimento do quadro de imagem, com 16 caracteres justificado a esquerda conn.send(str(len(stringData)).ljust(16)); # Envia quadro de imagem serializado em string conn.send(stringData) # Realiza leitura de quadro da webcam, grava em frame ret,frame = capture.read() # Encerra conexao com camera usb - webcam capture.release() # Encerra o socket de comunicacao serverSocket.close() |
Após ter salvado o arquivo, é preciso dar ao mesmo permissão de execução, o que é possível por meio do seguinte comando:
1 |
# chmod +x openCV-Server.py |
Agora, para executar o programa basta digitar:
1 |
# ./openCV-Server.py |
Feito isso, a aplicação será iniciada, e ficará aguardando uma conexão da aplicação Cliente. Agora vamos à codificação da aplicação cliente.
Codificação da Aplicação Cliente
O objetivo da aplicação cliente é conectar na aplicação servidora pela rede, receber os quadros de imagem via socket e exibir esse stream para o usuário em uma janela.
A aplicação cliente pode ser executada tanto em ambiente Windows, quanto em ambiente Linux. Bastando que Python esteja instalado, juntamente com as bibliotecas OpenCV e Numpy também! Dessa forma, o presente exemplo foi executado em ambiente Windows 10 por meio do IDLE – Python’s Integrated Development and Learning Environment. O IDLE possui tanto o acesso ao console Python, como também pode criar arquivos Python. Basta ir na opção File -> New, que aparecerá uma janela onde você poderá tranquilamente digitar seu código Python com destaque para palavras da sintaxe.
Em se tratando de ambiente Windows, veja como instalar o Python, com suporte a OpenCV para Python aqui. Neste mesmo link também é informado onde baixar o Numpy. E sobre como instalar e configurar o Python com OpenCV para ambiente Linux, tendo como base o Ubuntu, veja aqui.
Na nossa sequência de código para a aplicação cliente, teremos a seguinte lógica:
- Criação de objeto de Socket;
- Conexão com máquina servidora;
- Loop infinito:
- Recebe tamanho da imagem;
- Recebe imagem completa;
- Reconstrói array de dados de string;
- Decodifica a imagem do array de dados;
- Exibe a imagem em janela para o usuário;
- Se digitar ‘q’, encerra loop;
- Encerramento de objetos de captura e socket.
Um método criado à parte para usar com a aplicação cliente, que receberá os quadros de imagem, é o recvall, método que irá receber como parâmetros um socket e uma quantidade de dados, quantidade que será usada como base de “tamanho” para retornar então a informação esperada. Tive uma base de inspiração para o modelo cliente-servidor utilizado no artigo neste tópico do StackOverflow.
Vamos então à codificação? Crie um arquivo na sua Máquina Desktop (Windows ou Linux – devidamente configurados com Python + OpenCV + Numpy, chamado openCV-Client.py, e cole o conteúdo de código apresentado adiante. Veja, novamente, que o código está muito bem comentado e apresenta detalhes sobre comandos e trechos de código.
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 |
import socket import cv2 import numpy # Rotina recvall - trata de receber informacao def recvall(sock, count): buf = b'' while count: newbuf = sock.recv(count) if not newbuf: return None buf += newbuf count -= len(newbuf) return buf # Atribui IP da estacao servidora - Intel Edison TCP_IP = '192.168.1.13' # Porta de comunicacao TCP_PORT = 5052 # Cria objeto socket para comunicacao conn = socket.socket() # Faz conexao com estacao servidora conn.connect((TCP_IP,TCP_PORT)) # Loop infinito: Recebe imagem, exibe. Se digitar q - sai. while True: # Recebe tamanho da imagem length = recvall(conn,16) # Recebe imagem propriamente stringData = recvall(conn, int(length)) # Recupera a imagem serializada em forma de string data = numpy.fromstring(stringData, dtype='uint8') # Decodifica a imagem decimg=cv2.imdecode(data,1) # Exibe a imagem cv2.imshow('EdisonCAM',decimg) # Se digitar 'q', encerra. if(cv2.waitKey(1) & 0xFF == ord('q')): break # Fecha as janelas do OpenCV cv2.destroyAllWindows() # Encerra o socket conn.close() |
Em se tratando do IDLE, em ambiente Windows, basta ir na opção “Run -> Run Module” para executar o código Python escrito. De outra forma, basta apertar F5 para iniciar a execução.
Em ambiente Linux, será preciso chamar o código em linha de comando, tal como:
1 |
$ python openCV-Client.py |
Se tudo correr certo, a aplicação irá conectar na Intel Edison, e logo aparecerá uma janela mostrando os quadros de imagem. Caso haja um certo delay, um atraso entre o cenário real e o exibido na janela, é normal. Temos um overhead de processamento ao usarmos Python, serialização de objeto, etc…
Caso não apareça sua janela, veja o erro ocorrido. Certifique-se de usar o IP correto da Intel Edison, de que a porta de comunicação está disponível, dentre outras coisas.
Execução
Veja na Figura 8 o console de execução na Intel Edison. A aplicação servidora foi iniciada e aguarda conexões.
Na Figura 9, é mostrado o IDLE em execução, com a janela de Shell, janela de código, e a janela do stream de quados mostrando o resultado do processo.
E na Figura 10 é mostrada uma foto da montagem da Intel Edison correspondente.
Aplicação Servidora – Aprimorada
Agora, vamos pensar no seguinte cenário: E se a aplicação servidora pudesse ser parametrizada? Ou seja, e se durante a execução do sistema, fosse possível mudar o algoritmo em execução, alterar parâmetros deste algoritmo, etc…?
Para isso, basta tratar na aplicação cliente e na aplicação servidora uma comunicação bidirecional. Ou seja, a aplicação servidora agora passará a receber parâmetros de comando da aplicação cliente, e esta, por sua vez, passará a enviar parâmetros de comando, conforme digitado pelo usuário. Agora, o usuário poderá ter o poder de mudar a forma como que os quadros serão processados na máquina servidora.
No exemplo em questão, irei trabalhar de forma que a aplicação trabalhe com 4 estados distintos, além de poder ser encerrada pela aplicação cliente, tendo por base os seguintes comandos recebidos via Socket:
- normal – Operação normal, não altera o quadro capturado;
- canny – Aplica o Filtro de Canny na imagem capturada;
- gray – Aplica a conversão de espaço de cores de colorida à escala de cinza na imagem capturada;
- blur – Aplica um filtro causando efeito de atenuação na imagem – efeito de “Blur”;
- sair – Encerra a aplicação.
Dessa forma, a aplicação servidora passa a ter a seguinte sequência de lógica de funcionamento:
- Criação de objeto de Socket;
- Vínculo com IP e Porta de comunicação;
- Habilitação de escuta de conexões;
- Criação de objeto de captura de imagem;
- Parametrização da codificação da imagem;
- Aguarda conexão de cliente;
- Loop infinito:
- Leitura de comando da aplicação cliente;
- Codificação da imagem;
- Serialização da imagem;
- Transmissão da imagem;
- Captura de nova imagem;
- Processo de imagem conforme comando;
- Encerramento de objetos de captura e socket.
De modo a diferenciar o código simplificado do código aprimorado, crie um novo arquivo para o código da aplicação servidora na Intel Edison, chamado openCV-Server-Tunado.py, e coloque neste arquivo o código de programa mostrado adiante. Veja os comentários e os trechos que tratam de avaliar o recebimento de comandos da aplicação cliente, e o processo de quadros com base nestes comandos.
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 |
#!/usr/bin/python import socket import cv2 import numpy # IP de vinculo - '' geral na maquina TCP_IP = '' # Porta de conexao TCP_PORT = 5052 #Variavel para estado de funcionamento - comando recebido do cliente comando = 'normal' #Variavel para kernel de blur kernel = numpy.ones((5,5), numpy.float32)/25 #Tamanho de buffer de comunicacao BUFFER_SIZE = 20 # Criacao do socket de comunicacao para servidor serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Vincula o socket de servidor com o IP e Porta. IP = '' - rede serverSocket.bind((TCP_IP,TCP_PORT)) # Habilita que socket de servidor ira escutar requisicoes serverSocket.listen(True) # Cria objeto de captura vinculado a device 0 - webcam capture = cv2.VideoCapture(0) # Captura um quadro para verificar conexao ret, frame = capture.read() # Parametrizacao da codificacao de imagem a ser transmitirda encode_param = [int(cv2.IMWRITE_JPEG_QUALITY),90] print 'Aguardando conexoes...' # Aguarda conexao com cliente e cria objeto "conn" - socket conn, add = serverSocket.accept() # Enquanto houver retorno de quadro na webcam while ret: # Espera envio de comando da maquina cliente. estado = conn.recv(BUFFER_SIZE) # codifica o quadro de imagem em formato jpg e grava em imgencode result, imgencode = cv2.imencode('.jpg', frame, encode_param) # tranforma imgencode em vetor - serializacao data = numpy.array(imgencode) # converte o vetor em string stringData = data.tostring() # Envia comprimento do quadro de imagem, com 16 caracteres justificado a esquerda conn.send(str(len(stringData)).ljust(16)); # Envia quadro de imagem serializado em string conn.send(stringData) # Realiza leitura de quadro da webcam, grava em frame ret,frame = capture.read() # processo do quadro conforme parametro enviado pela maquina cliente if comando == 'normal': # Se for normal, nao faz nada. frame = frame elif comando == 'canny': # Se for canny, aplica o detector de bordas. frame = cv2.Canny(frame,100,200) elif comando == 'gray': # Se for blackwhite - aplica transformacao de cor frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) elif comando == 'blur': # Se for blur, aplica filtro parametrizado frame = cv2.filter2D(frame, -1, kernel) elif comando == 'sair': # Se for sair, encerra. break # Encerra conexao com camera usb - webcam capture.release() # Encerra o socket de comunicacao serverSocket.close() |
Para executar o programa, digite o seguinte comando no console Linux da Intel Edison:
1 |
# ./openCV-Server-Tunado.py |
A aplicação será iniciada e aguardará a conexão da aplicação cliente.
Aplicação Cliente – Aprimorada
Não obstante, a aplicação cliente agora também deve implementar uma comunicação bidirecional, ou seja, deve ser capaz de também enviar mensagens, comandos para a aplicação servidora. E como a “coisa” está arquitetada, constantemente a aplicação servidora precisa receber o parâmetro indicando como deve processar o quadro de imagem capturado pela câmera USB. Dessa forma, constantemente também a aplicação cliente deverá enviar o comando que determina isso.
Assim sendo, a aplicação cliente terá uma variável chamada estado, que irá variar conforme as seguintes teclas digitadas pelo usuário:
- n – Ao digitar n, o estado atual é ‘normal’, e não é feita nenhuma modificação na imagem capturada pela Edison;
- w – Ao digitar w, o estado atual será ‘gray’, e a aplicação servidora irá tornar os quadros de imagem capturados de colorido para escala de cinza;
- c – Ao digitar c, o estado atual passa para ‘canny’, em que a aplicação servidora irá então aplicar o Filtro de Canny nos quadros de imagem subsequentes;
- b – Ao digitar b, o estado será ‘blur’, e a aplicação servidora irá passar a aplicar o respectivo filtro em cada imagem capturada;
- q – Ao digitar q, a aplicação será encerrada, enviando também comando para encerrar a aplicação servidora.
Com o estado definido, a cada execução do loop infinito while true é então enviado o estado de operação para a aplicação servidora, que o tomará por base para a execução do correspondente algoritmo ou ação sobre a imagem capturada, e podendo eventualmente até mesmo encerrar a própria aplicação servidora.
Dito isso, veja abaixo o código da aplicação cliente devidamente comentado, indicando campos, trechos e rotinas de código com suas correspondentes funcionalidades. Para reproduzir o feito, salve o código em um arquivo com nome openCV-Client-Tunado.py para logo ser executado.
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 |
import socket import cv2 import numpy def recvall(sock, count): buf = b'' while count: newbuf = sock.recv(count) if not newbuf: return None buf += newbuf count -= len(newbuf) return buf # Atribui IP da estacao servidora - Intel Edison TCP_IP = '192.168.1.13' # Porta de comunicacao TCP_PORT = 5052 # Cria variavel para referencia de estado de operacao - comeca normal estado = 'normal' # Cria objeto socket para comunicacao conn = socket.socket() # Faz conexao com estacao servidora conn.connect((TCP_IP,TCP_PORT)) # Envia estado de operacao via socket conn.send(estado) # Loop infinito - recebe quadro de imagem, exibe, verifica tecla digitada # pelo usuario. while True: length = recvall(conn,16) stringData = recvall(conn, int(length)) data = numpy.fromstring(stringData, dtype='uint8') decimg=cv2.imdecode(data,1) cv2.imshow('EdisonCAM',decimg) if(cv2.waitKey(1) & 0xFF == ord('c')): estado = 'canny' print 'Estado de operacao: Canny' elif(cv2.waitKey(1) & 0xFF == ord('w')): estado = 'gray' print 'Estado de operacao: Escala de Cinza' elif(cv2.waitKey(1) & 0xFF == ord('b')): estado = 'blur' print 'Estado de operacao: Blur' elif(cv2.waitKey(1) & 0xFF == ord('n')): estado = 'normal' print 'Estado de operacao: Normal' elif(cv2.waitKey(1) & 0xFF == ord('q')): estado = 'sair' print 'Encerrando aplicacao...' break conn.send(estado) # Fecha as janelas do OpenCV cv2.destroyAllWindows() # Encerra socket de conexao conn.close() |
Salvado o arquivo, é possível executá-lo no IDLE pressionando a tecla F5, ou em ambiente Linux por meio do comando em console:
1 |
$ ./openCV-Client-Tunado.py |
Se tudo correr bem, irá aparecer uma janela com o stream de imagem, colorida, normal. Para mudar o estado de operação e verificar a “imediata” mudança no processo, mantenha pressionada a tecla correspondente até que a aplicação cliente faça a leitura. Pode demorar “um pouquinho”. Assim que a aplicação cliente lê o comando do usuário, o mesmo é transmitido para a aplicação servidora, que passa a mudar seu processo. Veja o resultado em execução!
Execução da solução aprimorada
Para execução da solução cliente-servidor aprimorada, agora dispondo de comunicação bidirecional e mudança de estado de operação, é preciso inicialmente começar a execução da aplicação servidora na Intel Edison. Veja na Figura 11 a aplicação servidora sendo iniciada.
E na máquina cliente, é então iniciada a aplicação. Com isso, surge a janela com o stream de imagens, agora podendo ser alterado pelas teclas configuradas na aplicação. Ao pressionar ‘c’, por exemplo, é informado que o estado de operação passa a ser ‘Canny’, devendo então a aplicação servidora realizar o correspondente algoritmo nos quadros capturados a partir de então. Veja na Figura 12 como ficou o resultado deste exemplo:
E adiante seguem as janelas de exibição para cada estado/modo de exibição. Na Figura 13 é mostrada a janela de captura para Estado Normal, na Figura 14 é mostrada a janela de captura para Estado Escala de Cinza, na Figura 15 é mostrada a janela de captura para Estado Blur, e na Figura 16 é mostrada a janela de captura para Estado Canny. Veja em cada imagem o resultado do processo.
Adiante segue também o vídeo que preparei, justamente quando acabei de escrever este artigo:
Gostou? Ficou interessado em Python e OpenCV?
Caso afirmativo, você pode fazer o curso de Python no CodeAcademy, e se quiser ver mais detalhes acerca de Python com OpenCV, o site PyImageSearch é uma opção, além do Python OpenCV Tutorials, sem esquecer, é claro, da documentação com exemplos oficiais.
Sinta-se à vontade para basear projetos nos códigos apresentados. As possibilidades são infinitas. Agora você pode controlar um robô com câmera, visualizando a câmera do robô e demais processos realizados sobre a imagem que ele captura. Ou melhor, você pode fazer um sistema de segurança com reconhecimento facial ou detecção de objetos, tais como faces humanas!
O que você vai fazer?
Olá, André! Tudo bom?
Primeiramente queria parabenizá-lo pelo tutorial, muito bem explicado! Está me ajudando muito.
Porém tenho um probleminha, estou fazendo um projetinho pra disciplina do Evandro com a Intel Edison. Segui tudo o que você especificou e também os outros links que você cita. Assim que o cliente (Windows 8.1) se conecta na placa eu vejo a imagem por cerca de 3 segundos e então a placa reinicia. Você teria alguma idéia do porquê isso está acontecendo? Qualquer ajuda é válida!
Muito obrigada!
Parabéns André, conteúdo bem detalhado e interessante.
Tive aula com o professor Evandro, meu interesse por embarcados começou com ele!
Uma dúvida por curiosidade: Por que foi escolhido na arquitetura do sistema um socket TCP e não UDP ?
Abraços!
Olá Fernando, obrigado pelos elogios!
Então, eu quis usar TCP mais por uma espécie de “controle” da conexão.
Facilmente o código pode ser adaptado para UDP 😉
André, muito bem detalhado e explicado o tutorial! Valeu mesmo por compartilhar e dividir um pouco o conhecimento com os mortais hahaha.
Ano passado fiz uns experimentos com OpenCV, nada de mais, mas deu pra ver a capacidade da coisa. Com certeza tem muitas aplicações.
Um abraço e até mais!
Obrigadão aí pelo feedback!
Abraço!
Olá @andrcurvello:disqus, tudo bem? Gostaria de lhe agradecer pelo ótimo trabalho e também gostaria de pedir sua permissão para usar seus scripts (pretendo usar UDP ao invés de TCP) em testes que pretendo fazer na área da robótica, mais voltado exatamente para detecção de obstáculos e recalculamento de rotas usando uma Raspberry Pi 3 para tratar da parte lógica e um Arduino Uno como servo. Além disso, pretendo trabalhar futuramente com processamento de imagens e visão computacional, sou bem leigo ainda, mas estou me esforçando pra aprender, ainda estou no 3º período da minha graduação em Análise e Desenvolvimento de… Leia mais »
Nossa! Parabens!! Sucesso!