Image Class
Biblioteca para Manipulação de Imagens

Prof. Márcio Sarroglia Pinho


Resumo

Esta página descreve o funcionamento da biblioteca ImageClass, construída em C++, com base
nas bibliotecas OpenGL, GLUT e SOIL. O objetivo da biblioteca é permitir a carga e a manipulação de imagens. São suportados os formatos BMP, PNG e JPG.

Condições Preliminares

Esta página assume que a biblioteca será utilizada no ambiente Code::Blocks, para Windows, Linux e MacOS. O compilador testado é o GCC.

Caso deseje utilizar em outro compilador, você precisará inicialmente gerar um projeto que contenha as bibliotecas da OpenGL. A seguir, inclua os fontes que são fornecidos com a ImageClass.

Para usar a bilbiteca no XCode, do Mac, você pode usar o projeto disponível neste link: ProjetoXCODE.zip. Note que no caso do MAC, será necessário editar no nome da imagem a se carregada, colocando o caminho completo do nome.


Exemplo de utilização da classe ImageClass

Para compilar programas com a ImageClass, baixe e descompacte este arquivo: ImageClass.zip.

A seguir, abra no Code::Blocks o projeto Imagens.cbp. Selecione o ambiente operacional que você está utilizando. Para tanto, abra o menu Build, acesse a opção Select target e abra a opção adequada.

Ative a opção Rebuild no Code::Blocks. Isto deverá compilar o programa e permitir a execução.

Execute o programa. Isto deve carregar uma imagem e exibi-la na tela, gerando uma tela semelhante à da figura a seguir.



Figura - Tela Inicial do Programa de Carga e Manipulação de Imagens

Pressione a tecla "2" e observe o que acontece. Isto deverá gerar, no lado direito da tela, uma versão em duas cores da imagem da esquerda.


Carga de uma Imagem

Abra o fonte ExemploDeManipulacaoDeImagens.cpp. voiNo início deste arquivo são definidos dois objetos da classe ImageClass:

ImageClass Image, NewImage;

Estes objetos são usados para representar as imagens usadas no programa.
 

Para carregar uma imagem em memória, observe o funcionamento da função void init().
Esta função, apresentada a seguir, realiza a carga de uma imagem através do método Load. Se desejar mudar a imagem que é carregada, altere a string passada como parâmetro para o método Load.

A função também faz a criação de uma imagem vazia(objeto NewImage), que possuirá o mesmo tamanho da imagem que for carregada. O objetivo é usar esta imagem para exibir o resultado de processamentos feitos sobre a imagem que for carregada. No exemplo da imagem anterior, trata-se da imagem da direita.

// **********************************************************************
//  void init(void)
// **********************************************************************
void init()
{
    int r;
    // Carrega a uma imagem
    r = Image.Load("Falcao.jpg"); // Carrega uma imagem
//    r = Image.Load("Ruido2.bmp"); // Carrega uma imagem

    if (!r) exit(1); // Erro na carga da imagem
    else cout << ("Imagem carregada!\n");

    // Ajusta o tamnho da imagem da direita, para que ela
    // passe a ter o mesmo tamnho da imagem recem carregada
    // Caso precise alterar o tamanho da nova imagem, mude os parâmetros
    // da na chamada abaixo
    NewImage.SetSize(Image.SizeX(), Image.SizeY(), Image.Channels());
    cout << "Nova Imagem Criada" << endl;

}

Exibição de uma Imagem

A exibição das imagens armazenadas na ImageClass é feita conforme o exemplo a seguir, retirado do fonte
ExemploDeManipulacaoDeImagens.cpp. No código, procure pela função void display().
Incialmente, deve-se definir o tamanho da exibição da imagem atraves dos métodos SetZoomH e SetZoomH, que permitem redimensionar a imagem que é exibida na tela. Este métodos não alteram o tamanho real da imagem, apenas mudam a apresentação desta na tela.
Por padrão, a imagem é exibida na coordenada (0,0) que, no exemplo, corresponde ao canto inferior direito.
 
// **********************************************************************
//  void display( void )
//
// **********************************************************************
void display( void )
{
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
   

// Ajusta o ZOOM da imagem para que apareca na metade da janela
    float zoomH = (glutGet(GLUT_WINDOW_WIDTH)/2.0)/(double)Image.SizeX();
    float zoomV = (glutGet(GLUT_WINDOW_HEIGHT))/(double)Image.SizeY();
    Image.SetZoomH(zoomH);
    Image.SetZoomV(zoomV);

// posiciona a imagem nova na metada da direita da janela
    NewImage.SetPos(glutGet(GLUT_WINDOW_WIDTH)/2, 0);

// Ajusta o ZOOM da imagem para que apareca na metade da janela
    NewImage.SetZoomH(zoomH);
    NewImage.SetZoomV(zoomV);

// Coloca as imagens na tela
    Image.Display();
    NewImage.Display();

// Mostra a tela OpenGL
    glutSwapBuffers();
}

Acesso aos Pontos da Imagem

Para acessar os pontos das imagem a ImageClass prossui os métodos ReadPixel, DrawPixel, GetPointIntensity, entre outros.
Via de regra este pontos são endereçados por coordenadas X,Y e possuem uma cor definida por três componentes R, G e B.
O detalhamento deste métodos é apresentado na proxima seção.
No código apresentado a seguir, a imagem representada pelo objeto Image é varrida ponto por ponto e a o valor da intersidade deste ponto é obtido pelo método
GetPointIntensity(x,y).  A partir desta intensidade, um ponto na imagem NewImage é exibido em preto e branco.
 
// **********************************************************************
//  void ConvertBlackAndWhite()
// **********************************************************************
void ConvertBlackAndWhite()
{
    unsigned char r,g,b;
    int x,y;
    int i;
    cout << "Iniciou Black & White....";

    for(x=0; x<Image.SizeX(); x++)
    {
        for(y=0; y<Image.SizeY(); y++)
        {
            i = Image.GetPointIntensity(x,y); // VERIFICA O TOM DE CINZA DA IMAGEM
            Image.ReadPixel(x,y,r,g,b);

            if (i < LIMIAR)
            {
                NewImage.DrawPixel(x, y,0,0,0);  // exibe um ponto PRETO na imagem
            }
            else NewImage.DrawPixel(x, y, 255,255,255); // exibe um ponto VERMELHO na imagem
        }
    }
    cout << "Concluiu Black & White.\n";
}

Métodos da Biblioteca

A biblioteca ImageClass permite o acesso a imagens através de um conjunto de métodos. O principais métodos são apresentados na tabela abaixo.

Método Descrição
ImageClass(void) Construtora da classe. Atenção: Não define um tamanho para a imagem. Posteriormente o programa deve carregar um arquivo de imagem.
ImageClass(int sizeX, int sizeY) Construtora da classe. Cria uma imagem que terá um tamanho pré-definido.
int Load(char *); Carrega uma imagem de disco. A biblioteca é capaz de carregar imagens PNG, BMP e JPG.
void Display(void); Exibe a imagem na tela. Deve ser usado somente dentro do método que trata o evento de redesenho da tela, em OpenGL.  
void Delete(void); Libera a área de memoria criada para armazenar a imagem. Não remove o objeto da memória. A partir da execução deste método pode-se executar novamente o método Load para carregar um arquivo de imagem.
void DrawPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b); Coloca um pixel de cor (r,g,b) na coordenada (x,y) da imagem. O pixel só aparece na tela quando da próxima execução do  método Display.
void DrawLineH(int y, int x1, int x2, unsigned char r, unsigned char g, unsigned char b); Traça uma linha horizontal entre os pontos (x1,y) e (x2,y), com a cor (r,g,b). A linha só aparece na tela quando da próxima execução do  método Display.
void DrawLineV(int x, int y1, int y2,unsigned char r, unsigned char g, unsigned char b ); Traça uma linha vertical entre os pontos (x,y1) e (x,y2), com a cor (r,g,b). A linha só aparece na tela quando da próxima execução do  método Display.
void ReadPixel(GLint x, GLint y, unsigned char &r, unsigned char &g, unsigned char &b); Lê da imagem a cor (r,g,b) do pixel que está na coordenada (x,y) da imagem. 
int GetPointIntensity(int x, int y); Retorna o nível de intensidade da cor do pixel (x,y). O valor retornado pode ser considerado como o nível de cinza do ponto (x,y).
int SizeX(); Informa a largura da imagem em pixels.
int SizeY(); Informa a altura da imagem em pixels.
void Clear(); Limpa o conteúdo da imagem, pintado o fundo de branco.  O resultado deste método só aparece na tela quando da próxima execução do  método Display.
void SetPos(int X, int Y); Posiciona o canto superior direito da imagem na coordenada (x,y) da janela.
void SetZoomH(float H);
void SetZoomV(float V);
Métodos que definem o nível de zoom para a exibição da imagem. A imagem só é atualizada na tela quando da próxima execução do  método Display.
float GetZoomH();
float GetZoomV(); 
Métodos que informam o nível de zoom que stá sendo usado para a exibição da imagem.



Tratamento de Eventos em OpenGL  -  Usando Teclado e Redesenhando a Tela

Um programa que utiliza OpenGL deve gerenciar os eventos que ocorrem durante a execução do programa
. Para tanto, inicialmente, devem ser declarados os tratadores de eventos. No exemplo desta página, a função main faz esta declaração. Veja os comentários no trecho de programa abaixo.

int  main ( int argc, char** argv )  
{
    glutInit            ( &argc, argv );
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB );
    glutInitWindowPosition (0,0);

    // Define o tamanho da janela grafica do programa
    glutInitWindowSize  ( 650, 500);


    // Cria a janela na tela, definindo o nome da
    // que aparecera na barra de título da janela.
    glutCreateWindow    ( "Manipula imagens" );
       
    // executa algumas inicializações, entre elas a
    // carga da imagem. 
    init ();

   
    // Define que o tratador de evento para
    // o redesenho da tela. A funcao "display"
    // será chamada automaticamente quando
    // for necessário redesenhar a janela   
    glutDisplayFunc ( display ); 


    // Define que o tratador de evento para
    // o redimensionamento da janela. A funcao "
reshape"
    // será chamada automaticamente quando
    // o usuário alterar o tamanho da janela
    glutReshapeFunc ( reshape );


    // Define que o tratador de evento para
    // as teclas. A funcao "
keyboard"
    // será chamada automaticamente sempre 
    // o usuário pressionar uma tecla comum

    glutKeyboardFunc ( keyboard );

    // Define que o tratador de evento para
    // as teclas especiais(F1, F2,... ALT-A,
    // ALT-B, Teclas de Seta, ...).
    // A funcao "
arrow_keys" será chamada
    // automaticamente sempre o usuário
    // pressionar uma tecla especial

    glutSpecialFunc ( arrow_keys );

    // inicia o tratamento dos eventos

    glutMainLoop ( );      
  

    return 0;
}


A seguir, são comentados os tratadores alguns dos eventos.

Tratador de evento de teclado

Este tratador de evento é chamado toda vez que uma tecla for pressionada. O parâmetro "key"  informa qual foi a tecla pressionada e os parâmetros x,y informa a posção do mouse sobre a janela no momento em que este evento ocorreu.

Importante: caso haja alguma modificação na imagem dentro deste tratador de evento, é obrigatória a chamada da função "
glutPostRedisplay()". Esta função irá forçar o sistema de gerência de janela a chamar o tratador de eventos responsável pela exibição da iamgem na tela. Caso isto não seja feito, a tela não será atualizada.
 
void keyboard ( unsigned char key, int x, int y ) 
{
    // Chame aqui os metodos que fazem o processamento da imagem
   
    switch ( key )
    {
        case 27:        // Termina o programa qdo
            exit ( 0 );   // a tecla ESC for pressionada
            break;
        case '2':
            NovaImagem.Clear(); // limpa a imagem que fica à direita da tela
            ConvertBlackAndWhite();
            glutPostRedisplay();  // obrigatorio para redesenhar a tela
            break;  
        default:       
            break;
    }
}


De forma similar a a função "keyboard", existe o tratador de teclas especiais. Veja a função "
arrow_keys" para maiores detalhes.

Tratador de evento de redesenho

Sempre que for necessário redesenhar a janela, o tratador de eventos "display" será ativado. Este evento é chamado automaticamente pelo sistema operacional quando, por exemplo, a janela está 'embaixo' de outra e por alguma razão passa 'para cima'.
Além disto, o programa deve forçar a ativação deste tratator sempre que fizer alguma alteração nas imagens que aparecem na janela. Esta ativação entretanto, deve ser feita a partir da chamada da função
"glutPostRedisplay()". Note que o programa nunca deve chamar a rotina "display()" de forma direta.
FIM.