INF01046
Fundamentos de Processamento de Imagens
Prof. Manuel M. Oliveira
Aluno: Lucas Enrique Guaycochea
1 Trabalho de Implementa o
OBJETIVO
O objetivo deste trabalho familiarizar-se com
algumas opera es importantes envolvendo imagens. Mais especificamente, ao
completar este trabalho se ter aprendido a:
a) Ler e gravar arquivos de imagens;
b) Exibir o conte do de um arquivo de imagem;
c) Converter uma imagem colorida em uma imagem em tons
de cinza;
d) Aplicar um esquema simples para quantiza o de
imagens;
e) Usar a biblioteca GLUT para gerenciar os servi os
de janela.
Parte I
Leitura e Grava o de Arquivos de Imagens
Pra esta
primeira parte do trabalho eu escriv o seguinte c digo de C++ :
#include
<imagems/jconfig.h>
#include
<imagems/jmorecfg.h>
#include
<imagems/jpeg_api.h>
int main(int
argc, char* argv[]){
int height, width, channels;
unsigned char *pixels;
if (!ReadJPEG(argv[1],
&pixels, &width, &height, &channels)){
WriteJPEG(argv[2], resultado, width,
height, channels);
}else
exit(-1);
return 0;
}
Este programa receve como par metros
os nomes dos arquivos de entrada e de sa da que se quer obter.
O programa foi testedo e seu
resultado foi correto.
[Discu ao sobre a diferen a ou n o
dos tamanhos dos arquivos: Pendiente]
Parte II Leitura, Exibi o e Opera es sobre Imagens
Fun es feitas pra o programa:
Espelhamentos
Horizontal:
unsigned char*
espejar_horizontal(unsigned char* pixels, int height, int widht, int channels){
int
totalbytes=height*widht*channels;
int
bytesFila=widht*channels;
unsigned
char* resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
for(int
i=0;i<height;i++)
for(int
j=0;j<widht;j++)
for(int
k=0;k<channels;k++)
resultado[(i+1)*bytesFila-(j+1)*channels+k]=pixels[i*bytesFila+j*channels+k];
return
resultado;
}
Vertical:
unsigned char*
espejar_vertical(unsigned char* pixels, int height, int widht, int channels){
int
totalbytes=height*widht*channels;
int
bytesFila=widht*channels;
unsigned
char* resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
for(int
i=0;i<height;i++){
memcpy(resultado+(totalbytes-bytesFila*(i+1))*sizeof(unsigned
char),pixels+sizeof(unsigned char)*(i*bytesFila),bytesFila);
}
return
resultado;
}
Exemplos dos resultados das fun es
s o:


Original Horizontal

Vertical
Conver o de imagem
colorida a tons de cinza:
Utiliza-se a seguinte f rmula de lumin ncia a partir da imagem RGB: L =
0.299*R + 0.587*G + 0.114*B
Na imagen resultante
vamos ter Ri=Gi=Bi=Li
unsigned char* obter_tons_cinza(unsigned char* pixels,
int height, int widht, int channels){
int totalbytes=height*widht*channels;
int bytesFila=widht*channels;
unsigned char* resultado=(unsigned
char*)malloc(totalbytes*sizeof(unsigned char));
for(int i=0;i<height;i++)
for(int j=0;j<widht;j++){
unsigned char ton_cinza=(unsigned
char)(0.299*pixels[i*bytesFila+j*channels+0]
+ 0.587*pixels[i*bytesFila+j*channels+1]
+ 0.114*pixels[i*bytesFila+j*channels+2]);
for(int k=0;k<channels;k++)
resultado[i*bytesFila+j*channels+k]=ton_cinza;
}
return resultado;
}
Os resultados podem-se olhar aqu :


Original
Tons de Cinza
Observa o: O qu acontece se o algoritmo de passagem a tons de cinza aplicado repetidas
vezes? A resposta f cil, olhe os pesos dos cores R, G e B para o c lculo da
lumin ncia, eles somam 1. Ent o ao aplicar repetidas vezes o algoritmo voce vai
ter sempre a mesma imagem em tons de cinza.
Quantiza o:
O algoritmo de quantiza o receve a quantidade de tons
em que voce quer quantizar (q), ent o divide o intervalo de 256 cores em esa
quantidade e temos intervalor de tamanho T=256/q.
Pra quantizar, o algoritmo pega o tons de cinza
original do pixels, olha a que intervalo pertenece e lhe asigna o valor m dio
desse intervalo. Esto :
Ton_quantizado=floor(Ton_original
/ T) * T + floor( T / 2)
unsigned char* cuantizacion(int cantTons,unsigned
char* pixels, int height, int widht, int channels){
int
totalbytes=height*widht*channels;
int
bytesFila=widht*channels;
unsigned
char* resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
int
tamInt=(int)(256/cantTons);
for(int
i=0;i<height;i++)
for(int
j=0;j<widht;j++)
for(int
k=0;k<channels;k++)
resultado[i*bytesFila+j*channels+k]=(unsigned
char)((int)(pixels[i*bytesFila+j*channels+k]/tamInt)*tamInt+(int)(tamInt/2));
return
resultado;
}
Distintos resultados podem-se ver a continua o:


Tons de Cinza Quantizado
com q=11


Quantizado
com q=8 Quantizado
com q=2
Salvamento dos
resultados:
Pra salvar o resultado eu fiz uso da
fun o WriteJPEG dada obtida da p gina
da disciplina.
Por exemplo: WriteJPEG(nomeSaida,resultado,
width, height, channels);
Uso de GLUT pra a
ger ncia das janelas
Como foi
sugerido, o programa usa a livrer a GLUT para a administra o das janelas. Um
dos inconvenientes do trabalho foi achado aqui, pois a imagem era mostrada na
janela como se estivese espelhada verticalmente. Depois de procurar alguma
solu o na WEB, agente ach que o origem pra o rastering fica abaixo esquerda (quando o primer pixel da imagem
o que fica arriba esquerda), ent o tive que fazer isto:
void
sourceDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, height);
glPixelZoom(1,-1);
glDrawPixels(width, height, GL_RGB,
GL_UNSIGNED_BYTE,pixels);
glutSwapBuffers();
}
Uso de GLUI pra a
intera o com o usu rio
O
programa usa a livrer a GLUI que trabalha sobre GLUT como tamb m foi sugerido. O
disenho do men b sico porque tinha problemas pra compilar. O linker tinha
problemas com redefini es de fun es em distintas bibliotecas de Microsoft.
Por
exemplo n o pude usar um spinner pra
a ele o da quantidade de tons pra fazer a quantiza o. Voce pode mudar a
quantidade original de tons (16), dobrando ou dividendo por 2 com os
respectivos bot es.
O programa
Finalmente,
vemos uma imagem (screenshot) do
programa rodando:

A continua o o c digo fonte dos
arquivos do programa: main.cpp, funciones.h e funciones.cpp.
/*
Universidade
Federal do Rio Grande do Sul
Engenharia
da Computa o
INF01046 -
Fundamentos de Processamento de Imagens
Prof.
Manuel M. Oliveira
1er
Trabalho de Implementa o
Data:
5/9/2007
Autor:
Lucas Enrique Guaycochea
arquivo:
main.cpp
descrip o:
Arquivo Principal do programa, administra as janelas e o menu
*/
#include "funciones.h"
#include <GL/glut.h>
#include <GL/glui.h>
#define SOURCE 0
#define TARGET 1
#define ESPELHAR_VERTICAL 1
#define ESPELHAR_HORIZONTAL 2
#define TONS_CINZA 3
#define QUANTIZAR 4
#define NEGATIVO 5
#define SALVAR 6
#define DOBRAR_TONS 7
#define DISM_TONS 8
int win_id[2];
unsigned char* resultado;
int height, width, channels;
unsigned char *pixels;
int qTons=16;
char* nomeSaida;
//*****************************************************
// Display function for SOURCE
//*****************************************************
void sourceDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, height);
glPixelZoom(1,-1);
glDrawPixels(width, height,
GL_RGB, GL_UNSIGNED_BYTE,pixels);
glutSwapBuffers();
}
//*******************************************************************************
// Reshape function for SOURCE
//*******************************************************************************
void sourceReshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w,
(GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, w, 0.0, h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//*****************************************************
// Display function for TARGET
//*****************************************************
void targetDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, height);
glPixelZoom(1,-1);
glDrawPixels(width, height,
GL_RGB, GL_UNSIGNED_BYTE,resultado);
glutSwapBuffers();
}
//*******************************************************************************
// Reshape function for TARGET
//*******************************************************************************
void targetReshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w,
(GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, w, 0.0, h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/***************************************** myGlutIdle() ***********/
void myGlutIdle( void )
{
/* According to the GLUT
specification, the current window is
undefined during an idle
callback. So we need to explicitly
change
it if necessary */
for (int i=0; i<2; i++) {
glutSetWindow(win_id[i]);
// seleciona a janela
glutPostRedisplay(); //
for a a atualiza o do conte do da janela
}
}
// Callback de menu
void TrataMenu ( int valor )
{
unsigned char* aux;
switch(valor)
{
case ESPELHAR_VERTICAL:
aux=pixels;
pixels=espejar_vertical(pixels,
height, width,channels);
free(aux);
break;
case
ESPELHAR_HORIZONTAL:
aux=pixels;
pixels=espejar_horizontal(pixels,
height, width,channels);
free(aux);
break;
case TONS_CINZA:
free(resultado);
resultado=obter_tons_cinza(pixels,
height, width,channels);
break;
case QUANTIZAR:
free(resultado);
aux=obter_tons_cinza(pixels,
height, width,channels);
resultado=cuantizacion(qTons,aux,
height, width,channels);
free(aux);
break;
case NEGATIVO:
negative(pixels,
height, width,channels);
break;
case SALVAR:
WriteJPEG(nomeSaida,resultado,
width, height, channels);
break;
case DOBRAR_TONS:
qTons*=2;
break;
case DISM_TONS:
qTons/=2;
break;
default:
break;
}
for (int i=0; i<2; i++) {
glutSetWindow(win_id[i]);
// seleciona a janela
glutPostRedisplay(); //
for a a atualiza o do conte do da janela
}
}
int main(int argc, char* argv[]){
nomeSaida=argv[2];
if (ReadJPEG(argv[1],
&pixels, &width, &height, &channels)){
//algun error
perror("Problema
ao tentar ler a imagem de entrada");
exit(-1);
}
resultado=(unsigned
char*)malloc(height*width*channels*sizeof(unsigned char));
memcpy(resultado,
pixels,width*height*channels);
//
// First, initialize
source window
//
glutInitDisplayMode(GLUT_DOUBLE
| GLUT_RGB);
glutInitWindowPosition(0,
0);
glutInitWindowSize(width,
height);
win_id[SOURCE] =
glutCreateWindow("Original Image");
glutDisplayFunc(sourceDisplay);
glutReshapeFunc(sourceReshape);
glPixelStorei(GL_UNPACK_ALIGNMENT,
1);
glClearColor(0.0, 0.0,
0.0, 0);
//
// Now, initialize
target window
//
glutInitDisplayMode(GLUT_DOUBLE
| GLUT_RGB);
glutInitWindowPosition(width+200,
0);
glutInitWindowSize(width,
height);
win_id[TARGET] =
glutCreateWindow("New Image");
glutDisplayFunc(targetDisplay);
glutReshapeFunc(targetReshape);
glPixelStorei(GL_UNPACK_ALIGNMENT,
1);
glClearColor(0.0, 0.0,
0.0, 0);
//MENU
/*int sourceMenu;
sourceMenu =
glutCreateMenu(TrataMenu);
glutAddMenuEntry("espelhar
vertical",ESPELHAR_VERTICAL);
glutAddMenuEntry("espelhar
horizontal",ESPELHAR_HORIZONTAL);
glutAddMenuEntry("tons de cinza",TONS_CINZA);
glutAddMenuEntry("quantizar
a dois tons",QUANTIZAR);
glutAddMenuEntry("negativo",NEGATIVO);
glutAddMenuEntry("salvar
resultado",SALVAR);
glutSetWindow(win_id[SOURCE]);
glutAttachMenu(GLUT_RIGHT_BUTTON);
*/
GLUI *glui =
GLUI_Master.create_glui( "controlador",0,width+10,height/2 );
new GLUI_Button(glui,
"Espelhar Vertical",ESPELHAR_VERTICAL ,TrataMenu );
new GLUI_Button(glui,
"Espelhar Horizontal",ESPELHAR_HORIZONTAL ,TrataMenu );
new GLUI_Button(glui,
"Tons de Cinza",TONS_CINZA ,TrataMenu );
//(new GLUI_Spinner(
glui, "Tons Pra Quantizar", &qTons ))->set_int_limits( 2, 256
);
char texto[50];
sprintf(texto,"Tons
Pra Quantizar inicial:%d ",qTons);
new GLUI_StaticText(
glui, texto );
new GLUI_Button(glui,
"(Tons Pra Quantizar) * 2",DOBRAR_TONS ,TrataMenu );
new GLUI_Button(glui,
"(Tons Pra Quantizar) / 2",DISM_TONS ,TrataMenu );
new GLUI_Button(glui,
"Quantizar",QUANTIZAR ,TrataMenu );
new GLUI_Button(glui,
"Obter negativo",NEGATIVO ,TrataMenu );
new GLUI_Button(glui,
"Salvar Resultado",SALVAR ,TrataMenu );
new GLUI_Button(glui,
"Sair", 0,(GLUI_Update_CB)exit );
glui->set_main_gfx_window(
win_id[SOURCE] );
/* We register the idle
callback with GLUI, *not* with GLUT */
GLUI_Master.set_glutIdleFunc(
myGlutIdle );
glutMainLoop();
free(resultado);
free(pixels);
return 0;
}
/*
Universidade
Federal do Rio Grande do Sul
Engenharia
da Computa o
INF01046 -
Fundamentos de Processamento de Imagens
Prof.
Manuel M. Oliveira
1er
Trabalho de Implementa o
Data:
5/9/2007
Autor:
Lucas Enrique Guaycochea
arquivo:
funciones.h
descrip o:
Arquivo Header com as fun es de opera es sobre imagens
*/
#ifndef _FUNCIONES_H__
#define _FUNCIONES_H__
#include <imagems/jconfig.h>
#include <imagems/jmorecfg.h>
#include <imagems/jpeg_api.h>
#include <string.h>
#include <stdlib.h>
void negative(unsigned char* pixels, int height, int widht, int
channels);
unsigned char* espejar_vertical(unsigned char* pixels, int height, int
widht, int channels);
unsigned char* espejar_horizontal(unsigned char* pixels, int height, int
widht, int channels);
unsigned char* obter_tons_cinza(unsigned char* pixels, int height, int
widht, int channels);
unsigned char* cuantizacion(int cantTons,unsigned char* pixels, int
height, int widht, int channels);
#endif
/*
Universidade
Federal do Rio Grande do Sul
Engenharia
da Computa o
INF01046 -
Fundamentos de Processamento de Imagens
Prof.
Manuel M. Oliveira
1er
Trabalho de Implementa o
Data: 5/9/2007
Autor:
Lucas Enrique Guaycochea
arquivo:
funciones.cpp
descrip o:
Arquivo com as fun es de opera es sobre imagens
*/
#include "funciones.h"
unsigned char* cuantizacion(int cantTons,unsigned char* pixels, int
height, int widht, int channels){
int
totalbytes=height*widht*channels;
int bytesFila=widht*channels;
unsigned char*
resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
int
tamInt=(int)(256/cantTons);
for(int i=0;i<height;i++)
for(int
j=0;j<widht;j++)
for(int
k=0;k<channels;k++)
resultado[i*bytesFila+j*channels+k]=(unsigned
char)((int)(pixels[i*bytesFila+j*channels+k]/tamInt)*tamInt+(int)(tamInt/2));
return resultado;
}
unsigned char* obter_tons_cinza(unsigned char* pixels, int height, int
widht, int channels){
int
totalbytes=height*widht*channels;
int bytesFila=widht*channels;
unsigned char*
resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
for(int i=0;i<height;i++)
for(int
j=0;j<widht;j++){
unsigned char
ton_cinza=(unsigned char)(0.299*pixels[i*bytesFila+j*channels+0] +
0.587*pixels[i*bytesFila+j*channels+1] +
0.114*pixels[i*bytesFila+j*channels+2]);
for(int
k=0;k<channels;k++)
resultado[i*bytesFila+j*channels+k]=ton_cinza;
}
return resultado;
}
unsigned char* espejar_horizontal(unsigned char* pixels, int height, int
widht, int channels){
int
totalbytes=height*widht*channels;
int bytesFila=widht*channels;
unsigned char*
resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
for(int i=0;i<height;i++)
for(int
j=0;j<widht;j++)
for(int
k=0;k<channels;k++)
resultado[(i+1)*bytesFila-(j+1)*channels+k]=pixels[i*bytesFila+j*channels+k];
return resultado;
}
unsigned char* espejar_vertical(unsigned char* pixels, int height, int
widht, int channels){
int
totalbytes=height*widht*channels;
int bytesFila=widht*channels;
unsigned char*
resultado=(unsigned char*)malloc(totalbytes*sizeof(unsigned char));
for(int i=0;i<height;i++){
memcpy(resultado+(totalbytes-bytesFila*(i+1))*sizeof(unsigned
char),pixels+sizeof(unsigned char)*(i*bytesFila),bytesFila);
}
return resultado;
}
void negative(unsigned char* pixels, int height, int widht, int channels){
int aux=0;
for(int i=0;i<widht;i++)
for(int
j=0;j<height;j++)
for(int
k=0;k<channels;k++){
pixels[aux]=255-pixels[aux];
aux++;
}
}
Miscel nea:
Como se pode observar tamb m se
implement uma fun o pra obter o negativo da imagem. Um exemplo de seu
resultado :


05/09/2007
- Lucas Enrique Guaycochea