Elaborazione digitale di immagini

Argomenti trattati:
1-Immagini digitali
2-Manipolazioni dei pixel
3-Manipolazioni geometriche
4-Decomposizione spettrale, filtraggio e compressione
5-Restauro di immagini degradate
6-Crittografia con le immagini


1-Immagini digitali

Un'immagine digitale bianco nero viene descritta da una matrice mxn di pixels (contrazione di picture elements). Il generico pixel p(i,j), il cui valore numerico puo' essere intero o reale,  fornisce la luminosita' del punto di coordinate (i,j) dell'immagine.

Formato PGM (Portable Gray Map). Nella convenzione PGM il file che contiene le informazioni sull'immagine e' organizzato nel modo seguente:

P2
# eventuali commenti
numero_righe, numero_colonne
valore_livello_bianco
p(1,1)
p(1,2)
.
.
p(1,n)
p(2,1)
p(2,2)
.
.


dove:
P2 indica al sistema che visualizzera' il file sullo schermo che si tratta di una immagine bainco nero nel formato PGM
# eventual commenti  sono commenti arbitrari preceduti da #
numero_righe e numero_colonne sono due interi che determinano le dimensioni dell'immagine (matrice dei pixels)
valore_livello_bianco e' un intero che determina il valore numerico del bianco (generalmente 255), mentre il nero e' 0.
p(i,j) sono i valori numerici dei pixels (fra 0 e valore_livello_bianco) relativi alla posizione (i,j). Essi sono ordinati per righe.

Esistono diversi programmi per visualizzare un'immagine sullo schermo di un computer. Due pacchetti che girano sotto linux sono xv e gimp.
Ad esempio, se immagine.pgm e' il nome di un file che contiene un'immagine codificata come file PGM, allora il comando

xv immagine.pgm

visualizza sullo schermo il contenuto di immagine.pgm.

Esempio di file PGM
P2
# esempio semplice
4,4
255
0
0
0
0
0
255
255
0
0
255
255
0
0
0
0
0

La stessa immagine poteva essere memorizzata come
P2
# esempio semplice
4,4
255
0   0      0  0
0 255 255 0
0 255 255 0
0    0     0  0


File ASCII e file RAW
Un file PGM puo' essere memorizzato nella modalita' ASCII o nella modalita' RAW.
Nella modalita' ASCII il valore del generico pixel viene scritto nella sua rappresentazione decimale con caratteri ASCII. Ad esempio. se il valore del pixel e' 123, vengono scritti i numeri 1,2 e 3, consumando quindi tre byte di memoria. Nella rappresentazione RAW, il pixel il cui valore e' 123 viene memorizzato con il singolo byte il cui valore e' 123. In questo modo un solo byte e' sufficiente per ciascun pixel. La rappresentazione ASCII e' piu' comoda perche' permette di essere visualizzata e manipolata piu' facilmente. Ha lo svantaggio che e' piu' ingombrante. Noi useremo la rappresentazione ASCII.

Per semplicita' eviteremo di scrivere commenti nei file PGM.



Esempio 1. Costruiamo un'immagine 100x100 che abbia valori di grigio che sfumano da 0 a 255 secondo la regola p(i,j)=i+j mod 256. Usiamo il linguaggio FORTRAN 90. E' possibile comunque usare un qualsiasi altro linguaggio di programmazione visto che non svolgiamo operazioni particolarmente complesse.

Program grigi
  implicit none
  integer :: i,j
  open(file="grigi.pgm", unit=2)
  write(2,'(A)') "P2"
  write(2,*)100,100
  write(2,*) 255
  do i=1,100
    do j=1,100
      write(2,*)mod(i+j,256)
    end do
  end do
end program grigi


L'immagine che si ottiene in questo modo e' la seguente:

 
Esempio 2
. Costruiamo una immagine 200x200 tale che il pixel di posto (i,j) sia 255 (bianco) se i+j e' un numero primo, sia 0 (nero) altrimenti. Per questo ci occorre una funzione (function in fortran) che dato un intero p ci dice se p e' primo o no.

Program evidenzia_primi
  implicit none
  integer :: i,j,p,n
  logical :: primo
  n=200
  open(file="primi.pgm", unit=2)
  write(2,'(A)') "P2"
  write(2,*)n,n
  write(2,*) 255
  do i=1,n
    do j=1,n
      if (primo(n*(i-1)+j)) then
        write(2,*) 255
        else
        write(2,*)0
      end if
    end do
  end do
end program evidenzia_primi

function primo(p)
  implicit none
  integer :: i,p,q
  primo :: logical
  q=sqrt(p*1.0)
  primo=.true.
  do i=2,q
    if(mod(p,i)==0) then
      primo=.false.
      exit
    end if
  end do
end function primo

La figura che si ottiene e' la seguente

Un pochino piu' complicato, ma vale la pena farlo, e' numerare i pixels a spirale che si dipana dal centro del quadrato. In questo modo si ottiene la spirale di Ulam.


2-Manipolazione dei pixel
Esempio 3.
Vediamo come e' possibile trasformare un'immagine in negativo. Supponiamo di avere nel file foto.pgm una fotografia che vogliamo leggere, trasformare in negativo e registrarla in un file diverso. La trasformazione che dobbiamo applicare a ciascun pixel e' f(p)=255-p

Program negativo
  implicit none
  integer :: i,j,p,m,n
  character(length=2) :: ch
  open(file="foto.pgm", unit=2)
  open(file="foto_negativa.pgm", unit=3)
  read(2,'(A)')ch

  write(3,'(A)') ch
  read(2,*)m,n
  write(2,*)m,n
  read(3,*)p
  write(2,*) p
  do i=1,m
    do j=1,n
      read(2,*)p
      write(3,*)255-p
    end do
  end do
end program negativo


Esempio 4. Cosa succede dell'immagine se applichiamo la trasformazione f(p)=min(255,p+20) oppure f(p)=max(0,p-20).  Cosa succede invece se la trasformmazione e' f(p)=min(255, 2p) oppure f(p)=parte_intera(p/3)? Osservate che le operazioni di min e max servono a tenere il valore della funzione dentro il segmento consentito [0,255]. Osservate ancora che occorre prendere la parte intera. Mediante queste trasformazioni si cambiano globalmente la luminosita' e il contrasto di una immagine. Volendo, potete aumentare il contrasto e schiarire le zone scure lasciando la situazione inalterata nelle zone chiare, ad esempio con

f(p)=p se p>128
f(p)=max(2p,128) se p<=128

Ancora provate a portare in negativo solo le zone "buie" dell'immagine con

f(p)=255-p se p<=128
f(p)=p se p>128.


Immagini a colori, modalita' RGB    
Una immagine a colori puo' essere memorizzata mediante la modalita' RGB (Red, Green, Blue). In questo modo ad ogni pixel viene assegnata una terna di numeri r,g,b, dove r fornisce l'intensita' del rosso, g l'intensita' del verde e b l'intensita' del blu. Il file che viene costruito e' organizzato come segue

P3
# eventuali commenti
numero_righe, numero_colonne
massima_luminosita'
r(1,1) g(1,1) b(1,1)
r(1,2) g(1,2) b(1,2)
.
.
.

Cio' che cambia rispetto ad un file b/n e' la prima riga che contiene P3 a indicare che e' un file a colori nella modalita' RGB e la presenza delle terne di pixels (un valore per ogni colore) anziche' dei singoli valori. Le terne dei valori rgb possono non stare sulla stessa riga ma essere distribuite su righe consecutive. I file di immagini a colori hanno generalmente l'estensione PNM o PPM.

Potete provare a effettuare le manipolazioni di contrasto e luminosita' sui singoli colori in modo diverso da colore a colore, oppure potete scambiare il rosso col blu o portare solo il verde in negativo o mescolare i colori su ciascun pixel secondo una regola prefissata, ad esempio, r'=3r+g-2b mod 256, g'=r+2g+b mod 256, b'=r-g+b mod 256 e vedere l'effetto sull'immagine.
Come e' possibile trasformare un'immagine da colori a b/n?

Esempio 5. (Insiemi di Julia e bacini di attrazione). Sapreste costruire un'immagine che rappresenti i bacini di attrazione del metodo di Newton per risolvere equazioni del tipo x^n-1=0?

Altri formati    La codifica di tipo PGM, anche nella modalita' RAW ha lo svantaggio di essere molto ingombrante. Esistono altri modi di rappresentare file di immagini piu o meno vantaggiosi. Un formato particolarmente usato, che pero' conduce ad un degrado qualitativo dell'immagine e' il formato JPG. Esso si basa su tecniche di compressione ottenute mediante trasformate di coseni. Vedremo piu' avanti cosa questo significhi.


Esempio 6. videnziare i contorni. Sapreste affrontare il problema di evidenziare i contorni di una immagine assegnata?