FAVORITAR
FecharPlease loginn
7 Comentários
FAVORITAR
FecharPlease loginn

Visão Computacional com OpenCV na Intel Edison

Visão Computacional

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!

Visão Computacional com OpenCV na Intel Edison
Figura 1 – Intel Edison – Não possui saída de vídeo!

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. 

Visão Computacional com OpenCV na Intel Edison: image recognition
Figura 2 – Reconhecimento de Objetos em Imagem. Fonte: thevoice.ottawachamber.ca

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.

Visão Computacional com OpenCV na Intel Edison: Detecção de Faces
Figura 3 – Detecção de Faces. Fonte: technologyreview.com

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.

Visão Computacional com OpenCV na Intel Edison: kinect
Figura 4 – Tela de depuração do Kinect em Xbox – Fonte: gamerhub.tv

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:

  1. Sua Intel Edison possui acesso à rede local e à Internet;
  2. 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):

Feito o processo, vamos conferir se as bibliotecas foram devidamente instaladas. Para isso, inicie o console Python por meio do comando:

E agora verifique se as bibliotecas foram devidamente instaladas digitando:

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.

Visão Computacional com OpenCV na Intel Edison: Teste de Imports em Python - Intel Edison
Figura 5 – Teste de Imports em Python – Intel Edison

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:

Se aparecer “/dev/video0”, então está tudo OK. A princípio… Veja como ficou a minha tela:

Visão Computacional com OpenCV na Intel Edison: Resposta ao comando "ls /dev/video0" na Intel Edison.
Figura 6 – Resposta ao comando “ls /dev/video0” na Intel Edison.

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:

  1. Linguagem de programação utilizada: Python;
  2. Forma de comunicação: Socket TCP via Rede Ethernet ou Wi-Fi;
  3. Abordagem Cliente-Servidor:
    • Intel Edison: Servidor;
    • Aplicação Desktop: Cliente;
  4. 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. 

Visão Computacional com OpenCV na Intel Edison: Arquitetura do Sistema Proposto
Figura 7 – Arquitetura do Sistema Proposto

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:

  1. Criação de objeto de Socket;
  2. Vínculo com IP e Porta de comunicação;
  3. Habilitação de escuta de conexões;
  4. Criação de objeto de captura de imagem;
  5. Parametrização da codificação da imagem;
  6. Aguarda conexão de cliente;
  7. Loop infinito:
    1. Codificação da imagem;
    2. Serialização da imagem;
    3. Transmissão da imagem;
    4. Captura de nova imagem;
  8. 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.

Após ter salvado o arquivo, é preciso dar ao mesmo permissão de execução, o que é possível por meio do seguinte comando:

Agora, para executar o programa basta digitar:

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:

  1. Criação de objeto de Socket;
  2. Conexão com máquina servidora;
  3. Loop infinito:
    1. Recebe tamanho da imagem;
    2. Recebe imagem completa;
    3. Reconstrói array de dados de string;
    4. Decodifica a imagem do array de dados;
    5. Exibe a imagem em janela para o usuário;
    6. Se digitar ‘q’, encerra loop;
  4. 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.

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:

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.

Visão Computacional com OpenCV na Intel Edison: conexão com Server
Figura 8 – Tela de console Linux da Intel Edison – Servidor Iniciado.

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.

Visão Computacional com OpenCV na Intel Edison: Execucao do PythonClientEdison
Figura 9 – Execução da aplicação Cliente – Exibição da janela de stream de imagens

E na Figura 10 é mostrada uma foto da montagem da Intel Edison correspondente.

Visão Computacional com OpenCV na Intel Edison: montagem da Intel Edison com Câmera USB
Figura 10 – Cenário real da montagem da Intel Edison com Câmera USB.

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:

  1. Criação de objeto de Socket;
  2. Vínculo com IP e Porta de comunicação;
  3. Habilitação de escuta de conexões;
  4. Criação de objeto de captura de imagem;
  5. Parametrização da codificação da imagem;
  6. Aguarda conexão de cliente;
  7. Loop infinito:
    1. Leitura de comando da aplicação cliente;
    2. Codificação da imagem;
    3. Serialização da imagem;
    4. Transmissão da imagem;
    5. Captura de nova imagem;
    6. Processo de imagem conforme comando;
  8. 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.

Para executar o programa, digite o seguinte comando no console Linux da Intel Edison:

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.

Salvado o arquivo, é possível executá-lo no IDLE pressionando a tecla F5, ou em ambiente Linux por meio do comando em console:

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.

Visão Computacional com OpenCV na Intel Edison: Iniciando Servidor Tunado na Intel Edison
Figura 11 – Iniciando Servidor Tunado na Intel Edison

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:

Visão Computacional com OpenCV na Intel Edison: Edison + Cliente Tunado
Figura 12 – Exemplo de execução da Aplicação Cliente “Tunada”

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.

Visão Computacional com OpenCV na Intel Edison: Edison Normal
Figura 13 – Execução Normal.
Visão Computacional com OpenCV na Intel Edison: Edison em Grayscale
Figura 14 – Execução em Escala de Cinza.
Visão Computacional com OpenCV na Intel Edison: Edison com Blur
Figura 15 – Execução em modo Blur.
Visão Computacional com OpenCV na Intel Edison: Edison com Canny
Figura 16 – Execução em Modo Canny.

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?

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Home » Hardware » Visão Computacional com OpenCV na Intel Edison

WEBINARS

LEIA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Comentários:
Notificações
Notificar
guest

7 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Juliana Rodrigueiro
Juliana Rodrigueiro
18/11/2015 21:38

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!

Fernando Luiz Cola
15/10/2015 09:12

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!

André Curvello
Reply to  Fernando Luiz Cola
15/10/2015 10:41

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 😉

Arthur Moisés
Arthur Moisés
14/10/2015 11:46

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!

André Curvello
Reply to  Arthur Moisés
15/10/2015 10:41

Obrigadão aí pelo feedback!
Abraço!

David Emanoell
David Emanoell
19/12/2016 15:17

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 »

Aline Cavalcante Farias
25/11/2016 21:27

Nossa! Parabens!! Sucesso!

Talvez você goste: