Trabalho Prático 2

Visualizador de Imagens HDR

Programação de Baixo Nível

10/2016

1 Introdução

Alcance ou intervalo dinâmico (em inglês, dynamic range) em fotografia descreve a razão entre as intensidades mínimas e máximas mensuráveis de luz (preto e branco, respectivamente). Porém, no mundo real nunca encontramos preto ou branco verdadeiros, apenas graus diversos de intensidade das fontes de luz e reflectância dos objetos.
Uma imagem HDR (high dynamic range) é uma imagem capaz de representar um intervalo maior de luminosidade do que é possível com técnicas convencionais de imagens digitais ou fotografia. O objetivo é mostrar um alcance similar ao que experimentamos com a visão humana. Esta, através de adaptação da íris e outros mecanismos, se ajusta constantemente e automaticamente de acordo com a variação de iluminação presente nos ambientes. O cérebro humano interpreta continuamente essa informação, de forma que possamos enxergar em uma variedade enorme de condições de iluminação.
Por exemplo, uma imagem que pode se beneficiar dessa técnica é a que aparece abaixo: ela contém luz solar direta e muito intensa, mas também áreas de sombra. Ao capturarmos essa imagem com uma câmera digital, precisamos decidir se desejamos ajustar a exposição para a parte clara ou para a parte escura - nesta imagem, estamos expondo a parte clara.
figure monte1.jpg
Se decidirmos ajustar a exposição para a parte escura, a parte clara desaparecerá (dizemos que ficará "superexposta"):
figure monte2.jpg
Uma imagem HDR resolve esse problema, pois consegue capturar toda essa variação de intensidade luminosa. Mas como produzir imagens desse tipo? Uma técnica utilizada é combinar várias imagens obtidas com uma câmera (não HDR), mas com exposições diferentes (o que é denominado bracketing em fotografia). Por exemplo, a partir de 5 imagens do mesmo ambiente, geramos uma imagem HDR:
figure cathedral.jpg
Porém, a maior parte das telas atuais de computador e TVs não têm a capacidade de exibir toda a variação luminosa em uma imagem HDR - apenas recentemente começaram a aparecer as TVs Ultra HD (4K) com HDR, bem como alguns modelos de tablets e celulares. Portanto, também é preciso desenvolver técnicas para exibir os resultados. Essas técnicas são chamadas, genericamente, de mapeamentos tonais (tone mapping). O objetivo é reduzir o contraste de uma imagem HDR, de forma a exibi-la em dispositivos com alcance dinâmico inferior.
Neste trabalho, você implementará um programa para ler uma imagem em um formato HDR e exibi-la usando um algoritmo de tone mapping. Um exemplo pode ser visto abaixo:
figure cathdr.jpg

2 Funcionamento

Ao ser iniciado, o programa deve carregar um arquivo de imagem. Essas imagens estão em um formato especial (sufixo .hdf). É importante entendermos a diferença entre esse formato e uma imagem convencional. Imagens são geralmente representadas por uma matriz de pontos (pixels) onde cada cor é definida por 3 componentes: vermelho (R), verde (G) e azul (B). Cada uma dessas componentes usualmente é codificada em um byte, o que produz 3 bytes por pixel (24 bits) - ou seja, 16 milhões de possíveis cores. Em outras palavras, as intensidades (R, G, B) variam de 0 a 255, onde 0 é escuro e 255 é claro.
Veja abaixo como diversas cores são representadas nesse formato - cada cor está expressa pelas componentes RGB em hexadecimal.
figure hex-colors.jpg figure rgbcolors.jpg
Já uma imagem HDR pode possuir valores maiores que esses limites, sendo geralmente representada em ponto flutuante (valores float, por exemplo). O formato HDF codifica as cores de forma diferente (ver seção 2.1↓), mas na prática o resultado é uma representação float.
Após a leitura da imagem, o usuário deverá definir o fator de exposição desejado, a fim de "clarear" ou "escurecer" a imagem. E finalmente, o programa deve aplicar o algoritmo de tone mapping e converter a imagem para 24 bits.
A ordem do processo é a seguinte:
  1. Carregar a imagem (seção 2.1↓).
  2. Aplicar o fator de exposição desejado (seção 2.2↓).
  3. Aplicar o algoritmo de tone mapping (seção 2.3↓).
  4. Converter o resultado para 24 bits (seção 2.4↓).
  5. Retornar ao passo 2, caso a exposição seja alterada pelo teclado.
As próximas seções detalham como implementar cada parte do processo.

2.1 Leitura da imagem HDF

A imagem no formato HDF consiste primeiramente em um header, onde as informações estão organizadas da seguinte forma:
Após a leitura do header, o programa deve alocar memória suficiente para armazenar o restante do arquivo, que contém os pixels da imagem. Cada pixel é codificado em 4 bytes:
Esse formato é capaz de representar dados em ponto flutuante de uma forma bastante econômica. Para obter os valores RGB float a partir dos 4 bytes do pixel, basta realizar o seguinte cálculo:
Onde é o fator de conversão calculado a partir da mantissa:
Por exemplo, supondo o trecho da imagem abaixo:
figure headerimg.png
O primeiro pixel da imagem tem os valores hexa (0x80, 0x40, 0x20, 0x81) - ou em decimal (128,64,32,129). O fator de conversão é então .
Portanto, os pixels convertidos para float resultam em: (1.0, 0.5, 0.25)
Observe que por ser uma imagem HDR, não há limite superior definido. Ou seja, as componentes , e podem assumir qualquer valor.

2.2 Aplicação do fator de exposição

O fator de exposição é meramente um valor float (f) que é multiplicado por cada componente de cor , antes de realizar o tone mapping:
Esse fator deve ser inicializado com 1.0, o que corresponde a nenhuma alteração.

2.3 Algoritmo de tone mapping

Como já mencionado, mapeamento tonal ou tone mapping tem o objetivo de reduzir o alcance dinâmico de uma imagem, de forma que ela possa ser exibida em um disposito não HDR. Há duas categorias principais de algoritmos desse tipo: tone mapping global e tone mapping local. O primeiro tipo, onde está o algoritmo que será implementado, visa alterar todos os pixels da imagem seguindo um mesmo critério (ou algoritmo). Já o segundo tipo leva em consideração outras características da imagem, como bordas de objetos, etc. Esse tipo de algoritmo é muito mais sofisticado e geralmente produz resultados melhores.
Neste trabalho, implementaremos um algoritmo de tone mapping global, denominado tone mapping por escala.
A ideia desse algoritmo é simples: o objetivo final é obter componentes de cor no intervalo 0...1, de forma que possam ser mapeadas diretamente para RGB de 24 bits (0...255). Portanto, uma forma de fazer isso é dividir cada componente pelo seu próprio valor acrescido de uma constante qualquer:
A constante k pode ser um valor entre 0 e 1, como 0.5.

2.4 Conversão para 24 bits

Após a aplicação do fator de exposição e do algoritmo de tone mapping, o resultado ainda será uma imagem representada por floats, mas agora possivelmente dentro do invervalo 0...1. Portanto, para converter cada pixel de entrada já corrigido pelo passo anterior ( para RGB 24 bits , basta fazer:
Ou seja, limita-se o valor a 1 e multiplica-se por 255, convertendo o resultado para inteiro no final - tecnicamente, deve ser gerada um vetor de unsigned char (bytes sem sinal).

3 Código base e imagens de teste

O arquivo hdr-base.zip contém o projeto completo do Code::Blocks para a implementação do trabalho. Esse código já implementa a exibição de uma imagem 24 bits na tela gráfica, usando a biblioteca OpenGL. O projeto pode ser compilado no Linux ou no Windows, bastando selecionar o target desejado no Code::Blocks.
Se for utilizado o Mac OSX, o projeto também pode ser compilado manualmente pelo terminal:
gcc main.c opengl.c -Wno-deprecated -o hdrvis -framework OpenGL -framework GLUT -lm

O arquivo imagens-hdf.zip contém diversas imagens que podem ser utilizadas para testar o programa. Cada imagem é oferecida em duas versões: formato .hdf e em JPEG, para conferência.
figure images.jpg

4 Avaliação

Leia com atenção os critérios de avaliação: