Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Manejo de imagenes BMP con Visual C++

Estas en el tema de Manejo de imagenes BMP con Visual C++ en el foro de C/C++ en Foros del Web. Hola a todos, estoy realizando un programa (en consola de Visual C++) y necesito leer el contenido de una imagen BMP pixel por pixel, he ...
  #1 (permalink)  
Antiguo 19/07/2007, 07:57
 
Fecha de Ingreso: junio-2004
Mensajes: 2
Antigüedad: 20 años, 6 meses
Puntos: 0
Manejo de imagenes BMP con Visual C++

Hola a todos, estoy realizando un programa (en consola de Visual C++) y necesito leer el contenido de una imagen BMP pixel por pixel, he visto que se puede hacer eso facilmente con Builder C++ (obtener el valor RGB de cada pixel e incluso separar los valores R, G y B independientemente) , pero debo hacerlo con Visual C++ y desde la consola, no se si existe alguna libreria util para este proposito o como puede hacerse.
Gracias anticipadas por su ayuda.
  #2 (permalink)  
Antiguo 19/07/2007, 09:10
Avatar de Instru  
Fecha de Ingreso: noviembre-2002
Ubicación: Mexico
Mensajes: 2.751
Antigüedad: 22 años, 1 mes
Puntos: 52
Re: Manejo de imagenes BMP con Visual C++

Cita:
Iniciado por acertijo_x Ver Mensaje
Hola a todos, estoy realizando un programa (en consola de Visual C++) y necesito leer el contenido de una imagen BMP pixel por pixel, he visto que se puede hacer eso facilmente con Builder C++ (obtener el valor RGB de cada pixel e incluso separar los valores R, G y B independientemente) , pero debo hacerlo con Visual C++ y desde la consola, no se si existe alguna libreria util para este proposito o como puede hacerse.
Gracias anticipadas por su ayuda.
Pues un BMP es un archivo como cualquier otro. Asi que un buen uso de fopen y fread te dara la solucion, solo que hay que tener buena informacion sobre el formato y la cabecera de archivos bmp.
Wn wotsit hay info dobre la cabecera de los bmps.

Si no tienes ni idea, intenta usar SDL o allegro, esas librerias tienen funciones directas para cargar y mostrar bmps.

Saludos
  #3 (permalink)  
Antiguo 27/07/2007, 09:26
 
Fecha de Ingreso: junio-2005
Ubicación: Argentina
Mensajes: 90
Antigüedad: 19 años, 6 meses
Puntos: 2
De acuerdo Re: Manejo de imagenes BMP con Visual C++

Hola. Mirá, yo programo con Dev-C++, así que supongo que el compilador que uso es el de ANSI C++ estándar.
Casualmente tuve que hacer lectura de archivos BMP e hice una class para resolver la cuestión.
En primer lugar, necesitás la documentación de cómo es la estructura de un archivo BMP. Eso lo podés encontrar en la siguiente página:

http://atlc.sourceforge.net/bmp.html

Te cuento que yo solamente necesité hacer reconocimiento de archivos blanco y negro monocromo, o sea que solamente hice lectura bit a bit, y un bit 1 significa PIXEL BLANCO y un bit 0 significa PIXEL NEGRO.

Hay tres cosas a tener en cuenta:

1) Los BMPs son archivos con una estructura específica y estandarizada. Los primeros bytes (que son muchos) se usan para dar información general del formato de la imagen, resolución, tamaño, paleta de colores, etc.
En la segunda y última parte del BMP se coloca la lista de pixels. El modo en que deben interpretarse dichos pixels está especificado en la parte de formato.

2) Lo más común es que los pixels se especifiquen uno a uno. En tal caso, la imagen se descompone en líneas de un mismo ancho. El ancho de la línea se debe ''redondear'' a un DWORD, esto quiere decir que, si al terminarse la línea, los pixels usados en el BMP no son un múltiplo exacto de un DWORD (4 Bytes), entonces se rellena con 0's lo que falta para completar el DWORD.

3) También es lo más común que las líneas se almacenen en orden inverso, es decir, que la primer línea de la imagen figure al final del archivo. O sea que la imagen te queda patas para arriba.

Acá te paso mi programita, que es muy básico, y solamente sirve para leer BMPs monocromos.
Como asumí que mis archivos de entrada eran monocromos, no hice verificaciones al respecto, y solamente usé los primeros 26 bytes de la sección de formato del BMP. El resto lo ignoré olímpicamente, y salté directamente a la lectura de datos.
Para que las cosas estén mejor hechas, tendrás que agregarle la capacidad de leer archivos con paletas de colores, etc.
El formato en tales casos es muy preciso, así que no creo que tengas problemas, solamente debes tener en cuenta que la interpretación de 1 bit = 1 pixel, solamente es válida en el caso monocromo.

Otra cosa más que noté en la práctica, es que la información está guardada en paquetes de DWORDs, y resultó que los Bytes me quedaban ''al revés'' de lo que me parecía lógico, así que tuve que intercalar un método make_swap.

Espero que mi código te sirva de inspiración al menos, para notar qué tipos de inconvenientes hay que resolver para leer un BMP.
Con colores la cosa será más interesante, buena suerte.


Código:
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <cstring>
#include <stdlib.h>

//////////////////////////////////////////////////////////////////////////////
// CLASS BMPimage
//////////////////////////////////////////////////////////////////////////////

// La clase BMPimage sólo reconoce archivos BMP blanco y negro de 1 bit

#define Bits_in_a_BYTE 8
#define DWORD 4                   // Cantidad de BYTES que ocupa un DWORD
#define Reading_Pack Bits_in_a_BYTE*DWORD

#define BYTE_base 0x100L          // == 256: Base numérica que se obtiene al 
                                  // representar números con una base igual al
                                  // total de números representables con 1 BYTE
// Potencias 0, 1, 2, 3, de BYTE_base
#define B0 1L
#define B1 B0*BYTE_base
#define B2 B1*BYTE_base
#define B3 B2*BYTE_base

#define MAX_PARAMS 0x001A
#define BMP_Identifier   0x0000L
#define BMP_FileSize     0x0002L
#define BMP_ResLaterUse  0x0006L
#define BMP_DataOffset   0x000aL
#define BMP_HeaderSize   0x000eL
#define BMP_PixelsWidth  0x0012L
#define BMP_PixelsHeight 0x0016L

#define MASK (0x1UL << (Reading_Pack - 1))

// Lo importante en la siguiente definición es que INTEGER sea un 
// entero de 4 BYTES. Si en su sistema el tipo INT no tiene 4 BYTES, 
// utilice otro, como LONG INT:

#define INTEGER unsigned int

class BMPimage {
   public:
     BMPimage(char *filename);
     ~BMPimage();
     
     void PrintBitMap(const char On, const char Off) const;
   protected:
     char *fname;
     ifstream f;
     int width, height;
     vector<bool> bitmap;     
};

BMPimage::BMPimage(char *filename)
{
  char parameters[MAX_PARAMS];
  union {
    char buffer[DWORD];
    unsigned char byte[DWORD];
    INTEGER bits;    

    void make_swap(void) { 
       unsigned char x; 
       x = byte[0]; byte[0] = byte[3]; byte[3] = x;
       x = byte[1]; byte[1] = byte[2]; byte[2] = x;
    }
    
    unsigned long int count(void)  {
       return B3*byte[3]+B2*byte[2]+B1*byte[1]+B0*byte[0];
    }

  } u;
  
  unsigned long begdata, enddata, width_inside_file;
  
  fname = filename;
      
  f.open(fname, ios::in | ios::binary);
                       
  f.read(parameters, sizeof(parameters));
  
  strncpy(u.buffer,parameters+BMP_PixelsWidth,DWORD);  
  width = u.count();
  
  strncpy(u.buffer,parameters+BMP_PixelsHeight,DWORD);
  height = u.count();
  
  strncpy(u.buffer,parameters+BMP_DataOffset,DWORD);
  begdata = u.count();
  
  strncpy(u.buffer,parameters+BMP_FileSize,DWORD);
  enddata = u.count();
  
  width_inside_file = (enddata - begdata) / height;
  
  for(int i = 1; i <= height; i++)
    {
      // Las líneas en el archivo BMP están guardadas de abajo hacia arriba,
      // luego conviene hacer una lectura hacia atrás, desde el fin del archivo
      f.seekg(enddata - width_inside_file*i);
      
      // Leer una línea      
      for(long int k = width; k>0; k -= Reading_Pack)
        {
          f.read(u.buffer, DWORD);
          
          // Los 4 Bytes leídos deben reordenarse 
          // porque el formato BMP originalmente los coloca invertidos           
          u.make_swap();               

          for (int j=0; j<((k>=Reading_Pack)? Reading_Pack: k); j++)
             bitmap.push_back((u.bits << j) & MASK);
        }
    }
    
  f.close();
}

BMPimage::~BMPimage()
{ 
   bitmap.clear();
   fname = NULL;
   width = 0;
   height = 0;
}

void BMPimage::PrintBitMap(const char On, const char Off) const
{
  unsigned long offset = 0;
  for (int i = 0; i < height; cout << endl, i++)
    for (int j = 0; j < width; j++, offset++)
       cout << (bitmap[offset]? On: Off);          
}

//////////////////////////////////////////////////////////////////////////////
// MAIN
//////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
  cout << "MASK == " << hex << MASK << endl;
  
  BMPimage dibujillo("./dibujo.bmp");
  
  dibujillo.PrintBitMap(' ','#');  

  system("PAUSE");      
  
  return 0;
}
Te convendría experimentar este código con imágenes monocromas que tengan menos de 80 pixels de ancho, para que el resultado te salga bien en la pantalla de MS-DOS.
Una vez que entiendas cómo funciona la clase BMPimage, podrás usar el vector de pixels para tus propósitos.

Un problema que no pude resolver es el de forzar al compilador a darme un tipo entero de 4 BYTES, sin importar cuántos BYTES ocupen los enteros en cada implementación.
¿Hay alguna forma de asegurar un entero de 4 Bytes usando las directivas de compilación #if, #elif?
No pude hacerlo porque no me deja usar el operador sizeof() en las expresiones que pongo en #if.
Si eso se arregla, en vez de especificar en forma explícita que INTEGER es un INT, podría usar algo como esto (que el compilador no me permite no sé por qué):

Código:
#if  (sizeof(int) == DWORD)
   #define INTEGER unsigned int
#elif  (sizeof(long int) == DWORD)
   #define INTEGER unsigned long
#else
   #error Compile Error: Necesito sizeof(INTEGER) == DWORD (== 4 BYTES)
#endif

Última edición por argentinator; 27/07/2007 a las 09:35
  #4 (permalink)  
Antiguo 27/07/2007, 14:13
Avatar de Instru  
Fecha de Ingreso: noviembre-2002
Ubicación: Mexico
Mensajes: 2.751
Antigüedad: 22 años, 1 mes
Puntos: 52
Re: Manejo de imagenes BMP con Visual C++

Cita:
Iniciado por argentinator Ver Mensaje
Hola. Mirá, yo programo con Dev-C++, así que supongo que el compilador que uso es el de ANSI C++ estándar.
Casualmente tuve que hacer lectura de archivos BMP e hice una class para resolver la cuestión.
En primer lugar, necesitás la documentación de cómo es la estructura de un archivo BMP. Eso lo podés encontrar en la siguiente página:

http://atlc.sourceforge.net/bmp.html

Te cuento que yo solamente necesité hacer reconocimiento de archivos blanco y negro monocromo, o sea que solamente hice lectura bit a bit, y un bit 1 significa PIXEL BLANCO y un bit 0 significa PIXEL NEGRO.

Hay tres cosas a tener en cuenta:

1) Los BMPs son archivos con una estructura específica y estandarizada. Los primeros bytes (que son muchos) se usan para dar información general del formato de la imagen, resolución, tamaño, paleta de colores, etc.
En la segunda y última parte del BMP se coloca la lista de pixels. El modo en que deben interpretarse dichos pixels está especificado en la parte de formato.

2) Lo más común es que los pixels se especifiquen uno a uno. En tal caso, la imagen se descompone en líneas de un mismo ancho. El ancho de la línea se debe ''redondear'' a un DWORD, esto quiere decir que, si al terminarse la línea, los pixels usados en el BMP no son un múltiplo exacto de un DWORD (4 Bytes), entonces se rellena con 0's lo que falta para completar el DWORD.

3) También es lo más común que las líneas se almacenen en orden inverso, es decir, que la primer línea de la imagen figure al final del archivo. O sea que la imagen te queda patas para arriba.

Acá te paso mi programita, que es muy básico, y solamente sirve para leer BMPs monocromos.
Como asumí que mis archivos de entrada eran monocromos, no hice verificaciones al respecto, y solamente usé los primeros 26 bytes de la sección de formato del BMP. El resto lo ignoré olímpicamente, y salté directamente a la lectura de datos.
Para que las cosas estén mejor hechas, tendrás que agregarle la capacidad de leer archivos con paletas de colores, etc.
El formato en tales casos es muy preciso, así que no creo que tengas problemas, solamente debes tener en cuenta que la interpretación de 1 bit = 1 pixel, solamente es válida en el caso monocromo.

Otra cosa más que noté en la práctica, es que la información está guardada en paquetes de DWORDs, y resultó que los Bytes me quedaban ''al revés'' de lo que me parecía lógico, así que tuve que intercalar un método make_swap.

Espero que mi código te sirva de inspiración al menos, para notar qué tipos de inconvenientes hay que resolver para leer un BMP.
Con colores la cosa será más interesante, buena suerte.


Código:
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <cstring>
#include <stdlib.h>

//////////////////////////////////////////////////////////////////////////////
// CLASS BMPimage
//////////////////////////////////////////////////////////////////////////////

// La clase BMPimage sólo reconoce archivos BMP blanco y negro de 1 bit

#define Bits_in_a_BYTE 8
#define DWORD 4                   // Cantidad de BYTES que ocupa un DWORD
#define Reading_Pack Bits_in_a_BYTE*DWORD

#define BYTE_base 0x100L          // == 256: Base numérica que se obtiene al 
                                  // representar números con una base igual al
                                  // total de números representables con 1 BYTE
// Potencias 0, 1, 2, 3, de BYTE_base
#define B0 1L
#define B1 B0*BYTE_base
#define B2 B1*BYTE_base
#define B3 B2*BYTE_base

#define MAX_PARAMS 0x001A
#define BMP_Identifier   0x0000L
#define BMP_FileSize     0x0002L
#define BMP_ResLaterUse  0x0006L
#define BMP_DataOffset   0x000aL
#define BMP_HeaderSize   0x000eL
#define BMP_PixelsWidth  0x0012L
#define BMP_PixelsHeight 0x0016L

#define MASK (0x1UL << (Reading_Pack - 1))

// Lo importante en la siguiente definición es que INTEGER sea un 
// entero de 4 BYTES. Si en su sistema el tipo INT no tiene 4 BYTES, 
// utilice otro, como LONG INT:

#define INTEGER unsigned int

class BMPimage {
   public:
     BMPimage(char *filename);
     ~BMPimage();
     
     void PrintBitMap(const char On, const char Off) const;
   protected:
     char *fname;
     ifstream f;
     int width, height;
     vector<bool> bitmap;     
};

BMPimage::BMPimage(char *filename)
{
  char parameters[MAX_PARAMS];
  union {
    char buffer[DWORD];
    unsigned char byte[DWORD];
    INTEGER bits;    

    void make_swap(void) { 
       unsigned char x; 
       x = byte[0]; byte[0] = byte[3]; byte[3] = x;
       x = byte[1]; byte[1] = byte[2]; byte[2] = x;
    }
    
    unsigned long int count(void)  {
       return B3*byte[3]+B2*byte[2]+B1*byte[1]+B0*byte[0];
    }

  } u;
  
  unsigned long begdata, enddata, width_inside_file;
  
  fname = filename;
      
  f.open(fname, ios::in | ios::binary);
                       
  f.read(parameters, sizeof(parameters));
  
  strncpy(u.buffer,parameters+BMP_PixelsWidth,DWORD);  
  width = u.count();
  
  strncpy(u.buffer,parameters+BMP_PixelsHeight,DWORD);
  height = u.count();
  
  strncpy(u.buffer,parameters+BMP_DataOffset,DWORD);
  begdata = u.count();
  
  strncpy(u.buffer,parameters+BMP_FileSize,DWORD);
  enddata = u.count();
  
  width_inside_file = (enddata - begdata) / height;
  
  for(int i = 1; i <= height; i++)
    {
      // Las líneas en el archivo BMP están guardadas de abajo hacia arriba,
      // luego conviene hacer una lectura hacia atrás, desde el fin del archivo
      f.seekg(enddata - width_inside_file*i);
      
      // Leer una línea      
      for(long int k = width; k>0; k -= Reading_Pack)
        {
          f.read(u.buffer, DWORD);
          
          // Los 4 Bytes leídos deben reordenarse 
          // porque el formato BMP originalmente los coloca invertidos           
          u.make_swap();               

          for (int j=0; j<((k>=Reading_Pack)? Reading_Pack: k); j++)
             bitmap.push_back((u.bits << j) & MASK);
        }
    }
    
  f.close();
}

BMPimage::~BMPimage()
{ 
   bitmap.clear();
   fname = NULL;
   width = 0;
   height = 0;
}

void BMPimage::PrintBitMap(const char On, const char Off) const
{
  unsigned long offset = 0;
  for (int i = 0; i < height; cout << endl, i++)
    for (int j = 0; j < width; j++, offset++)
       cout << (bitmap[offset]? On: Off);          
}

//////////////////////////////////////////////////////////////////////////////
// MAIN
//////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
  cout << "MASK == " << hex << MASK << endl;
  
  BMPimage dibujillo("./dibujo.bmp");
  
  dibujillo.PrintBitMap(' ','#');  

  system("PAUSE");      
  
  return 0;
}
Te convendría experimentar este código con imágenes monocromas que tengan menos de 80 pixels de ancho, para que el resultado te salga bien en la pantalla de MS-DOS.
Una vez que entiendas cómo funciona la clase BMPimage, podrás usar el vector de pixels para tus propósitos.

Un problema que no pude resolver es el de forzar al compilador a darme un tipo entero de 4 BYTES, sin importar cuántos BYTES ocupen los enteros en cada implementación.
¿Hay alguna forma de asegurar un entero de 4 Bytes usando las directivas de compilación #if, #elif?
No pude hacerlo porque no me deja usar el operador sizeof() en las expresiones que pongo en #if.
Si eso se arregla, en vez de especificar en forma explícita que INTEGER es un INT, podría usar algo como esto (que el compilador no me permite no sé por qué):

Código:
#if  (sizeof(int) == DWORD)
   #define INTEGER unsigned int
#elif  (sizeof(long int) == DWORD)
   #define INTEGER unsigned long
#else
   #error Compile Error: Necesito sizeof(INTEGER) == DWORD (== 4 BYTES)
#endif
Que bueno que tengas el tiempo de dar una explicacion tan completa,pero si vez la fecha del post, veras que el que pregunto, nunca regreso. Son usuarios, como dice Eternal Idol, inexistentes.

Saludos
  #5 (permalink)  
Antiguo 29/07/2007, 12:46
 
Fecha de Ingreso: junio-2005
Ubicación: Argentina
Mensajes: 90
Antigüedad: 19 años, 6 meses
Puntos: 2
Re: Manejo de imagenes BMP con Visual C++

Bueno, pero no ha pasado tanto tiempo desde el momento que se posteó la pregunta. Hay gente que no está enganchada al foro todos los días.
Además, los foros son públicos para que les sirva a todos, no solamente al que preguntó. Así que no me parece mal poner una explicación completa, ni tampoco me lleva demasiado tiempo (escribo a la velocidad que pienso, gracias a los programitas que enseñan a tipear rápido, como el TyperShark Deluxe, que es un juego muy bien hecho,).

Y además veo que hacés citas de posts completos. No entiendo por qué, si no hace falta. Conviene usar el botón RESPONDER antes que el boton CITAR para responder mensajes, si no, queda todo muy redundante.



Te retruqué todo lo que dijiste Instru, pero es con buena onda.
Saludos
  #6 (permalink)  
Antiguo 29/07/2007, 18:50
Avatar de Instru  
Fecha de Ingreso: noviembre-2002
Ubicación: Mexico
Mensajes: 2.751
Antigüedad: 22 años, 1 mes
Puntos: 52
Re: Manejo de imagenes BMP con Visual C++

Cita:
Iniciado por argentinator Ver Mensaje
Bueno, pero no ha pasado tanto tiempo desde el momento que se posteó la pregunta. Hay gente que no está enganchada al foro todos los días.
Además, los foros son públicos para que les sirva a todos, no solamente al que preguntó. Así que no me parece mal poner una explicación completa, ni tampoco me lleva demasiado tiempo (escribo a la velocidad que pienso, gracias a los programitas que enseñan a tipear rápido, como el TyperShark Deluxe, que es un juego muy bien hecho,).

Y además veo que hacés citas de posts completos. No entiendo por qué, si no hace falta. Conviene usar el botón RESPONDER antes que el boton CITAR para responder mensajes, si no, queda todo muy redundante.



Te retruqué todo lo que dijiste Instru, pero es con buena onda.
Saludos
Jejeje no hay problema.
Si uso el boton de citar es por la costumbre de querer responder posts especificos, noal aire.

Saludos
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 18:28.