HDR com Python e GTK+ Aldo Nogueira PUC-Rio Introdução Durante o curso de Fundamentos de Computação Gráfica, foram oferecidos vários temas para o quarto trabalho. Um deles que me despertou interesse foi o de implementar o algoritmo para geração de imagens HDR descrito em [debevec]. A princípio, pensei em desenvolver essa aplicação em C, usando a Gnu Scientific Library [gsl] para o problema de minimização e GTK+ [gtk]para interface gráfica. Cheguei a traduzir o algoritmo em Matlab/Octave do fim do artigo para C com usando GSL. Foi durante o desenvolvimento da interface, que percebi que poderia fazer todo o projeto em Python com muito menos esforço. HDR Um imagem, tipicamente, tem 256 níveis de intensidade para cada canal de cor vermelha, verde e azul. Essa faixa não permite representar adequadamente, cenas que possuam áreas com muita ou pouca exposição. Áreas pouco expostas são mapeadas para a cor preta e áreas muito expostas para branco. Um fotógrafo deve então escolher em que faixa está o foco de interesse da imagem e ajustar o tempo de exposição e/ou abertura do diafragma da câmera de acordo. Uma imagem HDR (High Dynamic Range) é uma imagem cuja faixa de variação na intensidade luminosa é maior do que uma imagem comum. Essa imagem pode ser obtida a partir de várias fotografias de uma mesma cena com tempos de exposição diferentes. Segundo [debevec], o processo de geração de uma imagem HDR começa com a obtenção da função de resposta da câmera. Essa função é obtida a partir de uma série de fotografias e seus tempos de exposição. O algoritmo usa minimização SVD. Veja em [otim], para mais informações sobre otimização em Computação Gráfica. Uma vez obtida a função de resposta desse sistema, o mapa de radiância pode ser obtido a partir de um somatório de todas as fotografias usando essa função. Para geração de uma imagem virtual a partir do mapa de radiância, deve-se estipular um tempo de exposição e então gerar essa imagem. O resultado final a x segundos, deve ser semelhante à fotografia original nesse tempo de exposição. Interface Já que os algoritmos para geração da imagem HDR requerem um bocado de computação, minha primeira opção foi desenvolvê-los em C ou C++. Além do código para o processamento das imagens, queria que tivesse um a interface gráfica para facilitar sua utilização. Minha experiência anterior com GTK+ foi boa, então decidi utilizá-la nesse projeto. GTK+ está presente em todas as distribuições Gnu/Linux e diversas outras plataformas, incluindo Windows e MacOSX. A interface com GTK+ pode ser feita de, duas formas: programando diretamente as janelas e widgets, ou utilizando-se o Glade [glade]. Glade é um construtor de interface para GTK+. Basicamente, o usuário clica em botões, arrasta widgets, define eventos de clique de botão e vai, assim, montando sua interface. No final, é possível gerar código em C (e em C++, Ada, etc) que mostre as janelas criadas. Basta, então, depois disso, programar o que será executado quando ocorrer cada evento especificado. Hoje em dia, na verdade, é mais comum, utilizar-se a Libglade [libglade] em vez de gerar código. O programador cria interface, salva-a no formato do Glade, um arquivo XML, e chama a Libglade a partir de seu programa, passando esse arquivo como parâmetro e assim a interface é mostrada. Essa nova abordagem tem várias vantagens, dentre elas, que a interface pode ser modificada mais facilmente e sem necessidade de recompilação. Independentemente de que maneira eu iria implementar esse programa, comecei a esboçar uma janela principal para o programa usando o Glade. Depois de algum tempo li alguns artigos [pytgtk_articles] sobre PyGTK [pygtk] e aí surgiu a idéia de utilizar Python e PyGTK para esse projeto. Tendo o arquivo XML gerado pelo Glade, bastou chamá-lo através do meu programa em Python para a janela aparecer. A programação dos eventos foi feita em seguida ligando os eventos gerados pelo Libglade aos métodos das classes no meu sistema. Python [python]é uma linguagem interpretada, orientada a objetos de altíssimo nível. Muitos nomes de peso dão crédito a essa linguagem como Bruce Eckel [eckel], Eric Raymond [raymond] e outros. Eu particularmente, achei-a interessante pela produtividade, “economia de dedos” (é concisa) e pela legibilidade. Existem diversos artigos comparando-a com Java, como [java] (um pouco tendencioso). Tipicamente, um programa em Python é escrito muito mais rapidamente do que o similar escrito em outra linguagem. A manutenção do programa também foi uma preocupação minha. Programas difíceis de manter tendem a morrer por falta de atenção de seus desenvolvedores. Processamento A princípio, pensei em programar apenas a interface em Python e a computação mais intensa seria feita em um módulo escrito em C. Python é bastante lenta por ser tão dinâmica e algum pontos no programa ganhariam muito se fossem reescritos em C. Aliás, não é difícil estender Python com módulos escritos em C, porém perde-se um pouco de portabilidade, pois adiciona-se um passo a mais no ciclo de desenvolvimento, a compilação. Uma biblioteca bastante popular no mundo Python é a Numeric Python [numpy]. Ela permite fazer contas com matrizes a partir do Python. Percebi então que a obtenção da função de resposta do câmera poderia ser feita usando essa biblioteca. Na verdade, não tem nenhum prejuízo de performance, comparado com C puro, pois a montagem da matriz do sistema, que é feita em código Python, é uma parte mínima do processamento. A maior parte do tempo é gasto na resolução do sistema usando SVD, que é passada para código nativo utilizando uma biblioteca em Fortran ou C como LAPACK (não sei ao certo qual é). A vantagem é que o programa permanece inteiramente em Python. Uma segunda parte do algoritmo é a montagem do mapa de radiância. Essa montagem é na verdade uma série de somatórios ponderados, um para cada pixel das imagens. Essa parte foi primeiramente implementada um método em Python com 4 loops for aninhados (!) e se revelou lenta demais, como era de se esperar. Uma seqüência de 7 imagens de 320x200 demorou 45 segundos para ser processada em um AMD Duron 800 rodando Conectiva 10. Um outro teste no Debian 3.1 (Sarge), mais recente, levou 26 segundos. Usando Psyco [psyco], também no Debian, um compilador Just-in-time para Python conseguiu reduzir o tempo para 11 segundos. Reescreverei o algoritmo em C para poder comparar o tempo de execução. Como qualquer manual sobre Matlab/Octave diz, é melhor evitar loops e codificar utilizando operações entre matrizes. Tentei escrever uma versão desse algoritmo com operações com matrizes, usando uma técnica chamada vetorização. As técnicas que encontrei não atendiam às as necessidades desse algoritmo especificamente. Enviei um email aos autores de um artigo sobre vetorização em Python, um deles me respondeu dizendo que meu caso era um “nice puzzle” e que a solução não era óbvia. Destaque para uma diferença encontrada entre o Numeric Python e o Matlab/Octave. A fatorização SVD do primeiro retorna U, Σ e V, enquanto o segundo retorna U, Σ e VT. Talvez isso seja até um bug que, aliás, me custou quase duas semanas para resolver. Screenshots Ilustração 1 Tela inicial Ilustração 2 Curvas de resposta da câmera Ilustração 3 Imagem logarímica Bibliografia debevec: Paul E. Debevec and Jitendra Malik, Recovering High Dynamic Range Radiance Maps from Photographs, 1997, http://www.debevec.org/Research/HDR/ gsl: M. Galassi et al, GNU Scientific Library Reference Manual, , http://www.gnu.org/software/gsl/ gtk: , GTK+: The GIMP Toolkit, , http://www.gtk.org/ otim: Velho, Luiz & Carvalho, Paulo C. P., Mathematical Optimization in Graphics and Vision, 2003, http://www.visgraf.impa.br/otim-03/course-notes.pdf glade: , Glade - a User Interface Builder for GTK+ and GNOME, , http://glade.gnome.org/ libglade: , Libglade, , http://www.jamesh.id.au/software/libglade/ pytgtk_articles: , Articles and Tutorials about PyGTK, , http://www.pygtk.org/articles.html pygtk: , PyGTK: GTK+ for Python, , http://www.pygtk.org/ python: , Python programming language, , http://www.python.org/ eckel: Bill Venners, Python and the Programmer, A Conversation with Bruce Eckel, 2003, http://www.artima.com/intv/aboutme.html raymond: Eric Raymond, Why Python?, 2000, http://www2.linuxjournal.com/article/3882 java: Steve Ferg, Python & Java: a Side-by-Side Comparison, 2004, http://www.ferg.org/projects/python_java_side-by-side.html numpy: , Numerical Python, , http://www.pfdubois.com/numpy/ psyco: , Psyco, , http://psyco.sourceforge.net/