miércoles, 6 de febrero de 2008

Destripando un BMP (24 bits)

Existe una gran variedad de formatos de imágenes tal como BMP, TIFF, GIF, PNG, JPEG, etc. El más sencillo de todos es el formato BMP de 24 bits.

Características:
No es comprimido (Una desventaja con respecto a espacio pero ventaja por su simplicidad)
Por no ser comprimido es de alta calidad
Se puede ver en cualquier visor de imágenes por ser el mas básico.

Veamos que hay dentro...



Los Primeros 54 Bytes contienen los metadatos de nuestra imagen y su estructura es la siguiente:

2 bytes - contienen siempre 'BM', para poder identificar que es un bmp
4 bytes - tamaño del archivo (en bytes)
4 bytes - Reservados, contienen ceros (reservados para usos futuros)
4 bytes - offset, distancia entre cabecera y píxels (no lo usaremos)
4 bytes - Tamaño de Metadatos (tamaño de esta estructura = 40)
4 bytes - Ancho (numero de píxeles horizontales)
4 bytes - Alto (numero de pixeles verticales)
2 bytes - Numero de planos de color (No lo usaremos, valdrá 1)
2 bytes - Profundidad de color (debe ser 24 para nuestro caso)
4 bytes - Tipo de compresión (valdrá 0, ya que es descomprimido)
4 bytes - Tamaño de la estructura Imagen
4 bytes - Píxeles por metro horizontal (No lo usaremos)
4 bytes - Píxeles por metro vertical (No lo usaremos)
4 bytes - Cantidad de colores usados (No lo usaremos)
4 bytes - Cantidad de colores importantes (Tampoco usaremos)


Suman 54 bytes, y son los primeros 54 bytes que debemos leer de nuestro archivo.
Podemos leerlo uno a uno pero lo mejor es crear una estructura y leerlos todo de un solo paso.

Ejemplo de Estuctura para Visual Basic:


Public Type tipoMetadatosBMP
       B As Byte
       M As Byte
       tamaño As Long
       reservado As Long
       offset As Long
       tamañoMetadatosB
       ancho As Long
       alto As Long
       planos As Integer
       profundidadColor As Integer
       tipoCompresion As Long
       tamañoEstructuraImagen As Long
       pixelsPorMetroH As Long
       pixelsPorMetroV As Long
       cantidadColoresUsados As Long
       cantidadColoresImportantes As Long
End Type


Ejemplo de estructura para C++:

struct tipoMetadatosBMP
{     char bm[2];
      int tamano;
      int reservado;
      int offset;
      int tamanoMetadatos;
      int alto;
      int ancho;
      short numeroPlanos;
      short profundidadColor;
      int tipoCompresion;
      int tamanoEstructura;
      int pxmh;
      int pxmv;
      int coloresUsados;
      int coloresImportantes;

} ;

No debe variar mucho de lenguaje en lenguaje.
Inmediatamente después de Nuestra cabecera (metadatos), vienen los píxeles (si, esos cuadraditos de colores que conforman nuestra imagen)

Cada Píxel se almacena en 24 bits (3 bytes)
De los 24 bits = 3 bytes, se utilizan 1 byte por cada color básico es decir...
Un byte parta R, Un byte para G, Un byte para B

NO, no son los colores rastas :P, son los colores básicos para el BMP, cada color puede ser obtenido mediante una combinacion de R (red), G (green) y B (blue).

Considerando que un byte puede almacenar 256 valores diferentes, tenemos:
256 (R) x 256 (G) x 256 colores (B) = 16777216 Colores RGB

Por alguna razón que desconozco al momento de diseñar el formato los ordenaron BGR y no RGB es decir primero el byte para azul, luego el byte para verde y luego el byte para verde.

Importante!!! :
Además despues de cada fila de pixels (barrido horizontal), si el ancho de
la imagen no es multiplo de 4, se rellena la linea con [ancho mod 4] bytes que
contienen 0.
Por ejemplo si la imagen tuviera 42 pixeles de ancho, se
rellenaria 2 bytes (el resto de 42/4) al final de cada fila de
pixeles.
Ahora ya que al leer los metadatos obtuvimos la resolución (Sí, la RESOLUCIÓN, o sea los píxeles horizontales x verticales)

Recuerdas?
...
ancho As Long
alto As Long
...

ahora podemos leer cada uno de los píxeles de la siguiente manera:

Codigo de ejemplo para leer una imagen en Visual Basic.


Dim meta As tipoMetadatosBMP
Dim i As Long, j As Long, k as Integer, resto as Integer
Dim pixelAux As tipoPixel
Dim relleno as Byte

Open nombreBMP For Binary As #1
Get #1, , meta ' Leemos los metadatos
resto = meta.ancho mod 4 'calculamos el resto

For i = 1 To meta.alto 'Recorremos las filas
    For j = 1 To meta.ancho 'Recorremos las columnas
        Get #1, , pixelAux 'leemos el pixel ...
    Next
    For k = 1 To resto 'leemos los pixels de relleno
        Get #1, , relleno
    Next
Next

Close #1

Asumimos que tenemos declarado el tipo:


Public Type tipoPixel
    b As Byte ' Azul
    g As Byte ' verde
    r As Byte ' rojo
End Type

Codigo de ejemplo para leer una imagen en C++

archivo.Open("imagen.bmp",CFile::modeCreateCFile::modeNoTruncateCFile::modeRead);

//cargamos los metadatos -----------------------------------

archivo.Read(&bm,2);
archivo.Read(&tamano,sizeof(tamano));
archivo.Read(&reservado,sizeof(reservado));
archivo.Read(&offset,sizeof(offset));
archivo.Read(&tamanoMetadatos,sizeof(tamanoMetadatos));
archivo.Read(&ancho,sizeof(ancho));
archivo.Read(&alto,sizeof(alto));
archivo.Read(&numeroPlanos,sizeof(numeroPlanos));
archivo.Read(&profundidadColor,sizeof(profundidadColor));
archivo.Read(&tipoCompresion,sizeof(tipoCompresion));
archivo.Read(&tamanoEstructura,sizeof(tamanoEstructura));
archivo.Read(&pxmh,sizeof(pxmh));
archivo.Read(&pxmv,sizeof(pxmv));
archivo.Read(&coloresUsados,sizeof(coloresUsados));
archivo.Read(&coloresImportantes,sizeof(coloresImportantes));
//----------------------------------------------------------


//Luego leemos los pixeles ---------------------------------

int resto = metadata.ancho % 4;
for (i=1;i<=metadata.alto;i++)

{    for (j=1;j<=metadata.ancho;j++)
     {    archivo.Read(&varB,1); //leemos el byte azul en la variable varB
          archivo.Read(&varG,1); //leemos el byte azul en la variable varB
          archivo.Read(&varR,1); //leemos el byte azul en la variable varB
     }
     for (k=1;k<=resto;k++)
          archivo.Read(&var,1); //leemos los pixels de relleno
}
archivo.Close(); //----------------------------------------------------------

El código es muy similar para cualquier otro lenguaje.
Espero les haya servido.
Comentarios y sugerencias a: carlosagreda-at-gmail.com


1 comentario:

Anónimo dijo...

Me salvaste la vida gracias por la informacion