/* * op_images.c * * Este programa ilustra as operações * lógico-aritméticas suportadas por OpenGL * e uma implementação de warping com as funções * de textura de OpenGL. * op_images * * São implementadas as seguintes funções: * '+': soma * '-': subtração * '*': multiplicação * 'a': AND * 'o': OR * 'x': XOR * 'i': NOT * 'w': warping (baseado no código disponível no site * http://local.wasp.uwa.edu.au/~pbourke/projection/imagewarp/) * 'z': zoom (3x Imagem 1) * 'c': convolucao * 's': filtragem + zooming (3x Imagem 1) * 'h': histograma (Imagem 1) * * As rotinas de leitura de imagens do formato * bmp foram obtidas do site * http://www.cs.ucdavis.edu/~amenta/s06/proj0/portrait.html * * Ting (5/10/06) */ // #include // Header File For The GLUT Library #include // Header File For The OpenGL32 Library #include // Header File For The GLu32 Library #include // Header file for standard file i/o. #include // Header file for malloc/free. #include // Header file for math functions. #include "readBMP.h" /* ascii code for the escape key */ #define ESCAPE 27 /* espaçamento (em pixels) entre as sub-janelas */ #define GAP 25 int win; // id da janela principal int swin1, swin2, swin3; // id das subjanelas do glut int n,m; // largura e altura das imagens int width, height; // largura e altura da janela principal GLushort opcode=GL_AND; char tipo_antigo[2]={'\0','\0'}; char tipo[2]={'a','\0'}; GLfloat gaussian[7][7] = { {1.,4.,8.,10.,8.,4.,1.}, {4.,12.,25.,29.,25.,12.,4.}, {8.,25.,49.,58.,49.,25.,8.}, {10.,29.,58.,67.,58.,29.,10.}, {8.,25.,49.,58.,49.,25.,8.}, {4.,12.,25.,29.,25.,12.,4.}, {1.,4.,8.,10.,8.,4.,1.}, }; GLfloat box[3][3] = { {1.,1.,1.}, {1.,1.,1.}, {1.,1.,1.}, }; BMPImage *image1, *image2; // data structure for image #define HISTOGRAM_SIZE 256 // potência de 2 /* Simple BMP reading code, should be adaptable to many systems. Originally from Windows, ported to Linux, now works on my Mac OS system. NOTE!! only reads 24-bit RGB, single plane, uncompressed, unencoded BMP, not all BMPs. BMPs saved by xv should be fine. */ // // This code was created by Jeff Molofee '99 // (www.demonews.com/hosted/nehe) // Ported to Linux/GLUT by Richard Campbell '99 // Code and comments for adaptation to big endian/little endian systems // Nina Amenta '04 // /* Reads a long 32 bit integer; comment out one or the other shifting line below, whichever makes your system work right. */ unsigned int endianReadInt(FILE* file) { unsigned char b[4]; unsigned int i; if ( fread( b, 1, 4, file) < 4 ) return 0; /* LITTLE VS BIG ENDIAN LINES - comment out one or the other */ i = (b[3]<<24) | (b[2]<<16) | (b[1]<<8) | b[0]; // big endian //i = (b[0]<<24) | (b[1]<<16) | (b[2]<<8) | b[3]; // little endian return i; } /* Reads a 16 bit integer; comment out one or the other shifting line below, whichever makes your system work right. */ unsigned short int endianReadShort(FILE* file) { unsigned char b[2]; unsigned short s; if ( fread( b, 1, 2, file) < 2 ) return 0; /* LITTLE VS BIG ENDIAN LINES - comment out one or the other */ s = (b[1]<<8) | b[0]; // big endian //s = (b[0]<<8) | b[1]; // little endian return s; } // quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only. // See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/BMP.txt for more info. int BMPImageLoad(char *filename, BMPImage *image) { FILE *file; unsigned long size; // size of the image in bytes. unsigned long i; // standard counter. unsigned short int planes; // number of planes in image (must be 1) unsigned short int bpp; // number of bits per pixel (must be 24) char temp; // temporary color storage for bgr-rgb conversion. // make sure the file is there. if ((file = fopen(filename, "rb"))==NULL) { printf("File Not Found : %s\n",filename); return 0; } // seek through the bmp header, up to the width/height: fseek(file, 18, SEEK_CUR); // read the width if (!(image->sizeX = endianReadInt(file))) { printf("Error reading width from %s.\n", filename); return 0; } printf("Width of %s: %lu\n", filename, image->sizeX); // read the height if (!(image->sizeY = endianReadInt(file))) { printf("Error reading height from %s.\n", filename); return 0; } printf("Height of %s: %lu\n", filename, image->sizeY); // calculate the size (assuming 24 bits or 3 bytes per pixel). size = image->sizeX * image->sizeY * 3; // read the planes if (!(planes=endianReadShort(file))) { printf("Error reading planes from %s.\n", filename); return 0; } if (planes != 1) { printf("Planes from %s is not 1: %u\n", filename, planes); return 0; } // read the bits per pixel if (!(bpp = endianReadShort(file))) { printf("Error reading bpp from %s.\n", filename); return 0; } if (bpp != 24) { printf("Bpp from %s is not 24: %u\n", filename, bpp); return 0; } // seek past the rest of the bitmap header. fseek(file, 24, SEEK_CUR); // read the data. image->data = (char *) malloc(size); if (image->data == NULL) { printf("Error allocating memory for color-corrected image data"); return 0; } if ((i = fread(image->data, size, 1, file)) != 1) { printf("Error reading image data from %s.\n", filename); return 0; } for (i=0;i rgb) temp = image->data[i]; image->data[i] = image->data[i+2]; image->data[i+2] = temp; } // we're done. return 1; } void drawstr(GLuint x, GLuint y, char* buffer) { char *s; /* setar a posição raster corrente em coordenadas da janela, de forma direta */ glWindowPos2i(x, y); for (s = buffer; *s; s++) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *s); } } void init(void) { int i, j; /* definir a cor em preto (0,0,0) como cor de "CLEAR" */ glClearColor (0.0, 0.0, 0.0, 0.0); /* definir os fatores de blending entre os valores das duas imagens na sub-janela 3 */ glutSetWindow(swin3); glBlendFunc(GL_ONE,GL_ONE); /* "normalizar" os valores do filtro */ for (i=0; i<7; i++) for (j=0; j<7; j++) gaussian[i][j] /= 1000.; for (i=0; i<4; i++) for (j=0; j<4; j++) box[i][j] /= 9.; } void main_display(void) { glClearColor(0.8, 0.8, 0.8, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(0.f, 0.f, 0.f); drawstr((int)n/2, height-m-2*GAP+5, "Oper. entre as duas imagens ou sobre a imagem 1"); drawstr((int)n/2+m+70, height-m-2*GAP+5,tipo); drawstr(0, height-GAP+5, "Imagem 1"); drawstr(n, height-GAP+5, "Imagem 2"); } void display_swin1() { // Desenhar os pixels da image1 glutSetWindow(swin1); glDisable(GL_DITHER); // resetar as cores nos buffers de cor glClear(GL_COLOR_BUFFER_BIT); /* Definir a origem da imagem em coordenadas da janela obtidas via transformações geométricas e projetivas */ glRasterPos2i(0,0); glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image1->data); // forcar a execucao dos comandos enviados a OpenGL */ glFlush(); } void display_swin2() { // Desenhar os pixels da image2 glutSetWindow(swin2); glDisable(GL_DITHER); // resetar as cores nos buffers de cor glClear(GL_COLOR_BUFFER_BIT); glRasterPos2i(0,0); glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image2->data); // forcar a execucao dos comandos enviados a OpenGL */ glFlush(); } void display_swin3() { int i, j, tam; GLfloat r, phi, nmax, mmax; char *data; GLuint texName; GLushort values[HISTOGRAM_SIZE][3]; // Desenhar os pixels resultantes da operação glutSetWindow(swin3); glDisable(GL_DITHER); // resetar as cores nos buffers de cor glClear(GL_COLOR_BUFFER_BIT); glRasterPos2i(0,0); if (opcode == GL_FUNC_SUBTRACT || opcode == GL_FUNC_ADD || opcode == 21) { switch (opcode) { case GL_FUNC_SUBTRACT: case GL_FUNC_ADD: /* operações aritméticas: adição e subtração */ /* Acumular os pixels no framebuffer (destino) */ glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image1->data); /* Habilitar operações de blending */ glEnable(GL_BLEND); /* Selecionar o peso de ponderação para os pixels de fonte e de destino */ glBlendFunc(GL_ONE,GL_ONE); /* Selecionar as operações aritméticas: GL_FUNC_ADD (default), * GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX */ glBlendEquation(opcode); /* Desenhar os novos pixels (fonte) */ glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image2->data); glDisable(GL_BLEND); break; case 21: /* multiplicação */ tam = n * m * 3; data = (char *) malloc(tam); if (data == NULL) { printf("Error allocating memory for color-corrected image data"); exit(0); return; } for (i=0;idata[i])/255.)*(((unsigned char)image2->data[i])/255.)); data[i+1] = (unsigned char)(255.*(((unsigned char)image1->data[i+1])/255.)*(((unsigned char)image2->data[i+1])/255.)); data[i+2] = (unsigned char)(255.*(((unsigned char)image1->data[i+2])/255.)*(((unsigned char)image2->data[i+2])/255.)); } /* transferir dados para framebuffer */ glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, data); free((char *)data); break; } } else if (opcode == GL_AND || opcode==GL_OR || opcode==GL_XOR || opcode==GL_INVERT) { /* operações lógicas */ glEnable(GL_COLOR_LOGIC_OP); /* transferir os pixels de destino para framebuffer */ glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image1->data); /* selecionar uma operação lógica */ glLogicOp(opcode); /* desenhar os novos pixels (fonte) */ glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image2->data); glDisable(GL_COLOR_LOGIC_OP); } else if (opcode == 11) { /* warping */ /* definir o modo como os pixels são armazenados na memória do processador */ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* definir forma de mapeamento da textura */ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* gerar mipmaps para evitar distorções */ gluBuild2DMipmaps (GL_TEXTURE_2D, GL_RGB, n, m, GL_RGB, GL_UNSIGNED_BYTE, image1->data); /* setar o modo de texturização: GL_REPLACE (substitui os valores * do pixel), GL_DECAL (pondera com o parâmetro alfa da cor), * GL_MODULATE (multiplica o valor-destino com o valor-fonte) */ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* habilitar a texturização */ glEnable(GL_TEXTURE_2D); r = sqrt (sqrt(n*n+m*m)); phi = atan2(m,n); mmax=r*sin(phi); nmax=r*cos(phi); for (i=0; idata); /* resetar o fator de zooming */ glPixelZoom(1.0,1.0); } else if (opcode == 41) { /* zoom */ glEnable(GL_CONVOLUTION_2D); // setar o modo de tratamento da borda: GL_REDUCE, GL_CONSTANT, GL_REPLICATE_BORDER glConvolutionParameteri(GL_CONVOLUTION_2D, GL_CONVOLUTION_BORDER_MODE, GL_REDUCE); // glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_LUMINANCE, 3, 3, GL_LUMINANCE, GL_FLOAT, box); glConvolutionFilter2D(GL_CONVOLUTION_2D, GL_LUMINANCE, 7, 7, GL_LUMINANCE, GL_FLOAT, gaussian); glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image1->data); glDisable(GL_CONVOLUTION_2D); } else if (opcode == 51) { /* filtragem + zooming */ data = (char *) malloc((n)*(m)*3*3*3); gluScaleImage(GL_RGB, n, m, GL_UNSIGNED_BYTE, image1->data,3*n, 3*m, GL_UNSIGNED_BYTE,data); glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, data); free((char *)data); } else if (opcode == 61) { /* histograma */ /* definir o formato e o tipo de dados do histograma */ glHistogram(GL_HISTOGRAM, HISTOGRAM_SIZE, GL_RGB, GL_FALSE); /* habilitar o histograma */ glEnable(GL_HISTOGRAM); /* transferir os dados para framebuffer */ glDrawPixels(n,m,GL_RGB, GL_UNSIGNED_BYTE, image1->data); /* ler o histograma de cor */ glGetHistogram(GL_HISTOGRAM,GL_TRUE, GL_RGB, GL_UNSIGNED_SHORT, values); glDisable(GL_HISTOGRAM); /* plotar o histograma: normalizar os valores para caber na janela */ glBegin(GL_LINE_STRIP); glColor3f(1.0, 0.0, 0.0); for (i=0; i< HISTOGRAM_SIZE; i++) glVertex2s(i, (values[i][0]/(6))); glEnd(); glBegin(GL_LINE_STRIP); glColor3f(0.0, 1.0, 0.0); for (i=0; i< HISTOGRAM_SIZE; i++) glVertex2s(i, (values[i][1]/(6))); glEnd(); glBegin(GL_LINE_STRIP); glColor3f(0.0, 0.0, 1.0); for (i=0; i< HISTOGRAM_SIZE; i++) glVertex2s(i, (values[i][2]/(6))); glEnd(); } /* atualizar a legenda na janela principal */ glutSetWindow(win); /* sobreescrever o operador antigo com a cor do fundo */ glColor3f(0.8, 0.8, 0.8); drawstr((int)n/2+m+70, height-m-2*GAP+5, tipo_antigo); /* escrever o novo operador */ glColor3f(0., 0., 0.); drawstr((int)n/2+m+70, height-m-2*GAP+5,tipo); // forcar a execucao dos comandos enviados a OpenGL */ glFlush(); } void main_reshape(int w, int h) { width = w; height = h; glViewport(0,0,(GLsizei)w,(GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, (GLfloat) 2*n, 0.0, (GLfloat) 2*(m+GAP)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* redefinir a posição e as dimensões da sub-janela 1 */ glutSetWindow(swin1); glutPositionWindow(0, GAP); glutReshapeWindow(n, m); /* redefinir a posição e as dimensões da sub-janela 2 */ glutSetWindow(swin2); glutPositionWindow(n, GAP); glutReshapeWindow(n, m); /* redefinir a posição e as dimensões da sub-janela 3 */ glutSetWindow(swin3); glutPositionWindow((int)n/2, m+2*GAP); glutReshapeWindow(n, m); } void reshape_swin1(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, (GLfloat) n, 0.0, (GLfloat) m); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void reshape_swin2(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, (GLfloat) n, 0.0, (GLfloat) m); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void reshape_swin3(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, (GLfloat) n, 0.0, (GLfloat) m); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /* ARGSUSED1 */ void keyboard(unsigned char key, int x, int y) { tipo_antigo[0] = tipo[0]; tipo[0] = key; switch (key) { case '+': opcode = GL_FUNC_ADD; break; case '-': opcode = GL_FUNC_SUBTRACT; break; case '*': opcode = 21; break; case 'a': opcode=GL_AND; break; case 'o': opcode=GL_OR; break; case 'x': opcode=GL_XOR; break; case 'i': opcode=GL_INVERT; break; case 'w': opcode= 11; break; case 'z': opcode= 31; break; case 'c': opcode= 41; break; case 's': opcode= 51; break; case 'h': opcode= 61; break; case 27: free((char *)image1->data); free((char *)image1); free((char *)image2->data); free((char *)image2); exit(0); return; } /* forçar o redesenho do conteudo da sub-janela 3 */ glutSetWindow(swin3); glutPostRedisplay(); } int main(int argc, char **argv) { int i, x, y; // allocate space for image data structure for the first image image1 = (BMPImage *) malloc(sizeof(BMPImage)); if (image1 == NULL) { printf("Error allocating space for image"); exit(0); } if (!BMPImageLoad(argv[1], image1)) { exit(1); } // allocate space for image data structure for the second image image2 = (BMPImage *) malloc(sizeof(BMPImage)); if (image2 == NULL) { printf("Error allocating space for image"); exit(0); } if (!BMPImageLoad(argv[2], image2)) { exit(1); } n= image1->sizeX; m = image1->sizeY; glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB ); glutInitWindowSize(n*2, 2*(m+GAP)); glutInitWindowPosition(0,0); win = glutCreateWindow(argv[0]); glutReshapeFunc(main_reshape); glutDisplayFunc(main_display); swin1 = glutCreateSubWindow(win,0,GAP,n,m); glutReshapeFunc(reshape_swin1); glutDisplayFunc(display_swin1); swin2 = glutCreateSubWindow(win,m,GAP,n,m); glutReshapeFunc(reshape_swin2); glutDisplayFunc(display_swin2); swin3 = glutCreateSubWindow(win,(int)n/2,m+2*GAP,n,m); glutReshapeFunc(reshape_swin3); glutDisplayFunc(display_swin3); glutKeyboardFunc(keyboard); init(); glutMainLoop(); return(0); }