IMPLEMENTAÇÃO DE MÉTODOS E CLASSES DE PROCESSAMENTO DE IMAGENS PARA SISTEMA DE NAVEGAÇÃO ROBÓTICA Rafael A. B. Barros ITA – Divisão de Ciência da Computação Praça Mal. Eduardo Gomes, 50 12228-900 São José dos Campos – SP [email protected] Carlos H. C. Ribeiro ITA – Divisão de Ciência da Computação Praça Mal. Eduardo Gomes, 50 12228-900 São José dos Campos – SP [email protected] RESUMO: O projeto consiste na implementação, em uma plataforma Java, de técnicas de detecção de guias de calçadas para a utilização em robótica móvel A plataforma realiza filtragem de imagens e processamento baseado em transformada de Hough, também sendo feito um controle de erros usando o filtro de Kalman. Este artigo detalha a arquitetura de software desenvolvida para a implementação do sistema de detecção de guias. ABSTRACT: This project consists in the implementation, in a Java platform, of techniques for sidewalk detection for posterior use in mobile robotics. The platform filters images and performs processing based on the Hough Transform, and error control using the Kalman filter. This paper details the software architecture developed for implementation of this sidewalk detection system. 1. INTRODUÇÃO Esse projeto consiste em implementar métodos de localização e posicionamento simples para um robô móvel que possui como sensor de visão uma câmera de baixo custo (webcam). O algoritmo resultante deve ser pouco sensível a erros e suportar a baixa resolução das imagens obtidas. O projeto conta com duas fases: a implementação do algoritmo em Matlab, para a realização de testes iniciais, e a implementação final em Java, utilizando-se de duas bibliotecas adicionais, Java Advanced Imaging e Java Multimedia Framework [1][2] para o funcionamento definitivo. A plataforma deve retornar com sucesso as coordenadas em parametrização circular da reta que melhor define a guia de calçada que o robô utilizará para se localizar. Também é objetivo desse projeto que o algoritmo funcione em um tempo razoável, já que uma demora excessiva na obtenção dos resultados pode retardar o movimento do robô, prejudicando desta forma a eficiência do mesmo. A implementação em Matlab não será discutida aqui. Maiores informações poderão ser encontradas em [3] e [4]. Apenas conceitos básicos para o entendimento dos algoritmos utilizados serão apresentados, e detalharemos a descrição da implementação em Java, além de uma breve descrição dos resultados. 2. CONCEITOS 2.1 Imagens e sua Obtenção A plataforma em Java obtém a imagem como uma matriz de pixels, definida por três valores correspondentes às componentes vermelho, azul e verde da imagem. A imagem é armazenada em um objeto Java chamado bufferedImage, que por sua vez é composto de um Raster (referente à matriz de pixels) e um modelo de cores (ColorModel), que registra a forma como as três componentes de cor distribuem-se pela imagem. Como uma bufferedImage é na verdade uma matriz de pixels, os algoritmos utilizados para manipular a imagem e a partir dela obter quaisquer informações necessárias são algoritmos que basicamente manipulam matrizes fazendo a análise direta dos valores nelas contidos e da forma como esses valores relacionam-se entre si. 2.2 Transformadas e Filtros Os filtros são modos de se manipular as matrizes de pixels de modo a obterem-se informações desejadas ou eliminar aquelas que prejudicariam o correto funcionamento da aplicação. Os filtros se baseiam em uma matriz de convolução, que é uma matriz responsável por definir, para cada elemento, como este se relaciona com os seus vizinhos. O valor de cada pixel da imagem obtida após a filtragem será determinado, então, como resultado das operações definidas pela matriz de convolução para os elementos correspondentes da matriz original. A transformada de Hough [5] foi um algoritmo desenvolvido por Paul Hough em 1962 e patenteado pela IBM. É um algoritmo de ordem O(n4) que transforma uma imagem convenientemente filtrada numa curva paramétrica, e obtém desta curva pontos que representam linhas retas na imagem original. A transformada de Hough é um algoritmo adequado para a detecção de retas, porém em imagens muito ruidosas pode cometer erros. Desse modo, é interessante a determinação de alguma forma de eliminar eventuais erros de modo a fornecer um funcionamento consistente para o robô, e nestas ocasiões pode ser conveniente estabelecer o posicionamento das retas na imagem com base em uma estimação baseada no filtro de Kalman [6]. 3. ARQUITETURA DE SOFTWARE: METODOLOGIA A plataforma Java é constituída por um conjunto de classes, cada uma definindo operações ou objetos que são de evidência na aplicação do algoritmo. Um esquema geral de classes definidas e relações existentes entre elas é mostrado na Figura 1. As setas indicam o relacionamento entre os diversos objetos, no que tange ao fluxo de informações. Figura 1 – Esquema geral de classes As classes estão divididas da seguinte forma: quatro delas definem a construção da interface gráfica (Direita, Principal, Origem e Resultados). Duas delas referem-se à captação de imagens da câmera (FrameGrabber e FrameGrabberException). Três contém métodos para as operações a serem realizadas nas Imagens (Kalman_2D, Hough e Filtros). Finalmente, a última, Comando e Matriz são classes de armazenamento de dados, utilizadas como auxiliares. 3.1 Interface Gráfica Embora a interface gráfica definida não seja utilizada no funcionamento do robô em si, ela é interessante na fase de testes. Por isso, foi incluída na especificação do projeto de modo a permitir manejar com facilidade os parâmetros dos filtros e algoritmos utilizados na plataforma. O modelo de interface gráfica escolhido foi de um frame onde podem ser anexados um ou mais painéis (Figura 2). Esses painéis também podem ser arrastados de modo a ajustar-se à visualização de acordo com o objetivo pretendido. Eles também possuem cada um uma caixa de texto, onde os comandos inseridos são interpretados e encaminhados para execução, sendo atualizados na próxima iteração do algoritmo. Figura 2 – Janela Principal São descritos a seguir cada classe constituinte da interface gráfica. 3.1.1 Principal A Classe principal é constituída de um JFrame que define uma barra de menu e no mínimo duas janelas que servem para controle de testes da interface gráfica. Seu funcionamento consiste em dois threads que fazem o controle geral do programa e sincronizam a amostragem de imagens na tela. A grande funcionalidade dessa interface é a possibilidade de se anexar várias janelas Resultados, obtendo dessa forma resultados de diferentes testes simultaneamente. 3.1.2 Original A Classe Original é um JPanel que conterá a imagem diretamente obtida da câmera. Ela também possui um pequeno espaço onde o usuário pode inserir os comandos referentes aos filtros que ele quer visualizar aplicados à imagem. Um detalhe importante: os filtros inseridos aqui farão o processamento em uma outra janela resultados, de modo que se a primeira não for fechada, ambos os processos ocorrerão paralelamente. A maior funcionalidade desse fato é que podemos observar a detecção de quantos filtros a resolução do monitor permitir ocorrendo simultaneamente, o que pode ser útil para determinar se tal filtro fornece ou não um resultado mais preciso do que outro. Ela difere da classe Resultados, a ser descrita em seguida, pelo fato de alterações nos comandos necessitarem de uma outra janela para a exibição dos resultados em si, enquanto que na classe Resultados todas as alterações são processadas automaticamente. 3.1.3 Resultados A Classe Resultados é um container que conterá dois JPanels. O primeiro contém a mesma caixa de inserção de comandos existente na janela Original, porém qualquer modificação nessa caixa, além de fazer com que o efeito seja imediato, apareça na mesma janela Resultados onde o texto está sendo digitado. O Segundo JPanel define uma área onde será anexado o objeto referente à classe Direita, a ser explicada adiante. 3.1.4 Direita A Classe Direita estende JPanel, sendo um painel onde serão exibidas as imagens referentes ao vetor construído baseado nos filtros, colocados na caixa de texto tanto na classe Resultados como na Original. 3.2 Comando A Classe comando é uma classe auxiliar para armazenamento de informações. Ela guarda os resultados de uma tokenização dos Strings pertencentes à JTextArea presentes nas janelas Original e Resultados. Após esse processo, os diversos objetos instanciados desta classe podem ser utilizados para a chamada dos comandos respectivos. 3.3 Matriz A classe Matriz é também uma classe de Armazenamento, utilizada pela classe Kalman_2D, e determina operações usuais de matrizes, como por exemplo, o cálculo da inversa. Essa classe foi definida pelo fato da Transformada de Kalman ter a necessidade do cálculo de muitas operações envolvendo matrizes, fazendo com que a definição de uma matriz como objeto tornasse o projeto da classe responsável pela transformada de Kalman bem mais simples. 3.4 FrameGrabber FrameGrabber é um pacote de classes fornecido pela Sun Microsystems que permite a obtenção de imagens diretamente de uma câmera. Ela é constituída de duas classes: FrameGrabber e FrameGrabberException. A classe faz a conexão com dispositivo de captura com o mesmo nome fornecido em um arquivo vídeo.properties, podendo disparar uma FrameGrabberException. A partir daí o programa tenta determinar o formato com que as informações obtidas pela câmera serão repassadas ao programa, e então determina um processador de dispositivo, que ficará responsável por fazer a coleta de dados. Finalmente, as informações obtidas são guardadas em um buffer. A Classe possui então um método getImage, que acessa esse buffer e recupera a imagem que está instantaneamente armazenada, retornando-a para o método que acessou esta classe. 3.5 Filtros A classe filtros possui os métodos de operação dos principais filtros utilizados que foram julgados de interesse para o projeto. A maioria dos filtros inseridos nessa classe possui uma matriz de convolução padrão. Alguns poucos, porém, possuem um método mais arrojado de processamento. Para inserir novos filtros nessa classe, basta então definir a matriz de convolução do filtro em questão, e inserir o filtro usando o método filtros.put(). A descrição resumida de cada filtro atualmente definido na classe segue abaixo: edge: ressalta os cantos e pontos com grande diferença dos pontos vizinhos na imagem. edge5: ressalta os cantos e pontos com grande diferença dos pontos vizinhos na imagem, agora vizinhos mais distantes. sobel0: obtém a aproximação de Sobel para a derivada, tomando a direção de 0º. sobel90: obtém a aproximação de Sobel para a derivada, tomando a direção de 90º. sobel180: obtém a aproximação de Sobel para a derivada, tomando a direção de 180º. sobel270: obtém a aproximação de Sobel para a derivada, tomando a direção de 270º. gaussiana3: aplicada o filtro gaussiano na imagem, tornando-a menos acidentada. Esse filtro melhora bastante o resultado do filtro sobel se utilizado antes do mesmo. gaussiana5: aplicada o filtro gaussiano na imagem, tornando-a menos acidentada. Esse filtro melhora bastante o resultado do filtro sobel se utilizado antes do mesmo. Nesse caso a gaussiana é uma matriz 5x5 ao invés de 3x3. blur: torna a imagem embaçada. Bisobel: Sobel em duas direções perpendiculares Quadsobel: Sobel em quatro direções perpendiculares. 3.6 Hough A Classe Hough, como o próprio nome diz, é a responsável direta por realizar a transformada de Hough na imagem filtrada, além de retornar as coordenadas da reta encontrada na imagem. A Classe Hough contém, além disso, uma série de operações para determinar o modelo de cores (ColorModel) da imagem de destino, de modo que se possa construir uma imagem que mostre o resultado da transformada. Segue uma descrição das operações realizadas: Primeiro, a classe calcula o raio máximo que a reta terá, ou seja, a diagonal da imagem. O raio mínimo é definido como zero. Já os valores do ângulo mínimo e máximo são definidos como – PI e +PI, diretamente no construtor da classe. O programa que acessar a classe Hough deverá fazê-lo também enviando os valores de pTheta (quantas divisões terá o intervalo de –PI a PI). pRo (número de divisões no Raio), Threshold e RenderingHints. É importante notar que pequenos valores para pTheta e pRo tornam o processo mais rápido, porém podem conduzir a erros. Gera-se então uma matriz com os valores de co-seno e seno para cada theta entre os valores mínimos e máximos, cujo número é definido por pTheta. Essa matriz serve para diminuir a quantidade de processamento da máquina, visto que é muito mais fácil conseguir os valores dos ângulos através do acesso à matriz de senos e co-senos do que recalcular esses valores toda vez que eles forem necessários. Varre-se então a imagem filtrada, checando-se para cada ponto se o valor dele é maior que o limiar (threshold) fornecido ao construtor. Caso o seja, as coordenadas do conjunto de retas que passam por aquele ponto são obtidas e o elemento do acumulador respectivo a cada uma é incrementado, caso o valor do raio para a reta em questão seja menor que o valor do raio máximo definido anteriormente, de modo a evitar exceções relativas ao tamanho do array definido. Feito isso, varre-se o array em busca daquele elemento que foi incrementado mais vezes, que logicamente guardará o maior valor. Depois de obtido, temos a coordenada da reta. A matriz de acumuladores ainda é normalizada de modo que se possa construir o gráfico do espaço ro x theta, além de ser construída a imagem com a reta detectada (que é impressa acima da imagem do último filtro que é passado na imagem antes da transformada de Hough). 3.7 Kalman_2D Filtro de Kalman é invocado da classe Hough, caso sejam fornecidos para o construtor desta classe um parâmetro adicional, que será utilizado na determinação do erro mostrado na curva parametrizada (o interior do círculo verde). Ele está especificado dentro da classe FiltroKalman2D. O modelo de movimento utilizado é estático, porque é intuitivo que na maioria do tempo de seu movimento o robô estará seguindo em linha reta, e portanto a calçada deverá permanecer na mesma posição em relação deste e, portanto, parada na imagem. A reta verde na tela principal mostra o resultado da transformada de Hough depois da filtragem de Kalman. O modelo de crença do filtro de Kalman foi definido de forma bem simples: se o ponto ficar dentro do erro considerado pelo filtro de Kalman, este funcionará normalmente. Porém, no caso de o ponto encontrado pela câmera em conjunto com a transformada de Hough for fora da região de erro, o filtro de Kalman não se atualizará e tratará o caso como uma exceção, mantendo o ponto como o ponto anterior. Logicamente, se passarem um dado número de iterações e o resultado continuar fora da região de erro, o filtro de Kalman considerará que a situação mudou, e adequar-se-á a nova situação. 4. TESTES Os testes foram realizados com uma plataforma com suporte para o notebook e uma câmera corretamente posicionada, de acordo com as especificações recebidas para o robô utilizado. Os testes foram feitos em três períodos do dia, separados de acordo com o grau de iluminação existente: meio-dia, fim de tarde e noite. Também foram feitos testes em curvas. O algoritmo também foi submetido a alguns testes em ambiente interno, de modo a testar-se a sua aplicabilidade. Maiores detalhes sobre a plataforma e testes realizados podem ser encontrados em [3] e [4]. 5. CONCLUSÃO A arquitetura de software desenvolvida se mostro plenamente adequada, especialmente em seus aspectos facilitadores de análise e calibração dos parâmetros dos algoritmos utilizados. Embora não tenhamos calculado formalmente o tempo de execução, a rapidez de processamento (em um notebook Toshiva Sattelite Pentium IV) foi suficiente para a aplicação, já que as velocidades a serem desenvolvidas pelo robô de vigilância são compatíveis com um mecanismo de controle servovisual baseado na arquitetura implementada. naturalmente, uma análise mais profunda dependeria da efetiva implementação da arquitetura em um robô móvel. Obtivemos um resultado bastante bom com a utilização da transformada de Hough e filtro de Kalman para detecção de guia de calçadas. Os algoritmos funcionam com exatidão e se mostraram robustos no que se refere ao tratamento da maioria das situações adversas [3][4]. Referências Bibliográficas 1. “Java Advanced Imaging (JAI) API Specification”, http://java.sun.com/products/javamedia/jai/forDevelopers/jai-apidocs/index.html. Acessado em 17/08/2005. 2. Deitel, H.M e Deitel P.J., JavaTM Como Programar. 6a Edição, Editora Prentice Hall, 2005. 3. Ferriani, V. M. P., “Detecção de Guias de Calçada usando variações da transformada de Hough”, Relatório Final PIBIC/CNPq, Instituto Tecnológico de Aeronáutica, 2005. 4. Reineri, S., “Implementação e análise de algoritmos de tempo real para detecção de bordas em navegação utilizando robô móvel de vigilância”, Relatório Final PIBIC/CNPq, Instituto Tecnológico de Aeronáutica, 2005. 5. Dudek, G. e Jenkin, M. “Computational principles of Mobile Robotics”, Cambridge University Press, 2000. 6. Russel, S. e Norvig, P., Inteligência Artificial. Tradução da Segunda Edição. Editora Campos, 2004.