Immagini
M odelli del colore
Visualizzazione di immagini
Cattura dei pixel di una immagine
Creazione di nuove
Cenni sui formati delle immagini
Immagini a colori
•
•
Visione Artificiale 08/09
Le immagini a colori nella loro forma più semplice sono
costituiti da pixel che memorizzano 3 valori distinti (uno per
la componente rossa, uno per la componente verde, il terzo
per il blu)
Normalmente per ogni colore si utilizza un byte per cui sono
rappresentabili 256x256x256 colori diversi (circa 16 milioni)
Operazioni puntuali
2
Immagini a colori
Immagine
originale
Il canale
verde
Visione Artificiale 08/09
Operazioni puntuali
Il canale
rosso
Il canale
blu
3
Immagini a colori e Java
•
Java normalmente utilizza un int per memorizzare un pixel
– il byte meno significativo per la componente blu, poi la verde,
quindi la rossa
– Il byte più significativo (componente alfa) è il grado di
trasparenza del pixel (255 pixel opaco, 0 pixel trasparente cioè
invisibile)
– int pixel = 0XAARRGGBB;
Visione Artificiale 08/09
Operazioni puntuali
4
Immagini a colori e Java
•
Dato il valore (V) di un pixel per ottenere le singole
componenti normalmente si procede:
– R = (V >> 16) & 255;// R = (V/0x10000) & 255
– G = (V >> 8) & 255;
– B = (V) & 255;
•
In modo analogo l’operazione inversa
– V = (R<<16) | (G<<8) | (B) | 0xff000000;
– l’ultimo termine è necessario per avere un pixel opaco
– ovviamente si deve avere: 0 ≤ R,G,B ≤ 255)
Visione Artificiale 08/09
Operazioni puntuali
5
Immagini a toni di grigio
•
Un immagine a toni di grigio è una immagine in cui le tre
componenti sono uguali
– Un metodo intuitivo per operare la trasformazione da colore a
grigio è
Grigio = (R+G+B)/3
– Dato che l’occhio ha una sensibilità diversa per i diversi colori
si preferisce una media pesata
• Una scelta frequente è
Grigio = 0.299*R + 0.587*G + 0.114*B
Visione Artificiale 08/09
Operazioni puntuali
6
Immagini e Java
•
Le immagini sono solitamente trattate tramite la classe
java.awt.Image
– È una classe astratta per cui normalmente si utilizzano classi
derivate
– Per la visualizzazione si può utilizzare la classe
javax.swing.ImageIcon
– Oppure utilizzare il metodo drawImage della classe Graphics
Visione Artificiale 08/09
Operazioni puntuali
7
Semplice visualizzazione
package va.imageframe;
import javax.swing.*;
import java.awt.Image;
public class ImageFrame extends JFrame {
public ImageFrame(String imageName) {
super();
ImageIcon ii = new ImageIcon(imageName);
initFrame(ii.getImage(), imageName, ii);
}
protected void initFrame(Image img, String title, ImageIcon ii) {
setTitle(title);
this.img = img;
getContentPane().add(new JLabel(ii));
setSize(img.getWidth(this), img.getHeight(this));
pack();
setDefaultCloseOperation(defaultCloseOperation); show();
}
Visione Artificiale 08/09
Operazioni puntuali
8
Semplice visualizzazione (2)
public static void main(String[] imageFileNames) {
for(int i=0; i<imageFileNames.length; i++) {
new ImageFrame(imageFileNames[i]);
}
}
public ImageFrame(Image img, String title) {
super();
initFrame(img, title, new ImageIcon(img));
}
protected Image img;
static int defaultCloseOperation = EXIT_ON_CLOSE;
protected ImageFrame() {}
}
Visione Artificiale 08/09
Operazioni puntuali
9
Uso di ImageIO
package va.imageframe;
import
import
import
import
java.awt.image.BufferedImage;
java.io.File;
java.io.IOException;
javax.imageio.ImageIO;
public class ImageIOTester {
public static void main(String[] imageFileNames)
throws IOException {
for(int i=0; i<imageFileNames.length; i++) {
BufferedImage img = ImageIO.read(
new File(imageFileNames[i]));
new ImageFrame(img, imageFileNames[i]);
}
}
}
Visione Artificiale 08/09
Operazioni puntuali
10
Uso di PixelGrabber
package va.imageframe;
public GrabImage(Image img, String title) {
super(img, title);
}
import java.awt.*;
import java.awt.image.*;
public int[] grabPixels() {
while ( img.getWidth(null) < 0 || img.getHeight(null) < 0);
int w = img.getWidth(null);
int h = img.getHeight(null);
int[] pixels = new int[w*h];
PixelGrabber pg = new PixelGrabber(img, 0, 0, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
throw new RuntimeException("interrupted waiting for pixels!");
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
throw new RuntimeException("image fetch aborted or errored");
}
return pixels;
}
public class GrabImage extends ImageFrame {
public static void main(String[] imageFileNames) {
Toolkit tk = Toolkit.getDefaultToolkit();
for(int i=0; i<imageFileNames.length; i++) {
Image img = tk.createImage(imageFileNames[i]);
GrabImage f = new GrabImage(img, imageFileNames[i]);
int w = img.getWidth(f);
int h = img.getHeight(f);
int[] pix = f.grabPixels();
for(int j=0; j<pix.length; j++) pix[j] ^= 0xffffff;
MemoryImageSource mis =
new MemoryImageSource(w, h, pix, 0, w);
img = tk.createImage(mis);
new ImageFrame(img, "negativo: "+imageFileNames[i]);
}
}
Visione Artificiale 08/09
}
Operazioni puntuali
11
Uso di BufferedImage
package va.imageframe;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
static private BufferedImage negativo(BufferedImage img) {
int w = img.getWidth();
int h = img.getHeight();
int[] pix = img.getRGB(0, 0, w, h, null, 0, w);
for(int j=0; j<pix.length; j++) pix[j] ^= 0xffffff;
BufferedImage neg =
new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
neg.setRGB(0, 0, w, h, pix, 0, w);
return neg;
}
}
public class BufferedImageTester {
public static void main(String[] imageFileNames) throws IOException {
for(int i=0; i<imageFileNames.length; i++) {
BufferedImage img = ImageIO.read(new File(imageFileNames[i]));
new ImageFrame(img, imageFileNames[i]);
BufferedImage neg = negativo(img);
String name = imageFileNames[i].substring(0, imageFileNames[i].lastIndexOf('.'))+"-neg";
new ImageFrame(neg, name]);
ImageIO.write(neg, "PNG", new File(name+".png"));
}
}
Visione Artificiale 08/09
Operazioni puntuali
12
Sistema di riferimento convenzionale
0
j
W-1
0
i
I(i,j)
Cattura una
immagine di
larghezza W e
altezza H
H-1
Cattura una porzione
di immagine di
larghezza w e altezza
h
int[] pix = img.getRGB(startX, startY, w, h, null, offset, scansize );
int pixel = pix[offset + (y-startY)*scansize + (x-startX)];
Numero di pixel allocati (offset + (h-startY)*scansize)
int[] pix = img.getRGB(0, 0, W, H, null, 0, W );
int pixel = pix[y*W + x]; (W*H)
Visione Artificiale 08/09
Operazioni puntuali
13
Modelli di colore
•
•
Sono stati proposti molti modi alternativi per rappresentare i
colori
I modelli di colore sono in genere relativi all’applicazione
– RGB - monitor
– CMYK – cyan, magenta, yellow, black – stampanti
Visione Artificiale 08/09
Operazioni puntuali
14
Modelli di colore
– YIQ – luminance, inphase, quadrature – tv color
– HIS – hue, saturation, intensity
– HSV – hue, saturation, value
– HSB – hue, saturation, brightness
Visione Artificiale 08/09
Operazioni puntuali
15
Altri modelli per il colore
0.114   R 
 Y   0.299 0.587
 I  =  0.596 − 0.274 − 0.322  G 
  
 
 Q   0.211 − 0.523 0.312   B 
U = 0.493(B-Y)
V = 0.877(R-Y)
Visione Artificiale 08/09
0,299
-0,147
0,615
Operazioni puntuali
0,587
-0,289
-0,515
0,114
0,437
-0,100
16
Altri modelli per il colore
•
HSI
– H è il colore dominante
• 0° rosso, 120° verde, 240° blu
– S (saturazione – cioè intensità di colore)
• 0 è un tono di grigio
I = (R + G + B) / 3



0.5[ ( R - G ) + ( R - B ) ]


 B < G acos
2


( R − G ) + ( R − B )( G − B ) 

H= 


0.5[ ( R - G ) + ( R - B ) ]



 B > G - acos
2


 ( R − G ) + ( R − B )( G − B ) 
S = 1 − min ( R, G, B ) / I
Visione Artificiale 08/09
Operazioni puntuali
17
HSV
Verde
V
0°: 255, 0, 0
Giallo
60°: 255, 255, 0
Ciano
Bianco
Blu
Rosso
120°: 0, 255, 0
180°: 0, 255, 255
Magenta
240°: 0, 0, 255
H
Bianco: H indefinito, 255, 255, 255
Visione Artificiale 08/09
Operazioni puntuali
300°: 255, 0, 255
S
18
Altri modelli per il colore
•
rgb
– r = R/(R+G+B)
– g = G/(R+G+B)
– b = B/(R+G+B)
•
È una rappresentazione normalizzata rispetto all’intensità
luminosa (r+g+b=1)
– Uno dei tre valori è ridondante
Visione Artificiale 08/09
Operazioni puntuali
19
Modello HSB
…
static private BufferedImage HSB(BufferedImage img) {
int w = img.getWidth(), h = img.getHeight();
BufferedImage hsb = new BufferedImage(w,h,BufferedImage.TYPE_4BYTE_ABGR);
float[] a = new float[3];
for(int i=0; i<h; i++) {
for(int j=0; j<w; j++) {
int rgb = img.getRGB(j, i);
int r= (rgb >> 16) & 255, g = (rgb >> 8) & 255, b = (rgb) & 255;
Color.RGBtoHSB(r, g, b, a);
float H = a[0], S = a[1], B = a[2];
if(B<0.1) {
B = 0;
} else if(S<0.1) {
S = 0; B = 1;
} else {
S = 1; B = 1;
}
hsb.setRGB(j, i, Color.HSBtoRGB(H, S, B));
}
}
return hsb;
…
Visione} Artificiale
08/09
Operazioni puntuali
20
Gamma di colori visualizzabili
•
Visione Artificiale 08/09
Operazioni puntuali
I colori rappresentabili
in un monitor, non
coincidono con i colori
stampabili
21
Immagini a colori
•
•
Visione Artificiale 08/09
Spesso per limitare l’occupazione di memoria si utilizzano
un numero limitato di colori, riuscendo quindi a
memorizzare l’immagine con un minore numero di bit per
pixel (8, 4, 1)
In questo caso però l’immagine deve memorizzare anche la
LUT (Look Up Table) dei colori
Operazioni puntuali
22
Immagini a colori
Immagine
originale
Immagine a
256 colori
Immagine a Immagine a
16 colori
8 colori
Visione Artificiale 08/09
Operazioni puntuali
23
LUT dei colori
valore
pixel
R1
G1
B1
R2
G2
B2
R3
G3
B3
R4
G4
B4
R5
G5
B5
R6
G6
B6
R7
G6
B7
Valore
effettivamente
visualizzato
(R5, G5, B5)
…
final static ColorModel cm;
static {
byte[] r = new byte[256], g = new byte[256], b = new byte[256];
for(int i=1; i<128; i++) {
g[256-i] = b[i] = r[i] = (byte) (i*2+1);
}
cm = new IndexColorModel(8, 256, r, g, b, 128);
}
…
Visione Artificiale 08/09
Operazioni puntuali
24
Immagini bitmap (bmp)
typedef struct {
short magic; /* "BM" */
long file_dim; /* dimensione file */
long l0; /* 0 */
Struttura del file:
long header_dim; /* dimensione header */
long l40; /* 40 */
long xsize; /* numero colonne */
long ysize; /* numero righe */
short nchan; /* 1 */
intestazione,
lut dei colori utilizzati
dati dell’immagine per righe
(le righe devono essere
di dimensione multipla
di 4 byte)
short zsize; /* 1-4-8-24 */
long compression; /* 0 -> non compresso */
long data_dim; /* dimensione dati */
long xppi;
long yppi;
long colors; /* dimensione lut */
long colors1;
} bmp_header;
Visione Artificiale 08/09
Operazioni puntuali
25
Immagini portable gray map (pgm)
•
Struttura del file:
prima riga “P5”
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente numero di colonne e numero di righe
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente il valore massimo del range (normalmente
255)
i dati dell’immagine (1 byte per pixel)
Visione Artificiale 08/09
Operazioni puntuali
26
Immagini portable gray map (pgm)
•
Struttura del file:
prima riga “P2”
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente numero di colonne e numero di righe
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente il valore massimo del range (normalmente
255)
i dati dell’immagine (interi in formato testo compresi fra 0 e
“valore massimo”)
Visione Artificiale 08/09
Operazioni puntuali
27
Immagini portable pixel map (ppm)
•
Struttura del file:
prima riga “P6”
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente numero di colonne e numero di righe
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente il valore massimo del range (normalmente
255)
i dati dell’immagine (3 byte per pixel, terne RGB: valore per il
colore rosso, verde, blu)
Visione Artificiale 08/09
Operazioni puntuali
28
Immagini portable pixel map (ppm)
•
Struttura del file:
prima riga “P3”
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente numero di colonne e numero di righe
un numero arbitrario (anche 0) di righe di commento che
iniziano con “#”
una riga contenente il valore massimo del range (normalmente
255)
i dati dell’immagine (3 interi per pixel, terne RGB: valore per il
colore rosso, verde, blu)
Visione Artificiale 08/09
Operazioni puntuali
29
Immagini gif
Permettono di memorizzare immagini con al più 256 colori
diversi
Iniziano con “GIF89a”
Segue un header con le informazioni sulla struttura
dell’immagine (righe, colonne, numero di colori, …)
Una tabella di lut con le terne corrispondenti ai colori
utilizzati
i dati dell’immagine (logicamente 1 byte per pixel) in
formato compresso
Visione Artificiale 08/09
Operazioni puntuali
30
Immagini gif
•
•
Immagine “ppm” circa 290 Kb
Immagine “gif” circa 53 Kb
•
N.B. a partire dalla “gif” è possibile riottenere l’immagine
“ppm” di partenza (se il numero di colori è limitato)
– Non si ha perdita di informazione
Visione Artificiale 08/09
Operazioni puntuali
31
Immagini jpg
•
•
Visione Artificiale 08/09
Si scompone l’immagine a colori in blocchi di 16x16 pixel, si
fa una analisi in frequenza del blocco e si eliminano le alte
frequenze (che l’occhio umano non percepisce bene)
Se le immagini devono essere utilizzate da un osservatore
umano il risultato è generalmente ottimo: buon aspetto e
pochi dati da trasmettere/memorizzare
Operazioni puntuali
32
Immagini jpg
•
•
Immagine “ppm” circa 290 Kb
Immagine “jpg” circa 25 Kb
•
N.B. a partire dalla “jpg” non è possibile riottenere
l’immagine “ppm” di partenza
– Si ha perdita di informazione
•
Il grado di compressione (ma quindi anche la perdita di
informazione) è un parametro del processo di
trasformazione
Visione Artificiale 08/09
Operazioni puntuali
33
Istogramma
•
•
•
Un primo esempio di elaborazione di immagini è l’istogramma
L’istogramma trasforma l’immagine in un vettore
Ogni elemento del vettore contiene il numero delle volte che il
valore v è presente nell’immagine (o se il vettore è
normalizzato la sua frequenza).
– Dal punto di vista della programmazione significa
scandire l’intera immagine e per ogni pixel
incrementare un elemento opportuno del vettore
• (C o Java) istogramma[ immagine[i][j] ]++
Visione Artificiale 08/09
Operazioni puntuali
34
Istogramma
package va.imageframe;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
public class Istogramma {
public static void main(String[] args) throws IOException {
BufferedImage img = ImageIO.read(new File(args[0]));
new ImageFrame(img, args[0]);
int w = img.getWidth(), h = img.getHeight();
int[] B = MemoryImageSourceTester.getB(img);
int[] istogramma = new int[256];
for(int i=0; i<B.length; i++) {
istogramma[B[i]]++;
B[i] = 0xff000000 | B[i] * 0x10101;
}
BufferedImage brightness = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
brightness.setRGB(0, 0, w, h, B, 0, w);
new ImageFrame(brightness, "Brightness").setLocation(100, 300);
String out = "";
for(int i=0; i<istogramma.length; i++) out += i + " " + istogramma[i] + "\n";
Frame f = new Frame("Istogramma");
f.add(new TextArea(out));
f.setBounds(600, 100, 150, 400);
f.setVisible(true);
}
}
Visione Artificiale 08/09
Operazioni puntuali
35
Istogramma
Visione Artificiale 08/09
Operazioni puntuali
36