Representación de imágenes de escala de grises en pantallas LCD

Introducción

1. Escala de grises
La escala de grises utiliza tonalidades de negro para representar objetos, empleando el negro como color base y distintos niveles de saturación para mostrar la imagen. Cada objeto en escala de grises posee un valor de brillo que va desde el 0% (blanco) hasta el 100% (negro). Las imágenes generadas con escáneres en blanco y negro o escala de grises suelen mostrarse en este formato.

El valor de cada píxel se cuantifica y almacena en un byte (8 bits). Si se discretizan los valores continuos de negro-gris-blanco en 256 niveles, el rango de valores será de 0 a 255, representando desde la oscuridad máxima (negro) hasta la luminosidad máxima (blanco). Una fotografía en blanco y negro contiene todos los tonos intermedios; cada píxel es uno de los 256 posibles valores de gris.

2. Imagen en escala de grises
Una imagen representada mediante escala de grises se denomina imagen en escala de grises. En este formato, los tres canales RGB cumplen la relación: R = G = B.

3. Formato de archivo BMP para imágenes en escala de grises
La estructura detallada del archivo BMP se resume en la siguiente tabla:

Sección Desplazamiento Nombre del campo Tamaño Descripción
Cabecera del archivo de imagen 0000h Identificador 2 bytes Contenido para reconocer el tipo de mapa de bits: 'BM' para Windows 3.1x, 95, NT, ...
0002h Tamaño del archivo 1 dword Tamaño total del archivo en bytes
0006h Reservado 1 dword Reservado, se establece a 0
000Ah Desplazamiento de datos de mapa de bits 1 dword Desplazamiento desde el inicio del archivo hasta el inicio de los datos de la imagen
000Eh Tamaño de la cabecera de mapa de bits 1 dword Longitud de la cabecera de información del mapa de bits, que describe colores, método de compresión, etc. Valores comunes: 28h (Windows), 0Ch (OS/2 1.x), F0h (OS/2 2.x)
0012h Ancho 1 dword Ancho de la imagen en píxeles
0016h Alto 1 dword Alto de la imagen en píxeles
001Ah Planos 1 word Número de planos de color del mapa de bits
Cabecera de información de la imagen 001Ch Bits por píxel 1 word Número de bits por píxel: 1 (monocromo), 4 (16 colores), 8 (256 colores), 16 (color alto), 24 (color verdadero), 32 (color verdadero con canal alfa)
001Eh Compresión 1 dword Método de compresión: 0 (BI_RGB, sin compresión), 1 (BI_RLE8), 2 (BI_RLE4), 3 (BI_BITFIELDS)
0022h Tamaño de datos de mapa de bits 1 dword Tamaño de los datos de la imagen en bytes. Debe ser múltiplo de 4
0026h Resolución horizontal 1 dword Resolución horizontal en píxeles por metro
002Ah Resolución vertical 1 dword Resolución vertical en píxeles por metro
002Eh Número de colores 1 dword Número de colores usados en la imagen. Ej.: 100h (256) para imágenes de 8 bits por píxel
0032h Colores importantes 1 dword Número de colores considerados importantes. Igual al número de colores indica que todos son igualmente importantes
Datos de la paleta 0036h Paleta N * 4 bytes Especificación de la paleta. Cada entrada usa 4 bytes para describir los valores RGB: 1 byte para azul, 1 byte para verde, 1 byte para rojo y 1 byte de relleno (0)
Datos de la imagen 0436h Datos del mapa de bits x bytes Tamaño dependiente del método de compresión. Contiene todos los bytes de datos de la imagen, que son índices hacia la paleta de colores

Ejemplo: gray.bmp

1) Información de la imagen gray.bmp

2) Datos del archivo gray.bmp

0000h: Identificador es "BM"

0002h: Tamaño del archivo es 0x13036 (77878 bytes)

0006h: Reservado

000Ah: Desplazamiento a los datos de mapa de bits es 0x436

000Eh: Longitud de la cabecera de información es 0x28

0012h: Ancho de la imagen es 0x140 (320 píxeles)

0016h: Alto de la imagen es 0xF0 (240 píxeles)

001Ah: Número de planos es 1

001Ch: Bits por píxel es 8

001Eh: Sin compresión

0022H: Tamaño de los datos de la imagen es 0x12C00 (76800 bytes)

0026H: Resolución horizontal es 7200

002Ah: Resolución vertical es 7200

002Eh: Número de colores - campo no asignado

0032h: Colores importantes - campo no asignado

0036h-0435h: Datos de la paleta

0436h: Zona de datos de la imagen

3) Interpretación

La zona de datos de la imagen almacena la información válida. Un byte de almacenamiento corresponde a un píxel de la imagen, y su valor numérico representa el nivel de gris de dicho píxel. Para una imagen de 320x240 píxeles, la zona de datos ocupa 76800 bytes. Sumando la cabecera de 1078 bytes, el tamaño total del archivo es 77878 bytes.

Durante la visualización, se leen los datos de la zona de datos de la imagen. Una vez obtenido el valor de gris de un píxel, se utiliza como índice para buscar en la paleta el color RGB correspondiente y luego se muestra en la pantalla.

Nota: Windows especifica que el número de bytes por fila de escaneo debe ser múltiplo de 4, rellenando con ceros si es necesario. Por ejemplo, una imagen de 318 píxeles de ancho realmente ocupa 320 bytes por fila.

4) Orden de almacenamiento de la imagen en archivos BMP

Los archivos BMP almacenan la imagen desde la esquina inferior izquierda hasta la superior derecha, es decir, "de abajo hacia arriba y de izquierda a derecha".

Al mostrar la imagen en pantalla se debe respetar este orden: de abajo hacia arriba y de izquierda a derecha. Si se muestra "de arriba hacia abajo y de izquierda a derecha", la imagen aparecerá invertida respecto al eje Y.

Conversión de formato de 8 bits en escala de grises a RGB565 de 16 bits

En una imagen en escala de grises, los canales RGB cumplen R=G=B. Para obtener datos en formato RGB, se sustituye el valor de gris en los tres canales. Considerando que el formato de visualización del LCD es 5:6:5 (rojo:verde:azul) y el valor de gris es de 8 bits, la conversión se realiza mediante las siguientes fórmulas:

  • Componente rojo (5 bits): r = valor_gris / 8 (equivalente a valor_gris >> 3)
  • Componente verde (6 bits): g = valor_gris / 4 (equivalente a valor_gris >> 2)
  • Componente azul (5 bits): b = valor_gris / 8 (equivalente a valor_gris >> 3)

La conversión de un valor de 8 bits a RGB565 de 16 bits se implementa con la siguiente macro:

#define ESCALA_GRISES_A_RGB16(nivel_gris) ((nivel_gris >> 3) | ((nivel_gris & ~3) << 3) | ((nivel_gris & ~7) << 8))

Programa de ejemplo

/**
 * @brief  Muestra una imagen en escala de grises en el LCD
 * @param  posX, posY: Coordenadas de inicio en el LCD
 *         archivo: Nombre del archivo BMP en escala de grises almacenado en la tarjeta SD
 * @retval Ninguno
 */
void PresentarImagenGrisLCD(unsigned short posX, unsigned short posY, char *archivo)
{
    int fila, columna;
    int ancho_img, alto_img, ancho_alineado;
    BITMAPFILEHEADER cabecera_archivo;
    BITMAPINFOHEADER cabecera_info;
    WORD tipo_archivo;
    unsigned int bytes_leidos;

    f_mount(0, &sistema_archivos[0]);
    DEBUG_BMP("Sistema de archivos montado\r\n");
    resultado = f_open(&archivo_bmp, (char *)archivo, FA_OPEN_EXISTING | FA_READ);

    if (resultado == FR_OK)
    {
        DEBUG_BMP("Archivo abierto con éxito\r\n");

        /* Leer los primeros dos bytes para identificar el tipo de archivo */
        f_read(&archivo_bmp, &tipo_archivo, sizeof(WORD), &bytes_leidos);

        /* Verificar si es un archivo BMP mediante la firma "BM" */
        if (tipo_archivo != 0x4d42)
        {
            DEBUG_BMP("El archivo no es de tipo BMP\r\n");
            return;
        }
        else
        {
            DEBUG_BMP("Archivo BMP reconocido correctamente\r\n");
        }

        /* Leer la cabecera del archivo BMP */
        f_read(&archivo_bmp, &cabecera_archivo, sizeof(tagBITMAPFILEHEADER), &bytes_leidos);
        MostrarCabeceraArchivo(&cabecera_archivo);

        /* Leer la cabecera de información de la imagen */
        f_read(&archivo_bmp, &cabecera_info, sizeof(BITMAPINFOHEADER), &bytes_leidos);
        MostrarCabeceraInfo(&cabecera_info);
    }
    else
    {
        DEBUG_BMP("Error al abrir el archivo\r\n");
        return;
    }

    ancho_img = cabecera_info.biWidth;
    alto_img = cabecera_info.biHeight;

    /* Calcular el ancho real de la fila, asegurando que sea múltiplo de 4 */
    ancho_alineado = CALCULAR_ANCHO_FILA(ancho_img);
    if (ancho_alineado > 320)
    {
        DEBUG_BMP("\nERROR: IMAGEN DEMASIADO GRANDE (MÁX. 320)\n");
        return;
    }

    /* Configurar la dirección de escaneo del LCD: esquina inferior derecha -> esquina superior izquierda */
    ConfigurarEscaneoLCD(3);

    /* Abrir una ventana en el LCD del tamaño de la imagen */
    AbrirVentanaLCD(posX, posY, ancho_img, alto_img);

    /* Verificar si la imagen es de 8 bits en escala de grises */
    if (cabecera_info.biBitCount == 8)
    {
        /* Mover el puntero del archivo al inicio de los datos de la imagen */
        f_lseek(&archivo_bmp, 0x0436);

        for (fila = 0; fila < alto_img; fila++)
        {
            /* Leer una fila completa de datos de la imagen */
            f_read(&archivo_bmp, buffer_fila, ancho_alineado, &bytes_leidos);

            for (columna = 0; columna < ancho_img; columna++)
            {
                unsigned short color_rgb;
                color_rgb = buffer_fila[columna];
                EscribirDatoLCD(ESCALA_GRISES_A_RGB16(color_rgb));
            }
        }
    }
    else
    {
        DEBUG_BMP("ERROR: LA IMAGEN NO ES DE 8 BITS EN ESCALA DE GRISES");
        return;
    }
    f_close(&archivo_bmp);
}

Referencias: Cabeceras de archivos BMP y visualización de imágenes de 8 bits en escala de grises.

Etiquetas: BMP escala de grises LCD STM32 RGB565

Publicado el 6-13 18:21