Immagini bitmap bit a bit
Preambolo
Oggigiorno esiste una grande varietà di formati per memorizzare le immagini
in digitale. Tutti abbiamo scattato una foto JPEG, navigando su Internet avremo
incontrato molte PNG, e chi non si è mai soffermato a vedere una GIF animata?
Il formato trattato in questa pagina è il Bitmap, uno dei primi formati
ideati per memorizzare immagini: si tratta di un formato piuttosto anziano,
progettato per essere elaborato con computer poco potenti, e non fa
uso di particolari algoritmi per la compressione dell'informazione.
Ne segue che l'algoritmo per leggere o scrivere un'immagine bitmap, è
estremamente semplice.
Indice generale
Introduzione
Il file contente un'immagine bitmap può essere scomposto in tre parti principali:
- Intestazione: contiene informazioni utili per interpretare il file e costruire l'immagine, come le dimensioni, il numero di colori utilizzati, la presenza di una eventuale compressione RLE (non trattata in questo documento), ...
- Tavolozza dei colori: contiene informazioni sui colori dell'immagine a bassa profondità di colore.
- Bitmap: contiene tutti i pixel (punti) che compongono l'immagine vera e propria, in sequenza.
Come già accennato, il formato fu progettato per macchine con processori x86 con limitate (rispetto ad oggi) risorse hardware, perciò, per semplificare il caricamento in memoria delle informazioni, i byte sono in ordine little endian.
Intestazione
L'intestazione di un file Bitmap occupa sempre uno spazio fisso, i primi 54byte del file, ed è così composta:
Offset | Dimensione (bit) | Default HEX | Descrizione |
0-1 | 2 (16) | 42 4D | In codifica ASCII 42 4D corrisponde a "BM" e sta per bitmap. È il magic number che identifica il tipo di immagine |
2-5 | 4 (32) | — | Dimensione totale del file, espressa in bytes (per esempio 00 01 00 00 = 256bytes) |
6-9 | 4 (32) | 00 00 00 00 | Sono posti sempre tutti e quattro a zero, non hanno significato |
10-13 | 4 (32) | — | Offset al quale iniziano i pixel della bitmap (la terza sezione del file). |
14-17 | 4 (32) | — | |
18-21 | 4 (32) | — | Larghezza dell'immagine, espressa in pixel |
22-25 | 4 (32) | — | Altezza dell'immagine, espressa in pixel |
26-27 | 2 (16) | 01 00 | Numero di livelli (in genere 1 solo) |
28-29 | 2 (16) | — | Profondità di colore in numero di bit. Può valere 1 (0x01), 4 (0x0400), 8 (0x0800), 16 (0x1000), 24 (0x1800) oppure 32 (0x2000). Le più frequenti sono 24, 8 e 1. Per immagini a profondità di colore di 8 bit o meno, viene utilizzata una tavolozza. |
30-33 | 4 (32) | 00 00 00 00 | Tipo di compressione RLE dell'immagine (se 0, allora non c'è compressione) |
34-37 | 4 (32) | — | Dimensione in bytes della bitmap (l'immagine vera e propria, nella terza sezione del file) |
38-41 | 4 (32) | 00 00 00 00 | Risoluzione orizzontale ottimale del dispositivo di output, espressa in Pixel Per Metro. 0 non indica un valore specifico. |
42-45 | 4 (32) | 00 00 00 00 | Risoluzione verticale ottimale del dispositivo di output, espressa in Pixel Per Metro. 0 non indica un valore specifico. |
46-49 | 4 (32) | 00 00 00 00 | Numero dei colori realmente utilizzati nella tavolozza: per esempio, può capitare di dover codificare 150 colori, perciò 4bit sono pochi, mentre 8bit sono troppi: in tal caso, si approssima per eccesso (8bit) e si specifica il numero di colori realmente utilizzati in questa locazione. Se vale 0, allora, se l'immagine ha una tavolozza, tutti i colori vengono utilizzati, altrimenti la tavolozza non è presente. |
50-53 | 4 (32) | 00 00 00 00 | Informazioni accessorie sui colori (normalmente 00 00 00 00) |
Codifica RGB a 24bit
Ogni colore della realtà può essere descritto come una miscela dei tre colori primari: rosso, verde e blu (Red, Green and Blue). Ogni colore viene quantizzato, cioè codificato in maniera finita e approssimata all'interno del computer, attraverso la quantizzazione delle sue singole componenti. Per ingannare l'occhio umano, un ragionevole compromesso di qualità/spazio, è quello di codificare ogni colore primario su un byte (8 bit = 28 = 256 sfumature). Ogni colore, quindi, occupa in memoria 3byte, uno per componente, per un totale di 23 × 8 = 24 = 16.777.216 colori diversi codificabili (sarà capitato di incontrare, qualche volta, l'espressione immagine a 16 milioni di colori).
Per comodità, dato che i processori erano a 8/16bit (successivamente a 32bit e oggi anche a 64bit, ma mai a 24bit), un colore viene codificato su 32bit, cioè 4bytes. Gli 8 bit che rimangono, nelle immagini Bitmap sono posti tutti a 0 e vengono ignorati.
Codifiche più recenti, come RGBA, utilizzano questi 8bit rimanenti per il canale alfa, cioè la trasparenza. Si noti che un'immagine opaca (come una bitmap), in questa codifica avrebbe trasparenza 0xFF, ma, nel suo formato originale, in cui il canale alfa viene comunque ignorato, è 0x00.
Per esempio, questa combinazione RGB di A0 00 BB genera questa particolare sfumatura di viola.
La tavolozza
Quando si trattano immagini con pochi colori, è conveniente utilizzare una tavolozza, che contiene informazioni sui colori dell'immagine. Attraverso di essa, si può associare un nome a un insieme più piccolo dei 16 milioni di colori, in maniera tale da utilizzare solo il nome simbolico per la sua rappresentazione, e risparmiare memoria. Naturalmente, anche i nomi simbolici sono numeri, ma, essendo pochi, sono rappresentabile su un minor numero di bit (8, 4 o addirittura 1).
Dal punto di vista pratico, la tavolozza non è altro che un array, di dimensioni pari a numero di colori × 4 bytes. Si trova appena dopo l'intestazione del file, a partire dall'offset 54. La tabella chiarirà le idee:
Offset | Caratteristica | N° Colore | Descrizione |
53 | ... | ... | Ultimo byte dell'header |
54 | Blu | 0 | Componente Blu del primo colore (0) |
55 | Verde | 0 | Componente Verde del primo colore (0) |
56 | Rosso | 0 | Componente Rossa del primo colore (0) |
57 | — | 0 | Componente Alpha (0) del primo colore (0) |
58 | Blu | 1 | Componente Blu del secondo colore (1) |
59 | Verde | 1 | Componente Verde del secondo colore (1) |
60 | Rosso | 1 | Componente Rossa del secondo colore (1) |
61 | — | 1 | Componente Alpha (0) del secondo colore (1) |
62 | Blu | 2 | Componente Blu del terzo colore (2) |
e così via...
Una tavolozza completa per immagini a 8bit inizia all'offset 54 e termina all'offset 1077, per una dimensione totale di 1024bytes (4 × 256 colori). Si osservi che, seguendo l'ordinamento little endian, si tratta più di una codifica BGR che RGB.
La Bitmap

Il formato immagine Bitmap possiede questo nome in quanto, una volta definite tutte le informazioni sull'immagine, creata cioè la mappa, l'immagine è costituita semplicemente da un flusso di bit, continuo, che può essere interpretato solo avendo acquisito le informazioni dell'intestazione e dell'eventuale tavolozza.
In caso di immagini con tavolozza, il flusso di bit inizia all'offset 1078; invece, in un'immagine senza tavolozza ad alta profondità di colore, il flusso inizia subito dopo l'intestazione, in sostituzione della tavolozza (offset 54). Se l'immagine è stata codificata correttamente, il preciso offset è leggibile direttamente dall'intestazione, rendendo superflue queste considerazioni.
Da questa posizione in poi, ogni gruppo di bit (da 1 a 32, a seconda della profondità di colore dell'immagine), rappresenterà il colore di un pixel, cioè un singolo punto dell'immagine, dal basso verso l'alto, da sinistra verso destra.
Per velocizzare la fase di lettura e scrittura dell'immagine, i primi software di codifica leggevano e scrivevano 32 bit alla volta (4 byte), perciò, ogni riga di pixel, deve essere rappresentata con un numero di bit multiplo di 32. Se l'immagine non ha una risoluzione orizzontale ed una profondità di colore che le permettano di rientrare in questo limite, i bit in eccesso vengono posti a 0.
Per esempio, in una Bitmap a 8bit di 37x25, tutte le righe terminano con 3 bytes posti a 0.
Esempi
Steganografia nelle bitmap in C++: Mr Hyde
L'idea che sta alla base dell'algoritmo di Mr Hyde è molto semplice. Nella codifica RGB si "miscelano" le tre componenti primarie di ogni colore su 8bit. Cambiando opportunamente il bit meno significativo di ogni componente, è possibile alterare leggermente l'immagine in maniera tale da farle contenere un messaggio a nostro piacere. Cambiando questa tonalità si genera, al massimo, una variazione percentuale di colore dell'ordine di 10-4, ampiamente sufficiente a nascodere un messaggio senza che nessuno si accorga della sua presenza.
Il programma dimostrativo mrhyde.tar.gz è interattivo e assume che il flusso dati sia una stringa C (terminata da NULL).
Queste due immagini Bitmap, apparentemente identiche, in realtà differiscono leggermente in alcuni pixel: quella di destra, infatti, contiene un messaggio nascosto:


Ad esempio, si assuma di avere un'immagine completamente bianca, cioè
nella quale tutte le componenti sono poste a 0xFF = 11111111.
Si nasconda la lettera A (ASCII 0x40 = 01000001) nell'immagine:
si prende il primo bit di 0x40, che è 0, e lo si sostituisce con l'ultimo bit
del primo componente; si prende il secondo bit di 0x40, che è 1, e lo si sostituisce
all'ultimo bit del secondo componente. E così via, per ogni bit di 0x40,
e poi ancora, per ogni bit dei caratteri successivi.
Questo algoritmo è piuttosto avido di memoria, in quanto l'informazione
viene espansa di 8 volte!
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 Prima 11111110 11111111 11111110 11111110 11111110 11111110 11111110 11111111 Dopo ^ ^ ^ ^ ^ ^ ^ ^ 0 -----+ | | | | | | | 1 --------------+ | | | | | | 0 -----------------------+ | | | | | A (0x40) 0 --------------------------------+ | | | | 0 -----------------------------------------+ | | | 0 --------------------------------------------------+ | | 0 -----------------------------------------------------------+ | 1 --------------------------------------------------------------------+
Si osservi che le parole Prima e Dopo sono state opportunamente colorate (basandosi sui cambiamenti dei primi 3 byte) per evidenziare quanto sia insignificante la variazione di colore.
Leggere una Bitmap in MS-DOS QBasic
Il seguente codice descrive una funzione in QBasic originale, ed è in grado di caricare sullo schermo un'immagine a 256 colori con risoluzione orizzontale multipla di 4 pixel: BAS — Immagine test
