Sei sulla pagina 1di 333

Tutte le informazioni contenute in questo volume sono

rigorosamente sotto COPYRIGHT di AMC ITALY e non


possono essere distribuite o utilizzate senza permesso
scritto della stessa.
QUALSIASI ABUSO SARA PERSEGUITO A TERMINI DI LEGGE

Versione 0.1.1
Flavio Bernardotti f.bernardotti@amcitaly.net
PROJECT MANAGER PROGETTO BIOTRACCIABILITA

Copyright 2004 AMC Italy


Via La Pira, 21
10028 Trofarello (TO)
ITALY
www.amcitaly.net
info@amcitaly.net

Introduzione
Il progetto biotracciabilit costituito da diversi gruppi logici
relativi ciascuno ad una problematica differente come ad esempio
l'acquisizione dell'immagine della retina, la sua elaborazione
grafica, la creazione del codice di preselezione all'interno di un
database, la sua comparazione con un'immagine di retina acquisita
precedentemente e cos via sino a giungere al sistema informativo
destinato alla gestione logica di tutto insieme.
Uno dei blocchi, tra l'altro uno di quelli pi delicati da cui
dipende la buona riuscita di tutto resto del progetto, quello
relativo alla creazione del sistema ottico capace di eseguire
l'acquisizione dell'immagine della retina.
Normalmente quando si progetto un obiettivo si tiene conto dei
parametri relativi allo stesso per cui le modifiche del percorso
ottico vengano considerate esclusivamente ed intrinsecamente al
obiettivo.
Purtroppo la retina non come uno degli oggetti che ci circondano
nel mondo esterno ma un qualche cosa presente all'interno di una
sfera dentro la quale si accede tramite un foro di dimensioni
ridotte il quale possiede a sua volta una lente utilizzata
dall'occhio stesso per la focalizzazione delle immagine del nostro
mondo reale.
Questa lente costituisce un'alterazione del percorso ottica o di
un ipotetico obiettivo studiato per acquisire l'immagine retinale.
In altre parole nella progettazione di questo obiettivo si deve
considerare il potere diottrico del cristallino.
Nella progettazione degli obiettivi fotografici ci si deve tener
conto delle focalizzazione relativamente al mondo esterno ma anche
del modo per illuminarlo.
Essendo la retina contenuta all'interno dell'occhio e chiaro che
l'unico metodo per illuminarla e quella di fare passare la luce
attraverso la pupilla e quindi attraverso il cristallino.
Senza considerare dati altamente scientifici e facile immaginare
come la luce potrebbe di fatto creare grossi problemi nell'ambito
del sistema di ripresa a causa della superficie lucida costituita
dalla cornea dell'occhio e di fatto anche dal potere al ferrante
rispetto a percorso ottico che possiede il cristallino.
In effetti la riflessione causata dalla lucidit della cornea
potrebbe costituire un problema all'interno dell'attivit di
ripresa.

Allo stesso modo le dimensioni della pupilla potrebbero indurre a


poter fornire la luce in pochi modi e quindi l'eliminazione di
certi tipi di problemi potrebbe essere di fatto un'operazione
critica.
Lo sviluppo di questa parte di progetto pretende alcuni tipi di
conoscenze legate alla fisiologia dell'occhio e alle propriet
della fisica dell'ottica.
Le informazioni che sono state utilizzate per lo sviluppo di tale
progetto verranno riportate all'interno di questo volume al fine
di sveltire l'opera di culturizzazione di una persona introdotta
di fresco all'interno dello staff di lavoro.
In tale documento verranno riportate soltanto le informazioni di
tipo tecnico in quanto quelle di tipo statistico potranno essere
inserite solo dopo un lungo periodo di prova.
Generalmente un progetto diviene ideato in una certa maniera e
successivamente, a seguito di test, alcuni metodi utilizzati
vengono modificati per adattarli alla forma ottimale.
Alcune tipologie di problema possono essere immaginate ma di fatto
l entit di queste possono essere solo considerate dopo prove
pratiche.
In effetti alcuni tipi di informazione potrebbero essere ricavate
da documenti provenienti da studi fatti da universit nel campo
dell'ottica come ad esempio quelle relative ai coefficienti di
riflessione della retina.
Questo tipo di dato dovrebbe essere utilizzato nell'ambito
dell'individuazione del sistema di illuminazione ideale.
Il problema che di fatto passare dal informazione teorica a
quella pratica in effetti potrebbe non essere cos semplice come
sembra per cui una serie di test pratici sar sicuramente alla
base della scelta effettiva.
Altri tipi di informazioni invece possono essere individuati
mediante
l'osservazione
degli
oggetti
esistenti
in
quel
determinato settore come ad esempio per quale che riguarda il
sistema di illuminazione.
Uno dei capitoli presenti in questo volume sar infatti quello
indirizzato
ad
eseguire
una
panoramica
sugli
oftalmoscopi
esistenti sul mercato per uso umano.
Generalmente la prima fase di un qualsiasi progetto e quella in
cui si va a ricercare tutte le informazioni esistenti relative
alla cultura specifica del progetto stesso.

In altre parole, una volta individuati i settori di intervento, si


cerca di creare dei poli culturali specifici di ogni settore.
Nel nostro caso la ricerca su Internet a costituisce uno dei punti
predominante nell'ambito della progettazione anche se di fatto,
per quello che riguarda il riconoscimento retinale, non esiste
cos tanta documentazione in merito neppure nel settore umano.
Chiaramente tale tipo di riconoscimento risultato penalizzato
dal suo alto coefficiente di invasivit rispetto ad altre
metodologie biometriche.
Un
sistema
di
identificazione
biometrico
come
ad
esempio
l'impronta
digitale
permette
di
eseguire
tale
funzione
semplicemente appoggiando il dito su un sensore di rilevamento.
Il
riconoscimento
dell'iride
invece
pretende
la
semplice
acquisizione dell'immagine dell'occhio mediante una telecamera
posizionata a qualche decina di centimetri dal volto.
La retina invece, proprio per le complicazioni indotte dalla
presenza del cristallino e dalle dimensioni ridotte della pupilla,
pretende che l'occhio venga appoggiato su un sistema di
rilevamento.
Questa procedura crea dei problemi in alcune persone.
Questo motivo stato quello per cui il riconoscimento retinale
stato poco utilizzato in campo umano anche se di fatto l'immagine
della retina e quella che permette delle percentuali di
riconoscimento maggiore a qualsiasi altro metodo biometrico.

Il sistema ottico.
La ripresa di una retina all'interno dell'occhio di una mucca di
fatto un'operazione complessa che deve tenere conto di molti
fattori.
Nei normali sistemi di ripresa l'obiettivo tiene conto di una
focalizzazione minima ma di fatto generalmente non deve aver a che
fare con una oggetti che modificano il percorso ottico.
Nel caso dell'occhio della mucca abbiamo, all'interno della
pupilla, una lente che possiede sui due lati delle focali
differenti che permettano di creare le immagine virtuale, da una
parte, sulla retina e, dall'altra parte, sull'ambiente esterno.
Se
mediante
una
macchina
da
ripresa
dovessimo acquisire le
immagine delle parti
esterne
dell'occhio
non
avremmo
nessun
tipo di problema in
quanto l'occhio stesso
risulterebbe
essere
come un normalissimo
oggetto
del
mondo
esterno per cui gli
unici problemi di cui
dovremmo tenere conto
sarebbero quelli della
quantit
di
luce
e
quindi
della
regolazione
di
un
diaframma atto a aumentare o diminuire la quantit di questa che
arriverebbe alla cella di ripresa
e della focale ovvero del
sistema di lenti necessario per mettere a fuoco loggetto ad una
certa distanza.
La massima difficolt che potremmo incontrare
potrebbe essere collegata al fatto che locchio
essendo una superficie lucida potrebbe riflettere
le cose posizionate davanti a lui oltre alla luce
dellilluminazione.
Essendo la retina oltre questa lente chiamata
cristallino
il
sistema
di
acquisizione
dell'immagine deve essere calcolato tenendo conto
delle focali relative alla sua parte interna e a
quella esterna.
Inoltre i problemi relativi all'acquisizione delle
retinali coinvolge anche il sistema di illuminazione.

immagini

La luce, o meglio il sistema di illuminazione, deve essere


calcolato in modo tale da considerare nel insieme anche le focali
del cristallino.
La
pupilla
subisce
linflusso della luce per
cui
qualsiasi
condizione
sfavorevole potrebbe indurre
ad
un
restringimento
di
questa e quindi un ulteriore
complicazione
nellambito
della ripresa.
Generalmente nel caso di
progetti
nuovi
la
prima
attivit

rivolta
alla
ricerca di quanto esiste gi
fatto
con il fine di
trovare almeno un indirizzamento.
Sfortunatamente in questo settore non esiste nulla se non gli
oftalmoscopi i quali comunque sono molto differenti in quanto la
ripresa dellimmagine retinale avviene a contatto con locchio.
I metodi legati all'acquisizione della retina nell'ambito umano
differiscono a causa di molte particolarit anche se di fatto la
cosa fondamentale rimane la non collaborazione che c' da parte
dell'animale al momento della ripresa stessa.
Gli portavano scoppi esistenti per uso umano sono essenzialmente
di due tipi e precisamente:

Tipo indiretto
Tipo diretto

Il primo tipo utilizza una lente da circa 20 diottrie tenute in


mano
dal
medico
e
da
un
illuminatore
posizionato in un caschetto tenuto in testa
sempre da questo.
Il tipo diretto invece praticamente una
macchina fotografica indirizzata alla ripresa
del fondo retinale.
A questa tipologia di oftalmoscopi appartengono
quelle definite con il termine di fundus camera
le quali generalmente sono progettate per uso
ambulatoriale ed in ogni modo per pazienti
collaborativi.
Questa tipologia di sistemi dispone di una
struttura
dentro
alla
quale
il
paziente
appoggia la testa la quale viene tenuta ad una

distanza fissata dallobbiettivo di acquisizione.


Questo tipo di oftalmoscopio possiede
un costo elevato e per questo motivo, a
parte quello di doverlo considerare
come un apparato fisso, pu essere
visto esclusivamente come uno strumento
diagnostico da utilizzare all'interno
di uno studio ottico.
Il sistema utilizza un insieme di
specchi e di prismi finalizzati a
fondere il percorso ottico con quello
dell'illuminatore.
L'osservazione
delle
tipologie
di
oftalmoscopi presenti in circolazione
ha reso in un certo senso difficile la scelta della metodologia
utilizzabile per la messa a fuoco della retina unita al sistema di
illuminazione.
Infatti, come abbiamo appena detto, il sistema di lenti utilizzato
per la messa a fuoco potrebbe essere in contrasto con il sistema
di illuminazione utilizzato a causa degli effetti collaterali
legati alla riflessione della luce stessa contro il vetro della
lente.
Alcuni tipi di oftalmoscopi sfruttano l'emisfero alto della lente
e quello basso per le due funzionalit ovvero il percorso ottico
utilizzato per la visualizzazione della retina passa attraverso la
parte alta mentre quella bassa viene utilizzata per focalizzare la
luce.
Questa metodologia permette di utilizzare le due parti in modo
separato in modo tale che il riflesso indotto dalla luce non vada
a disturbare l'immagine acquisita.
Purtroppo questa metodologia sicuramente quella pi complicata
da mettere a punto.
Una soluzione alternativa e quella di mettere illuminatore dopo la
lente di focalizzazione.
Anche in questo caso bisogna prestare molta attenzione in quanto
l'illuminatore stesso non deve coprire parte della zona utilizzata
per la visualizzazione della retina.
Come
vedremo
successivamente
i
metodi
utilizzati
negli
oftalmoscopi per uso umano purtroppo non possono essere utilizzati
nel caso dell'acquisizione dell'immagine della retina legata ad
una mucca.

L'occhio della mucca infatti risulta essere molto pi grosso di


quello di un uomo anche se di fatto possiede alcune agevolazioni
non disponibili in ambito umano come ad esempio le dimensioni
molto pi grande della pupilla.

La tabella delle lenti


Nell'ambito del nostro progetto abbiamo utilizzato
scelte dai cataloghi della Edmund e della Thor.

delle

lenti

A parte essere le compagnie pi conosciute per quanto riguarda la


fornitura di componenti per l'ottica sono anche quelle i cui
cataloghi sono presenti all'interno dei programmi di simulazione
di banchi ottici come ad esempio Zemax.
Quest'ultimo stato utilizzato in sostituzione del Banco ottico
fisico per tutto quello che riguarda la simulazione relativa
all'obiettivo ottico e al sistema di illuminazione.
Dato che il manuale si prefigge d'essere anche
consultazione
vengono
riportate
le
tabelle
commercializzate dalle varie societ.

una guida di
delle
lenti

Questo viene fatto per facilitare il lavoro in fase di


progettazione senza necessariamente doversi collegare su Internet
per reperire i dati legati alle focali e alle dimensioni delle
varie lenti utilizzabili.

Tech Spec Near Infrared Achromats

Designed to Give Increased Resolution and Smaller Spot Sizes for NIR Wavelengths (700-1100nm)
Decreased Spherical Aberration for Monochromatic Sources Out to 2m
Broadband AR Coating has <1% Reflectivity Between 7001550nm
Our new Tech Spec Near IR doublets are designed to provide the smallest spot size possible for
polychromatic light between 700 and 1100nm. By utilizing our NIR doublets instead of standard doublets
designed for the visible, RMS spot diameter can be reduced from 43m to 22.5m, for example, when using
polychromatic light. Spot size will be smaller when focusing monochromatic sources. Near-IR doublets also
reduce spherical aberration and have superior performance when used with a monochromatic source up to 2m
in wavelength. Typical applications for these doublets include: CCD imaging lenses for the near infrared,
focusing and expanding of NIR lasers, and focusing/collimating lenses for fiber optics and NIR LEDs .
Specification Table

Diameter

Surface Quality

Dia. Tolerance

C.T. Tolerance

Centering Tolerance

3.00

40-20

+0.0/-0.05

0.2

3 - 5 arc min.

6.00 - 25.00

40-20

+0.0/-0.10

0.2

3 - 5 arc min.

50.00

60-40

+0.0/-0.10

0.2

3 - 5 arc min.

Notes

Bevel

If the diameter >= 0.85 x (radius of curvature), then no bevel is required.

Clear Aperture

Description

LENS ACH-

3.00mm dia.:

Max Bevel = 0.1mm x 45

6.00 - 25.00mm dia.:

Max Bevel = 0.25mm x 45

50.00mm dia.:

Max Bevel = 0.3mm x 45

3.00 - 10.00mm dia.:

CA >= 90% of diameter

10.1 - 50.00mm dia.:

CA = diameter - 1.00mm

Glass Type

LaKN22-SFL6

Focal Length

Defined at 880nm; focal length tolerance 2%

Coating

NIR II AR (See Curves)

E.T. Tolerance

Reference

Dia. E.F.L.
(mm) (mm)

Back
Radius Radius Radius
C.T.1 C.T.2 E.T.
MTF
F.L.
R1
R2
R3
(mm) (mm) (mm)
Curve
(mm)
(mm) (mm) (mm)

Stock Number

12

18

12.3

2.5

8.41

12.05

-8.65

-37.28

MTF
Curve

NT45-791

12

20

14.18

2.5

8.65

13.13

-9.59

-45.11

MTF
Curve

NT45-792

12

25

21.05

4.5

2.5

5.58

15.56

-13.75

-84.13

MTF
Curve

NT45-793

12

30

25.83

4.5

2.5

5.82

17.77

-16.46

-136.8

MTF
Curve

NT45-794

12

35

30.73

4.5

2.5

20.23

-19.17 -193.84

MTF
Curve

NT45-795

12

40

35.67

4.5

2.5

6.12

22.81

-21.91 -250.49

MTF
Curve

NT45-796

12

50

46.7

2.5

4.8

28.51

-322.09

MTF
Curve

NT45-797

12

60

56.67

2.5

4.92

33.92

-33.81 -419.99

MTF
Curve

NT45-798

12

75

71.65

2.5

4.03

42.11

-565.11

MTF
Curve

NT45-799

15

25

19.49

5.65

3.7

6.76

14.94

-14.94 -120.18

MTF
Curve

NT45-826

15

30

25.62

5.15

17.36

-17.36 -187.86

MTF
Curve

NT45-827

NIR 25 X 100
NIR-II

25

100

93.89

8.49

57.01

-56.72 -656.51

MTF
Curve

NT45-806

LENS ACH-

25

200

193.9

9.25

NIR 12 X 18
NIR-II

LENS ACHNIR 12 X 20
NIR-II

LENS ACHNIR 12 X 25
NIR-II

LENS ACHNIR 12 X 30
NIR-II

LENS ACHNIR 12 X 35
NIR-II

LENS ACHNIR 12 X 40
NIR-II

LENS ACHNIR 12 X 50
NIR-II

LENS ACHNIR 12 X 60
NIR-II

LENS ACHNIR 12 X 75
NIR-II

LENS ACHNIR 15 X 25
NIR-II

LENS ACHNIR 15 X 30
NIR-II

LENS ACH-

NIR 25 X 200

-28.2

-42.3

112.88 -112.88

MTF
1415.62 Curve

NT47-271

NIR-II

LENS ACHNIR 25 X 35
NIR-II

LENS ACHNIR 25 X 40
NIR-II

LENS ACHNIR 25 X 45
NIR-II

LENS ACHNIR 25 X 50
NIR-II

LENS ACHNIR 25 X 60
NIR-II

LENS ACHNIR 25 X 75
NIR-II

LENS ACHNIR 3 X 10
NIR-II

LENS ACHNIR 3 X 15
NIR-II

LENS ACH-

NIR 50 X 100
NIR-II

LENS ACHNIR 50 X 150


NIR-II

LENS ACHNIR 6 X 12
NIR-II

LENS ACHNIR 6 X 15
NIR-II

LENS ACHNIR 6 X 20
NIR-II

LENS ACHNIR 6 X 25
NIR-II

LENS ACHNIR 6 X 30
NIR-II

LENS ACHNIR 6 X 35
NIR-II

LENS ACHNIR 6 X 40
NIR-II

LENS ACHNIR 6 X 9
NIR-II

LENS ACHNIR 9 X 15
NIR-II

LENS ACHNIR 9 X 20
NIR-II

MTF
Curve

NT45-800

-22.26 -105.93

MTF
Curve

NT45-801

29.01

-25.53 -132.92

MTF
Curve

NT45-802

8.94

31.69

-28.45 -161.05

MTF
Curve

NT45-803

9.46

36.27

-33.8

-248.86

MTF
Curve

NT45-804

7.98

43.96

-42.9

-392.21

MTF
Curve

NT45-805

8.09

1.8

2.58

5.2

-5.2

MTF
Curve

NT45-822

15

13.07

1.8

2.65

7.71

-7.71

MTF
Curve

NT45-823

50

100

87.86

14.5

5.5

13.89

59.74

-59.74

-494.7

MTF
Curve

NT47-317

50

150

139.87

11

5.5

12.47

86.84

-86.84

-894.7

MTF
Curve

NT47-318

12

9.3

3.2

3.15

6.84

-5.99

-69.82

MTF
Curve

NT45-784

15

12.42

1.2

3.61

8.8

-7.93

-71.17

MTF
Curve

NT45-785

20

17.27

1.3

3.86

11.29

-10.63

-134.8

MTF
Curve

NT45-786

25

22.24

1.3

3.95

13.95

-13.4

-193.19

MTF
Curve

NT45-787

30

27.22

1.3

4.01

16.66

-16.18 -246.07

MTF
Curve

NT45-788

35

32.22

1.3

4.05

19.39

-297.39

MTF
Curve

NT45-789

40

37.17

1.3

4.08

21.81

-22.07 -454.78

MTF
Curve

NT45-790

6.66

1.3

4.26

7.34

-4.18

-11.58

MTF
Curve

NT45-783

15

11.59

4.2

4.84

9.68

-7.91

-39.89

MTF
Curve

NT45-824

20

16.46

3.5

4.49

11.01

-11.01

-222.6

MTF
Curve

NT45-825

25

35

26.87

11

10.48

23.4

25

40

31.74

11

11.09

26.27

25

45

37.8

9.59

25

50

43.29

25

60

52.96

25

75

69.06

10

-20.11

-19

-87.52

Le lenti THORLAB
Product Summary
Wavelength Range: 170nm to 8.0m, Un coated
Focal Lengths from 20mm to 1000mm

Plano-Convex optics are best used where one conjugate point (object distance, S or image distance S) is more than five
times the other.This lens shape is near best-form for either focusing collimated light or for collimating a point source.

Part Number

LA5315
LA5183
LA5458
LA5370
LA5763
LA5042
LA5817
LA5012
LA5714
LA5255
LA5464
LA5956

DIA

efl

tc

Material

12.7

20.0

4.3

CAF2

12.7

50.0

2.5

CAF2

12.7

80.0

2.1

CAF2

25.4

40.0

7.5

CAF2

25.4

50.0

6.1

CAF2

25.4

75.0

4.6

CAF2

25.4

100.0

3.9

CAF2

25.4

150.0

3.3

CAF2

25.4

200.0

2.9

CAF2

25.4

250.0

2.8

CAF2

25.4

500.0

2.4

CAF2

25.4

750.0

2.3

CAF2

25.4

LA5835

1000.0

2.2

CAF2

Product Summary
Best Possible Performance from a Spherical Singlet
Ideal for High Powered Applications

These lenses are an ideal choice for application from 200nm to 6m. Magnesium Fluoride is extremely durable in
comparison to other materials that reach this far down into the UV or out into the IR. The C-axis is oriented to minimize
birefringence.
Plano-Convex optics are best used where one conjugate point (object distance, S or image distance S) is more than five
times the other.This lens shape is near best-form for either focusing collimated light or for collimating a point source.

Order
Part Number

LA6002
LA6003
LA6004
LA6005
LA6006
LA6007
LA6008
LA6009
LA6010

DIA

efl

fb

tc

Material

25.4

50.0

45.0

6.9

MgF2

25.4

60.0

55.5

6.0

MgF2

25.4

75.0

71.4

5.0

MgF2

25.4

100.0

97.1

4.3

MgF2

25.4

150.0

147.7

3.2

MgF2

25.4

200.0

197.7

3.2

MgF2

25.4

250.0

248.0

2.8

MgF2

25.4

500.0

498.1

2.6

MgF2

25.4

1000.0

998.3

2.4

MgF2

Product Summary
Wavelength Range: 185nm to 2.1m, Uncoated
Focal Lengths from 10mm to 1000mm
Diameters from 5mm to 75mm
Standard AR UV Coating Available: 290-370nm

Plano-Convex optics are best used where one conjugate point (object distance, S or image distance S) is more than five
times the other.This lens shape is near best-form for either focusing collimated light or for collimating a point source.

Part Number

LA4249
LA4280
LA4917
LA4194
LA4966
LA4647
LA4936
LA4130
LA4765
LA4327
LA4600
LA4052
LA4306
LA4148
LA4725
LA4380
LA4236
LA4874
LA4924
LA4102
LA4158
LA4579
LA4184
LA4716
LA4663
LA4464
LA4078
LA4545
LA4904
LA4984
LA4538
LA4855
LA4782

DIA

efl

tc

Material

Price

5.0

10.0

2.2

UVFS

$73.20

6.0

10.0

2.6

UVFS

$69.80

6.0

15.0

2.2

UVFS

$69.50

6.0

20.0

2.0

UVFS

$68.00

6.0

30.0

1.8

UVFS

$68.00

12.7

20.0

4.3

UVFS

$71.80

12.7

30.0

3.4

UVFS

$67.00

12.7

40.0

2.9

UVFS

$60.00

12.7

50.0

2.7

UVFS

$54.00

12.7

75.0

2.4

UVFS

$54.00

12.7

100.0

2.2

UVFS

$48.00

25.4

35.0

8.2

UVFS

$113.00

25.4

40.0

7.1

UVFS

$88.00

25.4

50.0

5.8

UVFS

$83.50

25.4

75.0

4.4

UVFS

$75.90

25.4

100.0

3.8

UVFS

$74.20

25.4

125.0

3.4

UVFS

$72.00

25.4

150.0

3.2

UVFS

$69.90

25.4

175.0

3.0

UVFS

$68.50

25.4

200.0

2.9

UVFS

$67.90

25.4

250.0

2.7

UVFS

$66.80

25.4

300.0

2.6

UVFS

$66.50

25.4

500.0

2.4

UVFS

$65.80

25.4

750.0

2.2

UVFS

$65.30

25.4

1000.0

2.2

UVFS

$65.00

50.8

60.0

19.8

UVFS

$240.00

50.8

75.0

14.2

UVFS

$230.00

50.8

100.0

10.7

UVFS

$214.00

50.8

150.0

7.8

UVFS

$206.00

50.8

200.0

6.6

UVFS

$194.80

50.8

250.0

5.8

UVFS

$182.00

50.8

300.0

5.4

UVFS

$168.00

50.8

500.0

4.4

UVFS

$156.00

LA4745
LA4337
LA4384
LA4372
LA4795
LA4246
LA4249-UV
LA4280-UV
LA4917-UV
LA4194-UV
LA4966-UV
LA4647-UV
LA4936-UV
LA4130-UV
LA4765-UV
LA4327-UV
LA4600-UV
LA4052-UV
LA4306-UV
LA4148-UV
LA4725-UV
LA4380-UV
LA4236-UV
LA4874-UV
LA4924-UV
LA4102-UV
LA4158-UV
LA4579-UV
LA4184-UV
LA4716-UV
LA4663-UV
LA4464-UV
LA4078-UV
LA4545-UV
LA4904-UV
LA4984-UV
LA4538-UV
LA4855-UV
LA4782-UV
LA4745-UV
LA4337-UV
LA4384-UV
LA4372-UV
LA4795-UV
LA4246-UV

50.8

750.0

3.9

UVFS

$146.00

50.8

1000.0

3.7

UVFS

$146.00

75.0

90.0

26.9

UVFS

$398.00

75.0

150.0

14.1

UVFS

$350.00

75.0

200.0

11.0

UVFS

$326.00

75.0

500.0

6.1

UVFS

$278.00

5.0

10.0

2.2

UVFS

$87.20

6.0

10.0

2.6

UVFS

$83.80

6.0

15.0

2.2

UVFS

$83.50

6.0

20.0

2.0

UVFS

$82.00

6.0

30.0

1.8

UVFS

$82.00

12.7

20.0

4.3

UVFS

$85.80

12.7

30.0

3.4

UVFS

$81.00

12.7

40.0

2.9

UVFS

$74.00

12.7

50.0

2.7

UVFS

$68.00

12.7

75.0

2.4

UVFS

$68.00

12.7

100.0

2.2

UVFS

$62.00

25.4

35.0

8.2

UVFS

$141.00

25.4

40.0

7.1

UVFS

$116.00

25.4

50.0

5.8

UVFS

$111.50

25.4

75.0

4.4

UVFS

$103.90

25.4

100.0

3.8

UVFS

$102.20

25.4

125.0

3.4

UVFS

$100.00

25.4

150.0

3.2

UVFS

$97.90

25.4

175.0

3.0

UVFS

$96.50

25.4

200.0

2.9

UVFS

$95.90

25.4

250.0

2.7

UVFS

$94.80

25.4

300.0

2.6

UVFS

$94.50

25.4

500.0

2.4

UVFS

$93.80

25.4

750.0

2.2

UVFS

$93.30

25.4

1000.0

2.2

UVFS

$93.00

50.8

60.0

19.8

UVFS

$292.00

50.8

75.0

14.2

UVFS

$282.00

50.8

100.0

10.7

UVFS

$266.00

50.8

150.0

7.8

UVFS

$258.00

50.8

200.0

6.6

UVFS

$246.80

50.8

250.0

5.8

UVFS

$234.00

50.8

300.0

5.4

UVFS

$220.00

50.8

500.0

4.4

UVFS

$208.00

50.8

750.0

3.9

UVFS

$198.00

50.8

1000.0

3.7

UVFS

$198.00

75.0

90.0

26.9

UVFS

$450.00

75.0

150.0

14.1

UVFS

$402.00

75.0

200.0

11.0

UVFS

$378.00

75.0

500.0

6.1

UVFS

$330.00

Come funziona locchio


Prima di poter supporre qualsiasi obiettivo adatto alla ripresa
del fondo retinale necessario conoscere il funzionamento
dell'occhio.
L'occhio, ovvero l'organo della vista, un meccanismo complesso
costituito da un'iride, paragonabile ad un diaframma automatico,
da una pupilla composta da una lente chiamata cristallino,
indirizzato alla focalizzazione verso un mondo esterno e verso la
retina, da una retina, ovvero il meccanismo che traduce la luce in
impulsi nervosi indirizzati al cervello e da altri componenti.
Anche quelli che sono considerati componenti secondari devono
essere presi in considerazione in quanto possono interagire con la
fisica della luce.
Per
fare
un
esempio
potremmo
un'utilizzare
l'umore vitreo ovvero il
liquido
esistenti
all'interno dell'occhio.
Questo liquido agisce nel
sistema
di
diffusione
della
luce
all'interno
dell'occhio stesso.
La
luce
emessa
dall'oggetto al quale si
sta
guardando
giunge
all'occhio attraverso la
cornea.
I
raggi
di
luce
attraversano la pupilla
(la
quale
regola
l'ammontare di luce che
giunge all'occhio), poi attraversano la lente (il cristallino).
Finalmente, i raggi di luce vengono concentrati sulla retina che
un strato sottile coperta con celle recettori di luce.
Grazie a reazioni elettrochimiche, la luce convertita in impulsi
elettrici emessi al cervello dal nervo ottico.
Circa al centro della retina ce una piccola depressione che
nota come macula.
Al centro della macula ci sono celle contenute molto densamente
(fovea) le quali forniscono le informazioni legate al colore. Ma,
nonostante l'importanza del fovea, le altre parti della retina
sono anche molto utili per scoprire il
moto, vedere in luce
fioca...
Da un punto di vista ottico, l'occhio pu essere comparato ad una
macchina fotografica.
Sulla mano del una, la combinazione di lente della macchina
fotografica forma un'immagine sul film sensibile e d'altra parte
l'occhio forma un'immagine sulla retina.
Si dice che un occhio normale, o emmetropico se l'immagine di un
di oggetto distante focalizza sulla retina:

altrimenti si dice che l'occhio


sia ametropico.
L'occhio normale anche capace
vedere oggetti vicini grazie ad
un meccanismo di messa a fuoco
eccellente, noto con il termine
di sistemazione.
Attraverso cambi nella sua forma,
la lente (cristallino) d una
lunghezza focale variabile
all'occhio.
Con questo meccanismo di sistemazione, il giovane occhio di
creatura umana normale capace vedere vicino ad oggetti, circa 25
cm di fronte a lui.
Con l et, la sistemazione comincia a fallire e l'immagine di
oggetti vicini pu non concentrarsi sulla retina.
Questo fenomeno noto come presbiopia e comincia a colpire
persone all'et di 40 anni.
Ametropia
Noi considereremo due generi di ametropia: miopia e hyperopia.
Nota che noi immaginiamo l'occhio per non essere in condizione
rilassata che dire la sistemazione in uso.
Miopia
Miopia la condizione in cui
l'immagine di oggetti distanti
cade di fronte alla retina.
Questo
accade
quando
la
lunghezza assiale della lente
troppo grande per il potere del
sistema della lente.

Presbiopia
Hyperopia la condizione in
che l'immagine di oggetti
distanti cade dietro alla
retina.
Questo
accade
quando
la
lunghezza assiale della lente
troppo corta per il potere
del sistema della lente.

Una parte che a prima vista sembrerebbe centrare poco con gli
studi relativi al sistema di acquisizione dell'immagine della
retina, potrebbe essere quella legata al uso delle lenti
finalizzata alla correzione dei difetti del cristallino.
Di fatto questo permette di capire come un sistema di lenti
esterne pu interagire con il cristallino spesso al fine di creare
una giusta focalizzazione sulla retina dell'occhio
Correzione della miopia

Un modo di correggere miopia mettere una lente negativa (o


lente divergente) di fronte all'occhio.
Correzione di hyperopia

Un modo di trattare hyperopia mettere una lente positiva (o


lente cobvergente) di fronte all'occhio.
Un modo comune di caratterizzare le necessit della lente
quello di dargli il suo potere di vertice.
Diamo un'occhiata alla definizione di potere di vertice di una
lente:

Come possibile vedere, per un oggetto all'infinito, l'immagine


formata in f': punto di fuoco di immagine.
Per definizione, il potere di vertice di una lente , l'inverso
della distanza tra il vertice della superficie di retro e f.'
Questa quantit data in metro-1 o diottrie (D).
Nel caso di miopia si pu avere bisogno di un D di -3 con lente
divergente per esempio, o un D del +3 con lente convergente nel
caso di hyperopia.
Ora immaginiamo una persona di emmetropica.
A questo punto, lui pu vedere chiaramente oggetti a distanze
diverse, ma solamente oggetti che sono solo di fronte a lui.
Se l'oggetto che lui sta guardando si muove nel campo di visione,
che cosa accade?

Come si pu vedere, l'immagine dell'oggetto che visto, non


precipita sulla fovea e la visione non perfetta (senza una buon
acutezza).
Questo problema pu avere soluzioni diverse.
La prima, si pu trasportare l'oggetto (come un orefice che guarda
ad un diamante) o lo volta per metterlo solo di fronte al suo
occhio.

Questo non pu essere sempre chiaramente in particolarmente se si


sta guardando le stelle nel cielo.
Un altro modo pi semplice e comune trasportare la testa o
girare gli occhi.
Nel seguente testo considereremo solamente il moto degli occhi,
per una posizione determinata della testa:
Infatti il bulbo oculare sta
muovendosi continuamente,
cos che luce emise da
oggetto di interesse primario
precipita sul fovea. Per
esempio, quando Lei sta
leggendo un giornale,
impossibile per leggere una
linea intera senza
trasportare la testa o gli
occhi perch questo compito
richiede una buon acutezza.
Una rappresentazione schematica dell'occhio come segue.
Il bulbo oculare ruota intorno ad un punto fisso noto come il
Centro di Rotazione dell Occhio (ECR) per concentrare l immagine
sul fovea.
Questo ottenuto quando i raggi di luce passano attraverso il
centro della pupilla ed attraverso il centro dell'occhio di
rotazione, definendo cos una posizione di sguardo fisso.

Cosa si intende comunque, per l'indossatore di lenti oftalmiche?


Veda attraverso lenti oftalmiche
Quando un myope o hypermetrope guarda attraverso una lente, pensa
un poco diverso. A causa delle leggi di Snell-Descartes, i raggi
di luce non attraversano prima, la lente in una linea diritta.

Occhio miope e lente che diverge

Hyperopic guarda e convergendo


lente

Come Si pu vedere, per la stessa posizione di sguardo fisso,


emmetropico, miope e hypermetropico non vedranno lo stesso
oggetto, o in altre parole, l'occhio di un myope ruoter meno, che
l'occhio di un hypermetrope per vedere lo stesso oggetto nel campo
di visione.
Questa la ragione perch i miopi possono portare piccole
montature e sono fortunati perch le lenti che divergono delle
quali loro generalmente hanno bisogno si riducono in grossezza.
Questi fenomeni sono noti come distorsione.
Ma c' a questo punto un ultimo problema: lenti oftalmiche offrono
solamente una buona visione mentre uno guarda attraverso i loro
centri. Questo non il caso come la posizione di sguardo fisso
non nullo, e la visione pu essere colpita. Un modo di
quantificare la qualit di visione sarebbe analizzare l'immagine
formata sulla retina. Ma, sulla mano del una, difficile fare
raggio che traccia nell'occhio a causa di lui struttura
complessa e, d'altra parte non facile interpretare come il
cervello "vede" questa immagine. Come evitare questo?
Noi analizzeremo l'immagine formata dalla lente perch, infatti,
questa l'immagine alla quale l'occhio sta guardando: uno parla
su immagine virtuale. Ma una cosa sicura: il meglio l'immagine
prodotta dalla lente, il meglio la qualit di visione per
l'indossatore.
Per una posizione nulla di sguardo fisso, noi sappiamo, che
l'immagine form dalla lente prescritta deve essere ad una
distanza 1/D metro dal vertice della superficie di schiena. Da
dilazione per le altre posizioni di sguardo fisso, ed a causa
della simmetria rotazionale circa l'ECR, l'immagine dovrebbe
essere formata su una sfera concentrata su ECR, e noto come
lontano sfera di punto.

Come Si pu immaginare, l'immagine formata dalla lente non


focalizzata sempre perfettamente sul lontano sfera di punto. Un
modo di sapere se l'immagine posizionata precisamente,
misurare la distanza tra l'intersecazione del raggio con la sfera
di vertice (quale una sfera concentr sull'ECR e tangente alla
superficie di schiena) ed il fuoco aguzza. Questa quantit
definita per ogni posizione di sguardo fisso e dovrebbe essere 1/D
metro sempre.
Aberrazioni presentate dalle lenti oftalmiche

Chiaramente, questa non la meta della lente per presentare


aberrazioni che saranno percepite direttamente dall'indossatore e
decresceranno l'acutezza, ma cose sono cose... Comunque,
nonostante le aberrazioni di tesi, fiducioso che lenti
oftalmiche sono valutate bene dagli indossatori.
Due generi delle aberrazioni possono deteriorare l'immagine:

aberrazioni cromatiche, a causa del fatto che il materiale


della lente ha un indice che dipende dal colore o la
frequenza della luce
le aberrazioni monocromatiche a causa della geometria della
lente

Noi considereremo due delle aberrazioni monocromatiche e


principali presi in considerazione nel contesto dell'ottica
oftalmica: motorizzi errore ed astigmatismo.
Motorizzi errore:
Come spiegato prima, per una posizione determinata di sguardo
fisso, il punto di fuoco pu essere davanti, su, o dietro al

lontano sfera di punto. Errore di potere solo la differenza tra


il potere prodotto dalla lente ed il potere prescritto. Errore di
potere positivo se il potere producesse dalla lente pi di D
(il potere prescritto), e negativo (o nullo) altrimenti.
Astigmatismo:
Astigmatismo asimmetricamente molto semplicemente, dovuto al
fatto il cono di incidente di scioperi di raggi la lente. Quando
il cono di raggi sped dall'oggetto osservato giace una distanza
apprezzabile dal centro della lente, loro non focalizzano in un
solo punto ma immaginano come due linee corte, noto come foci
astigmatico. Il rosso il foci tangenziale ed il blu il sagittal
foci.

Questa una vista schematica con l'occhio, la lente ed il foci


astigmatico:

Ora definiamo quantit diverse:

il potere tangenziale, T = 1 / Jt
i sagittal motorizzano, S = 1 / Js

Cattivo potere = (T + S) / 2

Astigmatismo = | T - S |
Motorizzi errore = il Cattivo potere - Prescrisse il potere

Astigmatismo nullo al centro della lente sempre e generalmente


aumenti con posizione di sguardo fisso. Errore di potere
supposto nullo al centro della lente, o nelle altre parole, noi
consideriamo, la lente ha il potere prescritto al suo centro
sempre.
Queste quantit sono definite per ogni posizione di sguardo fisso
e possono essere rappresentate su tale grafico:

Come leggere questo grafico?


Il rosso, curve blu e nere rappresentano rispettivamente il potere
tangenziale, i sagittal motorizzano ed il cattivo potere di un D
del +9 lente oftalmica. In questo caso, e per una posizione del
25 di sguardo fisso, T> 9 D, S <9 D e (T+S)/2> 9 D. Power errore
che la differenza tra il cattivo potere ed il potere prescritto
(9 D) pi grande che 0, ed inscatola ape rosso sull'asse
orizzontale, scal tra -1 D e +1 Astigmatismo di D. pu essere
valutato come la distanza orizzontale tra il T e le curve di S.
Motorizzi errore ed astigmatismo sono stampati anche in verde per
la posizione attuale di sguardo fisso.
La conoscenza dellocchio potrebbe indurre alla creazione di un
illuminatore che permetta di non fare restringere la pupilla della
mucca.
Quando si cercano i sistemi usati in campo umano per osservare la
retina senza far dilatare la pupilla troviamo:
TRUCCHI PER OSSERVARE LA RETINA SENZA UTILIZZARE COLLIRI
MIDRIATICI

Far rilassare il paziente

Eseguire lesame in ambiente non luminoso (meglio se quasi


buio)
Chiedere al paziente di fissare un punto lontano
Utilizzare un oftalmoscopio che dia una fioca illuminazione
Evitare di dirigere il fascio di luce sulla macula del
paziente. Questo scatena la miosi. Cominciare losservazione
dalla papilla e dai vasi della met nasale.
Non fare stancare il paziente. Lasciandolo riposare per
qualche secondo guadagneremo minuti preziosi grazie ad una
migliore collaborazione

Se la parte di illuminazione non colpisce la fiovea la riduzione


pupillare sar minore.

Le seguenti
della mucca.

immagini

mostrano

le

varie

componenti

dellocchio

Il taglio della cornea

La sua estrazione

Lestrazione del cristallino

Lo spessore

Liride

Il cristallino

Il fondo retinale

La retina

Le ghiandole

Il nervo ottico

Gli oftalmoscopi
Come dicevamo prima all'inizio di una ricerca sempre necessario
crearsi una visione di tutto quello patrimonio informativo che
disponibile a riguardo di quanto gi fatto in questo determinato
settore.
Chiaramente le riprese dei fondi retina alle in campo veterinario
non sono un settore cos diffuso per cui l'unica documentazione
reperibile relativa a tutto quello che riguarda la ripresa delle
retina in campo umano.
In questo settore bisogna per mettere un filtro alle ricerche in
quanto la caratteristica fondamentale del nostro progetto rimane
quella, come in tanti altri progetti, relativa al prezzo al quale
si dovr vendere il prodotto.
In campo oftalmico i prodotti sono spesso costosissimi per cui
molti progetti sono relativi ad apparecchiature composte da
strumenti tecnologicamente avanzatissime e quindi anche molto
costose.
Il tipo di apparecchiature d'anno ricercato
relativo a prodotti composte da un sistema di
illuminatore e da una cella di ripresa.

doveva
lenti,

essere
da un

Tutte quelle apparecchiature utilizzate dagli ottici nei loro


studi, quelle in cui il capo del paziente viene posizionato su un
apposito appoggia il mento, non sono state prese in considerazione
se non per motivi culturali generale.
Le tecnologie degli stanno scoppi fondamentalmente si attengono a
due metodi differenti
e precisamente quelli
legati
al
normale
telecamere di ripresa
le
quali
mediante
opportune
lenti
focalizzano
la
retina, e quelle che
utilizzano
la
tecnologia a scanner
per
acquisire
la
stessa.
Come
abbiamo
gi
detto nei capitoli precedenti tutti gli oftalmoscopi sono stati
concepiti per uso umano a parte alcuni a contatto di usati in
campo veterinario.

Inizialmente il tentativo d'anno fatto to quello di cercare di


acquisire la retina da una distanza di circa 50 cm.
Tale distanza si dimostr subito essere qualche cosa di irreale in
quanto, come possibile vedere dagli schemi e gli oftalmoscopi
reperiti, la visualizzazione della retina pu avvenire, in
dimensioni utili per poter eseguire un riconoscimento, solo
nell'istante in cui la focalizzazione della lente viene eseguita
sulla sclera.
Utilizzando questo tipo di focalizzazione diventa una questione di
calcolo matematico capire quale potrebbe essere la superficie
della retina acquisita.
Si consideriamo una lente che focalizza a 5 cm e si consideriamo
che la retina si trova a 2 cm dietro la sclera di fatto mediante
un calcolo sui triangoli creati riusciamo ad avere la superficie
la quale sar appunto di circa 2 cm.

United States Patent Application


Kind Code
Goldfain, Ervin ; et al.

20030098952
A1
May 29, 2003

Eye viewing device for large field viewing


Abstract
The invention is a low cost, low input power eye viewing device well suited for viewing wide field
retinal images through an undilated pupil. Included in the device are a converging light illumination
system and an aperture stop. The converging light illumination system provides ease of entry of
light rays into an eye, wide field retinal illumination, reduced glare and reduced power
consumption. The aperture stop blocks unwanted received glare light not forming part of the retinal
image. The device is made especially well suited for retinal viewing through an undilated pupil if
the aperture is sized in accordance with the diameter of an undilated pupil.
Inventors:

Goldfain, Ervin; (Syracuse, NY) ; Lagerway, William; (Auburn, NY) ; Roberts,


Chris R.; (Skaneateles, NY) ; Slawson, Steven R.; (Camillus, NY) ; Krauter,
Allan I.; (Skaneateles, NY)
Correspondence GARY RITTS
Name and
P.O. BOX 7651
Address:
NORTHRIDGE
CA
913277651

Assignee Name Welch Allyn, Inc.


and Adress:
Serial No.:
337588
Series Code:
10
Filed:
January 7, 2003
U.S. Current Class:
U.S. Class at Publication:
Intern'l Class:

351/200
351/200
A61B 003/00

United States Patent Application


Kind Code
Slawson, Steven R. ; et al.

20030063386
A1
April 3, 2003

Eye viewing device comprising eye cup


Abstract
The invention is a hand-held eye viewing device adapted to be readily positioned in an operative
radial displacement, angular orientation and axial standoff position relative to an eye. The eye
viewing device includes an eye cup extending from a patient end of the device having a patient end

adapted to be received at a patient's eye orbit. An outer diameter of a patient end of the eye cup is
sized to correspond to a patient eye orbit such that a viewing axis is substantially centered on
patient's pupil when the eye cup is received at an eye orbit. The eye cup is preferably made
deformable so that patient comfort is improved and further so that contact of the eye cup with an
eye orbit alerts a physician that the device is approaching an operative axial standoff position. The
eye cup may also be made so that the device pivots about a pivot point toward a patient end of the
eye cup such that the angular orientation of the device can be adjusted without disrupting the
device's operative radial displacement and axial standoff positioning. The eye cup further blocks
ambient light from impinging on an eye, thereby substantially eliminating a source of external glare.
By allowing the device to be stabilized against an eye orbit the eye cup eases the task of
maintaining an operative position once an operative position has been achieved.
Inventors:

Slawson, Steven R.; (Camillus, NY) ; Roberts, Chris R.; (Skaneateles, NY) ;
Krauter, Allan I.; (Skaneateles, NY) ; Goldfain, Ervin; (Syracuse, NY)
Correspondence George S. Blasiak
Name and
WALL MARJAMA & BILINSKI
Address:
101 South Salina Street, Suite 400
Syracuse
NY
13202
US
Assignee Name Welch Allyn, Inc.
and Adress:
Serial No.:
783224
Series Code:
09
Filed:
February 14, 2001
U.S. Current Class:
359/600
U.S. Class at Publication:
359/600
Intern'l Class:
G02B 021/00; G02B 023/16

United States Patent Application


Kind Code
Tanassi, Cesare ; et al.

20030157464
A1
August 21, 2003

Instrument for eye examination and method


Abstract
The present invention relates to a novel instrument for the examination of an eye, namely the retina.
The instrument features a LCD display for projection of various types of patterns and stimuli via an
optical system onto the retina. The retina can be visualized by live IR image sequences as well as
by visible light still frame images. It combines five examination types within one instrument,
namely a perimetry examination, a microperimetry examination, a fixation stability examination, a
scotoma boundary detection and psychophysical examinations as well as comparison experiments
for comparing the results of two examinations of the above types which have been carried out at
different times or with different patients.
Inventors:

Tanassi, Cesare; (Pont della Priula (TV), IT) ; Piermarocchi, Dott. Stefano;
(Padova, IT) ; Buscemi, Philip M.; (Greensboro, NC)
Correspondence Walter L. Beavers
Name and
326 South Eugene Street
Address:
Greensboro
NC
27401
US
Serial No.:
364869
Series Code:
10
Filed:
February 11, 2003
U.S. Current Class:
434/81
U.S. Class at Publication:
434/81
Intern'l Class:
G09B 011/00

United States Patent Application


Kind Code
Cornsweet, Tom N. ; et al.

20030206272
A1
November 6, 2003

Ocular fundus auto imager


Abstract
An ocular fundus imager (8) automatically aligns fundus illuminating rays to enter the pupil (P) and
to prevent corneal reflections from obscuring the fundus image produced. Focusing the produced
fundus image is automatically performed and is based upon the fundus image iself. A head restraint
for the patient undergoing examination is in the form of a pair of spectacles which is not only easy
to use accurately but significantly reduce the gross alignment between the optical system (8) and the
patient's pupil (P). pupil (P).
Inventors:

Cornsweet, Tom N.; (Prescott, AZ) ; Buck, Gary F.; (Prescott, AZ)

Correspondence
Name and
Address:

C Robert von Hellens


Cahill Von Hellens & Glazer
Suite 155
2141 E Highland Avenue
Phoenix
AZ
85016
US
Serial No.:
311492
Series Code:
10
Filed:
December 16, 2002
PCT Filed:
July 6, 2001
PCT NO:
PCT/US01/21410
U.S. Current Class:
U.S. Class at Publication:
Intern'l Class:

351/206
351/206
A61B 003/14

Le ipotesi iniziali
Data la natura dellocchio ed in particolare del fondo retinale
riflettente i problemi legati alla creazione di un illuminatore
possono derivare da tre problemi fondamentali.

La
La
La
La

poca potenza
troppa potenza
posizione sbagliata
lunghezza donda errata
Nelle varie prove fatte
subito apparso il problema.

Nella prima prova ipotizzammo


che la luce dovesse essere
allinfrarosso
e
sufficientemente
forte
per
non fare ridurre la pupilla
dellanimale
e
che
questa
dovesse essere anulare per
distribuire meglio la luce.
La
pupilla
dellocchio
funziona da diaframma per cui
maggiore la quantit di luce pi questa si fa piccola.
Nel caso dei sistemi di ripresa per uso umano vengono spesso
utilizzate sostanze atte a dilatare le pupille cosa non fattibile
nel caso di mucche o animali in generale.
Locchio della mucca possiede una pupilla ovale orientata in senso
orizzontale, come si pu vedere nel disegno precedente.
La prima soluzione a cui pensammo che era legata alla telecamera e
al tipo di illuminatore da utilizzare con questa.
Le ipotesi che vennero
dell'occhio che potevano
dall'esterno.

fatte
essere

furono legate alle propriet


viste mediante un'osservazione

Riuscire a trovare i dati relativi alla fisiologia dell'occhio


della mucca era alquanto complicato in quanto l'unico mezzo a
disposizione era quello di cercare su Internet sui siti legati
alla veterinaria.
Le informazioni a cui mi riferisco sono di fatto, ad esempio, le
diottrie del cristallino, le dimensioni della retina, la distanza
di questa dietro al cristallino e cos Via.

Successivamente alcune informazioni vennero ipotizzate basandosi


sui dati relativi alla focalizzazione sulla parte interna e su
quella esterna dell'occhio.
Tornando quello che dicevamo prima precedentemente alla prima
prova ipotizzammo un'idea su come doveva essere fatta una
telecamera adatta alla ripresa del fondo oculare.
Pensando che dovevamo passare attraverso la pupilla il fattore
dingrandimento doveva essere tale
da visualizzare locchio grosso
come tutta limmagine.
Per quanto riguarda il sistema di
illuminazione ipotesi fatta per
quella per cui questo dovrebbe
mettere una luce non visibile
all'occhio
e
sufficientemente
forte
da
poter
penetrare
all'interno di questo.
Ignorando che il fondo retinale riflette il risultato avuto fu il
seguente.
La
retina
al
fine
di
ottimizzare la luce riflette
indietro sui ricettori la luce
in eccesso.
In altre parole la luce che
arriva
dalla
pupilla
batte
contro i fotorecettori mentre
altri vanno sul fondo il quale
li riflette indietro contro a
questi ultimi.
Questo motivo quello che
causa gli occhi del gatto che
riflettono la luce che li colpisce.
Anche nel nostro
vedere il fondo.

caso

locchio

La luce doveva quindi essere


eccesso veniva riflessa.

divent

luminoso

equilibrata

in

impedendo

quanto

quella

di
in

Un altro dei problemi che vennero subito alla luce era causato
dalla natura riflettente del cristallino.
La sorgente di luce veniva riflessa
problemi nellimmagine dellocchio.

per

cui

questa

creava

A seguito delle prime prove ci


informammo maggiormente sulla
natura
dellocchio
degli
animali.
La lente, di cui parleremo
dopo, aveva avuto anche si dei
problemi
in
quanto
la
focalizzazione
la
faceva
sullesterno dellocchio e non
dietro al cristallino dove di
fatto posizionata la retina.
Di fatto il primo esperimento ci fece capire che non potevamo
trattare locchio come se fosse un oggetto da fotografare come se
fosse un paesaggio.
La pupilla che costituisce lunico punto di passaggio, le

L illuminatore

Sin dall'inizio il sistema di illuminazione sembrato essere


critico in quanto la luce, quella destinata ad illuminare la
retina, doveva tener conto di quello che era cristallino e quindi
della deviazione che questo induceva sul percorso ottico.
Il sistema adatto a descrivere otticamente tale insieme quello
definito con il termine di sistema telecentrico.

Loss
Inoltre un altro parametro critico legato alla scelta del tipo di
illuminazione era la lunghezza d'onda e la potenza della luce
stessa.
In fatti si pensava che la luce visibile potesse di fatto far
restringere la pupilla complicando in questo modo il metodo di
acquisizione.
L'idea di poter utilizzare una luce all'infrarosso, teoricamente,
ci fece pensare che questa di fatto non avrebbe interagito con
pupilla.
D'altra parte una luce all'infrarosso di fatto non avrebbe potuto
avere una potenza pari ad una lampadina con luce visibile in
quanto avrebbe potuto recare danni all'occhio della mucca.
Alcune oftalmoscopiche avevamo in prova presso la nostra societ
utilizzavano la luce visibile ed inoltre possedevano alcuni filtri
colorati che permettevano di cambiare il contrasto relativo
all'immagine della retina.

Tre
oftalmoscopi
presi
in
esame
illuminazione simile ma non uguali.

possedevano

metodi

di

Il primo era un oftalmoscopio indiretto costituito da una lente


tenute in mano nelle vicinanze dell'occhio e da un illuminatore
situato
all'interno
di
un
caschetto
tenute
in
testa
dall'operatore.
La luce destinata ad illuminare la retina di veniva focalizzato su
questa mediante la stessa lente utilizzata per la visione.
L'utilizzo di questo tipo di apparecchio abbastanza critica in
quanto la visione della retina avviene in un punto ed in un punto
solo.
Come vedremo successivamente l'idea che ci permise di creare un
prototipo utile per l'osservazione della retina provenne da questa
apparecchiatura.
Il secondo era costituito da un oftalmoscopio a contatto
costituito da un obiettivo di circa 1 cm e mezzo di diametro della
cui illuminazione era fornita in modalit simile assiale da un
prisma posizionato sulla parte bassa della lente.

Il sistema anulare non funziona in quanto considerando che la


lente focalizza a 5 CM attraverso un foro di 1 cm circa ,
considerando
queste
misure
avremmo,
senza
considerare
lo
spostamento del fascio ottico del cristallino, un illuminazione
che non coprirebbe la parte centrale della retina.

La simulazione dello spostamento del fascio di luce indotto da una


lente con fuoco 2 cm dalla parte interna e a 20 cm quella esterna
si avrebbe.

The Keeler Professional total control at your fingertips


The New Professional Ophthalmoscope has been designed to ensure
ultimate control.
Lenses, graticules and filters can all be introduced at the touch of a
fingertip. No need to pull away from the patient, the Professional
Ophthalmoscope makes routine examination far easier.
Xenon Illumination
We recognize that illumination is of fundamental importance to good
diagnosis. Thats why we have selected Xenon illumination for the
new Professional ophthalmoscope.
The brighter and whiter longer lasting light will meet any diagnostic
challenge. With Xenon you have the power to diagnose even through
cloudy media. Because it is longer lasting, that means fewer bulb
changes giving greater peace of mind.
Positive Action Maywheel
The positive action easy access maywheel not only allows you to dial
up the required lenses quickly it also allows you to know where you
are without the need to pull away from the patient.

Comprehensive Lens Range


The extensive range of +29D to -25D in a single dioptre steps ensures ea
structures and covers a wide range of patient/user prescriptions. A quick
up diagnosis.
Brow Rest
The integral soft brow allows for easy positioning and protects your lense
Swing Over Filter
The red free filter for enhanced vessel examintion can be used in
conjunction with any of the 6 diagnostic beams.

Welch Allyn PanOptic Ophthalmoscope

Introduction
Product Overview and Description
Product Use and Procedure
Features and Benefits
Frequently Asked Questions
Customer Testimonials
Technical Specifications
Warranty
Other Information
Product Ordering Information

Introduction
The Welch Allyn PanOptic Ophthalmoscope is a revolutio
visualizing the fundus.

Developed by Welch Allyn the originator of the world's first ha


ophthalmoscope, the PanOptic incorporates an innovative an
called Axial PointSource Optics. This enabling technology m
pupils and provides a dramatically wider, more panoramic view
than ever before achieved with a standard ophthalmoscope in

Product Overview and Description


By converging the illumination to a point on the cornea, Axial P
entry into even the smallest pupils. It then enables the illumina
retina, creating a very wide illuminated area of the fundus. Sim
viewing system allows the practitioner to see the illuminated ar
creating the widest field of view attainable in undialated ophtha
practitioner is actually viewing the fundus through the mirror tha
the eye, maximizing the alignment of the viewing axis with the

In addition to achieving superior views of the fundus, the PanO


provides enhanced magnification-26% more than standard sco
retinal details.

Product Use and Procedure


To see diagrams, descriptions or a video clip of the PanOptic's

Features and Benefits


Illumination system converges the light to a point at the corn
entry into small, undilated pupils.
Patented new Axial PointSource Optics.
The illumination pathway then diverges to the retina, illumin
area of the fundus.
Viewing system enables the operator to view the illuminated
same axis, creating the widest field-of-view attainable.
Provides a 5-times larger view of the fundus than ever befor
standard ophthalmoscope in an undilated eye.
Enables a 25-degree field-of-view vs. the standard 5-degree
Increases magnification by 26% over a standard ophthalmo
Patented Glare Extinguishment System prevents interferenc
glare and reflections.
New Halogen HPX lamp provides bright, white light.
Greater working distance improves the comfort of both exam
Works with all existing Welch Allyn 3.5v power sources (bat
transformer), saving additional investment.
The patient eyecup establishes and maintains proper viewin
stabilization for the view, is a pivoting point for leverage in p
retina, occludes ambient light and helps with orientation.

The dynamic focusing wheel adjusts the focus in a continuo


more precise control and the optimum view, with a focusing
diopters.
The aperture dial has micro, small and large spot sizes, a sl
free filter. (Cobalt blue filter is available for an additional cha
Model # 11820).
The ergonomic design of the built-in, soft grip handle provid
balance and access to controls.

To see pictures of each feature on the PanOptic, click here to v

Frequently Asked Questions


Q: What is the PanOptic Ophthalmoscope?
A: Welch Allyn's PanOptic Ophthalmoscope is a revolutiona
concept in ophthalmoscopy. Developed and patented by W
the originator of the world's first hand-held, direct-illuminatin
ophthalmoscope, the PanOptic Scope incorporates an in
patented new optical design called Axial PointSource Op
enabling technology makes it easy to enter small pupils and
dramatically wider, more panoramic view of the fundus com
standard ophthalmoscope in an undilated eye

Q: What can the PanOptic Ophthalmoscope do for me that a


ophthalmoscope can't?
A: The PanOptic Ophthalmoscope makes it easy to enter sm
and provides a view of the fundus that's five times larger th
before achieved with a standard ophthalmoscope in an und
The PanOptic Ophthalmoscope enables a 25 field-of-vie
easier to see the different features of the fundus more effici
the standard 5 field-of-view. The scope's design also creat
greater working distance between the practitioner and the p
improving the comfort level of both during the exam. The Pa
Scope also delivers a 26% increase in magnification compa
standard ophthalmoscope, making it easier to see retinal de

Q: How does the PanOptic Scope work?


A: The PanOptic Opthalmoscope's patented new Axial Poin
optics converges the illumination to a point at the cornea, a
entry into even the smallest pupils. Then, Axial PointSource
enables the illumination pathway to diverge to the retina, cr
wide illuminated area of the fundus.

Q: I have a 3.5V Power Source already; do I need to buy a spe


use with my PanOptic?
A: No, the PanOptic is compatible with any standard 3.5v pow
including: desk charger handles, convertible battery handle
transformers and the new Lithium Ion Power Handle.
Q: What is the focusing range with the PanOptic?
A: Available focusing range is -20 to +20 diopters.

Q: What does the eyecup on my PanOptic do and can I reuse


A: The eyecup establishes and maintains proper viewing dista
provides image stability, serves as a pivoting point for pann

the retina, helps with orientation and screens our ambient li


eyecup is cleanable and reusable.
Q: What is better about the Halogen HPX Lamp?
A: The Halogen HPX lamp provides whiter, brighter light.

Q: Is it difficult to learn how to use PanOptic Ophthalmoscop


A: No! Although the PanOptic Ophthalmoscope looks differe
new features, users of standard ophthalmoscopes or even
beginners will find it easy to learn and easy to use.

Customer Testimonials
To read numerous testimonials on the PanOptic Ophthalmosco
website.

Technical Specifications
Dimensions:

5.12"L x 1.40"W x 3.75"H without eyecup, w

Weight:

.48 lbs. without eyecup, .50 lbs. with eyecup

Warranty
One-year warranty on the PanOptic Ophthalmoscope.

Other Information
To view Pathologies such as normal fundus, toxoplasmas, retin
through a PanOptic click here to visit the Pathologies page on

La fisica dellottica
In questa sezione vengono riportati i principi fondamentali della
fisica dell'ottica.
Tale riporto allo scopo di indicare le teorie che sono state
utilizzate per il calcolo delle parti composte da lenti.
Le onde elettromagnetiche sono descritte da componenti costituiti
da campi elettrici e magnetici.
Una parte relativamente piccola dello spettro elettromagnetico
viene fisicamente percepita dall'occhio come colore.
Normalmente siamo abituati a dare il nome di luce a questa
porzione.
Qualsiasi campo elettrico o magnetico pu essere misurato.
L'altezza dell'onda va via con il tempo.
La sua propagazione descritta dalla formula :

La frequenza della luce e la lunghezza corrispondente sono


relazionate dalla formula :

nel processo di formazione di un'immagine, capiamo che un punto di


luce della sorgente deve essere piazzato in un punto dell'oggetto
destinazione.
Un'immagine particolarmente buona e quando tutti i raggi di una
regione interferiscono in modo costruttivo.
Per gli scopi che si prefigge di raggiungere ci interessa avere
alcuni nozionismi legati alla teoria delle lenti.
Le lenti esistenti sono dei tipi classificati qui a seguito:

Nei sistemi ottici vengono generalmente utilizzate lenti


rotazionalmente simmetriche.
Alcune tipologie di lenti non rotazionalmente simmetriche sono
quele cilindriche e quelle toroidali.

Alcune videocamere compatte utilizzano dei sistemi di lenti


simmetriche ed asferiche.

Utilizzando un sistema per la formazione di un'immagine reale


utilizzando una lente singola di piacere per conoscere i parametri
pi importanti con i quali descrivere il nostro sistema.
Le lenti sono piazzate a certe distanze dal oggetto.
La direzione della propagazione definita dal oggetto alle lenti.
La regione precedente alle lenti viene definita con il nome di
spazio dell'oggetto.
La distanza tra la lente e l'immagine invece viene definita come
distanza del immagine a.

La figura precedente ha sufficienti parametri per poter calcolare


la maggior parte delle quantit dimensionali pi importanti del
sistema del immagine come ad esempio.

Le seguenti formule servono a caratterizzare le lenti.

La Percezione Visiva

LOttica Geometrica e i Raggi luminosi


La luce una radiazione elettromagnetica, cio una perturbazione
periodica dei campi elettrico e magnetico che si propaga nel vuoto
e nei mezzi materiali. Da un punto di vista fisico non c alcuna
differenza tra la luce emessa da una stella, il campo irradiato
dallantenna del nostro telefonino, il calore emanato da una stufa
e i raggi X usati per le radiografie; il nostro occhio che
riesce a "captare" la prima ed insensibile alle altre.
Analogamente a tutti i fenomeni propagativi ondulatori (come i
suoni e le vibrazioni, per esempio) unonda elettromagnetica
caratterizzata da una velocit di propagazione v (dipendente dal
mezzo in cui avviene) e da una frequenza n (numero di oscillazioni
che si susseguono nellunit di tempo e di lunghezza;
indipendente dal mezzo); note queste due, si pu ricavare la
lunghezza donda l (la distanza tra due "picchi" vicini),
applicando la relazione:

v = ln

Nel vuoto v = c = 3108 m/s; in tutti gli altri mezzi v minore di


c, notoriamente la massima velocit possibile secondo le leggi
della fisica.
Locchio umano riesce a percepire le lunghezze donda da un minimo
dellordine di l = 0,4 mm (violetto) ad un massimo di l = 0,7 mm
(rosso); tale intervallo (o banda) dello spettro elettromagnetico
detto, appunto, luce. Lunit di misura per le lunghezze in
ottica usualmente il micron (1 mm = 10-6 m).

Figura 5.1

Sarebbe estremamente complicato studiare la luce con le leggi


fisiche
generalizzate
che
si
applicano
a
tutti
i
campi
elettromagnetici. Nei casi in cui le lunghezze donda in gioco
sono molto pi piccole dei mezzi materiali con i quali
interagiscono, si pu adoperare una teoria approssimata, lottica
geometrica, che si basa sulle propriet geometriche della luce
quando incide su discontinuit dello spazio come, ad esempio,
specchi e lenti. Una discontinuit una regione di spazio
riempita con un materiale diverso dallaria o dal vuoto (vetro,
acqua, metallo, ecc..).
Senza aver la minima pretesa di una trattazione esaustiva
dellargomento, introduciamo ora quei pochi concetti fondamentali
che ci serviranno per spiegare a grandi linee il funzionamento di
lenti, specchi e telescopi (naturalmente...).
Si definisce raggio ottico (o luminoso) la direzione nella quale
si propaga una determinata radiazione luminosa che, nei casi che
ci interessano, una linea retta.

Ogni mezzo materiale caratterizzato da un indice di rifrazione n


che inversamente proporzionale alla velocit di propagazione nel
mezzo stesso secondo la relazione

Nel vuoto abbiamo n = 1; per laria n praticamente pari a quello


del vuoto; per tutti gli altri mezzi abbiamo n > 1.

Indice di rifrazione per alcuni mezzi alle frequenze ottiche


vuoto
aria
diamante
quarzo
vetro crown
vetro flint
acqua

1,0000
1,0003
2,47 2,75
1,46
1,51 1,57
1,54 1,75
1,33

Un raggio ottico che incide su un materiale viene in parte


riflesso e in parte rifratto, cio si propaga allinterno del
suddetto
materiale
secondo
una
nuova
direzione.
Per
la
fondamentale legge di conservazione dellenergia, la somma delle
parti riflessa Er e rifratta Ei deve essere pari alla quantit di
energia incidente Ei :

Ei = Er + Ei
Possiamo,
quindi,
introdurre
due
rispettivamente
di
riflessione
e
trasmissione), in modo da avere:

coefficienti
r
di
rifrazione

e
(o

t,
di

Er = r Ei Ei = t Ei

Sostituendo
ottiene:

queste

due

espressioni

nella

prima

relazione

si

r + t = 1

I coefficienti r e t sono legati alle caratteristiche dei due


corpi tramite gli indici di rifrazione n 1 e n2 e allangolo di
incidenza qi del raggio (vedi figg. 5.2a e 5.2b).
I mezzi trasparenti (come il vetro) sono caratterizzati da un alto
coefficiente di rifrazione t e, di conseguenza, da un basso
coefficiente di riflessione r; al contrario, i mezzi riflettenti
(come gli specchi) sono caratterizzati da un alto coefficiente di
riflessione r e un basso coefficiente di rifrazione t.
La riflessione della luce da parte di un corpo riflettente
(specchio) un fenomeno indipendente dalla frequenza della
radiazione incidente, mentre la rifrazione attuata da una lente
dipende da essa. Lindice di rifrazione, infatti, funzione della
frequenza ed maggiore per la parte bassa dello spettro (rosso) e
minore per la parte alta (violetto); tale fenomeno detto
dispersione e tutti i corpi trasparenti sono dispersivi nella
banda ottica.
Per il raggio luminoso che incontra la superficie di discontinuit
tra due mezzi di propagazione, valgono le seguenti propriet
fondamentali (vedi fig. 5.2a):

i raggi incidente, riflesso e rifratto e la perpendicolare alla


superficie di discontinuit nel punto in cui arriva il raggio
incidente, giacciono sullo stesso piano, detto piano di incidenza;

langolo di incidenza qi e langolo di riflessione qi sono uguali


e giacciono da parti opposte rispetto alla perpendicolare alla
superficie di discontinuit;

- langolo di incidenza qi e langolo di rifrazione qr giacciono


da parti opposte rispetto alla perpendicolare alla superficie di
separazione tra i due mezzi e sono legati dalla legge di Snell:

n1 sen qi= n2sen qr

Figura 5.2a

Questultima relazione, importantissima per lo studio e la


costruzione dei dispositivi ottici, deriva dal famoso principio di
Fermat, un postulato che il geniale matematico francese Pierre de
Fermat (1601-1665) pose alla base di ogni fenomeno ottico. Tale
principio stabilisce che tra tutti i possibili percorsi ottici tra
A e B (vedi fig. 5.2b), quello che fisicamente si realizza
quello minimo, cio quello che richiede il minor tempo possibile.
Il volenteroso lettore pu provare a ricavare la legge di Snell
tramite questo postulato, impostando geometricamente un problema
di minimo.

Figura 5.2b

A partire dal principio di Fermat e con le assunzioni da noi fatte


sulle propriet dei raggi luminosi, possiamo ricavare le equazioni
che ci descrivono il comportamento dei raggi ottici quando
incidono su discontinuit non piane come specchi e lenti. Questo
possibile perch si suppone che la superficie concava o convessa
sia piana in prossimit del punto di incidenza. Inoltre, tutti i
dispositivi ottici (come i telescopi) sono sistemi centrati, in
cui le lenti o gli specchi che li compongono sono costituite da
superficie simmetriche di rotazione con tutti i rispettivi assi
centrati sulla stessa retta (asse ottico); possibile, quindi,
studiare il sistema ottico sul piano, lungo una qualsiasi sezione
contenente lasse ottico.
Senza ricorrere ad alcuna equazione, si pu descrivere il
dispositivo
ottico
tramite
procedimenti
grafici,
tracciando
direttamente alcuni fondamentali raggi luminosi che godono di ben
determinate propriet, alcune delle quali saranno esposte nei
paragrafi che seguono.

5.2 Le Lenti e Gli Specchi


Le lenti sono costituite da una porzione di materiale trasparente
(nei telescopi si adoperano particolari tipi di vetro) delimitato
tra due superficie sferiche. Una lente pu essere convergente o
divergente, a seconda della concavit o della convessit di ognuna
delle due superficie e in rapporto ai rispettivi raggi di
curvatura.
Una lente convergente quando tutti i raggi paralleli allasse
ottico che incidono su di essa fuoriescono convergenti in un unico

punto F detto fuoco situato sullasse ottico dalla parte opposta


alla sorgente a distanza f dal centro della lente (vedi fig.
5.3a). In una lente divergente i raggi fuoriescono, ovviamente,
divergenti, come se tutti provenissero da un punto F detto fuoco
virtuale, situato tra la sorgente e la lente, a distanza f da
questultima; in altre parole sono gli immaginari prolungamenti
dei raggi a convergere in F. In entrambi i casi f detta focale
o distanza focale della lente (vedi fig. 5.3b).
Una lente centrata e simmetrica ha le medesime propriet,
indipendentemente dalla parte in cui situata la sorgente:
possiede, quindi, un solo centro e due fuochi situati alla
medesima distanza da esso. Ci sono particolari tipi di lenti
convergenti e divergenti, dette a menisco, in cui le due
superficie
che
le
delimitano
hanno
curvature
diverse;
di
conseguenza i fuochi saranno situati a due diverse distanze dal
centro.
Le lenti godono di tre importanti propriet che ci permettono,
come dicevamo prima, di studiarne il comportamento senza bisogno
di fare alcun calcolo. In questo modo si pu costruire la
cosiddetta immagine (reale o virtuale) di una sorgente puntiforme
o estesa "vista" attraverso una lente.

Figura 5.3a

Figura 5.3b

I raggi ottici paralleli allasse della lente convergono,


realmente o virtualmente, in un unico punto detto fuoco che
situato dalla parte opposta (lenti convergenti), ovvero dalla
stessa parte (lenti divergenti) rispetto alla sorgente.

I raggi ottici passanti per il centro della lente la attraversano


senza essere deviati.

I raggi ottici, o i relativi prolungamenti virtuali, passanti per


il fuoco, fuoriescono paralleli allasse ottico.

Nelle due figure 5.4a e 5.4b, vediamo applicate queste tre regole
per costruire graficamente limmagine di una sorgente estesa. Nel
primo caso (fig. 5.4a), loggetto posto ad una distanza
superiore rispetto alla focale: si vede che i raggi ottici
convergono dallaltra parte e ci restituiscono unimmagine reale
(potremmo
proiettarla
su
uno
schermo,
ad
esempio,
oppure
sfruttarla come sorgente per una seconda lente). Nel secondo caso
(fig. 5.4b), loggetto posto tra il fuoco e il centro della
lente: i prolungamenti virtuali dei raggi si incontrano dietro la
sorgente
reale,
dandoci
in
prossimit
del
secondo
fuoco
unimmagine ingrandita di essa che, essendo virtuale, pu essere
soltanto osservata dal nostro occhio ma non pu essere proiettata
su uno schermo, n usata come sorgente per una seconda lente.

Figura 5.4a

Figura 5.4b

Gli specchi sono costituiti da un supporto di vetro ricoperto da


un sottile ed uniforme strato di materiale riflettente, di solito
platino, argento o alluminio. Le forme degli specchi che si usano
nei telescopi astronomici sono piana, sferica, parabolica o
iperbolica. La lavorazione di tali dispositivi deve avere un alto
grado di accuratezza, al fine di assicurare unelevatissima
qualit degli strumenti ottici in cui sono adoperati.
Uno specchio ben costruito riflette la quasi totalit della
radiazione incidente su di esso e non ha comportamenti dispersivi,
ovverosia le sue propriet non dipendono dalla frequenza.
Esaminiamo brevemente le propriet degli specchi parabolici e
sferici (vedi fig. 5.5). Uno specchio parabolico ha la propriet
di concentrare i raggi luminosi che incidono parallelamente
allasse ottico in un punto F (fuoco) dellasse stesso situato ad
una distanza f (focale) dal vertice del paraboloide. In uno
specchio
sferico,
invece,
i
raggi
ottici
che
incidono
parallelamente allasse non convergono in un unico punto, ma si
distribuiscono in prossimit del centro della sfera generatrice su
una superficie detta caustica.

Figura 5.5

Entrambi questi specchi si adoperano nei telescopi riflettori


(vedi 5.6); quello parabolico pi preciso, ma di pi costosa
lavorazione, mentre quello sferico necessita di dispositivi di
correzione (vedi 5.5), ma il suo costo inferiore ne consente
lutilizzo anche in strumenti di fascia economica (e alla portata
delle tasche degli astrofili...).
Per avere unidea della precisione che ci vuole nella lavorazione
delle superficie riflettenti dei telescopi, interessante sapere
che uno specchio "fuori misura" come il parabolico di 5 metri del
telescopio Hale di Monte Palomar in California (inaugurato nel
l948) stato ottenuto a partire da uno sferico di 34 m di
curvatura dopo molti anni di rifinitura visto che la differenza
tra superficie sferica e parabolica era dellordine del decimo di
millimetro e la precisione richiesta inferiore al decimillesimo di
millimetro!

5.3 Il Cannocchiale
Il primo cannocchiale dovrebbe essere stato costruito da un ottico
fiammingo allinizio del XVII secolo. Galileo, avuta notizia
dellinvenzione, ne produsse uno proprio, perfezionandolo in
seguito,
e
lo
rivolse
subito
al
cielo,
facendo
le
sue
importantissime
scoperte.
Keplero
ne
miglior
ancora
le
prestazioni sostituendo con una lente convessa quella concava
delloculare (vedi pi avanti) di Galileo.

Figura 5.6a

In figura 5.6a riportato lo schema di un semplice cannocchiale;


abbiamo un sistema centrato di due lenti convergenti, una detta
obbiettivo e rivolta verso la sorgente, unaltra detta oculare,
rivolta verso locchio dellosservatore e con la possibilit di
traslare lungo lasse ottico del sistema. Nella figura loggetto
da osservare situato a distanza finita e molto maggiore della
focale dellobbiettivo (nel disegno la scala non stata
rispettata per ragioni di praticit); loculare viene posto in
modo che limmagine AB della sorgente AB cada in prossimit del
suo fuoco e agisce, in questo modo, come una lente di
ingrandimento, dandoci unimmagine A"B" virtuale ingrandita e
capovolta di AB (vedi 5.2). Il rapporto tra le dimensioni di A"B"
e AB detto ingrandimento lineare.
Le sorgenti astronomiche sono poste a distanza praticamente
infinita dallobbiettivo (vedi fig. 5.6b), quindi la funzione del
cannocchiale quella di ingrandire gli angoli sotto i quali sono
visti
gli
oggetti
celesti,
migliorando
lacuit
visuale
dellocchio nudo (vedi 5.10). Loculare (di focale f) viene posto
in modo che il suo fuoco coincida con quello dellobbiettivo (di
focale f) Il rapporto a / a detto ingrandimento angolare e si
pu dimostrare sfruttando la sola geometria piana che

essendo y e y rispettivamente le dimensioni trasversali di un


fascio di raggi luminosi uscenti dallobbiettivo e dalloculare.

Figura 5.6b

Un cannocchiale di questo tipo inadatto allosservazione


terrestre
perch
limmagine
osservata

capovolta;
con
linserzione di una terza lente tra le prime due si pu
raddrizzare limmagine, a scapito della sua qualit perch ogni
lente introduce alcuni difetti (vedi 5.5); per luso astronomico
linversione dei riferimenti non crea eccessivi problemi, quindi
non si usano dispositivi raddrizzatori.
Numerosi
miglioramenti
sono
stati
fatti
al
"primitivo"
cannocchiale che noi abbiamo esaminato, fino ad arrivare al
monumentale rifrattore di Yerkes (Wisconsin, USA), con un
obbiettivo di 102 cm di diametro...
Una lente, inoltre, assorbe una considerevole parte della
radiazione luminosa incidente e questo degenera le prestazioni dei
telescopi rifrattori al crescere del diametro e dello spessore
delle lenti (vedi nota 4). Gli specchi che si usano negli
strumenti ottici, al contrario, riflettono almeno il 90% della
radiazione incidente e non sono dispersivi; per questo motivo i
telescopi di qualit sono tutti riflettori (vedi 5.6).

LOcchio Umano
Prima di tutto opportuno dare una descrizione sommaria del
funzionamento del "sensore" della percezione visiva, cio locchio
umano, le parti principali del quale sono rappresentate in figura
5.8.
La superficie esterna anteriore dellocchio ricoperta da una
membrana trasparente, detta cornea, che, insieme al cristallino,
una vera e propria "lente", focalizza unimmagine reale e
capovolta degli oggetti sulla rtina, una membrana sensibile
interna allocchio. La luce che incide sulla retina viene
convertita in segnali elettrici. Attraverso il nervo ottico,
questi vengono condotti al cervello che provvede ad elaborarli e a
generare la sensazione visiva.
La retina costituita da uno strato di 0,2 mm di due tipi di
cellule diverse: i coni e i bastoncelli, in totale 125 milioni di

elementi. I bastoncelli sono sensibili allintensit luminosa, i


coni al colore. In situazioni di bassa luminosit (luce
crepuscolare) i coni non sono eccitati ed per questo che in tali
condizioni vediamo "in bianco e nero". Siamo nella tipica
situazione in cui ci si trova durante unosservazione astronomica;
solo delle stelle pi luminose possiamo percepire il colore,
mentre tutte le altre, come pure le sgargianti nebulose (nelle
foto delle riviste specializzate...) che puntiamo col nostro
telescopio, ci appaiono di un bianco un po sbiadito.

Figura 5.8

Di coni ce ne sono di tre tipi sensibili a tre colori


fondamentali: il rosso, il verde e il blu. Essi, inoltre, sono
circa un quarto in numero rispetto ai bastoncelli; ci significa
che il nostro occhio ha maggiore potere risolutivo per punti
luminosi piuttosto che per punti colorati. Infatti, a meno di
variazioni individuali, possiamo distinguere due punti luminosi su
sfondo scuro che formano con la pupilla un angolo di un primo
darco (due punti separati da 1 mm visti dalla distanza di 3,44
m), ma se i due punti fossero di diverso colore, non saremmo in
grado di apprezzare anche la differenza cromatica se non
raddoppiando (almeno) la distanza fra di essi.
I coni sono pi concentrati al centro della retina (macchia
lutea), i bastoncelli predominano allesterno; per questo motivo,
per aumentare lacuit visuale e la sensibilit quando si osserva
col telescopio, si consiglia di tenere locchio obliquo, affinch
limmagine si proietti in una zona della retina ricca di
bastoncelli.

Locchio comprende anche un diaframma opaco, detto iride, che


regola le dimensioni dellapertura (la pupilla) attraverso la
quale la luce entra: unazione muscolare riflessa provvede a
restringerla se lintensit luminosa troppo elevata e ad
allargarla se bassa. Il diametro della pupilla pu variare tra
un minimo di poco pi di 1 mm (luce forte) e un massimo di circa 6
mm (al buio).
Anche la curvatura del cristallino pu essere regolata tramite
unazione muscolare. In questo modo, locchio in grado di
mettere a fuoco oggetti posti a varie distanze da esso: una
persona dotata di vista normale pu focalizzare oggetti tra
linfinito e la distanza minima di visione distinta, il cui valore
di circa 2025 cm. Il processo di messa a fuoco detto
accomodamento.
La sensazione visiva il risultato dellelaborazione da parte
dellocchio stesso e del cervello delle due immagini focalizzate
sulle retine dei due occhi. Si scoperto, infatti, che i segnali
elettrici provenienti dai 125 milioni di cellule sensibili della
retina
vengono
affasciati
in
un
numero
molto
minore
di
terminazioni
nervose
che
costituiscono
il
nervo
ottico;
questultimo porter al cervello il risultato di questa prima
elaborazione, il quale provveder a darci la sensazione visiva.
Gli occhi possono presentare difetti che pregiudicano la corretta
visione. Tra questi, i pi comuni sono le ametropie, la presbiopia
e lastigmatismo.
Le ametropie sono connesse ad una deformazione dellocchio lungo
il suo asse longitudinale (vedi fig. 5.9). Nella miopia,
limmagine di un oggetto lontano, da cui provengono quindi raggi
luminosi pressoch paralleli, viene a formarsi su un piano
anteriore a quello retinico e loggetto viene visto sfocato; se,
invece, loggetto si trova a distanza ravvicinata, esso pu ancora
essere visto distintamente. La miopia si corregge utilizzando
lenti divergenti.
Nellipermetropia accade esattamente lopposto: limmagine si
forma posteriormente alla retina e perci sono gli oggetti vicini
ad apparire sfocati. Lipermetropia si corregge attraverso lenti
convergenti.
La presbiopia una progressiva perdita di elasticit del
cristallino che riduce la capacit di accomodamento dellocchio e,
di conseguenza, fa aumentare la distanza minima di visione
distinta. La presbiopia si instaura lentamente anche nellocchio
normale a partire dai 40-45 anni, ed pi avvertita dagli
ipermetropi (che, come detto, hanno gi problemi a vedere da
vicino) che non dai miopi. Anche la presbiopia si corregge con
lenti convergenti.

Lastigmatismo
quale, da sferica,
allora, non sono
determinato asse e
dalla distanza a
lenti cilindriche.

unanomalia della curvatura della cornea la


tende a diventare cilindrica: i raggi luminosi,
pi focalizzati in un punto ma lungo un
loggetto viene visto sfocato indipendentemente
cui si trova. Lastigmatismo si corregge con

In ogni caso, mai usare gli occhiali quando si osserva


dalloculare di un telescopio; tramite lapposita manopola si pu
adattare la messa a fuoco al proprio occhio correggendo miopia e
ipermetropia. Usare gli occhiali, oltre che rendere difficoltosa
losservazione,
pu
rigare
la
superficie
antiriflesso
delloculare, come pure le parti dure di questultimo possono
graffiare i vostri occhiali!

Figura 5.9

Generalit sulla Sensazione Visiva


La sensazione visiva un fenomeno assai complesso che comporta
informazioni di diversa natura. Tra di esse distinguiamo:

1) informazione spazio-temporale: conformazione degli oggetti nel


campo visivo, loro estensione, posizione e movimenti.
2) informazione luminosa e cromatica: intensit della sensazione
di luminosit o brillantezza degli oggetti e loro caratteristiche
cromatiche.

Nei limiti di queste dispense, ci interessa dare qualche elemento


sulle informazioni del secondo tipo, studiate dalla fotometria e
dalla colorimetria che costituiscono due branche della fisica
tecnica.
Una caratteristica fondamentale della visione che la sensazione
di luminosit di un oggetto, sia esso una sorgente (una lampadina,
una stella), o un corpo da essa illuminato (un dipinto, una
parete), non proporzionale alla potenza fisica emessa o riflessa
dalloggetto. Per potenza fisica W intendiamo lintensit del
campo elettromagnetico che costituisce i raggi luminosi (vedi
5.1).
Il nostro occhio pi sensibile alla luce monocromatica gialloverde di lunghezza 0,55 mm. Radiazioni monocromatiche di lunghezza
maggiore (verso il rosso fino a 0,77 mm) o minore (verso il blu
fino a 0,38 mm) vengono percepite sempre meno intensamente secondo
una curva ricavata sperimentalmente (vedi fig. 5.10) e definita
curva normale di visibilit V(l).
Tramite questa curva possiamo definire il flusso luminoso di una
radiazione monocromatica

F = V(l) W

che rappresenta una misura equivalente dello stimolo percepito;


uguale flusso luminoso significa uguale sensazione di luminosit,
essendo diverse, per quanto detto sopra, le potenze luminose W che
danno tali sensazioni. Il flusso luminoso costituisce una
grandezza psicofisica che lega, cio, una grandezza fisica come la
potenza ad una psichica come la visibilit. In figura 5.10
rappresentato il grafico del coefficiente di visibilit v(l),
ottenuto dividendo V(l) per il valore massimo V max che la curva
raggiunge in corrispondenza della radiazione a 0,55 mm.
Altra grandezza psicofisica lintensit luminosa che rappresenta
il flusso emesso in un angolo solido unitario; si misura in

candele (cd) e costituisce lunit di misura primaria della


fotometria; la candela inserita nel Sistema Internazionale (SI)
delle unit di misura.
La magnitudine di un corpo celeste basata proprio sullintensit
luminosa della radiazione dellastro in questione.
Altre caratteristiche fotometriche della visione che influenzano
losservazione astronomica sono lacuit visuale, la soglia di
visibilit e labbagliamento.
Lacuit visuale rappresenta la capacit di distinguere punti
vicini (una stella doppia, per esempio); in condizioni ottimali
pari a 1 darco.
La soglia di visibilit rappresenta la minima intensit luminosa
percepibile.
Uno strumento ottico come il telescopio in grado di aumentare
sia lacuit che la soglia proprie dellocchio umano.
Labbagliamento la momentanea riduzione delle facolt visive
provocata dalla presenza nel campo visivo di un oggetto molto
brillante rispetto agli altri circostanti.

Figura 5.10
Questi parametri non sono assoluti; la visione
"con memoria", ovvero fortemente influenzata
osservato in precedenza. Il nostro cervello in
la
percezione
allintensit
globale
dello

una sensazione
da ci che si
grado di adattare
stimolo.
Se
la

luminosit media degli oggetti nel campo visivo elevata, la


soglia di visibilit tende a salire, mentre se bassa, la soglia
tende a scendere. Questo adattamento non istantaneo, ma richiede
un certo numero di minuti che dipende dalla differenza tra i due
stimoli medi.
Quando si arriva sul campo di osservazione essendo prima passati
per luoghi illuminati, si ha una ridotta percezione del cielo
stellato; occorre aspettare al buio un po di tempo per permettere
al cervello di adattarsi alle nuove condizioni di luminosit. Se
il cambiamento troppo repentino, si ha un abbagliamento
momentaneo; come quando si esce da un luogo buio verso uno
spazio assolato, o quando qualcuno accende improvvisamente una
luce nella nostra camera buia appena ci siamo svegliati...
Per questo motivo, durante le osservazioni astronomiche bisogna
evitare di accendere luci di qualsiasi tipo; anche una semplice
torcia elettrica pu disturbare la visione per vari minuti.
La percezione dellintensit luminosa , inoltre, legata al
contrasto presente nel campo visivo; le macchie solari ci appaiono
quasi nere perch molto meno luminose del resto del disco solare,
ma prese singolarmente sarebbero anchesse molto brillanti, cos
come ad occhio nudo vediamo scuri i "mari" della Luna perch
contrastati dalle altre zone della superficie pi riflettenti,
mentre isolati nel campo del nostro oculare gli stessi si rivelano
molto pi chiari.
Diamo
ora
qualche
breve
cenno
di
colorimetria.
Oltre
allintensit,
infatti,
abbiamo
altri
due
attributi
della
sensazione visiva legati alla percezione del colore: il tono (o
tinta) e la saturazione.
Il tono si riferisce al colore base con cui classifichiamo la
sensazione luminosa: rosso, verde, blu, giallo, ecc...
La saturazione si riferisce alla purezza del colore; possiamo
avere un rosso pi o meno vivo o sbiadito. Colori a bassa
saturazione tendono al grigio, perdendo cos lattributo cromatico
della sensazione. Il grigio, infatti, non un colore, ma una
sensazione di sola luminosit pi o meno intensa.
Le radiazioni monocromatiche vengono percepite come colori puri (o
saturi): i cosiddetti colori spettrali. Le radiazioni composte da
onde di lunghezza diversa, come sono le tipiche sorgenti che
illuminano il nostro campo visivo (luce del sole, lampadine,
ecc.), sono percepite come colore pi o meno saturo che
rappresenta la sensazione risultante dalla combinazione di una
radiazione di spettro continuo.
Luci di differenti caratteristiche cromatiche si possono sommare
tra di loro per ottenere un terzo colore. Le regole sperimentali

che definiscono ladditivit dei vari colori sono le leggi di


Grassmann. E grazie a tale principio che funzionano la
televisione, il cinema, la fotografia e la stampa. Da tre sorgenti
fondamentali, il rosso, il verde e il blu, si riescono ad
ottenere, per additivit, quasi tutti i colori normalmente
visibili.
Se ricordiamo quanto detto nel 5.9, nel nostro occhio vi sono tre
tipi di recettori (coni) per tre diverse bande luminose
fondamentali: rosso, verde e blu; ognuno viene stimolato in modo
differente a seconda dellintensit della gamma cromatica a cui
esso sensibile. La sensazione risultante dallelaborazione da
parte del sistema nervoso dei tre diversi stimoli il colore, che
una nostra percezione e non una propriet fisica della
radiazione luminosa. La sensazione del colore verde, per esempio,
pu essere data da una radiazione pura (monocromatica) a 0,5 mm,
oppure da una radiazione costituita da onde nella banda del giallo
insieme ad onde della banda del blu (vedi fig. 5.1). Nel primo
caso "verde" un attributo convenzionalmente dato ad una
radiazione in un ben determinato campo spettrale, nel secondo caso
una caratteristica della sensazione visiva.
L 'ottica studia tutti quei fenomeni relativi alla visione e di
conseguenza riguarda le propriet della luce e degli strumenti
ottici, cio quegli strumenti in grado di alterare le immagini.
La luce elemento fondamentale, indispensabile alla vita sulla
terra. naturale allora che l'interesse verso di essa e verso i
fenomeni ad essa legati si manifestasse fin dall'antichit. Ma
come spesso accaduto nello studio dei fenomeni naturali in tempi
remoti, l'osservazione propriamente scientifica si confusa con
congetture di carattere filosofico, e ci ha ostacolato la giusta
impostazione del problema. Si pensi ad esempio che filosofi e
scienziati greci, seguaci di Pitagora, sostenevano che la visione
degli oggetti dipendesse esclusivamente dal soggetto, grazie ad un
fuoco che uscendo dagli occhi si posa sulle cose rendendole
visibili.
chiaro che una tale concezione cade in contraddizione di fronte
a fenomeni molto semplici e comuni, quali il fatto che al buio non
possibile vedere. Molti illustri personaggi si cimentarono nel
tentativo di dare una spiegazione al fenomeno della visione delle
cose, fra gli altri Lucrezio, il quale osserv che avvicinando ad
uno schermo bianco un oggetto vivamente colorato e fortemente
illuminato, sullo schermo appaiono dei bagliori del colore
dell'oggetto. Ne concludeva allora che ogni corpo emette delle
Scorze che raggiungono l'occhio provocando la visione. Nessuno era
per in grado di stabilire come tali scorze viaggiassero da un
corpo all'altro e di che natura fossero, visto che erano in grado
di attraversare oggetti trasparenti, contraddicendo il principio
di impenetrabilit dei corpi.

Fu solo nel 1600 che si riusc a dare una impostazione corretta al


problema, stabilendo che l'immagine da noi percepita si forma
all'interno dell'occhio e non all'esterno, ed provocata
dall'interazione della luce, proveniente dalle diverse sorgenti
luminose, con gli oggetti. Automaticamente il problema si spostava
alla ricerca della natura della luce e del suo comportamento
nell'interazione con gli altri elementi. Si apriva cos uno dei
capitoli pi affascinanti dello sviluppo del pensiero scientifico,
che avrebbe coinvolto le pi grandi menti della fisica moderna,
spesso impegnate in diatribe dai toni aspri, talvolta sconcertate
e disorientate di fronte a fenomeni che apparivano contraddittori.
A posteriori sappiamo che tali difficolt erano dovute ai limiti
del modello classico della fisica, correttamente interpretati solo
agli inizi di questo secolo con l'introduzione della fisica
quantistica.
Lo studio dei fenomeni ottici si svilupp in due direzioni: da un
lato la ricerca di un modello matematico in grado di descrivere
con buona approssimazione il comportamento della luce nei fenomeni
osservabili, dall'altro la formulazione di teorie sulla natura
della
luce
compatibili
con
il
modello
sperimentale
di
comportamento.
Lo studio condotto nella prima direzione port alla scoperta delle
leggi che regolano i fenomeni di RIFLESSIONE e RIFRAZIONE della
luce e dei princpi di funzionamento dei vari tipi di specchi e di
lenti. Quello condotto nella seconda direzione si dibatt a lungo
in una disputa fra la teoria Corpuscolare, proposta da Newton e la
teoria Ondulatoria, il cui principale fautore fu Huygens.
Accenniamo brevemente a queste due teorie evidenziando, man mano
che saranno presentate, come furono interpretati i diversi
fenomeni nell'ambito di esse.
La teoria ondulatoria
La teoria ondulatoria trov le sue basi nell'osservazione del
fatto che la luce pu attraversare un corpo trasparente senza
interagire con le molecole che lo compongono. Inoltre, se due
fasci di luce si intersecano, non modificano la loro direzione, e
ci a prova del fatto che la propagazione luminosa non comporta
trasporto di materia. Pertanto, Huygens postul che la luce, in
analogia a quanto accade per il suono, si trasmette per mezzo di
onde.
Scrive Huygens nel suo trattato della luce del 1690:
"... Quando si consideri l'estrema velocit con cui la luce si
propaga tutt'intorno, e che quando essa proviene da differenti
sorgenti, anche del tutto opposte, la luce dell'una traversa
quella dell'altra senza alcun perturbamento, si comprende bene che
quando vediamo un soggetto luminoso, ci non sar dovuto a

trasporto di materia che dall'oggetto luminoso viene fino a noi,


nella maniera che una palla e una freccia traversa l'aria.
dunque in altra maniera che essa si propaga, e quel che pu
condurci a comprenderla la conoscenza che abbiamo della
propagazione del suono nell'aria....".
La teoria corpuscolare
La maggiore difficolt incontrata per l'accettazione della teoria
ondulatoria dovuta al fatto che le onde allora note avevano
bisogno di un supporto di materia per potersi propagare, infatti
il suono non si propaga nel vuoto. La luce invece viaggia dal sole
fino a noi attraversando spazi privi di materia; inoltre se la
luce si diffondesse mediante onde sferiche dovrebbe formare delle
ombre confuse e non ben delineate come invece accade, segno che la
propagazione della luce avviene lungo delle rette.
Huygens tent di dare una spiegazione alla prima obiezione
immaginando l'esistenza dell' Etere come mezzo di supporto alla
propagazione postulando l'esistenza di una sostanza estremanente
fluida e rarefatta tanto da non condizionare minimamente il moto
dei corpi nello spazio, ma con straordinarie capacit di
elasticit; ci per giustificare l'elevata velocit con cui si
trasmettono i segnali luminosi.
Secondo Huygens allora la luce era prodotta dalla vibrazione delle
molecole dei corpi resi incandescenti.
Tali vibrazioni si trasmettono all'etere, che avrebbe
permeare tutto l'universo, giungendo fino all'osservatore.

dovuto

Scrive ancora Huygens:


"...... Il movimento della luce deve avere origine da ciascun
punto dell'oggetto luminoso, perch possa far percepire tutte le
diverse parti dell'oggetto stesso. E io credo che in questo
movimento trovi la sua migliore spiegazione supponendo che i corpi
luminosi allo stato fluido, come la fiamma e apparentemente il
Sole e le Stelle, composti di particelle che si muovono in una
materia molto pi sottile, che le agita con grande rapidit e le
fa urtare contro le particelle dell'etere, che le circondano e che
sono in molto minor numero di esse, e che nei corpi solidi come il
carbone o un metallo arroventato al fuoco, questo movimento
causato da violento scuotimento delle particelle del metallo e del
legno, delle quali, quelle che sono alla superficie urtano
egualmente la materia eterea.
...... Mostrer qui in qual modo io concepisco che avvenga la
propagazione di movimento della luce. Prendendo un gran numero di
palle di egual grandezza e di materia durissima e disponendole in
linea retta e a contatto l'una con l'altra si trova che, urtando
con una palla identica contro la prima, il movimento si trasmette

rapidamente fino all'ultima, la quale si separa dal rango e non ci


accorgiamo che le altre si siano mosse .....
Ora per applicare questa specie di movimento a quello che produce
la luce, niente ci impedisce di supporre le particelle di etere
costituito di materia che si avvicina alla durezza perfetta e di
una elasticit tanto pronta quanto vogliamo ......"
Furono in molti per a rifiutare l'idea dell'esistenza dell'etere,
poich non esistevano prove sperimentali a conferma di questa
congettura. Primo fra tutti Newton, il quale al riguardo scriveva
nel suo trattato di Ottica del 1704:
``Per spiegare i moti regolari e perpetui dei pianeti e delle
comete, necessario svuotare i cieli da tutta la materia, .....,
ed
anche
da
questo
mezzo
etereo
eccezionalmente
rarefatto, ......., che non di nessuna utilit ed ostacola le
operazioni
della
natura;
inoltre
non
vi

alcuna
prova
inconfutabile della sua esistenza, la quale perci dovrebbe essere
rifiutata.
E se si rifiuta questa esistenza, anche le ipotesi secondo le
quali la luce consiste in un moto che si propaga attraverso tale
mezzo vanno rigettate con esso.''
L'autorit e la grande considerazione di cui Newton godeva fece
propendere la maggior parte degli studiosi dell'epoca verso la
teoria corpuscolare sulla natura della luce, secondo la quale essa
composta da uno sciame di particelle di dimensioni infinitesime,
soggette anch'esse alle leggi di gravitazione.
Rimanevano per aperti i gravi problemi gi accennati insiti in
tale teoria, e di conseguenza rimaneva aperto lo scontro
dialettico fra l'una e l'altra ipotesi che doveva concludersi,
come vedremo, con il trionfo del modello ondulatorio della luce e
successivamente con il recupero di alcuni concetti espressi nella
teoria corpuscolare, all'interno della fisica quantistica e con
l'introduzione del concetto di Fotone.
La propagazione rettilinea della luce: la formazione delle ombre
ed il modello del "fronte di onda" di Huygens
Abbiamo accennato, in precedenza, al fatto che il modello
ondulatorio della luce fosse da taluni considerato in contrasto
con la propagazione rettilinea della luce in qualunque mezzo
trasparente ed omogeneo.
L' osservazione sperimentale di questo fenomeno (la propagazione
rettilinea della luce) molto semplice e certamente ognuno avr
avuto modo di osservarlo, ad esempio esaminando la luce che filtra
dalle imposte e la formazione delle ombre.

Vediamo brevemente le caratteristiche di tale fenomeno.


Una sorgente luminosa pu essere Puntiforme o Estesa. Da essa
partono i fasci di raggi di luce, che possono essere intercettati
da un corpo. Se esso arresta completamente il cammino della luce
si dir Opaco, altrimenti sar detto Trasparente.
Solitamente una sorgente luminosa qualsiasi emette un fascio di
raggi divergenti, cio essi costituiranno un cono di luce come in
figura (3.1).

Si possono ottenere in laboratorio, utilizzando filtri e lenti


anche dei fasci di luce paralleli o cilindrici e convergenti (fig.
(3.2)).

Supponiamo di proiettare come in figura (3.3) su uno schermo la


luce proveniente da una sorgente approssimativamente puntiforme.
Se ora intercettiamo il fascio di luce con un corpo opaco, vedremo
prodursi sullo schermo una macchia scura, l'ombra dell'oggetto,
dai contorni ben delineati. Tale figura costituisce la base del
cono ideale che si ottiene considerando le rette che passano per
il punto S dov' la sorgente, e si appoggiano al contorno del
corpo opaco, come in figura (3.3).

Se invece di considerare una sorgente puntiforme consideriamo una


sorgente estesa, ma minore del corpo opaco, si determinano sullo
schermo delle zone di penombra, cio i contorni della zona d'ombra

sullo schermo non sono ben


dall'ombra alla luce piena.

delineati

si

passa

gradualmente

\noindent La figura che si otterr sullo schermo si costruisce


geometricamente considerando le rette che passano per gli estremi
della sorgente luminosa e che si appoggiano al contorno del corpo
opaco. Si otterranno in tal modo due coni la cui intersezione
delimita la zona d'ombra pi marcata: il resto costituisce la
penombra (fig. (3.4)).

Se, invece, la sorgente luminosa pi estesa del corpo, l'ombra


convergente, come appare in figura (3.5).

Al di l del vertice P non c' pi alcuna ombra.


Questo spiega anche il fenomeno delle ECLISSI. In tal caso la
sorgente S il sole, il corpo opaco C pu essere la terra, nel
caso delle eclissi di luna, oppure la luna nel caso delle eclissi
di sole.
Nel secondo caso, ad esempio, se la terra viene a trovarsi fra C e
P, nel cono d'ombra determinato da C, nella regione della terra su
cui tale cono si proietta vi sar una eclissi totale di sole.
Nelle regioni che verranno a trovarsi nelle zone 1 o 2 di
penombra, l'eclissi sar parziale.
Come si vede, la descrizione dei fenomeni collegati alla
formazione dell'ombra pu avvenire utilizzando nozioni di tipo
geometrico lecite solo se si ammette che la luce viaggia in linea
retta. Newton riteneva che il modello ondulatorio proposto da
Huygens non riuscisse a spiegare questo tipo di fenomeni e portava
a riprova di ci la seguente analogia:

le onde sferiche si possono visualizzare ad esempio perturbando la


superficie di uno stagno in quiete lanciandovi un sasso.
Se durante l'espansione le onde incontrano un ostacolo esse non
formano dietro ad esso un profilo ben definito, ma si accavallano
disordinatamente.
La
stessa
cosa
dovrebbe
avvenire
nella
propagazione della luce.
Huygens ribatteva mediante un'altra analogia:
Se una barca naviga su un fiume, lascia dietro di s una scia ben
definita e dal contorno regolare. Ci dovuto al fatto che la
superficie di un fiume perturbata da una serie di piccole onde
che si confondono se osservate macroscopicamente. Dava inoltre un
modello matematico per spiegare la propagazione rettilinea della
luce, partendo dalla teoria ondulatoria: il Principio di HuygensFresnel.
Vediamo l'enunciato di tale principio.
Abbiamo gi detto che Huygens riteneva la luce composta da onde
elementari sferiche, prodotte nei vari punti dello spazio.
Esaminiamo la seguente situazione: alcune onde sferiche vengono
prodotte nei punti
onde, dopo un istante

; se C la velocit di tali
, il fronte di ogni singola onda di luce

sar una circonferenza di raggio uguale a

e centro in

Ci che noi percepiamo per la sintesi dei vari fronti d'onda, o


meglio l'inviluppo delle varie circonferenze che costituiscono
ognuna il fronte di una singola onda, dove per "inviluppo"
intendiamo quella particolare linea che risulta tangente a
ciascuna circonferenza.
In particolare se i punti
sono allineati su una retta,

dove si producono le onde,


l'inviluppo che percepiamo dopo

l'istante
sar ancora una retta. L'avanzamento che noi
osserviamo pertanto quello di un' onda piana che, come sappiamo,
viaggia in linea retta (fig. (3.6)).

La determinazione della velocit della luce


Uno dei primi problemi quantitativi affrontati nello studio della
natura della luce e della sua propagazione la determinazione

della sua velocit. Si riteneva giustamente che tale velocit, per


quanto grande, non fosse infinita, anche se la finitezza non era
percepibile nella maggior parte dei fenomeni naturali.
Il primo che tent di dare una valutazione della velocit della
luce fu Galileo Galilei.
Scriveva in un suo trattato:
" SIMPLICIO: Mostra l'esperienza quotidiana, l'espansione del lume
esser istantanea; mentre che vedendo in gran lontananza sparar una
artiglieria, lo splendor della fiamma senza interposizion di tempo
si produce agli occhi nostri, ma non gi il suono all'orecchie, se
non dopo notabile intervallo di tempo.
SEGREDO: Ehi, Signor Simplicio, da codesta notissima esperienza
non si raccoglie altro se non che il suono si conduce al nostro
udito in tempo men breve di quello che si conduce il lume; ma non
mi assicura se la venuta del lume sia perci istantanea, pi che
temporanea, ma velocissima .... ''
L'esperimento che tent Galileo fu quello di porre due persone
l'una di fronte all'altra munite di due lumi. La prima persona
scopre il proprio lume, la seconda esegue la medesima operazione
non appena scorge il segnale. In tal modo la prima persona avrebbe
dovuto avere la possibilit di misurare il tempo necessario alla
luce per compiere il percorso di andata e ritorno. Ma tale
velocit era veramente troppo grande per poter essere apprezzata
su distanze terrestri, cosicch l'esperimento, pur se ripetuto
ponendo le persone a distanza di due o tre miglia, non diede alcun
risultato.
In linea di principio la tecnica deve considerarsi corretta, ma
c' bisogno, per ottenere risultati apprezzabili, di osservare le
cose su distanze estremamente grandi, dell'ordine di milioni di
miglia!
Distanze
di
tale
ordine
di
grandezza
intervengono
nelle
osservazioni astronomiche, e fu proprio nell'ambito di studi
astronomici che si ebbe la possibilit, molti anni dopo
l'esperimento di Galileo e precisamente nel 1676, di ottenere una
stima sufficientemente precisa della velocit della luce. Fu
grazie al contributo del danese Romer (1644 - 1710), e dei suoi
studi sulle eclissi dei satelliti di Giove. Come abbiamo gi detto
un'eclissi si osserva quando il satellite entra nel cono d'ombra
del pianeta. Se il periodo di rivoluzione costante allora le
eclissi
avvengono
ad
intervalli
regolari.
Ciononostante
l'osservatore sulla terra sperimenta che tali intervalli variano
durante l'anno e ci dovuto alla diversa distanza in cui viene a
trovarsi la terra rispetto a Giove.

Iniziamo la prima osservazione dell'eclissi quando la posizione


dei vari astri come quella descritta in figura (4.1).

Dall'analisi dei tempi di rivoluzione si calcola che la seconda


eclissi dovrebbe osservarsi esattamente dopo sei mesi. In effetti
l'osservazione avviene con un ritardo di tempo
. Ci dovuto
alla nuova posizione della terra: (lo spostamento di Giove in 6
mesi trascurabile).

Per poter osservare la seconda eclissi la luce dovr percorrere un


tratto pi lungo per raggiungere la terra: la differenza di
lunghezza dei due percorsi
, pari al diametro dell'orbita
terrestre. Il ritardo di osservazione sar pertanto:
dove C la velocit della luce.
Da ci si ricava:

Romer misurando un ritardo


pari a
, ricav per la luce una velocit pari a:

Successivi
esperimenti
approssimare il valore a

pi

precisi

gli

, noto il valore

consentirono

di

Altri esperimenti di tipo astronomico furono condotti da Bradley


studiando il fenomeno della Aberrazione Astronomica; i risultati
da lui ottenuti non si discostarono molto da quelli di Romer.
I primi a tentare la misurazione della velocit della luce con
metodi non astronomici furono Fizeau e Foucault. Essi riuscirono
ad applicare praticamente l'idea teorica di Galileo di misurare il
tempo impiegato dalla luce a percorrere un certo cammino nei due
sensi.
Fizeau utilizz uno strumento cos composto: una sorgente
luminosa, uno specchio ed una ruota dentata posta fra di essi
(fig. (4.3)).

Il raggio di luce parte dalla sorgente e passando attraverso uno


spazio vuoto della ruota dentata raggiunge lo specchio, viene
riflesso e ritorna al punto di partenza. Il raggio di ritorno pu
essere intercettato da un "dente" della ruota se essa gira con
sufficiente velocit angolare. proprio la misura della velocit
angolare necessaria ad intercettare il raggio riflesso che
consente la misurazione della velocit della luce. Infatti se la
luce riflessa viene intercettata, vuol dire che il tempo impiegato
dalla luce per percorrere due volte (andata e ritorno) la distanza
ruota-specchio uguale al tempo impiegato dalla ruota a
percorrere l'angolo
un dente.

, fra il centro di un faro e il centro di

Tale intervallo di tempo sar dato, se


la velocit della ruota
necessaria affinch avvenga il fenomeno, da:

Questo fornisce:

dove l la distanza ruota-specchio.


Il valore di C ottenuto in tal modo fu:

L'imprecisione per eccesso era dovuta al fatto che l'osservazione


della "sparizione della luce", segno che il raggio viene
intercettato, una operazione che risente dei "riflessi"
dell'osservatore per cui soggetta ad errori.
L'esperienza fu migliorata da Foucault, il quale
ruota dentata utilizz uno specchietto rotante.

anzicch

una

Il raggio parte da S e viene riflesso dallo specchio rotante,


raggiunge il secondo specchio e viene nuovamente riflesso dallo
specchio rotante, che nel frattempo avr compiuto una rotazione
pari a un angolo (fig. (4.5)).

Dunque il raggio partito dal punto A sar proiettato sul punto B.


La distanza fra A e B consente di ottenere l'angolo
e se

nota, si ottiene il valore di


, che il tempo impiegato dalla
luce per percorrere la distanza fra i due specchi per due volte.
Con questo metodo Foucault ottenne un valore per C pari a:

Altre esperienze condotte da Michelson agli inizi del secolo


utilizzando gli stessi strumenti di Foucault, diedero un valore:

Il valore pi preciso finora ottenuto per la velocit della luce


risale al 1970, ed :

Nel 1983, la velocit della luce stata utilizzata per definire


l'unit di lunghezza. Oggi infatti si chiama metro la distanza
percorsa dalla luce nel vuoto in un intervallo di tempo pari a
1/299792458 secondi. Dunque la velocit della luce posta per
definizione pari a

La luce come onda elettomagnetica


Analisi microscopica della emissione luminosa
Si deve a Maxwell l'identificazione della luce come fenomeno
elettromagnetico; l'idea nacque osservando che la velocit delle
onde elettromagnetiche nel vuoto

dove

ed

sono delle costanti, pari alla velocit della luce.

La cosa pi sorprendente che si osservata nella prima met di


questo secolo che il comportamento della luce avvalora in certi
fenomeni la teoria corpuscolare ed in certi altri la teoria
ondulatoria, ed in un certo senso contraddice alcune osservazioni
ed interpretazioni di fenomeni che verso la fine del secolo XIX
avevano portato al trionfo della teoria ondulatoria. Si ha infatti
che la luce si comporta come un'onda nella sua propagazione
attraverso lo spazio e si comporta come uno sciame di particelle
(dette fotoni) nella sua interazione con la materia.
La spiegazione di tale ambivalenza si ha solo nell`ambito della
Meccanica quantistica, per cui le particelle cui si accennava
prima non sono altro che "Pacchetti" di onde elettromagnetiche di
piccola lunghezza d'onda.
Cerchiamo ora di spiegare in termini semplici tali fenomeni.
L'emissione della luce avviene a livello atomico; sappiamo che un
atomo costituito da un nucleo centrale in cui sono concentrati
protoni e neutroni e da un certo numero di elettroni che orbitano
attorno al nucleo. In un certo senso l'atomo pu essere pensato
come un sistema planetario in cui al centro c il nucleo atomico
dove risiede quasi tutta la massa e le cariche positive (tenute
dai protoni). Attorno ad esso, in maniera simile ai pianeti,

orbitano i
(5.1.1)).

singoli

elettroni

che

hanno

carica

negativa

(fig.

Ogni orbita corrisponde ad un "livello energetico", cio gli


elettroni ruotano seguendo una data orbita a seconda della
quantit di energia che in loro possesso. Tale energia non
fissa: pu anche essere ceduta o assorbita, nel qual caso
l'elettrone cambier orbita, passando ad un livello energetico
rispettivamente pi basso o pi alto. Tale passaggio non avviene
mediante spostamenti graduali e continui, ma "a salti", cio
direttamente da un'orbita all'altra.
\noindent Da notare anche che questi livelli energetici sono in
numero finito. L'aumento di energia corrisponde all' assorbimento
nell'atomo di un fotone di luce; in tal modo l'atomo viene a
trovarsi in una condizione di eccitamento, con gli elettroni che
si spostano su orbite corrispondenti a livelli energetici pi
alti. In modo del tutto casuale, gli elettroni ritornano al loro
stato energetico di partenza. In tale situazione si ha una
emissione di un fotone di luce, con l'insorgere spontaneo del
raggio luminoso.
La produzione della luce avviene allora nel modo seguente:
fornendo energia al corpo (ad esempio surriscaldandolo fino a
farlo diventare incandescente) si provoca una eccitazione degli
atomi. Ci comporta una variazione del livello orbitale degli
elettroni. Quando questi, casualmente, saltano alle orbite
originarie, si ha emissione di luce.
Sorgenti luminose
Allora una sorgente luminosa non altro che un corpo che emette
una radiazione elettromagnetica pura.
Le sorgenti luminose si possono classificare in diversi modi:
distinguiamo le Sorgenti primarie, che effettivamente emettono la
radiazione luminosa, dalle Sorgenti secondarie, che riflettono la
luce proveniente da altre fonti; le sorgenti Puntiformi dalle
sorgenti Estese, intendendo per sorgenti Puntiformi le sorgenti
sufficientemente piccole (cio le cui dimensioni non superino 1/10
della distanza fra di esse ed una superficie in esame).

A seconda del tipo di luce che viene emessa possiamo classificare


le sorgenti in Coerenti e Incoerenti: le prime emettono radiazioni
luminose in fase fra di loro, mentre le seconde emettono
radiazioni sfasate. (Intendiamo per radiazioni in fase delle
radiazioni i cui rispettivi minimi e massimi coincidono (fig.
(5.2.1)).

In natura
ciascuna
coerenti
strumenti

tutte le sorgenti sono incoerenti, poich l'emissione di


radiazione avviene in modo casuale. Le radiazioni
si ottengono solo in laboratorio mediante sofisticati
ottici (ad esempio il LASER).

Intensit luminosa e d'illuminazione


Per quanto detto in precedenza, possiamo affermare che l'emissione
di luce sempre legata ad un assorbimento e ad un rilascio di
energia; il bilancio energetico complessivo della quantit di
energia assorbita e di quella emessa sotto forma di radiazione
luminosa dipende dal corpo. Sappiamo ad esempio che alcuni corpi
trattengono una maggiore quantit di energia, altri la trasformano
in forme diverse dalla radiazione luminosa. importante pertanto
dare una classificazione dei corpi in base alla quantit di
energia che irradiano sotto forma di luce. A tal fine si
introducono i concetti di Intensit luminosa e di Intensit di
illuminazione.

L'intensit luminosa la quantit di energia che un corpo irradia


in un secondo.
Pertanto essa misurata in Watt.

L'intensit di illuminazione misura la quantit di energia che


arriva su una superficie di un
Essa sar misurata in

a causa dell'illuminazione.
.

E' necessario anche introdurre una unit di misura diretta della


sensazione
luminosa
percepita
visivamente;
ci
si
ottiene
introducendo l'unit Violle:

Un

Violle

l'intensit

luminosa

prodotta

dalla

superficie

di

di platino alla temperatura di fusione (1769 C).


Ai fini pratici molto pi usata la Candela internazionale (CD)
che vale 1/20 del Violle.
Bunsen costru uno strumento detto Fotometro, in grado di misurare
l'intensit luminosa utilizzando semplicemente un foglietto di
carta abbastanza spesso in modo da non far passare la luce, con
una macchia d'olio al centro. Posta di fronte ad una sorgente
luminosa, tale macchia appare scura se la guardiamo dallo stesso
lato della sorgente, appare chiara se la guardiamo dall'altro
lato. Infatti l'olio rende la carta traslucida per cui lascia
filtrare una certa quantit di luce.
Se la quantit di luce che filtra attraverso la macchia viene
compensata ponendo dall'altra parte del foglio una sorgente della
stessa intensit, allora la macchia non pi visibile. Il
fotometro di Bunsen costituito da un'asta graduata su cui
possono scorrere tre sostegni di cui uno al centro porta la carta
con la macchia e gli altri due le sorgenti luminose, per una delle
quali nota la sua intensit luminosa (fig. (5.3.1)).

Se le due sorgenti sono poste alla stessa distanza dal foglio ed


hanno la stessa intensit, la macchia sparir alla vista; ma se ad
esempio poniamo la seconda sorgente ad una distanza dal foglio di
carta doppia rispetto alla prima, si osserver che la macchia
scomparir solo se l'intensit luminosa della seconda sorgente M
volte maggiore della prima.
Questo suggerisce che l'intensit di illuminazione sul foglio
dipende dalla distanza della sorgente, e varia in ragione del
quadrato
della
distanza,
cio
se
tale
distanza
raddoppia
l'intensit diventa 1/4; se triplica diventa 1/9, etc. Cio se K
l'intensit prodotta alla distanza unitaria, alla distanza d si
avr l'intensit
Se

sono

.
rispettivamente

le

intensit

sorgenti
e se
sono le distanze di
carta, vale allora la seguente legge:

luminose

delle

dal foglio di

da cui si ottiene:

Se

nota, si pu cos facilmente ottenere il valore di

Una spiegazione di tale fenomeno sperimentale pu


partendo dalla propagazione rettilinea della luce.

essere

data

Sia S una sorgente che possiede una intensit luminosa I e sia A


una qualsiasi superficie posta a distanza d da S.

La luce che passa attraverso A, passer anche attraverso una


superficie B posta a distanza 2d da S e costruita in modo che
risulti essere la base del cono che si ottiene considerando le
rette spiccate da S e tangenti al contorno di A (fig. (5.3.3)).

Ma la superficie di B , per come costruita, quattro volte


maggiore
rispetto
a
quella
di
A,
pertanto
l'intensit
d'illuminazione su A sar I/A, mentre su B sar I/B e poich B=4A,
ne segue che su B l'intensit

Osserviamo che la luce, attraversando mezzi trasparenti, viene


rifratta. Tale fenomeno sar interpretato per mezzo della teoria

ondulatoria
e
corpuscolare
e
permetter
comportamento della luce nelle lenti.

di

studiare

il

La rifrazione
Consideriamo un raggio luminoso che viaggia attraverso un mezzo
trasparente, ad esempio l'aria. Se sul suo cammino incontra un
nuovo mezzo trasparente, ad esempio acqua o vetro, nel passaggio
dall'uno all'altro mezzo il raggio subisce una deviazione
abbastanza netta. Si dice in tal caso che il raggio viene
rifratto.

La rifrazione la causa di numerosi fenomeni visivi facilmente


osservabili.
Ad esempio se immergiamo una sbarretta in un bicchiere pieno
d'acqua, avremo l'impressione che essa sia spezzata, poich la
parte immersa non appare allineata con la parte fuori dall'acqua;
oppure collocando una monetina in una bacinella non trasparente e
ponendosi in modo che il bordo del recipiente impedisca di
scorgere la moneta, si osserva che riempendo d'acqua la bacinella,
si vedr ricomparire la monetina. Ci accade perch in presenza
dell'acqua i raggi luminosi seguono una traiettoria "spezzata" che
consente di aggirare l'ostacolo costituito dal bordo.
Le leggi sperimentali che regolano la rifrazione furono date per
la prima volta da Cartesio e da Snellius.
Il raggio luminoso, prima di incontrare la superficie di
separazione dei due mezzi trasparenti, viene detto raggio
incidente; dopo la rifrazione raggio rifratto.
L'angolo di incidenza quello formato dal raggio incidente con la
normale alla superficie di separazione; l'angolo di rifrazione
invece quello formato con la stessa normale dal raggio rifratto.

La prima osservazione che si pu fare la seguente:

il raggio incidente, quello rifratto e la normale giacciono su di


uno stesso piano

Inoltre
sussiste
rispettivamente gli
osserva che:

la
seguente
relazione:
angoli di incidenza e di

se
sono
rifrazione, si

(1.1)
La (1.1) trova una spiegazione differente a seconda che
consideri valida la teoria corpuscolare o quella ondulatoria.

si

Vediamo come la teoria corpuscolare spiega il fenomeno di


rifrazione: Newton suppose che la variazione dell'angolo subita
dal raggio a causa della rifrazione fosse dovuta ad una variazione
di velocit delle particelle luminose che si determina nel
passaggio dall'uno all'altro mezzo, e portava la seguente
analogia: se una sferetta rotola su un piano inclinato con una
traiettoria inclinata di un angolo
rispetto alla normale al bordo
del piano, tale angolo pu essere ridotto se la sferetta subisce
un aumento repentino di velocit, ad esempio a causa di un
dislivello sul piano stesso (fig. (1.2)).

Riferendo l'analogia alla rifrazione, il piano inclinato a


corrisponde al primo mezzo; il piano inclinato b, al secondo. La
superficie di separazione corrisponde invece al dislivello fra i
due piani a, b.
Se quindi un raggio luminoso arriva a contatto della superfie con
velocit
, secondo l'idea di Newton, le particelle di luce sono
sottoposte ad una forza perpendicolare alla superficie di
separazione, per effetto della quale la componente perpendicolare
della velocit varia, mentre rimane inalterata la componente
parallela.
Se
tale componente parallela, e
la stessa componente
relativa alla velocit del raggio rifratto, varr:
(1.2)

Ma come sappiamo, si ha

(1.3)
e quindi risulta:

(1.4)
Diciamo che il mezzo 2 pi rifrangente del mezzo 1 se l'angolo
di incidenza maggiore di quello di rifrazione quando un raggio
luminoso passa dal mezzo 1 al mezzo 2.
Se allora conduciamo il nostro esperimento scegliendo, ad esempio,
i mezzi aria e vetro, oppure aria ed acqua (l'acqua e il vetro
sono pi rifrangenti dell'aria), si avr:
per cui dalla teoria
corpuscolare si ricava che

la velocit della luce maggiore nei mezzi pi rifrangenti.


Vediamo come la teoria
della rifrazione.

corpuscolare

spiega

invece

Costruiamo,
quindi,
il
raggio
rifratto
con
dell'inviluppo piano gi introdotto precedentemente.

il
il

fenomeno
metodo

Siano A,B due punti qualsiasi del fronte d'onda. Se la direzione


di propagazione del fronte inclinata di un angolo
rispetto alla
normale alla superfice, o il punto A o il punto B giunger prima
dell'altro a contatto della superfice. Assumiamo che sia A tale
punto. Dal momento in cui l'onda in A viene rifratta, il punto B

giunger sulla superfice dopo un tempo


un segmento di lunghezza pari a

, cio dopo aver percorso

Nello stesso intervallo di tempo, il punto A si sar spostato di


un segmento pari a

, che non uguale a

, poich si

suppone che

Allora, come mostrato in figura (1.5), l'inviluppo dopo il tempo


avr cambiato direzione di propagazione, e quindi, se l la
lunghezza del segmento A'B,

(1.5)
da cui:

(1.6)
Risulta pertanto dalla teoria ondulatoria una legge inversa da
quella ricavata nella teoria corpuscolare, cio:
e quindi
maggiore nei mezzi meno rifrangenti.

la

velocit

della

luce

Questo fatto costitu la chiave di volta che permise di stabilire


definitivamente quale delle due teorie era valida: poich l'acqua

risulta pi rifrangente dell'aria, bastava misurare la velocit


della luce nei due mezzi.
Tale prova venne realizzata intorno al 1860 da Foucault
utilizzando la tecnica dello specchio girevole e forn un
risultato che diede ragione alla teoria ondulatoria: la luce si
propaga nell'acqua con la velocit di
circa 3/4 del valore con cui si propaga nell'aria.

, uguale a

Indice di rifrazione relativo e assoluto


Il rapporto:

viene indicato solitamente con


e si chiama indice di rifrazione
relativo ai mezzi 1 e 2, quando la luce passa dal mezzo 1 al mezzo
2. Per le osservazioni precedenti possiamo dire che se
mezzo 1 meno rifrangente del mezzo 2; viceversa se

il

Osserviamo che anche nel caso della rifrazione vale il principio


della reversibilit del cammino luminoso. Questo ci permette di
dedurre la relazione

Si chiama indice di rifrazione assoluto l'indice calcolato


rispetto al vuoto. Per la (1.6) possiamo esprimere l'indice di
rifrazione come il rapporto fra la velocit della luce nei due
mezzi, cio:

da questo, l'indice di rifrazione assoluto sar:

Se consideriamo due mezzi il cui indice di rifrazione assoluto


rispettivamente

si ottiene facilmente:

(2.1)
relazione che lega l'indice di rifrazione relativo con gli indici
di rifrazione assoluto dei due mezzi. Nella tabella abbiamo gli
indici di rifrazione assoluto per alcuni mezzi.
MEZZO

INDICE

Aria

1,000294

Idrogeno

1,000139

Ossigeno

1,000272

Acqua

1,33

Alcol etilico

1,36

NaCl

1,53

Vetro Flint

1,579

Vetro Crown

1,516

Diamante

2,419

Zaffiro

1,76

Glicerina

1,474

Olio di cedro

1,515

Quarzo

1,544

Quarzo fuso

1,46

Etere

1,352

Angolo limite e riflessione totale


Se consideriamo un raggio che passa da un mezzo pi rifrangente ad
uno meno rifrangente, si potr considerare un particolare valore
dell'angolo di incidenza per il quale il raggio rifratto risulta
parallelo e radente alla superficie di separazione (fig. (3.1)).

Se
tale valore, per un angolo di incidenza maggiore di
, non
si avr la formazione di nessun raggio rifratto, ma il raggio
verr deviato totalmente all'interno del mezzo pi rifrangente,
dando luogo al fenomeno della cosiddetta riflessione totale.
prende il nome di angolo limite.

Il fenomeno ora descritto trova numerose applicazioni, ad esempio


nella costruzione delle fibre ottiche, che imprigionano la luce
all'interno di esse facendole percorrere anche dei cammini curvi.
Un'altra applicazione la costruzione dei periscopi, in cui viene
utilizzata una doppia riflessione totale.

Dalla (2.1), poich

si ricava anche

Se

uguale all'angolo limite, si ha

allora conoscendo gli indici di rifrazione assoluta dei mezzi, si


pu ottenere il valore dell'angolo limite dalla formula:

(3.1)

La rifrazione anche causa di numerosi fenomeni naturali, ad


esempio il miraggio e la fata morgana. Entrambi sono dovuti alla
diversa densit degli strati di aria, per cui quelli pi densi
risultano pi rifrangenti rispetto a quelli meno densi.
Nelle giornate molto calde, l'aria vicina al suolo molto pi
calda di quella che si trova negli strati superiori, e di
conseguenza pi rarefatta. Se un raggio luminoso parte dalla
cima di un albero diretto verso il basso, esso subisce una
deviazione man mano che incontra gli strati meno rifrangenti,
finch l'inclinazione del raggio raggiunge e supera l'angolo
limite, ed diretto verso l'alto. Se il raggio, dopo questa
traiettoria, incontra un osservatore, questi avr l'impressione
che esso ha compiuto un cammino in linea retta, per cui scorger
l'albero e la sua immagine come se fosse riflessa in uno specchio
d'acqua. Si ha in tal caso il miraggio.
Viceversa se le condizioni atmosferiche sono tali che l'aria pi
rarefatta in superfice, un raggio diretto verso l'alto pu
venire rifratto fino ad essere deviato verso il basso. In tal caso
l'oggetto pu apparire sollevato dal suolo.
Tale fenomeno si verifica in modo evidente sullo stretto di
Messina: la costa siciliana in particolari condizioni pu apparire
sospesa nell'aria. Si ha in tal caso il fenomeno detto fata
morgana.
Le lenti
Chiameremo lente un qualsiasi corpo trasparente limitato da due
superfici curve o da una superficie piana ed una curva, in grado
di modificare la traiettoria di un raggio luminoso che la
attraversi.
Le lenti si distinguono in convergenti, riconoscibili per una
forma pi spessa al centro e pi sottile ai bordi, oppure
divergenti, di forma pi spessa ai bordi e pi sottile al centro.
A loro volta le lenti convergenti si suddividono in biconvesse,
piano-convesse e menisco-convesse (fig. (4.1));

e quelle divergenti in biconcave, piano-concave e menisco-concave


(fig. (4.2)).

Chiamiamo: raggi di curvatura di una lente i raggi delle sfere cui


le calotte che costituiscono la lente appartengono. L'asse che
congiunge i centri di tali sfere detto asse principale. Il punto
in cui l'asse ottico incontra la lente detto invece centro
ottico. Ogni retta per il centro ottico detta asse secondario.
Lenti convergenti
Cominciamo ad analizzare gli effetti che una
produce sui raggi luminosi.

lente convergente

Consideriamo un fascio di raggi paralleli all'asse principale. Si


sperimenta facilmente che tutti i raggi vengono rifratti in modo
da occorrere in un unico punto detto FUOCO della lente.

Ogni lente ovviamente possiede due fuochi simmetrici, posti


dall'una e dall'altra parte della lente, poich l'effetto prodotto
dalla lente indipendente dalla faccia impressionata dalla luce.
Lo stesso si osserva per un qualsiasi altro asse, per il quale si
determina un fuoco secondario F', che contribuisce a formare il
piano focale della lente.
Per costruire geometricamente l'immagine prodotta da una len\-te
sufficiente tenere presente che:
1) Il raggio passante per il centro ottico attraversa la lente
senza essere deviato.
2) Un raggio parallelo all'asse principale viene deviato sul fuoco
della lente.

3) Un raggio che passa


all'asse principale.

per

il

fuoco

deviato

parallelamente

A differenza degli specchi sferici, il fuoco di una lente non pu


essere determinato mediante costruzioni puramente geometriche,
poich la rifrazione dipende dal materiale che costituisce la
lente.
Se consideriamo una sorgente luminosa puntiforme posta davanti ad
una lente, notiamo che tutti i raggi provenienti da tale sorgente
vengono rifratti in modo da intersecarsi in un altro punto, che
sar il punto coniugato del punto oggetto in questione.

Possiamo allora affermare che

una lente convergente di piccola apertura e di piccolo spessore


trasforma un fascio omocentrico di raggi di vertice S in un altro
fascio omocentrico di vertice S'; S ed S' sono punti coniugati.
E andiamo ora ad analizzare il tipo di immagine prodotta da una
lente convergente quando si considera un oggetto esteso, che
graficamente rappresentiamo mediante una freccetta.
L'immagine

dipender

Indichiamo con
il centro ottico.

dalla

distanza

dell'oggetto

dalla

lente.

i due fuochi principali della lente e con C

Situazione 1: Oggetto distante dalla lente pi del doppio della


distanza focale

L'immagine

capovolta,

della lente tra

rimpicciolita
.

posta

dall'altra

parte

Situazione 2: L'oggetto ad una distanza doppia di quella focale.

L'immagine capovolta ma delle stesse dimensioni dell'oggetto,


posta in

Situazione 3: L'oggetto fra

L'immagine capovolta, ingrandita e ad una distanza maggiore del


doppio di quella focale.
Situazione 4: Oggetto posto nel fuoco della lente.

L'immagine appare all'infinito, ingrandita al massimo e capovolta.


Situazione 5: Oggetto posto tra il fuoco e la lente.
L'immagine stavolta virtuale; compare dalla stessa
dell'oggetto, diritta e ingrandita rispetto ad esso.

parte

Formule per i punti coniugati di lenti convergenti


Dato un punto oggetto A, la sua immagine B sar il punto in cui
occorrono tutti i raggi provenienti da A. In particolare per
determinare la posizione del punto coniugato nota quella del punto
oggetto, sufficiente disegnare la traiettoria di due raggi. Il
problema ulteriormente semplificato se l'oggetto sull'asse
principale. In tal caso sappiamo che un raggio che coincide con
tale asse attraversa la lente senza subire deviazioni. Basta
allora esaminare un solo raggio e vedere in quale punto esso,
rifratto (o il suo prolungamento), interseca l'asse principale.
Discorso analogo vale per oggetti estesi. In tal
costruzione dovrebbe essere ripetuta per tutti i punti.

caso

la

Consideriamo un segmento di estremi X,Y posto davanti ad una lente


convergente. Se esso posto prima del fuoco della lente, avr un'
immagine costituita dal segmento X', Y' come in figura (4.2.1).
Per costruzione, i triangoli XYC e X'Y'C sono simili; pertanto si
ricava:

(4.2.1)
Se poniamo le distanze XC e CX' pari rispettivamente a p e a q, la
(4.2.1) si scrive:

(4.2.2)
Sono inoltre simili i triangoli
Da ci possiamo ricavare:

(4.2.3)
dove f la distanza focale.

Ovviamente si ha XY =AC, per cui la (4.2.3) analoga alla

(4.2.4)
e confrontando la (4.2.2) e la (4.2.4) otteniamo

quindi
(4.2.5)
e dividendo ogni termine per pqf

(4.2.6)
Come si vede, la (4.2.6) analoga alla formula dedotta per gli
specchi sferici.
Abbiamo
ricavato
inoltre
la
formula
per
l'
ingrandimento
trasversale, definito dal rapporto fra la lunghezza dell'oggetto e
quella della sua immagine. La (4.2.4) infatti fornisce:

Si pu anche ricavare dalla (4.2.5):

(4.2.7)

per cui si ha:

(4.2.8)
Lenti divergenti
La principale differenza di una lente divergente rispetto ad una
convergente costituita dal fatto che se consideriamo un fascio
di raggi paralleli all'asse principale della lente, essi verranno
rifratti non in modo da intersecarsi in un punto, ma in modo da
"divergere" senza mai intersecarsi.
Se per consideriamo i prolungamenti dei
occorrono in un punto sull'asse principale.

raggi

rifratti,

essi

Dunque il fuoco di una lente divergente un punto virtuale, nel


senso che determinato non direttamente dai raggi rifratti, ma
dai loro prolungamenti.
Poich discorso analogo si pu riferire ad un qualsiasi altro asse
che non sia quello principale, diremo che il piano focale di una
lente divergente di tipo virtuale e sar definito come il piano
nel quale si forma l'immagine virtuale di un oggetto posto
all'infinito.

La formazione delle immagini mediante le lenti divergenti


ottiene utilizzando i metodi gi visti in precedenza.
Per quanto osservato
dell'oggetto rispetto

avremo che qualunque sia la


alla lente, la sua immagine

si

posizione
sempre

virtuale, diritta
dell'oggetto.

rimpicciolita,

posta

dalla

stessa

parte

Anche per le lenti divergenti valgono le formule dei punti


coniugati prima ricavate: si ottengono in modo analogo purch si
consideri negativa la distanza focale f della lente.
Se per studiare i fenomeni della riflessione e della rifrazione
possibile utilizzare un modello geometrico in grado di "simulare"
con buona approssimazione i fatti fisici reali, ci non si pu
fare per i fenomeni legati al colore. Il colore riguarda la natura
della luce non disgiunta, e dalla costituzione fisiologica degli
organi visivi (gli occhi) d la sensazione visiva.
Dispersione della luce: il colore
Il problema di dare una spiegazione alla natura e alla formazione
del colore si trascin per lunghissimo tempo su impostazioni
sbagliate ed anche dopo che gli studi sulla natura della luce
avevano raggiunto livelli accettabili, ci volle un bel p prima
che si riuscisse a capire che il colore non sussiste solo come
entit oggettiva, ma condizionato da numerosi fattori. Noi
infatti siamo in grado di attribuire, in base alle nostre
percezioni, ad ogni oggetto il proprio colore; per verifichiamo
continuamente
come
questo
colore
risenta
delle
condizioni
ambientali circostanti, del grado di illuminazione ed anche delle
particolari condizioni in cui si trovano i nostri occhi, come
facili e comuni esperienze ci consentono di verificare. Ad esempio
se proviamo a guardare il sole con gli occhi chiusi, essi
risulteranno impressionati per un certo periodo di tempo dalla
luce rosso-arancio che filtra attraverso le palpebre, s da
diventare particolarmente sensibili a questo colore. Quando
riapriremo gli occhi tutte le tinte ci appariranno "povere" di
rosso-arancio e quindi di tonalit pi fredda, tendente al verdeazzurro.
I fattori fisiologici accennati pur non formando oggetto di studio
dell'ottica fisica, sono importanti poich possono anche suggerire
delle piste di ragionamento che portano a conclusioni sbagliate,
come in effetti successo.
Fu Newton il primo a stabilire che il colore non una qualit
intrinseca degli oggetti che la luce mette in evidenza, ma

l'effetto dell'interazione del raggio luminoso con la sostanza;


cio la luce che ha in s il colore. Ci stato provato con la
scoperta, da parte di Newton, del fenomeno della dispersione della
luce, gi intuito da Cartesio nel tentativo di spiegare la
formazione dell'arcobaleno.
Newton osserv un raggio di luce che filtrando attraverso un foro
dell'imposta, attraversava un prisma di vetro. L'immagine cos
prodotta si raccoglieva sulla parete opposta formando una fascia
illuminata oblunga suddivisa in bande di sette colori diversi, che
andavano dal rosso al violetto.
Se mediante una lente la luce viene concentrata nuovamente, si
riottiene un fascio di luce bianca, segno inequivocabile che la
luce bianca la sintesi dei raggi colorati e che la loro
formazione dipende dal raggio luminoso e non dal prisma. Newton
prov inoltre che ogni singolo colore non ulteriormente
scomponibile: grazie ad un diaframma egli isol dal fascio un
raggio di un unico colore, e lo filtr mediante un prisma, ma il
fascio di luce non sub alcuna ulteriore modificazione.
Nel dare una spiegazione al fenomeno della dispersione Newton
rest fedele ai principi della teoria corpuscolare. La luce,
evidentemente, composta da particelle che non subiscono tutte la
medesima rifrazione. Poich secondo la teoria corpuscolare, la
rifrazione dovuta ad una forza che agisce quando la luce
attraversa la superficie di separazione fra due sostanze, ne segue
che le particelle che hanno una massa pi piccola subiscono una
maggiore deviazione, e vanno a costituire le bande luminose
colorate pi esterne.
D'altro canto anche nell'ambito della teoria ondulatoria la
dispersione
trova
una
spiegazione
plausibile
semplicemente
supponendo che ogni fascia di colore comprende onde di una certa
frequenza e che quindi un fascio di luce bianca composto da onde
con differenti lunghezze d'onda. Poich, come sappiamo, in certi
mezzi onde di differente frequenza viaggiano con differente
velocit, ecco spiegato il fenomeno della dispersione.
A prima vista, entrambe le spiegazioni appaiono ugualmente
accettabili,
ma
grazie
agli
studi
condotti
da
Eulero
e
dall'inglese J. Dallan, si provato che la prima ipotesi
errata.
Una prova si avuta nell'ambito degli studi sulla Aberrazione
cromatica, fenomeno che si ha quando si osserva un corpo luminoso
con una lente. Essa, infatti, in una certa misura, si comporta
come
un
prisma,
disperdendo
parte
della
luce
proveniente
dall'oggetto osservato, che apparir cos con un contorno sfumato
di colore. Ci fastidioso soprattutto nelle osservazioni
astronomiche poich i telescopi forniscono immagini sfocate. Il
fenomeno dell'aberrazione riguarda la rifrazione della luce ma non

la riflessione, pertanto
sistema di specchi per
telescopio "acromatico".

Newton ebbe l'idea di utilizzare un


eliminarla costruendo cos il primo

L'idea di ricorrere agli specchi gli era suggerita anche dalla


concezione che aveva sulla natura della luce; se infatti
l'aberrazione dovuta alle differenti masse delle particelle di
luce, un qualsiasi sistema di lenti non potr mai correggere
l'aberrazione, poich particelle con masse differenti devono
sempre essere deviate in modo diverso. Eulero teoricamente e
Dallan
praticamente
provarono
invece
il
contrario,
cio
utilizzando solo delle lenti, costruirono un obiettivo acromatico.
Questo risultato compatibile solo con la teoria ondulatoria. In
essa infatti lecito che, combinando opportunamente lenti
differenti, si riesca a rifrangere allo stesso modo componenti
monocromatiche diverse.
Analisi del colore: gli spettri
Se consideriamo un raggio di luce che proviene da una qualsiasi
sorgente, interessante studiare quali sono le componenti
monocromatiche che lo compongono. Questo tipo di studio si chiama
Spettroscopia.
Lo"spettro" dunque la composizione monocromatica che determina
un certo tipo di luce. Negli esperimenti condotti da Newton la
luce conteneva tutte le componenti monocromatiche. In tal caso si
dice che lo spettro di emissione continuo. Molte altre sorgenti
naturali per emettono una luce che contiene solo alcune
componenti monocromatiche rispetto alle sette individuate da
Newton. Ad esempio se osserviamo allo spettroscopio la luce emessa
da una lampada al neon, lo spettro che ci apparir non continuo,
cio composto da un'unica striscia luminosa orizzontale che
comprende i vari colori, ma discontinuo, cio composto da tante
righe verticali.
In generale la luce emessa da sostanze gassose quando sono
eccitate , elettricamente sempre di tipo discontinuo. Inoltre se
la sostanza allo stato atomico, le righe sono molto distanziate.
Se allo stato molecolare, le righe sono riunite in modo da
formare delle bande verticali.
Un altro modo per ottenere lo spettro di emissione di una sostanza
quello di dirigere su di essa un fascio di luce il cui spettro
di tipo continuo e di analizzare la luce dopo l'interazione con la
sostanza.
Si osserva che una sostanza che in grado di emettere ad esempio
una luce gialla, "sottrae" al fascio di luce bianca la componente
monocromatica del giallo, come provato dal fatto che lo spettro
della luce riflessa dalla sostanza, presenta una striscia scura

nella zona del giallo. Si ha allora il Principio di inversione


dello spettro, secondo il quale una sostanza assorbe le radiazioni
monocromatiche che in grado di emettere.
Ci spiega anche perch gli oggetti ci appaiono di un determinato
colore: dovuto all'assorbimento di particolari componenti
monocromatiche della luce che li colpisce. Ad esempio se
analizziamo la luce diffusa da un limone, notiamo che lo spettro
presenta delle zone scure nelle regioni del blu, del rosso e del
violetto. Dunque dal limone noi riceviamo una luce prevalentemente
composta dal giallo e dal verde. La sintesi di questi due colori
fornisce la sfumatura di colore giallo che caratteristica del
limone.
Diffrazione ed interferenza
Una volta assunta valida la teoria ondulatoria, c' da aspettarsi
che per la luce sussistono alcuni fenomeni che si osservano in
generale per le onde, cio la diffrazione e l'interferenza.
Questi fenomeni sono facilmente riscontrabili per quel che
riguarda ad esempio le onde sonore, ma non cos per la luce
poich le onde che compongono il raggio luminoso sono di
dimensioni notevolmente pi piccole rispetto alle onde sonore, per
cui fenomeni che si producono a livello microscopico sono talvolta
non bene osservabili a livello macroscopico. Ci nonostante
fenomeni di diffrazione e di interferenza furono osservati gi nel
1600 dal Bolognese Padre Francesco Maria Grimaldi, il quale
pubblicava nel 1665 la sua opera "Physicamathesis de Lumine,
Coloribus et Tride", in cui per la prima volta si parlava di
diffrazione della luce. Ma solo successivamente tali fenomeni
venivano interpretati a conferma della teoria ondulatoria: per
lungo tempo, anche a causa della grande autorit di cui Newton
godeva negli ambienti scientifici, furono oggetto di discussioni
che erano destinate a non portare alcun contributo alla
comprensione dei fenomeni, poich non si sganciavano dalla
concezione errata della luce come sciame di particelle materiali.
Vediamo ora in dettaglio di cosa si tratta: supponiamo che un
raggio di luce venga diretto su uno schermo nel quale sia
praticato
un
piccolo
foro
(con
diametro
dell'ordine
del
millimetro). Se la luce che attraversa il foro viene raccolta su
di un secondo schermo, si noter che essa delimita un contorno le
cui dimensioni sono ben pi grandi di quelle del foro. questo il
fenomeno della diffrazione. Ma qual la sua causa?

Appare evidente che quanto descritto contrasta con l'ipotesi


corpuscolare, poich non c' alcuna causa che possa alterare il
moto rettilineo uniforme delle particelle di luce; mentre
l'ipotesi ondulatoria fornisce una spiegazione abbastanza precisa
delle cose.
Il raggio che colpisce il primo schermo si pu approssimare come
inviluppo di onde sferiche, secondo il principio di HuygeusFresnel. Ogni punto di ogni singola onda sferica diventa a sua
volta centro di un'onda elementare, cos avviene l'avanzamento
globale del fronte d'onda piana. Ma nel passaggio attraverso il
foro, solo poche onde elementari filtrano attraverso di esso, per
cui l'inviluppo che determina il fronte d'onda non sar pi piano
ma tender ad allargarsi a ventaglio.
Se il foro piuttosto largo allora le onde che riescono a passare
attraverso di esso sono numerose, per cui si conserva il fronte
d'onda piano, a parte piccole sfrangiature sui bordi.
Il fenomeno dell' interferenza
generale, valido per qualsiasi
Principio di sovrapposizione.

invece dovuto al
tipo di onda, noto

fenomeno
come il

La somma vettoriale degli spostamenti prodotti da singoli moti ondulatori


uguale ad uno spostamento reale ancora di tipo ondulatorio.

In particolare da ci si ricava che quando due o pi vibrazioni di


uguale frequenza o due o pi onde di uguale lunghezza d'onda si
sovrappongono si pu avere un rafforzamento del risultante moto
ondulatorio.
Nel primo caso si avr una interferenza costruttiva che si ottiene
quando la differenza di fase pari a
; nel secondo caso si
avr una interferenza distruttiva, quando la differenza di fase
dove n un numero intero.
Nel caso della luce, per avere l'interferenza necessario
disporre di due sorgenti luminose coerenti, cio in fase o in
opposizione di fase per un periodo sufficientemente lungo. Per
ottenerle si pu dirigere un fascio di luce su uno schermo su cui
siano praticati due forellini molto piccoli e molto vicini: essi

costituiranno le nostre due sorgenti (questo dispositivo stato


utilizzato da Young), oppure (dispositivo di Fresnel) si pu
considerare una superficie speculare che forma un angolo di
ampiezza molto vicina ai 180deg.. Dirigendovi un fascio di luce,
esso verr riflesso come se provenisse da due sorgenti virtuali
.
In tal modo, poich le due sorgenti derivano da una sola sorgente,
le radiazioni emesse sono sempre coerenti poich le variazioni
casuali delle onde che le generano si ripercuotono su entrambe
nella stessa misura. Cos le condizioni di intererenza costruttiva
e distruttiva sussistono per un periodo sufficientemente lungo da
poter essere osservate.

L'immagine prodotta in questo modo, raccolta su uno schermo,


presenta le seguenti caratteristiche: una tipica figura di
diffrazione,
sovrapposta
alla
quale
vi

una
figura
di
interferenza costituita da una serie di sottili striscie di colore
scuro, disposte perpendicolarmente rispetto al piano che contiene
le sorgenti.
Se consideriamo un punto sullo schermo interessato dal fascio di
luce proveniente dalle due sorgenti coerenti, si osserva

che in tale punto si ha la condizione di interferenza costruttiva


e quindi un massimo luminoso, quando la differenza delle distanze
dal punto in questione dalle due sorgenti un multiplo della
lunghezza d'onda della luce.
Cio

se

sono

le

distanze

rispettive

del

punto

dalle

sorgenti
la lunghezza d'onda della luce, la condizione
di interferenza costruttiva
(3.1)
La condizione di interferenza distruttiva si ha invece quando si
verifica:

(3.2)
Infatti nell'esperimento, la differenza di fase che genera
l'interferenza dovuta proprio al fatto che le onde luminose
prodotte dalla stessa sorgente segnano due cammini differenti.
Come si vede dalle formule
lunghezza d'onda della luce.

l'interferenza

dipende

anche

dalla

Se si utilizzano diversi tipi di luce monocromatica, si otterranno


diversi tipi di figure d'interferenza, in particolare le fasce
scure di interferenza distruttiva hanno larghezza massima se si
utilizza luce rossa e minima se si utilizza luce violetta.
E' anche possibile, analizzando l'ampiezza delle fasce scure,
ottenere la lunghezza d'onda della luce. Infatti se chiamiamo d la
distanza fra le due sorgenti coerenti, per un qualsiasi punto P
sullo schermo la differenza
fra i cammini effettuati dalla luce
proveniente dalle due sorgenti per raggiungerlo, data da
(3.3)
dove

l'angolo indicato in figura (3.3).

Allora la (3.2) e la (3.3) diventano:


(3.4)

Ora se la distanza fra i due schermi e


la distanza fra il
massimo di luminosit centrale (che si ottiene per k=0) e quello
adiacente (per k=1), cio l'ampiezza della prima fascia scura, si
pu porre:

(3.5)
per cui

(3.6)
Polarizzazione
Fu solo agli inizi del 1800 che alcune importanti esperienze
condotte
soprattutto
da
Fresnel
provarono
una
importante
caratteristica delle onde luminose che era insospettata fino ad
allora,
cio
le
onde
luminose
sono
trasversali
e
non
longitudinali, come quelle sonore. Ricordiamo che un'onda si dice
trasversale quando lo spostamento avviene perpendicolarmente alla
direzione di propagazione; si dice longitudinale se lo spostamento
parallelo alla direzione di propagazione. Tale scoperta legata
allo studio del fenomeno della polarizzazione. Abbiamo visto che
la luce si forma per le vibrazioni che avvengono a livello atomico
corrispondenti ad assorbimento e successiva emissione di energia.
Pertanto da una sorgente luminosa vengono emessi numerosissimi
treni d'onda che oscillano in differenti piani, senza che se ne
possa individuare qualcuno privilegiato.

Esistono in natura alcuni materiali che hanno una sorprendente


capacit. Se un raggio luminoso viene fatto passare attraverso di
essi, sono in grado di riallineare tutti i piani di vibrazione dei
vari treni d'onda. La luce che si ottiene in tal modo viene detta
polarizzata e la sostanza utilizzata viene detta polarizzatore.
Per
usare
una
analogia
meccanica,
possiamo
pensare
polarizzatore come una grata composta da sbarre verticali.

un

Una qualsiasi lamina, che nell'analogia sta per piano di


oscillazione potr passare tra le sbarre solo se allineata con
esse (fig. (4.2)).

Se consideriamo un altro elemento in grado di polarizzare la luce


e lo applichiamo ad un raggio polarizzato, se esso disposto
nello stesso verso del primo polarizzatore, il raggio non subisce
alcuna modificazione. Se invece facciamo ruotare il secondo
polarizzatore, che per la funzione che svolge nell'esperimento
chiamiamo analizzatore, l'intensit luminosa va sempre diminuendo,
fino ad annullarsi completamente quando la rotazione di 90deg..
La spiegazione di tale fenomeno immediata se pensiamo
all'analogia della grata (fig. (4.3)).

La variazione dell'intensit luminosa epressa dalla


Malus:

Legge di

(4.1)
dove

l'angolo

polarizzatore, e

di

cui

dotato

l'analizzatore

rispetto

al

la massima intensit finale del raggio.

La conferma del fatto che le onde luminose sono trasversali


dovuta al fatto che se ripetiamo gli esperimenti che producono
l'interferenza con raggi di luce polarizzata, non si produce sullo
schermo alcuna figura d'interferenza. Questa propriet tipica
delle onde trasversali.
La natura ed il comportamento della luce ci consentono di
interpretare alcuni fenomeni tramite i raggi luminosi, ognuno dei
quali si pu pensare come un segmento di retta che ha la direzione
di propagazione del fronte d'onda.
Tale modello, noto come "ottica geometrica", fu introdotto da
Keplero e costituisce una buona approssimazione della realt ed
di estrema utilit nello studio dei fenomeni di riflessione e
rifrazione, nonch degli effetti prodotti dai vari tipi di specchi
(piani, concavi e convessi) e dalle lenti.

Abbiamo gi utilizzato questo modello nella spiegazione del


fenomeno della formazione delle ombre. Ovviamente i fenomeni
interpretati mediante l'ottica geometrica possono essere spiegati
anche con la teoria ondulatoria della luce sebbene con maggiore
difficolt nell'effettuare i calcoli.
Riflessione e diffusione della luce
Si consideri una sorgente luminosa che emette un raggio di luce;
se esso viene proiettato su una superficie molto ben levigata,
ritorna indietro come se fra il raggio e la superficie fosse
avvenuto un urto elastico.
Il fenomeno descritto la riflessione della luce ed facilmente
osservabile munendosi semplicemente di una lampadina e di uno
specchio. Il raggio che parte dalla sorgente luminosa viene detto
raggio incidente. Quello che esce dalla superficie riflettente si
chiama raggio riflesso. L'angolo che forma il raggio riflesso con
la normale alla superficie l' angolo di riflessione.

La riflessione della luce verifica la seguente legge sperimentale,


nota sotto il nome di Legge di Snellius-Cartesio:
i) Il raggio incidente, la normale alla superficie riflettente nel
punto di incidenza ed il raggio riflesso giacciono sullo stesso
piano;
ii) L'angolo di incidenza uguale all'angolo di riflessione.
Se la luce viene proiettata su una superficie non levigata,
assistiamo al fenomeno della diffusione. La superficie scabra su
cui si proietta il fascio luminoso pu essere approssimata
microscopicamente con una spezzata composta da tanti segmentini
ognuno piano.
I raggi, colpendo i segmentini, vengono riflessi secondo le leggi
della riflessione, ma globalmente il fascio non viene deviato
uniformemente ma diffuso in tante direzioni (fig. (1.2)).

Formazione dell'immagine su uno specchio


Se poniamo un oggetto di fronte ad uno specchio, osserviamo
facilmente che la sua immagine viene riprodotta in una regione di
spazio che sembra trovarsi all'interno dello specchio. Noi infatti
normalmente abbiamo la visione di un oggetto grazie ai raggi che
partendo da esso giungono in linea retta fino ai nostri occhi. Nel
caso dello specchio i raggi giungono a noi dopo aver subito una
riflessione, per noi percepiamo l'immagine ugualmente come se i
raggi avessero viaggiato in linea retta, cio come se l'oggetto si
trovasse dall'altra parte della superficie. Tale immagine viene
detta Immagine virtuale dell'oggetto.
Specchi sferici

Una superficie riflettente a forma di calotta sferica sar da noi


chiamato specchio sferico
Se la superficie riflettente interna alla calotta, parleremo di
specchio concavo; se esterna, di specchio convesso. Analizzeremo
dapprima il comportamento dei raggi luminosi nei confronti degli
specchi concavi.
Le leggi che regolano la riflessione valgono ovviamente anche nel
caso degli specchi sferici. Per le normali ai diversi punti degli
specchi sono disposte radialmente e non parallelamente come negli
specchi piani. Questo significa che raggi paralleli che colpiscono
lo specchio in due punti diversi avranno un angolo di riflessione
diverso. Ci ha come conseguenza una deformazione delle immagini
riflesse dallo specchio. Tale deformazione dipender dalla
posizione relativa dell'oggetto rispetto allo specchio.
Il centro della sfera cui la calotta riflettente appartiene viene
chiamato centro di curvatura. L'asse di simmetria della calotta
che passa per il centro di curvatura, si chiama asse ottico
principale, mentre ogni altra retta per il centro di curvatura che
incontra la superficie riflettente si chiama asse secondario (fig.
(2.1.1)).
L'angolo di apertura invece l'angolo compreso fra due rette che
passano per il centro di curvatura e sono tangenti al bordo della
calotta.

Fissato un punto della superficie, la normale


dato sar la retta per il punto passante
curvatura; un raggio che parte dalla sorgente
sull'asse ottico principale, sar riflesso in
alla normale relativa al punto, l'angolo di
all'angolo di riflessione (fig. (2.1.2)).

ad essa nel punto


per il centro di
S posta ad esempio
modo che, rispetto
incidenza uguale

Il raggio incidente viene riflesso in modo da intersecare l'asse


principale nel punto S'. Si osserva sperimentalmente che se
poniamo la sorgente luminosa in S' e consideriamo un raggio che
colpisce lo specchio nel medesimo punto A (fig. (2.1.2)), il
raggio riflesso intersecher in S l'asse principale; in altre
parole

si pu invertire il raggio incidente con il raggio riflesso


lasciando inalterata la costruzione geometrica. Questo fenomeno
costituisce il principio di reversibilit del cammino luminoso. I
punti S ed S' che si individuano in questo modo costituiscono una
coppia di punti coniugati.
Da qui discende l'importante conseguenza:

Se poniamo una sorgente luminosa in S, tutti i raggi da essa


uscenti saranno riflessi in modo da concorrere in S'.
Consideriamo ora un fascio di raggi tutti paralleli all'asse
principale. facile osservare, mediante la costruzione geometrica
vista, ed anche sperimentalmente, che essi sono riflessi in modo
da intersecare l'asse principale nello stesso punto F che prende
il nome di Fuoco (fig. (2.1.3)).

Se poniamo una sorgente luminosa nel fuoco di uno specchio


concavo, poich per il principio di reversibilit del cammino
luminoso i raggi saranno riflessi tutti parallelamente all'asse
principale, non potremo determinare per F alcun punto coniugato, a
meno di considerare il punto all'infinito. Questa osservazione ci
permette di definire il fuoco come il punto coniugato del punto
all'infinito.
Il fuoco di uno specchio concavo viene a trovarsi, con buona
approssimazione, a met fra il centro c e la superficie
riflettente. Cio, se f la distanza focale, ovvero la distanza
del fuoco dallo specchio, si ha:

dove r il
appartiene.

raggio

della

sfera

cui

la

calotta

riflettente

Il discorso fatto per un fascio di raggi parallelo all'asse


principale, si pu ripetere per un fascio parallelo ad un
qualsiasi altro asse, cosicch per ogni asse secondario si pu
determinare un fuoco secondario. L'insieme dei fuochi che si
individuano in tal modo al variare degli assi, costituisce un
piano, che si chiama piano focale. In analogia alla definizione
che abbiamo dato del fuoco principale, diciamo che il piano focale
di uno specchio concavo il piano nel quale bisogna porre un
oggetto luminoso per ottenere la sua immagine all'infinito (fig.
(2.1.4)).

Costruzione dell'immagine di un oggetto

Se poniamo un punto luminoso davanti ad uno specchio concavo, la


sua immagine sar il punto in cui concorrono tutti i raggi
riflessi dallo specchio. Chiamiamo "punto oggetto" la sorgente
puntiforme e "punto immagine" il corrispondente punto coniugato.
Per il principio di reversibilit del cammino luminoso,
possibile scambiare il punto immagine con il punto oggetto e
viceversa. Abbiamo gi avuto modo di verificare che se un punto
oggetto collocato ad una distanza maggiore di 2f dalla
superficie speculare, la corrispondente immagine verr a formarsi
davanti allo specchio, in un punto reale, tanto da poter essere
raccolta su di uno schermo (fig. (3.1)).
Inoltre abbiamo visto che se il punto oggetto posto esattamente
nel fuoco, tutti i raggi che partono da esso vengono riflessi
parallelamente, per cui diciamo che il punto immagine posto
all'infinito. L'immagine riflessa potr essere percepita solo se i
raggi riflessi "intercettano" l'osservatore, ma non pu essere
raccolta su uno schermo.

Vediamo ora cosa succede se l'oggetto viene posto ad una distanza


minore di quella focale. In tal caso i raggi riflessi sono
divergenti. Come nel caso dell'oggetto posto nel fuoco, potr
essere osservata una immagine solo se l'osservatore si trova sulla
traiettoria dei raggi. Inoltre stavolta, come accade per gli
specchi piani, l'immagine appare al di l della superficie
riflettente,
poich
essa
si
determina
come
il
punto
di
intersezione del prolungamento dei raggi riflessi. Diremo in tal
caso che l'immagine virtuale.

Se invece di considerare un singolo punto oggetto consideriamo un


oggetto di dimensioni estese, per determinare la sua immagine si
dovrebbe teoricamente costruire l'immagine di ogni punto che lo
compone. In pratica l'immagine si pu ottenere considerando solo
alcuni raggi, il cui cammino risulta di facile determinazione.

Si considerano di solito i seguenti raggi:


1) quello parallelo all'asse principale, il cui raggio riflesso
passa per il fuoco dello specchio;
2) quello che passa per il fuoco principale, che sar riflesso
parallelamente all'asse principale;
3) quello che passa per il centro, che in quanto coincidente con
la normale, verr riflesso su s stesso.
Vediamo, utilizzando le figure (3.3) - (3.7), come pu essere
costruita l'immagine.
Il nostro oggetto sar simboleggiato da una freccetta. Supponiamo
per semplicit, che la sua base poggi sull'asse ottico principale.
In tale situazione l'immagine riflessa si potr ottenere una volta
noto il punto immagine della punta della freccetta. Baster da
tale punto considerare la perpendicolare sull'asse ottico. Il
segmento che l'asse taglia su questa perpendicolare costituir
l'immagine cercata del nostro oggetto.
Situazione 1: oggetto posto
pertanto oltre il centro.

ad

una

distanza

maggiore

di

2f,

L'oggetto appare rimpicciolito e capovolto, tra il fuoco ed il


centro (fig. (3.3)).
Situazione 2: oggetto posto ad una distanza pari a 2f nel centro.

L'immagine ha le stesse dimensioni


centro ma capovolta (fig. (3.4)).

dell'oggetto,

Situazione 3: oggetto tra il fuoco ed il centro.

anch'essa

nel

L'immagine capovolta ed
maggiore di 2f (fig. (3.5)).

ingrandita,

posta

ad

una

distanza

Situazione 4: oggetto nel piano focale.

L'immagine risulta ingrandita al massimo, all'infinito.


Situazione 5: oggetto tra lo specchio e il fuoco.
\fig{}
L'immagine virtuale: appare al di l dello specchio, diritta e
ingrandita.
Quanto
descritto
vale
per
specchi
sferici
approssimativamente le seguenti condizioni:

che

1) angolo di apertura piccolo, cio la sfera che


calotta speculare molto estesa rispetto ad essa.

soddisfano
contiene

la

2) raggi parassiali, i raggi luminosi formano con l'asse ottico


angoli molto piccoli.
Se tali condizioni, dette approssimazioni di Gauss, non sono
rispettate, la formazione delle immagini avviene in modo meno
nitido; ci dovuto al fatto che un fascio di raggi luminosi
paralleli all'asse ottico principale, non concorrer esattamente
nel fuoco, ma in una regione pi ampia che viene chiamata
superficie caustica.
Relazioni fra punti coniugati
Vediamo ora di dare una interpretazione teorica dei fenomeni
descritti precedentemente. Studiamo le relazioni fra due punti
coniugati, tenendo conto delle approssimazioni di Gauss.

\fig{}
Come abbiamo visto, un punto oggetto S posto dinanzi ad uno
specchio concavo avr una immagine S' determinato dal punto di
occorrenza di tutti i raggi che partono da S.
Se, per semplicit, il punto oggetto collocato sull'asse
principale il punto immagine si determina considerando un singolo
raggio incidente e l'intersezione del raggio riflesso con l'asse
ottico, poich ogni raggio che coincide con un asse viene riflesso
su s stesso.
Sia SA il raggio incidente nel punto A della superficie speculare
e AS' il segmento del raggio riflesso tagliato dall'asse
principale. Per le leggi della riflessione, se CA il segmento
normale allo specchio nel punto A, gli angoli SAC e CAS' sono
uguali cio
(4.1) SAC=CAS
Come sappiamo, la bisettrice di un angolo interno di un triangolo
divide il lato opposto in parti proporzionali agli altri due lati.
Applicando questa regola al triangolo SAS', se ne ricava:

(4.2)
Poich stiamo supponendo valide le approssimazioni di Gauss, esse
ci consentono di utilizzare le seguenti espressioni approssimate:
(4.3)
Sostituendo la (4.3) in (4.2) si ottiene:

(4.4)
Poniamo ora la distanza VS pari a p e la distanza VS' pari a q. Da
ci ne segue

dove R
speculare.

il

raggio

Allora la (4.4) diventa:

della

sfera

cui

appartiene

la

calotta

(4.5)
ovvero
(4.6)
e dividendo ogni termine per pqR, rimane:

(4.7)
ma come sappiamo la distanza focale f pari a: f=R/2, per cui si
pu riscrivere la (4.7) come:

(4.8)
Vediamo quali
tale formula:

informazioni

si

possono

ricavare

dall'analisi

di

anzitutto otteniamo una conferma del principio di reversibilit


del cammino luminoso, poich la (4.8) risulta simmetrica rispetto
a p e q, per cui

se poniamo il
riprodotta in S.

punto

oggetto

in

S',

la

sua

immagine

verr

Inoltre al crescere del valore di p, il rapporto 1/p diventa


sempre pi piccolo, per cui se pensiamo p infinitamente grande,
1/p tender a zero. In tale situazione, la (4.8) assume la forma
seguente:

e ci giustifica l'asserto che avevamo ottenuto sperimentalmente


secondo il quale

per ottenere il punto immagine nel fuoco dello specchio occorre


collocare il punto oggetto ad una distanza infinita.

Per converso, grazie al principio di reversibilit del cammino


luminoso, un punto oggetto nel fuoco, produrr una immagine
"all'infinito". Partendo dalla (4.8) avremo

e dividendo per p sia il numeratore che il denominatore

(4.9)
dalla quale possiamo determinare le informazioni sulla distanza q
dell'immagine relativamente alla distanza p dell'oggetto.
Ad esempio se

, la frazione f/p sar minore di 1 e quindi

con
Dunque

(poich

).

Ci esprime il fatto che

l'immagine si forma sempre ad una distanza maggiore di quella


focale se l'oggeto posto pi lontano del fuoco.
Per ottenere delle ulteriori informazioni, conviene riscrivere la
(4.9) nella forma:

(4.10)
che si ottiene sostituendo ad f la sua espressione rispetto ad R:

. Allora se

si avr:

ovvero

l'immagine di un punto che si trova al di l di C apparir in un


punto fra C ed F.
Se

questo assicura che

un punto oggetto coincide con la sua immagine se viene posto in C.

Se
, per la (4.9) l'immagine sar ad una distanza
maggiore di quella focale; per la (4.10),

ovvero

per un punto posto ad una distanza minore del raggio ma maggiore


di quella focale, l'immagine si trover ad una distanza maggiore
del raggio.

Infine,

se

il

punto

specchio, per cui

oggetto

collocato

fra

il

fuoco

lo

, dalla (4.9) ricaviamo:

In questo ambito il valore negativo della distanza non assurdo,


ma sta semplicemente ad indicare che

l'immagine appare all'osservatore


specchio, cio virtuale.

come

se

fosse

dietro

lo

Le considerazioni ora esposte valgono anche se utilizziamo degli


oggetti estesi. In tal caso per utile anche indagare
sull'ingrandimento o la deformazione dell'immagine dell'oggetto,
gi osservata sperimentalmente.
Nel modello geometrico adottato per descrivere i fenomeni,
simuliamo un oggetto con una freccetta. Ci proponiamo di
descrivere
mediante
relazioni
matematiche
l'allungamento
o
l'accorciamento che subisce l'immagine fornita dallo specchio
sferico. Descriviamo cio l' ingrandimento lineare dovuto ad uno
specchio concavo.
\fig{}
I triangoli ABC e A'B'C risultano simili, per cui si ottiene:

(4.11)
Poniamo ora
AB=h e A'B'=h'.
Inoltre si osserva che B'C=q-R e CB=R-p, pertanto si ha:

(4.12)
ma per la (4.5), la (4.12) diventa:

(4.13)
ovvero per la (4.9):

(4.14)
La (4.14) esprime il rapporto fra la lunghezza dell'oggetto e
della sua immagine.
Come si vede, nota la distanza focale,

il rapporto fra la lunghezza dell'oggetto e della sua immagine


dipende solo dalla posizione dell'oggetto nei confronti dello
specchio
Specchi convessi
Le regole della riflessione
specchi convessi.

valgono

ovviamente

anche

per

gli

Per determinare la traiettoria di un raggio riflesso da uno


specchio convesso, occorre tenere presente che la normale alla
superficie la semiretta spiccata nel punto di riflessione e che
appartiene alla retta che congiunge il punto stesso con il centro,
il quale si trova stavolta dall'altra parte della superficie
speculare (fig. (4.1.1)).
\fig{}
Come appare evidente dalla figura (4.1.1) il punto immagine di un
qualsiasi punto oggetto posto dinanzi ad uno specchio convesso
sempre virtuale.
Se consideriamo un fascio di raggi paralleli all'asse principale,
i corrispondenti raggi riflessi saranno tutti divergenti, ma in
modo che i loro prolungamenti si intersecano in un punto virtuale.
Dunque

il fuoco di uno specchio convesso virtuale; anche tutti gli


altri fuochi relativi agli assi secondari sono virtuali; si
conclude allora che il piano focale di uno specchio convesso
situato dietro la superficie speculare.

Se consideriamo un oggetto esteso, la sua immagine si otterr


sfruttando la stessa costruzione geometrica adoperata per gli
specchi concavi. Si constata facilmente che qualunque sia il punto
dove l'oggetto viene situato, la sua immagine sar virtuale,
diritta e rimpicciolita
Valgono inoltre per i punti coniugati le stesse formule ricavate
nel caso degli specchi concavi, con la sola differenza che la
distanza focale deve essere considerata stavolta col segno
negativo.
La spiegazione della riflessione nella teoria ondulatoria
Per spiegare fisicamente il fenomeno della riflessione, assumendo
la teoria corpuscolare sufficiente supporre che le particelle
che compongono la luce urtino elasticamente la superficie
riflettente.
Nell'ambito
della
teoria
ondulatoria
occorre
ricorrere
al
principio di Huygens-Fresnel. Ogni singola onda elementare che
compone il fronte d'onda viene riflessa secondo la legge
sperimentale prima citata. Poich la velocit la stessa per
tutte le onde, a causa dell'inclinazione che il raggio ha rispetto
alla normale, il fronte non verr riflesso simultaneamente, ma
verranno riflesse prima le onde elementari pi vicine alla
superficie speculare e via via le altre, cosicch globalmente il
fronte d`onde subisce la riflessione rispettando la legge secondo
cui l'angolo di incidenza uguale all'angolo di riflessione (fig.
(5.1) - (5.2)).

Riporta le specifiche relative alla modalit di utilizzo delle


lenti della camera di ripresa.
I materiali utilizzate per le lenti possono essere di fatto vetro
e plastica.
La scelta del vetro o della plastica, come materiale da utilizzare
per una lente include un grosso numero di vantaggi e svantaggi per
ciascuna delle due.
Il vetro generalmente il materiale pi utilizzato nell'ambito
dell'ottica.
Esiste una grossa selezione di vetri disponibile con differenti
gli indizi di riflessione e di dispersione.
Le lenti di vetro sono pi dura e quindi anche pi durature
rispetto alle controparti di plastica.
Oltre a questo le lenti di vetro sono anche molto meno sensibile
alla temperatura e all'umidit.
Le tecniche di costruzione utilizzate per la fabbricazione delle
lenti di vetro, ad ogni modo, sono generalmente pi costose
comparate alle metodologie utilizzate per la costruzione delle
lenti di plastica.
La tecnologia utilizzata per la costruzione comprende in genere
due fasi ovvero la rottura del detto e la sua lucidatura
successiva.
La scelta dei materiali per le lenti di plastica e limitata a un
paio di dozzine.
I disegnatori ottici devono utilizzare un limitato numero di
indizi di riparazione per permettere alla lente un miglior
utilizzo di applicazione.
I ottica di plastica sono anche intolleranti alle alte temperature
e quindi non devono essere utilizzate quando queste superano i 70
to 90 gradi.
Questo tipo di lente viene prodotto mediante iniezione del
materiale plastico per cui rispetto alla metodologia di produzione
delle lenti di vetro risulta essere molto meno costosa.
Inizialmente optammo per un tipo di CCD della National in quanto
questa disponeva una risoluzione adeguata oltre ad essere un ci
era molto elastico a livello di funzionalit.
Ad esempio l'azione attiva accettabile mediante un sistema che
permette che di controllare il chip.

National vende anche il kit di sviluppo composto da un supporto


per la CCD, un grabber e da un kit ha di lenti utilizzabile per la
creazione dell'obiettivo e per sperimentazioni in campo ottico.
La prima lente costituita da un sistema a quattro elementi di
vetro a basso profilo.
Il secondo obiettivo invece composto da un sistema al cinque
lenti di vetro.
L'immagine formata da una lente rotonda e ed conosciuta con il
termine di cerchio dell'immagine.

Le alterazione patologiche dellocchio.


Durante il periodo di addestramento relativo a tutte le nozioni
necessarie per la creazione di un sistema di identificazione della
retina stato necessario anche tenere in considerazione tutte
quelle patologie che ha avrebbero potuto modificare l'occhio
durante il ciclo di vita dell'animale.
Un alcuni tipi di patologie sono relative alla retina mentre altre
riguardano
la
parte
esterna
dell'occhio, il cristallino e la
sclera.
Nel primo caso la valutazione
doveva essere fatta per valutare
quanto una determinata patologia
avrebbe
potuto
modificare
la
configurazione di de l'immagine
della retina.
Se la alterazione risulta essere
contenuta
entro
determinate
percentuali
di
identificazione
rimane
comunque
possibile
in
quanto il software addestrato
per poter tenere in considerazione
determinati livelli di modificazione.
Ad esempio alcune tipologie di macula sono portate da patologie
come ad esempio il diabete.
Alcune

alterazione,

invece,

delle parti esterne dell'occhio


causano
l'impossibilit
di
eseguire
l'identificazione
in
quanto
quelle
parti
che
normalmente sono trasparenti, come
nel
caso
del
cristallino,
diventano opachi e quindi risulta
impossibile acquisire l'immagine
del fondo retinale se non dopo un
intervento di tipo chirurgico.
La tipologia pi conosciuta di
questo tipo di alterazione della
parte trasparente dell'occhio e
quella legata alle catarrata.

Come possibile vedere nelle seguenti immagine


patologia rende opaca il cristallino dell'occhio.
Le patologie pi importanti della cornea sono:

questo

tipo

di

cheratite
ulcerativa
o
ulcera
corneale,
causata
da
malattie
batteriche, micotiche, virali e
clamydiosi,
traumi,
corpi
estranei, danni di natura chimica.

Tutte le informazioni contenute in questo volume sono


rigorosamente sotto COPYRIGHT di AMC ITALY e non
possono essere distribuite o utilizzate senza permesso
scritto della stessa.
QUALSIASI ABUSO SARA PERSEGUITO A TERMINI DI LEGGE

Versione 0.1.2
Flavio Bernardotti f.bernardotti@amcitaly.net
PROJECT MANAGER BIOTRACCIABILITA

Copyright 2004 AMC Italy


Via La Pira, 21
10028 Trofarello (TO)
ITALY
www.amcitaly.net
info@amcitaly.net

INTRODUZIONE
Circa un anno f si inizio a pensare seriamente a tutte le
possibili soluzioni ai problemi che componevamno il progetto
definito con il termine di BIOTRACCIABILITA.
Questo era costituito da diversi componenti tra i quali alcuni
hardware e altri software.

Lo scopo di tutto il progetto era quelloi di fornire un ambiente


completo che permettesse di tracciare la produzione e vendita
della carne bovina in modo sicuro e autenticato.
Il problema alla base di tutto era quello di trovare una
particolarit biometria capace di permettere lidentificazione di
un certo capo.
Lunica disponibile era di fatto la retina.
Questo attributo biometrico analizzato da due punti di vista era
sicuramente il pi complesso da gestire.
La prima complicazione era di fatto lacquisizione stessa.
Essendo una parte biologica interna allocchio dentro al quale si
accede solo tramite la pupilla la prima difficolt era proprio
quella di progettare una telecamera capace di fare questo tipo di
acquisizione.

La seconda complicazione era di fatto quella algoritmica legata al


sistema di identificazione.
In altre parole il problema abbinato alla domanda :
Come confrontare due retine per affermare che si tratta di quella
relativa ad una mucca o no ?
Sin dallinizio il problema subito apparso essere collegato
allinformatica che si interessa dellidentificazione di oggetti
allinterno di immagini composte.
Questa scienza di fatto composta da un infinit di teorie,
alcune applicabili al nostro problema e altre no.
La complicazione di fatto stata quella che per poter dire se il
problema era risolvibile in un modo o in un altro si doveva, dopo
aver ipotizzato la soluzione, eseguire delle prove mediante i
linguaggi o le librerie adatte.
Tutto questo si risolto in un lungo periodo di ricerca e
sviluppo durante il quale ho :
Raccolto informazioni
Ipotizzato soluzioni
Scritto programmi
In questo volume ho raccolto tutto il percorso tecnologico legato
allo sviluppo della soluzione informatica del riconoscimento
retinale.
Le soluzioni sono riportate ad una certa versione saranno
soggette alle modifiche che le ricerche e lo sviluppo indurranno.
Il tutto serve a testimoniare e a mantenere il livello culturale a
cui il lavoro relativo al problema ci ha condotto.

Premessa
Identificare una retina
per accertare lidentit di un capo
animale dopo averla ripresa dinamicamente da una telecamera una
funzione per la quale diventa necessario utilizzare diverse
metodologie informatiche a partire dai sistemi di reti neurali per
giungere sino a quelli che vengono definiti con il termine di
HIDDEN MARKOV MODELLS (modelli di Markov nascosti e ibridi
neurali/markoviani).
Tutte queste tecnologie sono considerate avanzate e se considerate
dal punto di vista algoritmico sono anche complicate da scrivere e
mettere a punto.
Le tipologie di problemi sono quelli legati alla COMPUTER VISION.
La computer vision quella branca dellinformatica che si
interessa
di
implementare
funzionalit
proprie
dellinterpretazione visiva umana.
Per svolgere queste funzioni ci si avvale di parti di software
algoritmico e di altre legate a sistemi che riproducono le reti
neurali.
Alcune funzionalit che a volte sembrano molto semplici da
svolgere a vista di fatto se analizzate dal punto di vista
algoritmico possiedono complicazioni notevoli.
Per questo motivo spesso lo svolgimento di tali funzioni vengono
eseguite mediante sistemi misti che utilizzano parti algoritmiche
e parti a reti neurali.
Le reti neurali riproducono mediante un modello matematico il
funzionamento del cervello umano simulando I modelli relativi a
neuroni, sinapsi e dendridi.
Tali sistemi mettono a punto la loro intelligienza
Un sottoinsieme della computer vision e quella definita con il
termine di object recognition il cui compito quello di
identificare, allinterno di un immagine che riproduce il nostro
spazio reale, degli oggetti particolari.
Collegate alle teorie dellobject recognition troviamo moltissime
metodologie algoritmiche che permettono lo svolgimento di compiti
particolari come ad esempio la segmentazione delle immagini.
Vedremo alcuni concetti nei capitoli che seguono.
Alcuni tipi di valutazioni legate allindividuazione di una
metodologia da applicare con la retina erano sorte dalla necessit
di estraniarsi da valutazioni di tipo geometrico-spaziale le
quali, come vedremo successivamente, non possono essere eseguite
per alcuni precisi motivi.
Solo per accennare a quelli che sono i problemi legati
allidentificazione di una retina possiamo riportare la diversit
tra una ripresa e laltra legata semplicemente al modo di tenere
la camera di acquisizione da parte delloperatore.
Questo
semplice
problema
inizialmente
ci
fece
capire
che
determinati metodi di confronto delle immagini utilizzanti dati
spaziali e geometrici non potevano essere utilizzati.
Trovandosi davanti al problema di confrontare due immagini il
metodo pi semplice ed immediato dovrebbe essere quello RAW ovvero

il confronto diretto di ogni singolo pixel dellimmagine.

==

Purtroppo questo per il motivo appena detto e per altri che


vedremo successivamente non pu essere utilizzato.
Il principio della ricerca venne orientata verso quelle che erano
le metodologie utilizzate per altri sistemi biometrici.
Limpronta digitale possedeva alcune caratteristiche che avrebbero
potuto essere simili ai problemi presenti con la retina.
Limmagine acquisita di un dito avrebbe ipoteticamente essere
differente tra una ripresa e laltra.
Nel caso dellimpronta digitale ad esempio vengono utilizzate
metodologie
per
la
segmentazione
dinamica
dellimmagine
dellimpronta e successivamente calcolando il centro di ogni
oggetto segmentato viene creato un poligono il quale verr
successivamente utilizzato per il confronto.
In questo caso il procedimento software, una volta eseguite tutte
le calibrazioni necessarie, serve ad informare il programma su
quali
oggetti
dell'immagine
noi
vogliamo
che
egli
esegua
l'analisi.
La parola oggetto usata per identificare ogni regione di
interesse all'interno dell'intera immagine.
Per segmentazione dell'immagine si intendono tutti quei passaggi
che consentono una descrizione dettagliata degli oggetti stessi
come il numero di pixels costituenti l'oggetto e le loro
coordinate, tutte le informazioni necessarie a tracciare i loro
contorni ecc..
Ogni oggetto viene quindi identificato per mezzo di un set
descrittivo di dati che vengono memorizzati e successivamente
impiegati per le operazioni di misura sugli oggetti stessi.

In questo caso le zone in cui vengono successivamente applicate le


funzioni di segmentazione sono delimitate dai flussi delle tracce
dellimpronta.
Sicuramente un algoritmo che
venne preso in considerazione
era quello relativo alliride.
In questa metodologia di fatto
esistono
molti
metodi
che
potrebbero
essere
utilizzati
anche per quanto riguarda la
retina.
Limmagine
iridea
viene
acquisita da una telecamera e
successivamente
mediante
una
trasformata
WAVELET
vengono
estratti dei particolari.
Limmagine catturata contiene solo una
parte interessata allelaborazione e
precisamente
quella
indicata
nellimmagine qui a fianco.
Questa porzione deve essere estratta e
riportata ad un immagine rettangolare
in cui esistono solo le informazioni
della zona relativa alliride.

In
una
seconda
fase
la
ricerca
venne
indirizzata per somiglianza di problema.
In altre parole se si guarda un immagine di una
retina di fatto sembra di vedere una traccia
fluviale presa da un immagine satellitare.
Anche in questo caso lacquisizione potrebbe
rendere un immagine ruotata, zoomata ecc.
I percorsi fluviali possono essere in piena o con il fiume in
secca.
Dicevamo prima dei problemi legati alle diverse
immagini acquisite in tempi differenti.
Per
capire
I
problemi
bisogna
pensare
alloperatore quando esegue lacquisiizione.
In pratica avviene quanto segue :
1 - Loperatore si mette davanti al capo animale
e punta con la telecamera locchio della mucca.
Ad
un
certo
punto
nel
visore
comparir
limmagine della retina. Il software deve essere
in grado di capire quando limmagine corretta
e quindi deve eseguire il ritaglio della
porzione
interessata
alloperazione
di
riconoscimento.
2
Limmagine
ritagliata
identificata tra quelle esistenti

deve

essere

Come dicevamo prima tali operazioni possono essere classificate


tra quelle definite con il termine di OBJECT RECOGNITION.
In effetti la retina non un oggetto reale ma ad ogni modo
possiede caratteristiche tali da poter essere considerata un
oggetto astratto dotato di proprie caratteristiche strutturali che
gli permettono di essere distinta da altri oggetti appartenenti al
background dellimmagine.
Il sistema software potrebbe essere considerato come indirizzato a
risolvere quattro funzioni particolari:

Regolazione e calibrazione telecamera


Creazione parametrizzazioni e training classificatori
Gestione dellidentificazione della retina
Acquisizione e riconoscimento

Nella prima fase si tratta solo di identificare una retina


allinterno
dellimmagine
mentre
nella
seconda
fase
lidentificazione non pu essere generalizzata.
In altre parole la prima parte del programma dovr essere in grado
di identificare una retina generica in modo tale da poter
delimitare la zona di immagine in cui questa inclusa.
La progettazione del software deve tenere conto di diverse
problematiche tra le quali le diversit che possono esserci tra le

varie immagini provenienti da acquisizione fatte in tempi


differenti.
Tali differenze possono essere di tipo cromatico e di tipo
spaziale-geometrico.
Le differenze cromatiche possono essere causate da cambiamenti del
tappeto retinale dovuto allet del capo.
Le
differenze
geometrico-spazali
invece
potrebbero
essere
indotte
da
patologie
particolari come anche solo una differente
pressione sanguigna.
Per
questo
motivo
ci
troviamo
nella
necessit di individuare un metodo di
riconoscimento in grado di ignorare o
perlomeno minimizzare tali effetti collegati
alla metodologia di ripresa:
1 - Langolatura dellimmagine retinale pu essere ruotata verso
destra o verso sinistra a causa della posizione della camera
stessa nelle mani delloperatore.
2 - Limmagine pu essere dimensionalmente
della diversa distanza di ripresa.

differente

causa

3 - Le trame venose possono essere pi o meno marcate a causa di


iper/ipo tensione venosa.
4 - La trama potrebbe essere leggermente differente a causa di
alcune patologie.
5 - Il colore di fondo potrebbe essere differente anche solo a
causa dellinvecchiamento del capo.
In senso generale il problema da risolvere con una delle
metodologie legate allobject recognition le quali si basano sullo
schema che segue.

Il progetto si sarebbe potuto affrontare in molti modi mediante


lutilizzo di reti neurali e di classificatori.
Nellambito della cultura informatica legata alla computer vision
esistono infinite teorie e metodi indirizzati alla soluzione di

questo tipo di problemi.


Nel limite del possibile tutte le ricerche sono state indirizzate
per individuare un pacchetto di libreria che disponesse al suo
interno delle funzioni idonee ad eseguire tutte le funzionalit
richieste.
Considerando nuovamente le due fasi in cui vengono prima
individuate
I Sistemi di Classificatori Multipli (MCS) si basano sulla
combinazione delle uscite di un insieme di classificatori
differenti.
Se si progetta una funzione di combinazione appropriata, e se i
classificatori dell'insieme commettono errori "diversi", un MCS
consente di ottenere una maggiore accuratezza di classificazione.
Ogni classificatore automatico caratterizzato da una probabilit
d'errore intrinseca (errore bayesiano), che pu essere ridotta con
tecniche di rigetto. Il rigetto consiste nella possibilit di non
eseguire la classificazione automatica di un pattern se questa
risultasse troppo incerta: il costo del trattamento "manuale" di
un pattern rigettato pu infatti essere inferiore al costo di una
classificazione errata (si pensi al caso della classificazione
automatica di patologie per scopi di diagnosi medica).
La regola di classificazione (regola di Chow) che consente di
ottenere il compromesso ottimale tra probabilit d'errore e di
rigetto basata sull'uso di una soglia di rigetto sui valori
delle probabilit a posteriori delle classi.
La regola di Chow ottimale se tali probabilit sono note con
esattezza. Tuttavia in applicazioni pratiche i classificatori
forniscono solo stime delle probabilit a posteriori.
Un termine non ancora riportato quello legato alla pattern
recognition in cui alcune metodologie algoritmiche vengono
utilizzate per il riconoscimento e lestrazione di particolari da
un immagine.
Il Pattern Recognition la disciplina scientifica che si occupa
delle teorie e dei metodi per la progettazione di macchine in
grado di riconoscere automaticamente "oggetti" in dati complessi.
Alcune tipiche applicazioni sono il riconoscimento di caratteri
manoscritti,
la
classificazione
di
immagini
telerilevate,
l'identificazione di persone sulla base di caratteristiche
biometriche come le impronte digitali.

Gli ambienti di sviluppo


Il sistema composta da un certo numero di moduli funzionali i
quali possiedono determinate caratteristiche e particolarit tali
da essere canditati idonei per determinati sistemi di sviluppo.
La prima scelta da fare era quella legata al sistema operativo
utilizzato nellambiente hardware scelto.
Molti modelli di POCKET PC utilizzano sistemi
operativi particolari, spesso proprietari.
La nostra scelta andata verso un sistema palmare
dotato di WINDOWS CE il quale possiede una certo
numero di scelta per tutto quello che riguarda il
software utilizzabile.
Le scelte software sono, ad ogni modo, indirizzate
a risolvere due tipi di problemi fondamentali.
1 Linguaggio di programmazione
2 Librerie di sviluppo
Come abbiamo accennato precedentemente la teoria per la scrittura
dellalgoritmo di riconoscimento pretende per lindividuazione in
un background di un immagine retinale luso di classificatori HAAR
opportunamente istruiti mediante un periodo di training.
INTEL fortunatamente ha messo a disposizione una libreria
scientifica indirizzata alla gestione delle immagini in cui molte
funzioni sono gi implementate e quindi luso di questa permette
di omettere la creazione di moduli software molto complessi sia
dal punto di vista concettuale che da quello della scrittura e
della messa a punto.
Per la scrittura di tale software sono quindi utilizzati i
seguenti sistemi di sviluppo :

MICROSOFT VISUAL C++ 6.0


MICROSOFT EMBEDDED VISUAL C++ 4.0 per Windows CE
OPENCV INTEL
IMAGE PROCESSING LIBRARY INTEL
MICROSOFT DIRECT X

Il metodo di programmazione legato al C++ per ambiente WINDOWS


utilizzando le classi MFC (Microsoft Foundation Class).
In
altre
parole
le
problematiche
legate
alla
gestione
dellinterfaccia
utente
devono
essere
risolte
con
le
API
(Application
Programming
Interface)
specifiche
del
sistema
operativo scelte.
Nel caso di WINDOWS le API sono generalmente presenti allinterno
del SDK.
Tali funzioni sono state successivamente incapsulate allinterno
di classi da parte di MICROSOFT la quale ha dato il nome di
MICROSOFT FOUNDATION CLASS a queste.
Luso del C++ in ambiente WINDOWS stato dettato dalla scelta del
sistema operativo WINDOWS CE utilizzato per la gestione del

palmare operativo.

Luso del sistema di sviluppo MICROSOFT permette di utilizzare il


Microsoft Visual C++ in ambiente Windows XP e Visual C++ 4.0 in
ambiente Windows CE senza apportare modifiche al software se non
quelle relative alla gestione dellinterfaccia utente.
Uno dei fattori che hanno indirizzato la scelta della libreria di
sviluppo stata la sua portabilit e quindi la sua distribuzione
con tanto di sorgenti.
La gestione delle immagini pu essere considerata a due livelli.
In quello definito Basso livello troviamo tutte quelle funzioni
che servono per la gestione dellimmagine mediante funzioni come
quelle legate alla lettura,m alla scrittura o a quelle modifiche
geometriche e cromatiche della stessa.
Questa tipologia di funzioni gestita mediante la libreria INTEL
IPL.
Questa liberia faceva parte di quelle che INTEL distribuiva
mediante la filosofia OPEN SOURCE.
Da un po di tempo tale libreria stata soppiantata da un'altra
libreria INTEL e precisamente IPP.
Le funzioni indirizzate alle immagini a pi alto livello sono
presenti dentro alla libreria OPENCV.
Questa per tutte le funzioni come ad esempio la lettura di un
immagine da disco si supporta su IPL o su IPP.
E sufficiente luso di una delle due librerie.
In
effetti
le
librerie
si
supportano
gerarchicamente
una
sullaltra.
Non esiste nellambito del sistema di riconoscimento retinale un
accenno diretto
Alcuni software li ho creati utilizzando il sistema di sviluppo
Visual Studio .NET, lultima versione del compilatore di casa

Microsoft.
Il sistema di sviluppo possiede, a parte maggiori funzionalit
legate allaspetto della progettazione, alcune estensioni del C++
particolarmente utili per quanto riguarda la stesura del progetto
di riconoscimento.

Gli studi iniziali.


Come abbiamo detto precedentemente allinizio si cercato di
individuare i metodi corretti per poter eseguire il confronto di
immagini provenienti da acquisizioni fatte in tempi differenti.
Un immagine possiede molti tipi di attributi, alcuni che si notano
immediatamente ed altri invece che derivano da metodologie di
valutazione.
Un immagine digitale di fatto un insieme di pixels i quali
possiedono determinate coordinate spaziali descritte dal valore di
X e Y e da un codice di colore.
Per questo motivo la descrizione di un immagine, e quindi i metodi
per confrontare una con un'altra, pu avvenire mediante le proprie
propriet spaziali-geometriche ma anche mediante caratteristiche
legate alle proprie propriet cromatiche.
I primi moduli software scritti avevano come scopo quello di
valutare
come
caratteristiche
alternative a quelle
spaziali-geometriche
potessero avere un
determinato livello
di
elasticit
nei
confronti
di
modifiche fatte alla
stessa immagine.
Determinate
teorie
alla base di quella
disciplina definita
con il termine di
IMAGE
RETRIEVAL
utilizzano
valori
come quelli relativi
agli
istogrammi
calcolati su certi
tipi di valori come
base
per
lesecuzione
delle
interrogazioni,
o
query,
indirizzate
a
identificare certe immagini in mezzo a molte altre.
Un esempio di tale uso il sistema IBM chiamato QBIC.
Un altro metodo utilizzato per calcolare le differenze tra
immagini quello basato sulla metodologia PCA (Principal
Component Analysis).
L'analisi delle componenti principali o trasformata di KarhunenLove effettua un mapping lineare tra un insieme di vettori
multidimensionali ed uno spazio di dimensione minore in modo tale
da minimizzare la somma dei quadrati degli errori commessi
approssimando i vettori originari con quelli ottenuti rimappando
sullo spazio di partenza i vettori dello spazio di dimensionalit
minore.
Tale programma stato scritto utilizzando le funzioni di OpenCV.

#include "stdafx.h"
int APIENTRY WinMain(HINSTANCE
HINSTANCE
LPSTR
int
{
#include
#include
#include
#include
#include
#include
#include
#include

hInstance,
hPrevInstance,
lpCmdLine,
nCmdShow)

"stdafx.h"
"cv.h" // include core library interface
"highgui.h" // include GUI library interface
"Recog.h"
<stdio.h>
<cmath>
<string.h>
<ctime>

int main()
{
const int n = 6;
// number of images used
const int nEigens = n-1;
// number of eigen objs
int i, j, k, digit = 6;
char name[30] = "../pca1.bmp";
// 6 bmp images
char nums[7] = "012345";
// each image has a different number
double max, val, dist;
// dist between images in nEigens-d
space
FILE* outFile;
// file to store results
CvMemStorage* storage = cvCreateMemStorage(0);
IplImage* images[n];
// input images
IplImage* eigens[nEigens];
// eigenobjects
IplImage* proj[n];
// decomposed object projection
to the eigen sub-space
IplImage* avg;
// averaged object
CvTermCriteria criteria;
// criteria to stop calculating eigen
objects
float vals[nEigens];
// eigenvalues
float coeffs[n][nEigens];
// decomposition coefficients
float normCoeffs[n][nEigens]; // normalised coefficients
cvvNamedWindow( "projections", 1);
cvvNamedWindow( "images", 1);

// ************initialise variables************
if((outFile = fopen("values.txt", "w")) == NULL)
printf(" error creating file\n");
for( i=0; i<n; i++ ){
name[digit] = nums[i]; /
images
IplImage* temp = cvvLoadImage( name );
IplImage objects

// get file name of


//

load

images

images[i] = cvCreateImage( cvGetSize( temp ), IPL_DEPTH_8U, 1 );


cvCvtColor( temp, images[i], CV_BGR2GRAY );

into

cvReleaseImage( &temp );
cvvShowImage( "images", images[i] );
cvvWaitKey(0);
}

proj[i] = cvCreateImage( cvGetSize( images[0] ), IPL_DEPTH_8U, 1 );

1 );

for( i=0; i<nEigens; i++ )


eigens[i] = cvCreateImage( cvGetSize( images[0] ), IPL_DEPTH_32F,
avg = cvCreateImage( cvGetSize( images[0] ), IPL_DEPTH_32F, 1 );
criteria.type = CV_TERMCRIT_ITER|CV_TERMCRIT_EPS;
criteria.maxIter = 10;
criteria.epsilon = 0.1;

// ************calc eigenobjects & eigenvals************


cvCalcEigenObjects( n, images, eigens, 0, 0, 0, &criteria, avg, vals );
fprintf(outFile, "eigenvalues\n" );
for( i=0; i<nEigens; i++ )
fprintf(outFile, "%15.2lf\n", vals[i] );

// ************calc decomposition coefficients************


fprintf(outFile, "decomposition coefficients\n" );

);

for( i=0; i<n; i++ ){


cvEigenDecomposite( images[i], nEigens, eigens, 0, 0, avg, coeffs[i]
fprintf(outFile, "obj:%d\t", i );
for( j=0; j<nEigens; j++ )
fprintf(outFile, "%25.2lf", coeffs[i][j] );
}

fprintf(outFile, "\n" );

// ************normalise decomposition coefficients************


for( i=0; i<nEigens; i++ ){
max = -100000.00;
for( j=0; j<n; j++ ){
val = fabs( coeffs[j][i] );
if( val > max )
max = val;

}
for( j=0; j<n; j++ )
normCoeffs[j][i]=coeffs[j][i]/max;

fprintf(outFile, "decomposition coefficients-normalised\n" );


for( i=0; i<n; i++ ){
fprintf(outFile, "obj:%d\t", i );

for( j=0; j<nEigens; j++ )


fprintf(outFile, "%7.2lf", normCoeffs[i][j] );
fprintf(outFile, "\n" );
}
// ************compare vectors************
for( i=0; i<n; i++ ){
fprintf(outFile, "\nvector %d\n", i );
for( j=i+1; j<n; j++ ){
dist = 0;
for( k=0; k<nEigens; k++ )
dist += fabs( normCoeffs[i][k] - normCoeffs[j][k] );
fprintf(outFile, "%d:\tdist = %10.5lf\n", j, dist );
}

// ************calc projection into the eigen subspace************


for( i=0; i<n; i++ )
cvEigenProjection( eigens, nEigens, 0, 0, coeffs[i], avg, proj[i] );
for( i=0; i<n; i++ ){
cvvShowImage( "projections", proj[i] );
cvvWaitKey(0);
}
for( i=0; i<n; i++ ){
cvReleaseImage( &images[i] );
cvReleaseImage( &proj[i] );
}
for( i=0; i<nEigens; i++ )
cvReleaseImage( &eigens[i] );
cvReleaseImage( &avg );
cvClearMemStorage( storage );
return 0;
}

Lutilizzo della libreria OPENCV si basa a sua volta sulla


libreria grafica dellINTEL IPL ovvero Image Processing Library.
In questa libreria sono supportate le funzioni di base relative
alla gestione delle immagini a basso livello.
Infatti
allinterno
di
OPENCV
non
sono
presenti
funzioni
indirizzate alleleborazione delle immagini.
Ad ogni modo mediante lutilizzo delle due librerie permette
qualsiasi tipo di elaborazione grafica sia indirizzata alla
modifica
delle
caratteristiche
geografiche
e
cromatiche
dellimmagine che a quelle funzioni che possono essere considerate
ad un livello di astrazione superiore.
Medi9ante luso delle due librerie sarebbe possibile creare un
programma di TEMPLATE MATCHING con pochissime linee di programma.
E un esempio quello che segue.

#ifdef _CH_
#pragma package <opencv>
#endif
#ifndef _EiC
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#endif
IplImage *Lena = 0;// Main Source Image
IplImage *Lena_Color = 0;
IplImage *sonuc = 0;
// Names's of windows
char barname01[] = "Threshold";
char wndname1[]="BINARY";
char wndname2[]="BINARY INV";
char wndname3[]="TRUNC";
char wndname4[]="TO ZERO";
char wndname5[]="TO ZERO INV";
/*******/
CvPoint pp1, pp2;//points for min & max values
double minVal, maxVal;// min & max values
/*******/
int main( int argc, char** argv )
{
Lena = cvLoadImage( "lena.bmp",0);
Lena_Color = cvLoadImage( "lena.bmp",3);
IplImage *arama = cvLoadImage( "lena_ara.bmp",0);
// Create Windows for each image.
cvNamedWindow("Lena", CV_WINDOW_AUTOSIZE);
cvNamedWindow("CV_TM_SQDIFF_NORMED", CV_WINDOW_AUTOSIZE);
cvNamedWindow("CV_TM_CCORR", CV_WINDOW_AUTOSIZE);
cvNamedWindow("CV_TM_CCORR_NORMED", CV_WINDOW_AUTOSIZE);
cvNamedWindow("CV_TM_CCOEFF", CV_WINDOW_AUTOSIZE);
cvNamedWindow("CV_TM_CCOEFF_NORMED", CV_WINDOW_AUTOSIZE);
cvNamedWindow("CV_TM_SQDIFF", CV_WINDOW_AUTOSIZE);
cvNamedWindow("Aranan", CV_WINDOW_AUTOSIZE);
int a = Lena->width;
int b = Lena->height;
int a2 = arama->width;
int b2 = arama->height;
sonuc = cvCreateImage( cvSize(a - a2 + 1,b - b2 +1),IPL_DEPTH_32F ,1);
cvShowImage( "Aranan",arama);// Display image
IplImage *Thresh_Sonuc5 = cvCreateImage( cvSize(a - a2 + 1,b - b2
+1),IPL_DEPTH_32F ,1);
IplImage *Thresh_Sonuc1 = cvCreateImage( cvSize(a - a2 + 1,b - b2
+1),IPL_DEPTH_32F ,1);
IplImage *Thresh_Sonuc2 = cvCreateImage( cvSize(a - a2 + 1,b - b2
+1),IPL_DEPTH_32F ,1);
IplImage *Thresh_Sonuc3 = cvCreateImage( cvSize(a - a2 + 1,b - b2
+1),IPL_DEPTH_32F ,1);
IplImage *Thresh_Sonuc4 = cvCreateImage( cvSize(a - a2 + 1,b - b2
+1),IPL_DEPTH_32F ,1);
IplImage *Thresh_Sonuc6 = cvCreateImage( cvSize(a - a2 + 1,b - b2
+1),IPL_DEPTH_32F ,1);
//cvCreateTrackbar( barname01, "Lena", &slider_pos[0], 255,
my_threshold );

cvMatchTemplate( Lena, arama,Thresh_Sonuc1, CV_TM_SQDIFF );


cvMinMaxLoc(Thresh_Sonuc1, &minVal, &maxVal, &pp1, &pp2);
// Find min and max locations in prob. denstiy image.
//cvCircle( Lena_Color, pp1, 15, CV_RGB(255,0,0),1 );
cvCircle( Lena_Color, pp2, 15,CV_RGB(0,0,255), 1 );
// draw circle at pos. w/ max probablity.
cvShowImage( "CV_TM_SQDIFF", Thresh_Sonuc1);
// Other ways of doing this. :)
/*cvMatchTemplate( Lena, arama,Thresh_Sonuc2, CV_TM_SQDIFF_NORMED );
cvShowImage( "CV_TM_SQDIFF_NORMED", Thresh_Sonuc2);
cvMatchTemplate( Lena, arama,Thresh_Sonuc3, CV_TM_CCORR );
cvShowImage( "CV_TM_CCORR", Thresh_Sonuc3);
cvMatchTemplate( Lena, arama,Thresh_Sonuc4, CV_TM_CCORR_NORMED );
cvShowImage( "CV_TM_CCORR_NORMED", Thresh_Sonuc4);
cvMatchTemplate( Lena, arama,Thresh_Sonuc5, CV_TM_CCOEFF );
cvShowImage( "CV_TM_CCOEFF", Thresh_Sonuc5);
cvMatchTemplate( Lena, arama,Thresh_Sonuc6, CV_TM_CCOEFF_NORMED );
cvShowImage( "CV_TM_CCOEFF_NORMED", Thresh_Sonuc6);
*/
cvShowImage( "Lena", Lena_Color);
cvWaitKey( -1 );

return 0;

#ifdef _EiC
main(1,"main.cpp");
#endif

Nei capitoli legati ai concetti di base sono stati visti alcuni


algoritmi come ad esempio quello legato alla definizione dei
contorni delle immagini.
In alcuni casi le prove fatte hanno utilizzato le funzioni di
OPENCV mentre in altri casi ho utilizzato programmi a se stanti.
In effetti la scelta di OPENCV stata fatta dopo un certo tempo
dopo aver provato moltissime soluzioni.
Alcune volte il linguaggio con cui ho scritto i moduli di prova
era JAVA e non C++.
Il seguente un esempio di programma indirizzato alla creazione
dei contorni di un oeggto dentro ad un immagine.
/*
* contour.c
*
*/
/* CONTOUR:
*
*
*/

program determines contour boundaries for binary image


usage: contour infile.img outfile.img

#include <stdio.h>
#include <string.h>

#include <stdlib.h>
#include "tiffimage.h"
#include "images.h"
extern void print_sos_lic ();
#define
#define
#define
#define
#define
#define
#define

ON 0
OFF 255
ONM1 1
CONTOUR 32
CENTROID 31
MAX_CONTOUR 10000
DISPLAY_FLAG_DFLT 0

/* picfile info on images */

/* binarization values */
/*
/*
/*
/*

value of contour pixel */


value of centroid location pixel */
maximum length of contour */
=0 contours; =1 centroids; =2 both */

#define SQUARE(IMG,X,Y) IMG[Y][X] = ONM1, IMG[Y-1][X-1] = ONM1,\


IMG[Y-1][X] = ONM1, IMG[Y-1][X+1] = ONM1,\
IMG[Y][X+1] = ONM1, IMG[Y+1][X+1] = ONM1,\
IMG[Y+1][X] = ONM1, IMG[Y+1][X-1] = ONM1,\
IMG[Y][X-1] = ONM1
int nextcntr (unsigned char **, long *, long *, long *);
long usage (short);
long input (int, char **, long *, short *, short *);
main (argc, argv)
int argc;
char *argv[];
{
Image *imgIO;
unsigned char **image;
long height, width;
long xEndM1, yEndM1;
struct cntr {
long x, y;
long iDirn;
long curve;
} *cntr;
long maxContour;
short displayFlag;
short printFlag;
long nContour;
long lContour;
long maxLContour;
long lContourSum;
double xCentroid, yCentroid;
long xStart, yStart;
long iDirn;
long x, y;
long temp;

/*
/*
/*
/*
/*
/*
/*
/*

I/O image structure */


input/output image */
size of I/O images */
end of rows/columns minus one */
structure for each contour pt */
location */
chain code direction */
local curvature */

/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

max. contour length (for memory alloc.) */


=0 contours; =1 centroids; =2 both */
=1 print contour info; =0 don't */
number of contours */
length of contour */
max. length of contour */
sum of contour lengths */
x,y centroid location for contour */
starting pixels of contour */
direction coming into contour */

/* user input */
if (input (argc, argv, &maxContour, &displayFlag, &printFlag) < 0)
return (-1);
imgIO = ImageIn (argv[1]);
image = imgIO->img;
height = ImageGetHeight (imgIO);
width = ImageGetWidth (imgIO);
printf ("image size is %dx%d\n", width, height);
/* allocate contour memory */
if ((cntr = (struct cntr *) calloc (maxContour, sizeof (struct cntr)))
== NULL) {

printf ("CONTOUR: not enough memory -- sorry.\n");


return (-1);

/* determine contours */
yEndM1 = height - 1;
xEndM1 = width - 1;
nContour = 0;
maxLContour = 0;
lContourSum = 0;
for (y = 1; y < yEndM1; y++) {
for (x = 1; x < xEndM1; x++) {
/* when find ON-pixel, trace contour */
if (image[y][x] == ON && image[y][x - 1] == OFF) {
nContour++;
iDirn = 2;
cntr[0].x = xStart = x;
cntr[0].y = yStart = y;
xCentroid = (double) x;
yCentroid = (double) y;
cntr[0].iDirn = iDirn;
cntr[0].curve = 0;
lContour = 1;
do {
image[y][x] = CONTOUR;
nextcntr (image, &x, &y, &iDirn);
cntr[lContour].x = x;
cntr[lContour].y = y;
cntr[lContour].iDirn = iDirn;
xCentroid += (double) x;
yCentroid += (double) y;
temp = iDirn - cntr[lContour - 1].iDirn;
if (temp > 4)
temp -= 8;
else if (temp < -4)
temp += 8;
cntr[lContour].curve = temp;
lContour++;
if (lContour == MAX_CONTOUR) {
printf ("Nuts -- maximum contour length reached = %d\n", lContour);
return (-1);
}
} while (!(x == xStart && y == yStart));
if (lContour > maxLContour)
maxLContour = lContour;
xCentroid = xCentroid / (double) lContour;
yCentroid = yCentroid / (double) lContour;
lContourSum += lContour;
if (displayFlag > 0)
image[(long) (yCentroid + 0.5)][(long) (xCentroid + 0.5)] = CENTROID;
if (printFlag != 0)
printf ("contour %ld: length = %ld, centroid = (%5.2f,%5.2f)\n",
nContour - 1, lContour, xCentroid, yCentroid);
}
}
}
switch (displayFlag) {
case 0:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
image[y][x] = (image[y][x] == CONTOUR) ? ON : OFF;

break;
case 1:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++) {
if (image[y][x] == CENTROID)
SQUARE (image, x, y);
else if (image[y][x] == ONM1);
else
image[y][x] = OFF;
}
break;
case 2:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++) {
if (image[y][x] == CENTROID)
SQUARE (image, x, y);
else if (image[y][x] == CONTOUR)
image[y][x] = ON;
else if (image[y][x] == ONM1);
else
image[y][x] = OFF;
}
break;
default:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
image[y][x] = (image[y][x] == CONTOUR) ? ONM1 : OFF;
}
if (nContour == 0)
printf ("no image regions\n");
else
printf ("no. contours = %d, max length = %d, avg. length = %d\n",
nContour, maxLContour, lContourSum / nContour);
ImageOut (argv[2], imgIO);
}

return (0);

/* NEXTCNTR:
*/

function examines neighborhood for next contour pixel

int
nextcntr (image, x, y, iDirn)
unsigned char **image;
long *x, *y;
long *iDirn;
{
long ring[16];
long i, j;

/* temporary x and y storage for trace */


/* direction from last pixel on contour */
/* neighborhood ring of pixels */

/* find neighborhood ring of pixels */


ring[0] = ring[8] = image[*y - 1][*x];
ring[1] = ring[9] = image[*y - 1][*x + 1];
ring[2] = ring[10] = image[*y][*x + 1];
ring[3] = ring[11] = image[*y + 1][*x + 1];
ring[4] = ring[12] = image[*y + 1][*x];
ring[5] = ring[13] = image[*y + 1][*x - 1];
ring[6] = ring[14] = image[*y][*x - 1];

ring[7] = ring[15] = image[*y - 1][*x - 1];


i = (*iDirn + 4 + 1) % 8;
for (j = 0; j < 8; j++, i++)
if (ring[i] != OFF && ring[i - 1] == OFF)
break;
if (j == 8)
return (0);

/* isolated pixel */

*iDirn = i % 8;
switch (i) {
case 1:
case 9:
*x = *x + 1;
*y = *y - 1;
break;
case 2:
case 10:
*x = *x + 1;
break;
case 3:
case 11:
*x = *x + 1;
*y = *y + 1;
break;
case 4:
case 12:
*y = *y + 1;
break;
case 5:
case 13:
*x = *x - 1;
*y = *y + 1;
break;
case 6:
case 14:
*x = *x - 1;
break;
case 7:
case 15:
*x = *x - 1;
*y = *y - 1;
break;
case 8:
case 0:
*y = *y - 1;
break;
default:;
}
}

return (0);

/* USAGE:
*
*

function gives instructions on usage of program


usage: usage (flag)
When flag is 1, the long message is given, 0 gives short.

*/
long
usage (flag)
short flag;
message */
{

/* flag =1 for long message; =0 for short

/* print short usage message or long */


printf ("USAGE: contour inimg outimg [-d DISPLAY] [-p] \n");
printf ("
[-m MAX_CONTOUR_LENGTH] [-L]\n");
if (flag == 0)
return (-1);
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
}

("\ncontour identifies contours, or boundaries, of regions\n");


("in a binary image, and determines features of the regions.\n\n");
("ARGUMENTS:\n");
("
inimg: input image filename (TIF)\n");
("
outimg: output image filename (TIF)\n\n");
("OPTIONS:\n");
("
-d DISPLAY: display just centroids (1), or both\n");
("
contours and centroids (2);\n");
("
default displays just contours.\n");
("
-p: PRINT DATA FLAG if set, prints contour\n");
("
number, length, and centroid data.\n");
(" -m MAX_CONTOUR_LENGTH: maximum contour length in pixel\n");
("
connections. (Default = %d)\n", MAX_CONTOUR);
("
-L: print Software License for this module\n");

return (-1);

/* INPUT:
*
*/

function reads input parameters


usage: input (argc, argv, &maxContour, &displayFlag)

#define USAGE_EXIT(VALUE) {usage (VALUE); return (-1);}


long
input (argc, argv, maxContour, displayFlag, printFlag)
int argc;
char *argv[];
long *maxContour;
/* maximum contour length (for memory alloc) */
short *displayFlag;
/* =0 contours; =1 centroids; =2 both */
short *printFlag;
/* =1, print contour data; =0 don't */
{
long n;
if (argc == 1)
USAGE_EXIT (1);
if (argc == 2)
USAGE_EXIT (0);
*maxContour = MAX_CONTOUR;
*displayFlag = DISPLAY_FLAG_DFLT;
*printFlag = 0;
for (n = 3; n < argc; n++) {
if (strcmp (argv[n], "-m") == 0) {
if (++n == argc || argv[n][0] == '-')
USAGE_EXIT (0);

*maxContour = atol (argv[n]);


}
else if (strcmp (argv[n], "-d") == 0) {
if (++n == argc || argv[n][0] == '-')
USAGE_EXIT (0);
*displayFlag = (short) atol (argv[n]);
}
else if (strcmp (argv[n], "-p") == 0) {
*printFlag = 1;
}
else if (strcmp (argv[n], "-L") == 0) {
print_sos_lic ();
exit (0);
}
else
USAGE_EXIT (0);

return (0);
}

Un esempio che ho creato mediante luso delle funzioni di OPENCV


quello che segue.

Il progetto essenzialmente costituito dal seguente file .cpp al


quale demandato il richiamo dellinterprete di processo.
#include "stdafx.h"
#include "cvapp.h"
ImageProcessor *proc = 0;
SequenceProcessor *procseq = 0;

void process(void* img) {


IplImage* image = reinterpret_cast<IplImage*>(img);
cvErode( image, image, 0, 2 );
}
void ImageProcessor::execute() {
process(img);
}
IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir)
{
BOOL
bFound = FALSE;
IEnumPins *pEnum;
IPin
*pPin;

pFilter->EnumPins(&pEnum);
while(pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
pPin->QueryDirection(&PinDirThis);
if (bFound = (PinDir == PinDirThis))
break;
pPin->Release();
}
pEnum->Release();
return (bFound ? pPin : 0);

Inizialmente lo studio fu indirizzato verso lidentificazione di


quali
potevano
essere
quelle
caratteristiche
in
grado
di
descrivere un immagine che non variassero a seguito di lievi
modifiche geometrico spaziali.
Per tale studio ho utilizzato la librearia grafica
IMAGEEN (www.hicomponents.com)
Con questa libreria grafica in cui erano presenti le funzioni di
base
per
la
gestione
di
immagini
come
la
lettura,
la
visualizzazione ecc. ho scritto un programma in cui venivano
calcolati determinati tipi di istogrammi, mediante le funzioni
CalcDensityHistogram,
e
venivano
valutati
dei
valori
che
succesimante descriver.
#include <vcl.h>
#pragma hdrstop
#include <stdlib.h>
#include "ComparePrj.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma link "ieopensavedlg"
#pragma link "ieview"
#pragma link "imageenio"
#pragma link "imageenproc"
#pragma link "imageenview"

#pragma link "histogrambox"


#pragma link "kfiledir"
#pragma link "ievect"
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
LoadImage2();
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender)
{
double psnr_min1,psnr_max1;
double mse_min1,mse_max1;
double rmse_min1,rmse_max1;
double pae_min1,pae_max1;
double mae_min1,mae_max1;
long ww, hh;
ww = ImageEnView1->Bitmap->Width;
hh =ImageEnView1->Bitmap->Height;
if (ww!=ImageEnView2->Bitmap->Width || hh!=ImageEnView2->Bitmap->Height)
ImageEnView2->Proc->Resample( ww,hh, rfNone);
if(ImageEnProc2->ComputeImageEquality(ImageEnView1->IEBitmap, psnr_min1,
psnr_max1, mse_min1, mse_max1, rmse_min1, rmse_max1, pae_min1, pae_max1,
mae_min1, mae_max1))
Edit1->Text = "SI";
else
Edit1->Text = "NO";
psnr_min->Text = psnr_min1;
psnr_max->Text = psnr_max1;
mse_min->Text = mse_min1;
mse_max->Text = mse_max1;
rmse_min->Text = rmse_min1;
rmse_max->Text = rmse_max1;
pae_min->Text = pae_min1;
pae_max->Text = pae_max1;
mae_min->Text = mae_min1;
mae_max->Text = mae_max1;
HistogramBox1->Update();
HistogramBox2->Update();
}

void __fastcall TForm1::Button4Click(TObject *Sender)


{
long ww, hh;
double gradi = Edit2->Text.ToDouble();
ImageEnProc2->Rotate(gradi, true, ierFast, 0);
Elaborate2();
}
//--------------------------------------------------------------------------void TForm1::LoadImage1()
{
if( OpenImageEnDialog1->Execute() ) {
ImageEnView1->IO->LoadFromFileJpeg( OpenImageEnDialog1>FileName );
ImageEnVect1->IO->LoadFromFileJpeg( OpenImageEnDialog1>FileName );
Elaborate1();
}
}
void TForm1::LoadImage2()
{
if( OpenImageEnDialog1->Execute() ) {
ImageEnView2->IO->LoadFromFileJpeg(
>FileName );
ImageEnVect2->IO->LoadFromFileJpeg(
>FileName );

OpenImageEnDialog1OpenImageEnDialog1-

Elaborate2();

}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
LoadImage1();
}
//---------------------------------------------------------------------------

void TForm1::Elaborate1()
{
long ww, hh, dimens;
ww = ImageEnView1->Bitmap->Width;
hh =ImageEnView1->Bitmap->Height;
dimens = hh - 60;
ImageEnView1->SelectEllipse( ww/2,hh/2, dimens,dimens,iespReplace );
HistogramBox1->Update();
ImageEnView1->Update();
}
void TForm1::Elaborate2()
{

>Height)

long ww, hh, dimens;


ww = ImageEnView1->Bitmap->Width;
hh =ImageEnView1->Bitmap->Height;
if (ww!=ImageEnView2->Bitmap->Width || hh!=ImageEnView2->BitmapImageEnView2->Proc->Resample( ww,hh, rfNone);
dimens = hh - 60;
ImageEnView2->SelectEllipse( ww/2,hh/2, dimens,dimens,iespReplace );

HistogramBox2->Update();
ImageEnView2->Update();

void TForm1::DensityHisto1()
{
int *VertHist,*HorizHist;
AnsiString temp;
int VertHistHeight,HorizHistWidth,VertHistWidth,HorizHistHeight;
int i;
VertHistWidth = ImageEnView3->ClientWidth;
VertHistHeight = ImageEnView1->Bitmap->Height;
HorizHistWidth =ImageEnView1->Bitmap->Width;
HorizHistHeight =ImageEnView4->ClientHeight;
VertHist = (int *)malloc(VertHistHeight*sizeof(int));
HorizHist = (int *) malloc(HorizHistWidth*sizeof(int));
ImageEnView1->Proc>CalcDensityHistogram(VertHist,HorizHist,VertHistWidth,HorizHistHeight);
ImageEnView3->Bitmap->Height =HorizHistHeight;
ImageEnView3->Bitmap->Width=HorizHistWidth;
// vertical
ImageEnView4->Bitmap->Height=VertHistHeight;
ImageEnView4->Bitmap->Width=VertHistWidth;
// draw histograms
ImageEnView3->Bitmap->Canvas->Pen->Color=clBlack;
ImageEnView4->Bitmap->Canvas->Pen->Color=clBlack;
for(int u=0;u!=ListBox1->Items->Count;u++)
ListBox1->Items->Delete(u);
for(i=0;i!=HorizHistWidth-1;i++) {
temp.printf("%d",HorizHist[i]);
ListBox1->Items->Add(temp);
ImageEnView3->Bitmap->Canvas->MoveTo(i, HorizHistHeight );
ImageEnView3->Bitmap->Canvas->LineTo(i, HorizHistHeight - HorizHist[i] );
}
for(int u=0;u!=ListBox2->Items->Count;u++)
ListBox2->Items->Delete(u);
for(i=0;i!=VertHistHeight-1;i++) {
temp.printf("%d",VertHist[i]);
ListBox2->Items->Add(temp);
ImageEnView4->Bitmap->Canvas->MoveTo(0, i );
ImageEnView4->Bitmap->Canvas->LineTo(VertHist[i],i);
}
free(VertHist);
free(HorizHist);
ImageEnView3->Update();
ImageEnView4->Update();

}
void __fastcall TForm1::Button5Click(TObject *Sender)
{
DensityHisto1();

}
//--------------------------------------------------------------------------void __fastcall TForm1::Button6Click(TObject *Sender)
{
int *VertHist,*HorizHist;
AnsiString temp;
int VertHistHeight,HorizHistWidth,VertHistWidth,HorizHistHeight;
int i;
TIEFtImage *ftimage = new TIEFtImage;
ftimage = ImageEnProc1->FTCreateImage(ieitRGB,180,120);
ImageEnProc3->FTDisplayFrom(ftimage);
ftimage->Free();
VertHistWidth = ImageEnView9->ClientWidth;
VertHistHeight = ImageEnView5->Bitmap->Height;
HorizHistWidth =ImageEnView5->Bitmap->Width;
HorizHistHeight =ImageEnView8->ClientHeight;
VertHist = (int *)malloc(VertHistHeight*sizeof(int));
HorizHist = (int *) malloc(HorizHistWidth*sizeof(int));
ImageEnView5->Proc>CalcDensityHistogram(VertHist,HorizHist,VertHistWidth,HorizHistHeight);
ImageEnView9->Bitmap->Height =HorizHistHeight;
ImageEnView9->Bitmap->Width=HorizHistWidth;
// vertical
ImageEnView8->Bitmap->Height=VertHistHeight;
ImageEnView8->Bitmap->Width=VertHistWidth;
// draw histograms
ImageEnView9->Bitmap->Canvas->Pen->Color=clBlack;
ImageEnView8->Bitmap->Canvas->Pen->Color=clBlack;
for(i=0;i!=HorizHistWidth-1;i++) {
ImageEnView9->Bitmap->Canvas->MoveTo(i, HorizHistHeight );
ImageEnView9->Bitmap->Canvas->LineTo(i, HorizHistHeight - HorizHist[i] );
}
for(i=0;i!=VertHistHeight-1;i++) {
ImageEnView8->Bitmap->Canvas->MoveTo(0, i );
ImageEnView8->Bitmap->Canvas->LineTo(VertHist[i],i);
}
free(VertHist);
free(HorizHist);
ImageEnView8->Update();
ImageEnView9->Update();
}
//--------------------------------------------------------------------------void TForm1::DensityHisto2(void)
{

AnsiString temp;
int *VertHist,*HorizHist;
int VertHistHeight,HorizHistWidth,VertHistWidth,HorizHistHeight;
int i;
VertHistWidth = ImageEnView6->ClientWidth;
VertHistHeight = ImageEnView2->Bitmap->Height;
HorizHistWidth =ImageEnView2->Bitmap->Width;
HorizHistHeight =ImageEnView7->ClientHeight;
VertHist = (int *)malloc(VertHistHeight*sizeof(int));
HorizHist = (int *) malloc(HorizHistWidth*sizeof(int));
ImageEnView2->Proc>CalcDensityHistogram(VertHist,HorizHist,VertHistWidth,HorizHistHeight);
ImageEnView6->Bitmap->Height=HorizHistHeight;
ImageEnView6->Bitmap->Width=HorizHistWidth;
// vertical
ImageEnView7->Bitmap->Height=VertHistHeight;
ImageEnView7->Bitmap->Width=VertHistWidth;
// draw histograms
ImageEnView7->Bitmap->Canvas->Pen->Color=clBlack;
ImageEnView6->Bitmap->Canvas->Pen->Color=clBlack;
for(int u=0;u!=ListBox3->Items->Count;u++)
ListBox3->Items->Delete(u);
for(i=0;i!=HorizHistWidth-1;i++) {
temp.printf("%d",HorizHist[i]);
ListBox3->Items->Add(temp);
ImageEnView6->Bitmap->Canvas->MoveTo(i, HorizHistHeight );
ImageEnView6->Bitmap->Canvas->LineTo(i, HorizHistHeight - HorizHist[i] );
}
for(int u=0;u!=ListBox4->Items->Count;u++)
ListBox4->Items->Delete(u);
for(i=0;i!=VertHistHeight-1;i++) {
temp.printf("%d",VertHist[i]);
ListBox4->Items->Add(temp);
ImageEnView7->Bitmap->Canvas->MoveTo(0, i );
ImageEnView7->Bitmap->Canvas->LineTo(VertHist[i],i);
}
free(VertHist);
free(HorizHist);
ImageEnView6->Update();
ImageEnView7->Update();
}
void __fastcall TForm1::Button7Click(TObject *Sender)
{
DensityHisto2();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button8Click(TObject *Sender)

{
int *VertHist,*HorizHist;
AnsiString temp;
int VertHistHeight,HorizHistWidth,VertHistWidth,HorizHistHeight;
int i;
TIEFtImage *ftimage = new TIEFtImage;
ftimage = ImageEnProc1->FTCreateImage(ieitRGB,180,120);
ImageEnProc2->FTDisplayFrom(ftimage);
ftimage->Free();
VertHistWidth = ImageEnView12->ClientWidth;
VertHistHeight = ImageEnView10->Bitmap->Height;
HorizHistWidth =ImageEnView10->Bitmap->Width;
HorizHistHeight =ImageEnView11->ClientHeight;
VertHist = (int *)malloc(VertHistHeight*sizeof(int));
HorizHist = (int *) malloc(HorizHistWidth*sizeof(int));
ImageEnView10->Proc>CalcDensityHistogram(VertHist,HorizHist,VertHistWidth,HorizHistHeight);
ImageEnView12->Bitmap->Height =HorizHistHeight;
ImageEnView12->Bitmap->Width=HorizHistWidth;
// vertical
ImageEnView11->Bitmap->Height=VertHistHeight;
ImageEnView11->Bitmap->Width=VertHistWidth;
// draw histograms
ImageEnView12->Bitmap->Canvas->Pen->Color=clBlack;
ImageEnView11->Bitmap->Canvas->Pen->Color=clBlack;
for(i=0;i!=HorizHistWidth-1;i++) {
ImageEnView12->Bitmap->Canvas->MoveTo(i, HorizHistHeight );
ImageEnView12->Bitmap->Canvas->LineTo(i, HorizHistHeight - HorizHist[i] );
}
for(i=0;i!=VertHistHeight-1;i++) {
ImageEnView11->Bitmap->Canvas->MoveTo(0, i );
ImageEnView11->Bitmap->Canvas->LineTo(VertHist[i],i);
}
free(VertHist);
free(HorizHist);
ImageEnView12->Update();
ImageEnView11->Update();
}
//---------------------------------------------------------------------------

In tale programma mediante lsuo di una funzione della libreria


ComputeImageEquality venivano valutati i seguenti valori :
psnr_min,psnr_max
mse_min,mse_max
rmse_min,rmse_max
pae_min,pae_max

:
:
:
:

minimum
minimum
minimum
minimum

and
and
and
and

maximum
maximum
maximum
maximum

peak
mean
root
peak

signal to noise ratio


squared error
mean squared error
absolute error

mae_min,mae_max

: minimum and maximum mean absolute error

Il risultato fu scadente in quanto questi valori erano troppo


soggetti alle mutazioni di angolazione dellimmagine.
Nel
programma
venivano
considerate
anche
diversi
tipi
di
trasformazioni finalizzate allo studio dellidentificazione di
oggetti.

Aggancio alla telecamera


OpenCV una libreria per le elaborazioni in REALTIME sulle
immagini acquisite da una telecamera.
La libreria possiede al suo interno tutte le funzioni per la
gestione di queste.
Inoltre OpenCV pu lavorare anche su filmati AVI allo stesso modo
con cui lavora sulle riprese dirette.
Sul sito in cui gestita la maillist di OpenCV presente un file
di configurazione di Visual C++ che permette di creare un WIZARD
per linizializzazione delle applicazioni OpenCV.
In pratica il file
OpenCV Framework.awx
Utilizzando
tale
modello
per
la
creazione
di
un
applicativo
viene
generata una serie di files tra i quali
sono anche presenti quelli che includono
la classe di gestione della telecamera.
La classe generata possiede i seguenti
metodi e membri :
class CCamera
{
protected:
CImage m_frame;
bool
m_isRunning;
CAPDRIVERCAPS m_caps;
HIC m_hic;
public:
BITMAPINFO bmi, bmi_out;
HWND
m_capWnd;
CImage& GetFrame() { return m_frame; }
CCamera();
virtual ~CCamera();
bool
bool

IsInitialized() { return m_capWnd != 0; };


IsRunning() { return m_isRunning; };

void
bool
void

OnFrame( BYTE* data, int width, int height, int format, int bpp );
Initialize(HWND parent );
Uninitialize();

void
void
void
void
void
void

ClearRect();
Start();
Stop();
VideoFormatDlg();
VideoSourceDlg();
UpdateParent( bool whole );

};

La dialog generata di default possiede alcuni pulsanti indirizzati


alla gestione delle funzionalit di base sulla telecamera e sui
files AVI.

Andando
ad
analizzare
la
classe
generata dal wizard troviamo una serie
di funzioni che possiedono la specifica
CAP prima del nome della funzione.
Precedentemente
abbiamo
detto
che
OPENCV si basa sulle librerie directx
per alcuni tipi di gestioni e tra
queste esistono quelle legate alla
telecamera.
Originariamente in WINDOWS la gestione dei filmati veniva fatta
dal pacchetto VIDEO FOR WINDOWS.
Le funzioni utilizzate per la gestione della camera provengono da
questa.
Ad esempio linterrogazione del formato viene eseguita mediante il
metodo :
void
{

CCamera::VideoFormatDlg()

if( m_capWnd && m_caps.fHasDlgVideoFormat )


{
capDlgVideoFormat( m_capWnd );
}
capGetVideoFormat (m_capWnd, &bmi, sizeof(bmi));
// install decompressor
m_hic=ICOpen
(ICTYPE_VIDEO,
bmi.bmiHeader.biCompression,
ICMODE_DECOMPRESS);
ICDecompressGetFormat (m_hic, &bmi, &bmi_out);
}

Allinterno
di
questo
viene
utilizzata
la
capGetVideoFormat.
La definizione delle funzioni cap quella che segue.

funzione

#define capSetCallbackOnError(hwnd, fpProc)


((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_ERROR, 0, (LPARAM)
(LPVOID)(fpProc)))
#define capSetCallbackOnStatus(hwnd, fpProc)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_STATUS, 0, (LPARAM)
(LPVOID)(fpProc)))
#define capSetCallbackOnYield(hwnd, fpProc)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_YIELD, 0, (LPARAM)
(LPVOID)(fpProc)))
#define capSetCallbackOnFrame(hwnd, fpProc)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_FRAME, 0, (LPARAM)
(LPVOID)(fpProc)))
#define capSetCallbackOnVideoStream(hwnd, fpProc)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, (LPARAM)
(LPVOID)(fpProc)))
#define capSetCallbackOnWaveStream(hwnd, fpProc)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_WAVESTREAM, 0, (LPARAM)
(LPVOID)(fpProc)))

#define capSetCallbackOnCapControl(hwnd, fpProc)


((BOOL)AVICapSM(hwnd, WM_CAP_SET_CALLBACK_CAPCONTROL, 0, (LPARAM)
(LPVOID)(fpProc)))
#define capSetUserData(hwnd, lUser)
WM_CAP_SET_USER_DATA, 0, (LPARAM)lUser))
#define capGetUserData(hwnd)
WM_CAP_GET_USER_DATA, 0, 0))

((BOOL)AVICapSM(hwnd,
(AVICapSM(hwnd,

#define capDriverConnect(hwnd, i)
((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_CONNECT, (WPARAM)(i), 0L))
#define capDriverDisconnect(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_DISCONNECT, (WPARAM)0, 0L))
#define capDriverGetName(hwnd, szName, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_GET_NAME, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPTSTR)(szName)))
#define capDriverGetVersion(hwnd, szVer, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_GET_VERSION, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPTSTR)(szVer)))
#define capDriverGetCaps(hwnd, s, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_DRIVER_GET_CAPS, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPCAPDRIVERCAPS)(s)))
#define capFileSetCaptureFile(hwnd, szName)
((BOOL)AVICapSM(hwnd, WM_CAP_FILE_SET_CAPTURE_FILE, 0, (LPARAM)
(LPVOID)(LPTSTR)(szName)))
#define capFileGetCaptureFile(hwnd, szName, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_FILE_GET_CAPTURE_FILE, (WPARAM)
(wSize), (LPARAM)(LPVOID)(LPTSTR)(szName)))
#define capFileAlloc(hwnd, dwSize)
((BOOL)AVICapSM(hwnd, WM_CAP_FILE_ALLOCATE, 0, (LPARAM)(DWORD)
(dwSize)))
#define capFileSaveAs(hwnd, szName)
((BOOL)AVICapSM(hwnd, WM_CAP_FILE_SAVEAS, 0, (LPARAM)(LPVOID)
(LPTSTR)(szName)))
#define capFileSetInfoChunk(hwnd, lpInfoChunk)
((BOOL)AVICapSM(hwnd, WM_CAP_FILE_SET_INFOCHUNK, (WPARAM)0,
(LPARAM)(LPCAPINFOCHUNK)(lpInfoChunk)))
#define capFileSaveDIB(hwnd, szName)
((BOOL)AVICapSM(hwnd, WM_CAP_FILE_SAVEDIB, 0, (LPARAM)(LPVOID)
(LPTSTR)(szName)))
#define capEditCopy(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_EDIT_COPY, 0, 0L))
#define capSetAudioFormat(hwnd, s, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_AUDIOFORMAT, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPWAVEFORMATEX)(s)))
#define capGetAudioFormat(hwnd, s, wSize)
((DWORD)AVICapSM(hwnd, WM_CAP_GET_AUDIOFORMAT, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPWAVEFORMATEX)(s)))

#define capGetAudioFormatSize(hwnd)
((DWORD)AVICapSM(hwnd, WM_CAP_GET_AUDIOFORMAT, (WPARAM)0,
(LPARAM)0L))
#define capDlgVideoFormat(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_DLG_VIDEOFORMAT, 0, 0L))
#define capDlgVideoSource(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_DLG_VIDEOSOURCE, 0, 0L))
#define capDlgVideoDisplay(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_DLG_VIDEODISPLAY, 0, 0L))
#define capDlgVideoCompression(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_DLG_VIDEOCOMPRESSION, 0, 0L))
#define capGetVideoFormat(hwnd, s, wSize)
((DWORD)AVICapSM(hwnd, WM_CAP_GET_VIDEOFORMAT, (WPARAM)(wSize),
(LPARAM)(LPVOID)(s)))
#define capGetVideoFormatSize(hwnd)
((DWORD)AVICapSM(hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0L))
#define capSetVideoFormat(hwnd, s, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_VIDEOFORMAT, (WPARAM)(wSize),
(LPARAM)(LPVOID)(s)))
#define capPreview(hwnd, f)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_PREVIEW, (WPARAM)(BOOL)(f), 0L))
#define capPreviewRate(hwnd, wMS)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_PREVIEWRATE, (WPARAM)(wMS), 0))
#define capOverlay(hwnd, f)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_OVERLAY, (WPARAM)(BOOL)(f), 0L))
#define capPreviewScale(hwnd, f)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_SCALE, (WPARAM)(BOOL)f, 0L))
#define capGetStatus(hwnd, s, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_GET_STATUS, (WPARAM)(wSize), (LPARAM)
(LPVOID)(LPCAPSTATUS)(s)))
#define capSetScrollPos(hwnd, lpP)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_SCROLL, (WPARAM)0, (LPARAM)
(LPPOINT)(lpP)))
#define capGrabFrame(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_GRAB_FRAME, (WPARAM)0, (LPARAM)0L))
#define capGrabFrameNoStop(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_GRAB_FRAME_NOSTOP, (WPARAM)0,
(LPARAM)0L))
#define capCaptureSequence(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_SEQUENCE, (WPARAM)0, (LPARAM)0L))
#define capCaptureSequenceNoFile(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_SEQUENCE_NOFILE, (WPARAM)0,
(LPARAM)0L))
#define capCaptureStop(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_STOP, (WPARAM)0, (LPARAM)0L))
#define capCaptureAbort(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_ABORT, (WPARAM)0, (LPARAM)0L))

#define capCaptureSingleFrameOpen(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_SINGLE_FRAME_OPEN, (WPARAM)0,
(LPARAM)0L))
#define capCaptureSingleFrameClose(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_SINGLE_FRAME_CLOSE, (WPARAM)0,
(LPARAM)0L))
#define capCaptureSingleFrame(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_SINGLE_FRAME, (WPARAM)0, (LPARAM)0L))
#define capCaptureGetSetup(hwnd, s, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_GET_SEQUENCE_SETUP, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPCAPTUREPARMS)(s)))
#define capCaptureSetSetup(hwnd, s, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_SEQUENCE_SETUP, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPCAPTUREPARMS)(s)))
#define capSetMCIDeviceName(hwnd, szName)
((BOOL)AVICapSM(hwnd, WM_CAP_SET_MCI_DEVICE, 0, (LPARAM)(LPVOID)
(LPTSTR)(szName)))
#define capGetMCIDeviceName(hwnd, szName, wSize)
((BOOL)AVICapSM(hwnd, WM_CAP_GET_MCI_DEVICE, (WPARAM)(wSize),
(LPARAM)(LPVOID)(LPTSTR)(szName)))
#define capPaletteOpen(hwnd, szName)
((BOOL)AVICapSM(hwnd, WM_CAP_PAL_OPEN, 0, (LPARAM)(LPVOID)(LPTSTR)
(szName)))
#define capPaletteSave(hwnd, szName)
((BOOL)AVICapSM(hwnd, WM_CAP_PAL_SAVE, 0, (LPARAM)(LPVOID)(LPTSTR)
(szName)))
#define capPalettePaste(hwnd)
((BOOL)AVICapSM(hwnd, WM_CAP_PAL_PASTE, (WPARAM) 0, (LPARAM)0L))
#define capPaletteAuto(hwnd, iFrames, iColors)
((BOOL)AVICapSM(hwnd, WM_CAP_PAL_AUTOCREATE, (WPARAM)(iFrames),
(LPARAM)(DWORD)(iColors)))
#define capPaletteManual(hwnd, fGrab, iColors)
((BOOL)AVICapSM(hwnd, WM_CAP_PAL_MANUALCREATE, (WPARAM)(fGrab),
(LPARAM)(DWORD)(iColors)))
Mediante queste funzioni possibile inserire altre funzione per
lo svolgimento di determinate esigenze.

Il lavoro di preparazione.
Il lavoro di preparazione prevede diverse funzioni tra le quali
alcune legate alla correzione grafica dellimmagine acquisita ed
altre invece indirizzate alla calibrazione della telecamera.
Tale
funzionalit
viene
eseguita
per
trovare
i
parametri
intrinsechi ed estrinsechi della telecamera.
Tra i primi ci sono :
Lunghezza focale
Posizione del centro dellimmagine
Dimensione effettiva del pixel
Coefficiente della distorsione radiale delle lenti
I parametri estrinsechi invece sono :
La matrice di rotazione
Il vettore di traslazione
La fase di calibrazione necessaria per determinare alcuni
parametri che identificano la posizione della telecamera nel
mondo.
Alcuni di questi parametri sono dati direttamente dal produttore
della telecamera (come il numero di pixel del CCD, le dimensioni
dei pixel stessi, ), altri invece dipendono dalla sua posizione
(come la matrice di rototraslazione).
Per la determinazione di questi parametri in una fase iniziale
stato utilizzato lalgoritmo di Horn.
Questo algoritmo sebbene di facile implementazione ed utilizzo
(necessitava di soli 5 punti), non dava una precisione sufficiente
al nostro fine, cos abbiamo optato per un algoritmo pi complesso
ma maggiormente stabile e preciso: il metodo di Tsai.
Nel caso specifico le maggiori imprecisioni del metodo di Horn
erano dovute alla soluzione di un sistema non lineare mediante
lalgoritmo di Newton-Rapson.
Il metodo di Tsai invece si basa sulla scomposizione del sistema
non lineare in due sottosistemi dei quali uno lineare.
Un ulteriore vantaggio nellutilizzo di Tsai il calcolo della
distanza focale.
Lo svantaggio sta nel numero di punti minimo richiesto per la
calibrazione: almeno 11 per ogni telecamera.
La calibrazione della camera nella sua forma pi semplice viene
eseguita dalla funzione :
CalibrateCamera
Calibrates camera with single precision
void cvCalibrateCamera( int numImages, int* numPoints, CvSize
imageSize,CvPoint2D32f* imagePoints32f, CvPoint3D32f*
objectPoints32f, CvVect32f distortion32f, CvMatr32f

cameraMatrix32f, CvVect32f transVects32f, CvMatr32f rotMatrs32f,


int useIntrinsicGuess );
Tale funzionalit indirizzata a correggere fattori legati
alluso delle lenti allinterno del sistema.
Uno dei metodi usati quello
definito con il termine di :
Direct
(DLT)

Linear

Transformation

Registrare
un
immagine

lequivalente di mappare un
punto delloggetto O presente
nello spazio di questo in un
punto dellimmagine l presente
nello spazio del film.
Nel
caso
di
digitazione
limmagine
registrata
viene
proiettata nuovamente in un
immagine
l
allinterno
del
piano di proiezione.
Per
semplicit
comqunue

possibile
relazionare
direttamente
limmagine
proiettata e loggetto.

Inizialmente abbiamo detto che esistono parametri intrinsechi ed


estrinsechi.
La correzione degli errori indotti dalle lenti possono essere
eseguite mediante :
UnDistortOnce
Corrects camera lens distortion
void cvUnDistortOnce( const CvArr* srcImage, CvArr* dstImage,
const float* intrMatrix,
const float* distCoeffs,
int interpolate=1 );

srcImage
Source (distorted) image.
dstImage
Destination (corrected) image.
intrMatrix
Matrix of the camera intrinsic parameters (3x3).
distCoeffs
Vector of the four distortion coefficients k1, k2, p1 and p2.
interpolate
Bilinear interpolation flag.

Esistono alcune funzioni di OPENCV


identificare questi tipi di parametri.

che

sono

utilizzate

per

FindExtrinsicCameraParams
Finds extrinsic camera parameters for pattern
void cvFindExtrinsicCameraParams( int numPoints, CvSize imageSize,
CvPoint2D32f* imagePoints32f, CvPoint3D32f*
objectPoints32f,
CvVect32f focalLength32f, CvPoint2D32f
principalPoint32f,
CvVect32f distortion32f, CvVect32f rotVect32f,
CvVect32f transVect32f );
numPoints
Number of the points.
ImageSize
Size of the image.
imagePoints32f
Pointer to the image.
objectPoints32f
Pointer to the pattern.
focalLength32f
Focal length.
principalPoint32f
Principal point.
distortion32f
Distortion.
rotVect32f
Rotation vector.
transVect32f
Translate vector.

I concetti algoritmici utilizzati.


Nelle pagine precedenti si sono utilizzati dei termini particolari
come
ad
esempio
OBJECT
RECOGNITION,
PATTERN
RECOGNITION,
SEGMENTAZIONE, HIDDEN MARKOV MODELLS ecc.
Tutte queste voci sono comuni nellambito delle metodologie
utilizzate allinterno della computer vision.
Nel sistema software di gestione dellidentificazione retinale
sono utilizzate molte procedure che si attengono a questi termini
e quindi prima di trattare dal punto di vista algoritmico la
problematica necessario avere ben chiari questi principi.
Alcuni termini si riferiscono a concetti in senso generale mentre
altri sono relativi ad algoritmi specifici utilizzati allinterno
dei procedimenti informatici.
Un concetto fondamentale comunque quello legato al fatto che
reale od astratto che sia il nostro lavoro sar indirizzato verso
la gestione di un oggetto che poi di fatto costituito dalla
nostra retina.
Nei sistemi indirizzati al riconoscimento doggetti in genere
questi sono presenti dentro ad immagini composte da altri oggetti
perci in alcuni casi prima di poter intervenire a livello
algoritmico, ad esempio per eseguire un riconoscimento,
necessario eseguire determinati metodi per semplificare il
contenuto dellimmagine.

SEGMENTAZIONE

Uno degli algoritmi utilizzati quello legato alla segmentazione.


La segmentazione di una immagine un processo che consente di
suddividere la stessa in alcune regioni significative.
In altre parole per segmentazione dell'immagine si intendono tutti
quei passaggi che consentono una descrizione dettagliata degli
oggetti stessi come il numero di pixels costituenti l'oggetto e le
loro coordinate, tutte le informazioni necessarie a tracciare i
loro contorni ecc..
Ogni oggetto viene quindi identificato per mezzo di un set
descrittivo di dati che vengono memorizzati e successivamente
impiegati per le operazioni di misura sugli oggetti stessi.
Il caso pi semplice consiste nel suddividere limmagine in due
sole regioni di cui una costituisce lo sfondo e laltra loggetto.
Nei casi pi complessi ciascuna regione definita con il termine
di classe.
Su questo metodo avevamo accennato il concetto nelle pagini
precedenti ma ripentendolo possiamo dire che esistono
sostanzialmente tre tipologie di segmentazione:
1 Thresholding
2 Edge based method
3 Relaxation
Lutilizzo della segmentazione ha numerosi vantaggi:

1. Trasformazione di unimmagine in regioni che possono essere


trattate separatamente. Ad esempio immagini binarie.
2. Identificazione delle caratteristiche dominanti di una
immagine.
3. Trasformazione di immagini complesse in immagini pi semplici
per le operazioni di riconoscimento (pattern recognition) nel
campo delle Reti Neurali o della IA
Metodo del livello di soglia (Threshold).
Questo metodo il pi semplice e consiste nel considerare come
sfondo tutti i pixels che hanno un valore di densit uguale o
maggiore di un determinato valore di soglia; essi verranno quindi
settati al valore del bianco (255). Invece sono considerati come
appartenenti ad oggetti tutti quei pixels che hanno un valore
inferiore al valore di soglia e verranno settati al nero (0).
In questo caso si parla di valore di soglia superiore.
A volte necessario scegliere l'inverso, cio settare al nero i
pixel aventi un valore maggiore del valore di soglia e si parla
allora di valore di soglia inferiore.
Il settaggio dei pixel oggetto al nero e di quelli dello sfondo al
bianco detto binarizzazione dell'immagine.
Da notare che non sempre necessario ottenere l'immagine binaria
per poter eseguire i calcoli richiesti; infatti spesso ci non
necessario e anzi non si deve fare quando si devono eseguire
analisi densitometriche in quanto nell'immagine binaria vengono
perse tutte le informazioni relative al valore dei pixel
dell'immagine nella scala dei grigi.

Tuttavia i calcoli delle grandezze morfometriche sull'immagine


binaria sono pi veloci e precisi rispetto agli stessi eseguiti su
un'immagine in scala di grigi o in falsi colori.
Ci a causa degli algoritmi usati per l'identificazione degli
oggetti e i calcoli relativi.
La scelta della soglia dipende dallistogramma dellimmagine.

Non per tutte le immagini possibile con una sola soglia ricavare
le informazioni desiderate.

Volendo ricorrere a pi soglie possibile utilizzare ler regole :

Nel caso bimodale una


dellimmagine.
In questo caso vale :

sola

soglia

separa

le

due

regioni

In questo caso si possono scegliere due valori di soglia e dunque


si hanno 4 possibilit.
In questo caso una sola soglia non sufficiente a caratterizzare
limmagine.
Un sottogruppo di questo metodo quello definito con il termine
di Metodo delle densit parziali (Density slice).
Esso consiste nello stabilire un livello di soglia inferiore e un
livello di soglia superiore nel quale tutti i pixels che hanno
valori compresi in tale intervallo sono considerati appartenenti
ad oggetti di interesse; mentre quelli al di sotto del valore

minimo e al di sopra del valore massimo di tale intervallo sono


considerati appartenenti allo sfondo.
Detto in altre parole la segmentazione una elaborazione
finalizzata al partizionamento delle immagini in regioni disgiunte
e
singolarmente
omogenee
rispetto
a
determinati
parametri
(luminanza, texture, forma, ecc.).
Per esempio, data un'immagine con il primo piano di una persona,
un problema di segmentazione potrebbe essere quello di individuare
e separare alcune parti dell'immagine come i capelli, gli occhi,
la bocca e via dicendo.
Nel caso dei capelli sarebbe opportuno, ad esempio, far
riferimento alle caratteristiche di texture dei dati al fine di
isolare l'intera regione dei capelli, senza incorrere nell'errore
di
segmentarla
ulteriormente
a
causa
di
variazioni
nella
luminanza.
Un applicazione tipica quella della classificazione, soprattutto
per le immagini da telerilevamento in cui si cerca di identificare
i diversi tipi di superfice terrestre (fiumi, boschi, campi,
insediamenti urbani, ecc.) in base ai dati osservati, generalmente
per fini di monitoraggio ambientale.
Edge based method
Se i contorni di unimmagine possono essere considerati come
frontiere di ciascuna regione di separazione, lapplicazione di un
metodo edge detector consente la segmentazione della stessa.
Il metodo anche detto Metodo dei Seguitori di Contorno ed i
passi per la realizzazione sono i seguenti:
1. Calcolo del Gradiente: vengono create due nuove immagini che
indicano il modulo e la direzione dei contorni attraverso
lutilizzo di filtri derivativi.
2. Assottigliamento degli spigoli: il gradiente produce spigoli di
dimensioni molto grandi. Maggiore la finestra della maschera
maggiori sono le dimensioni degli spigoli.Pertanto devono essere
selezionati i soli spigoli in cui il massimo valore il massimo
locale nella direzione del gradiente stesso.Rimane cos un solo
pixel.
3. Costruzione delle catene tra pixel: vengono costruite le
catene tra tutti i pixel selezionati in precedenza. Si creano
s delle curve che costituiscono il contorno delle regioni.

Relaxation
Effetto Rilievo: si basa su operatori di gradiente, opportunamente
modificati. Ai diversi livelli di luminosit si applica un
algoritmo che restituisce delle curve di livello.

Solarizzazione: E una modifica (taglio) dellistogramma


dellimmagine:

Lutilizzo

della

segmentazione

di

fatto

semplifica

la

gestione

degli oggetti presenti in quanto delimita il contenuto di questi


in modo netto.
Generalmente la rappresentazione degli oggetti allinyterno di un
immagine costituita da gruppi di pixels di colorazioni
differenti.
In questo modo diventa complicato concettualizzare queli possono
essere i limiti degli oggetti da passare alle varie funzioni di
elaborazione.
La segmentazione semplifica notevolmente i processi successivi che
vengono eseguiti nellambito dellidentificazione degli oggetti.
Nellambito di OPENCV le funzionalit di gestione del threshold
possono essere eseguite molto semplicemente mediante lsuo
dellapposita funzione.
Threshold
Applies fixed-level threshold to array elements
void cvThreshold( const CvArr* src, CvArr* dst, double threshold,
double maxValue, int thresholdType );
src
Source array (single-channel, 8-bit of 32-bit floating point).
dst
Destination array; must be either the same type as src or 8-bit.
threshold
Threshold value.
maxValue
Maximum value to use with CV_THRESH_BINARY, CV_THRESH_BINARY_INV,
and CV_THRESH_TRUNC thresholding types.
thresholdType
Thresholding type (see the discussion)
The function cvThreshold applies fixed-level thresholding to
single-channel array. The function is typically used to get bilevel (binary) image out of grayscale image or for removing a
noise, i.e. filtering out pixels with too small or too large
values. There are several types of thresholding the function
supports that are determined by thresholdType:
thresholdType=CV_THRESH_BINARY:
dst(x,y) = maxValue, if src(x,y)>threshold
0, otherwise
thresholdType=CV_THRESH_BINARY_INV:
dst(x,y) = 0, if src(x,y)>threshold
maxValue, otherwise
thresholdType=CV_THRESH_TRUNC:
dst(x,y) = threshold, if src(x,y)>threshold
src(x,y), otherwise

thresholdType=CV_THRESH_TOZERO:
dst(x,y) = src(x,y), if (x,y)>threshold
0, otherwise
thresholdType=CV_THRESH_TOZERO_INV:
dst(x,y) = 0, if src(x,y)>threshold
src(x,y), otherwise

Come avevamo detto inizialmente il problema dopo averlo analizzato


dal punto di vista del geometric matching stato subito
inquadrato nella tipologia di problemi affrontabili mediante le
metodologie di riconoscimento e classificazione di pattern,
template o shapes.

Il pattern viene definito, con un termine molto rasente alla


filosofia, come lopposto del chaos, o, in altre parole, forma
vagamente definita a cui pu essere attribuito un nome.
Un esempio di pattern pu essere una retina, un impronta, un volto
, ecc
Nel contesto del pattern recognition il riconoscimento inteso
come classificazione o categorizzazione ovvero lassegnamento del
pattern a una classe.
Per classe intendiamo un insieme di entit aventi propriet comuni
(ad. esempio i diversi modi in cui le persone scrivono il
carattere A).
Il concetto di classe semantico e dipende strettamente
dallapplicazione.
Le classi possono essere definite dal progettista (classificazione
supervisionata)
o
imparate
autonomamente
dal
sistema
(classificazione non supervisionata).
Come abbiamo visto prima il metodo classico di confronto diretto
delle immagini non funziona per i seguenti motivi :

Traslazione, rotazione, scala e prospettiva.


Deformazione e variabilit dei pattern.
Cambiamenti di illuminazione.
Rumore e diverse tecniche di acquisizione.

Ad esempio perche limmagine I1 I2 non funziona ?

I principali approcci della pattern recognition sono quelli che


seguono.
Template matching

La distanza diretta tra immagini non fornisce buoni risultati


spesso a causa di traslazioni, rotazioni, variabilit dei
pattern.

Approccio Statistico

Ogni pattern rappresentato da un punto nello spazio


multidimensionale.
Prevede
una
fase
di
estrazione
delle
caratteristiche
estrazione delle caratteristiche che mappa il pattern nel
punto e una fase di classificazione classificazione che
associa il punto a una classe.
I classificatori utilizzati sono fondati su solide basi
statistiche.

Di ogni oggetto viene misurata lestensione dei pixels rispetto


al barricentro.
Nel caso di classificazione :
Se E lestensione delloggetto da classificare in una delle 2
classi T (oggetti allungati a forma di T) e C (oggetti pi
circolari).

Dove Avgt e AvgC sono le estensioni medie degli oggetti delle 2


classi T e C misurate su un set di addestramento.
Approccio Strutturale (sintattico)

I pattern sono codificati in termini di componenti primitive


componenti
primitive
e
di
relazioni
relazioni
che
intercorrono tra esse.
Il confronto avviene confrontando primitive e relazioni.

Reti Neurali

Sono costituite da grafi orientati i cui nodi (neuroni)


processano le informazioni trasmesse da altri neuroni ad essi
collegati.
Consentono
di
codificare
complessi
complessi
mapping
mapping non non-lineari lineari, che vengono solitamente
appresi da esempi.

Il termine template matching template matching molto generico


nellambito del PR, ma normalmente fa riferimento allo ricerca
di un template T allinterno di unimmagine I con lobiettivo di
determinare se I contiene loggetto (match) e in quale posizione T
appare nellimmagine.

Un concetto matematico utilizzato nel Template Matching quello


di Correlazione nello spazio di Fourier Fourier
Il
teorema
di
Correlazione
afferma
che
unoperazione
di
correlazione eseguita nel dominio spaziale corrisponde a una
moltiplicazione eseguita nel dominio delle frequenze (Fourier):
La correlazione tra due funzioni f e g in ciascun punto (x,y)
definita (nel continuo) come:

Il teorema di correlazione afferma che:

dove F la trasformata di Fourier di f, G* la complessa


coniugata della trasformata di Fourier di g e F-1 loperazione
di lantitrasformata.
Per eseguire la correlazione nello spazio delle frequenze
pertanto necessario:
1. Calcolare la trasformata dellimmagine I (con algoritmo FFT
su immagine di dimensione pari a una potenza di 2).
2. Calcolare la trasformata del template T (che deve essere
esteso a
una dimensione pari a quella dellimmagine) e la

complessa coniugata.
3. Eseguire la moltiplicazione pixel a
complessi)
4. Antitrasformare (con algoritmo InvFFT).

pixel

(tra

numeri

Per unimmagine di N*N pixel e un template di M*M pixel la


complessit nel dominio dello spazio : N2M2 mentre nel
dominio delle frequenze 3 N2 ln (N2) + N2

Considerando che nello spazio delle freq. sono richieste


operazioni floating-point e allargamento di immagini (per
compensare effetti di bordo e adeguamento a potenza del 2) si
ha maggiore efficienza solo quando: M2 >> ln (N2)

Nel caso
seguono :

di

TEMPLATE

DEFORMABILI

metodi

sono

quelli

che

Free-Form deformable:
il template non vincolato a forme precise (anche se generalmente
si impongono criteri di regolarit). Si fa uso di un potential
field, cio di una funzione di energia prodotta dalle feature
salienti
dellimmagine,
per
guidare
il
processo
verso
le
deformazioni maggiormente significative.

Eint lenergia interna che controlla la regolarit e


lelasticit dello snake.

Eimage lenergia dellimmagine che indica quanto la forma


delloggetto si adatta allimmagine sottostante (bordi,
gradiente, ...).
Eext lenergia esterna determinata da altri eventuali

componenti esterne.
Analytic-Form based (Parametric deformable Parametric deformable):
si tratta di template parametrici (polilinee, archi, curve spline,
o combinazione dei precedenti) regolate da un numero limitato di
parametri agendo sui quali si ottengono deformazioni controllate.

Prototype based(Parametric deformable Parametric deformable):


viene definito un template master e una serie di trasformazioni
parametriche che ne producono versioni deformate.

Shape learning (Apprendimeto della forma da esempi):


Questo il caso nostro o perlomeno quello che si adatta alle
nostre esigenze.
Spesso risulta complicato fornire espressioni analitiche di un
template
e
altrettanto
difficile
definire
precisamente
le
trasformazioni che mappano un oggetto nelle sue possibili
deformazioni.
I metodi di Shape learning hanno come obiettivo lapprendimento
della forma di un oggetto e delle sue possibili variazioni a
prtire da un insieme di esempi.

Nel metodo di Cootes il template viene modellato da poligonali;


durante laddestramento gli esempi nel training set sono allineati
(manualmente) sulla base di punti notevoli.
I molteplici gradi di libert dati dalla mobilit spaziale di
punti corrispondenti nei diversi pattern di esempio vengono
fortemente ridotti (trasformata KL) per ottenere un modello
parametrico con pochi gradi di libert.
Una
parte
operativa
legata
al
riconoscimento

quello
dellestrazione dei particolari.
Feature Extraction (estrazione delle caratteristiche)
Lapproccio statistico richiede il mapping di un pattern in uno
spazio multidimensionale.
Come gi detto, unimmagine pu essere considerata come un punto
multidimensionale, ma tale rappresentazione mal si presta alla
maggior parte dei problemi di riconoscimento (scarsa invarianza a
piccoli
cambiamenti
locali,
deformazioni,
prospettiva,
illuminazione, ...).
Con il termine feature extraction si intende in generale il
processo di estrazione di caratteristiche da unimmagine.

Il
pre-processing
dellimmagine

unelaborazione
iniziale
dellimmagine eseguita allo scopo di semplificare e rendere pi
efficace la successiva estrazione delle caratteristiche.
Si utilizzano tecniche di filtraggio per:

Migliorare il contrasto dellimmagine.


Ridurre il rumore.
Separare meglio gli oggetti dallo sfondo.

Istogramma
Listogramma di unimmagine digitale a livelli di grigio indica il
numero di pixel dellimmagine per ciascun livello di grigio:

Dallistogramma possono essere estratte informazioni interessanti


sullimmagine:

se la maggior parte dei pixel assume valori di grigio


condensati
ci significa che limmagine ha uno scarso contrasto (range
dinamico).

se nellistogramma sono predominanti le basse intensit,


limmagine molto scura e viceversa ...
un istogramma bimodale denota spesso la presenza di un
oggetto abbastanza omogeneo su uno sfondo di luminosit
pressoch costante.

Una delle parti del software da noi scritto ipotizza lsuo dei
classificatori la cui teoria, in linea di massima, quella che
segue.
Classificazione (approccio statistico)
Una volta estratte le feature dai pattern, la classificazione
cnsiste nellassociare ogni pattern a una o pi classi.

Se un pattern viene associato a una sola classe si parla di


classificazione
esclusiva
classificazione
esclusiva;
se
invece il pattern pu appartenere, con un certo grado di
probabilit, a pi classi si parla di classificazione
classificazione continua o continua o fuzzy fuzzy.

La classificazione si dice supervisionata supervisionata nel


caso in cui le classi sono note a priori e i pattern del
training
set
sono
etichettati;
non
supervisionata
supervisionata nel tal caso in cui le classi sono sconosciute
e devono essere derivate dai dati stessi.

Approccio Bayesiano
Il problema posto in termini probabilistici.
Se tutte le le distribuzioni in gioco sono note lapproccio
byesiano costituisce la migliore regola di classificazione
possibile.
Notazione e definizioni di base

Sia V uno spazio di pattern d-dimensionali e


W = {w1, w2, ..., ws}
un insieme di classi disgiunte costituite da elementi di V
Per ogni
e per ogni
ndichiamo con p(x|wi) la densit di
probabilit condizionale di x data wi, ovvero la densit di
probabilit che il prossimo pattern sia x sotto lipotesi che la
sua classe di appartenenza sia wi
Per ogni
indichiamo con P(wi) la probabilit a priori di wi
ovvero la probabilit, indipendentemente dallosservazione, che il
prossimo pattern da classificare sia di classe wi
Per ogni
indichiamo con p(x) la densit di probabilit
assoluta di x, ovvero la densit di probabilit che il prossimo
pattern da classificare sia x.

Per ogni
e per ogni
indichiamo con P(wi|x) la probabilit
probabilit a posteriori a posteriori di wi dato x, ovvero la
probabilit che avendo osservato il pattern x, la classe di
appartenenza sia wi. Per il teorema di Bayes:

Il controllo dinquadratura della retina.


In questa fase lidentificazione dellOGGETTO retina allinterno
di un immagine viene eseguita in senso generale.

La funzionalit indirizzata a individuare una retina dentro ad


un immagine con il fine di ritagliare la porzione di questa e con
questo parte creare una nuova immagine la quale verr utilizzata
per la fase dellidentificazione.
Il sistema utilizza il metodo haar-like per lindividuazione della
forma ottimale della retina inquadrata.
Questa fase della programmazione pu essere suddivisa in due fasi
ben precise e precisamente quella in cui si esegue il training per
fare in modo che il sistema riesca ad individuare automaticamente
quando allinterno dellimmagine giunta dalla camera presente
una retina analizzabile.
Per fare questo necessario spiegare al sistema quali possono
essere immagini accettabili (positive) e quelle che no (negative).
La
struttura
letta
da
disco
relativa
alla
cascata
di
classificatori indirizzati alla retina viene creata mediante una
serie di operazioni che devono essere eseguite.
Il sistema necessita di una serie di immagini positive e negative
relative al concetto di retina.
In altre parole prima di creare il training si devono creare
immagini che NON contengono rappresentazioni dellimmagine ed
altre invece che la contengono.
La struttura della directory quella che segue:
/img
img1.jpg
img2.jpg
bg.txt
Dove il file bg.txt:
img/img1.jpg
img/img2.jpg
La creazione dei campioni positivi invece deve essere eseguita
mediante una determinata sequenza di funzioni facenti parte del
programma chiamato CREATESAMPLES.
Questo ha tra le funzioni quella di analizzare i parametri

passati.
Le altre funzioni utilizzate
definito nel file .h

possiedono

come

prototipo

quello

/*
* cvhaartraining.h
*
* haar training functions
*/
#ifndef _CVHAARTRAINING_H_
#define _CVHAARTRAINING_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* cvCreateTrainingSamples
*
* Create training samples applying random distortions to sample image and
* store them in .vec file
*
* filename
- .vec file name
* imgfilename
- sample image file name
* bgcolor
- background color for sample image
* bgthreshold
- background color threshold. Pixels those colors are in
range
*
[bgcolor-bgthreshold, bgcolor+bgthreshold] are considered as transparent
* bgfilename
- background description file name. If not NULL samples
*
will be put on arbitrary background
* count
- desired number of samples
* invert
- if not 0 sample foreground pixels will be inverted
*
if invert == CV_RANDOM_INVERT then samples will be inverted randomly
* maxintensitydev - desired max intensity deviation of foreground samples
pixels
* maxxangle
- max rotation angles
* maxyangle
* maxzangle
* showsamples
- if not 0 samples will be shown
* winwidth
- desired samples width
* winheight
- desired samples height
*/
#define CV_RANDOM_INVERT 0x7FFFFFFF
void cvCreateTrainingSamples( const char* filename,
const char* imgfilename, int bgcolor, int
bgthreshold,
const char* bgfilename, int count,
int invert = 0, int maxintensitydev = 40,
double maxxangle = 1.1,
double maxyangle = 1.1,
double maxzangle = 0.5,
int showsamples = 0,
int winwidth = 24, int winheight = 24 );
void cvCreateTestSamples( const char* infoname,
const char* imgfilename, int bgcolor, int bgthreshold,
const char* bgfilename, int count,
int invert, int maxintensitydev,
double maxxangle, double maxyangle, double maxzangle,

int showsamples,
int winwidth, int winheight );
/*
* cvCreateTrainingSamplesFromInfo
*
* Create training samples from a set of marked up images and store them into
.vec file
* infoname
- file in which marked up image descriptions are stored
* num
- desired number of samples
* showsamples - if not 0 samples will be shown
* winwidth
- sample width
* winheight
- sample height
*
* Return number of successfully created samples
*/
int cvCreateTrainingSamplesFromInfo( const char* infoname, const char*
vecfilename,
int num,
int showsamples,
int winwidth, int winheight );
/*
* cvShowVecSamples
*
* Show samples stored in .vec file
* filename - .vec file name
* winwidth - sample width
* winheight - sample height
*/
void cvShowVecSamples( const char* filename, int winwidth, int winheight );
/*
* cvCreateCascadeClassifier
*
* Create cascade classifier
* dirname
- directory name in which cascade classifier will be
created.
*
It must exist and contain subdirectories 0, 1, 2, ... (nstages-1).
* vecfilename
- name of .vec file with object's images
* bgfilename
- name of background description file
* npos
- number of positive samples used in training of each stage
* nneg
- number of negative samples used in training of each stage
* nstages
- number of stages
* numprecalculated - number of features being precalculated. Each precalculated
feature
*
requires (number_of_samples*(sizeof( float ) + sizeof( short ))) bytes of
memory
* numsplits
- number of binary splits in each weak classifier
*
1 - stumps, 2 and more - trees.
* minhitrate
- desired min hit rate of each stage
* maxfalsealarm
- desired max false alarm of each stage
* weightfraction
- weight trimming parameter
* mode
- 0 - BASIC = Viola
*
1 - CORE = All upright
*
2 - ALL
= All features
* symmetric
- if not 0 vertical symmetry is assumed
* equalweights
- if not 0 initial weights of all samples will be equal
* winwidth
- sample width
* winheight
- sample height
* boosttype
- type of applied boosting algorithm
*
0 - Discrete AdaBoost

*
1 - Real AdaBoost
*
2 - LogitBoost
*
3 - Gentle AdaBoost
* stumperror
- type of used error if Discrete AdaBoost algorithm is
applied
*
0 - misclassification error
*
1 - gini error
*
2 - entropy error
*/
void cvCreateCascadeClassifier( const char* dirname,
const char* vecfilename,
const char* bgfilename,
int npos, int nneg, int nstages,
int numprecalculated,
int numsplits,
float minhitrate = 0.995F, float maxfalsealarm =
0.5F,
float weightfraction = 0.95F,
int mode = 0, int symmetric = 1,
int equalweights = 1,
int winwidth = 24, int winheight = 24,
int boosttype = 3, int stumperror = 0 );
#ifdef __cplusplus
}
#endif /* extern "C" */
#endif /* _CVHAARTRAINING_H_ */
Una volta creata la struttura mediante le funzioni di training il software la
utilizzer per identificare in quale momento in una certa sequenza di immagini
provenienti dalla telecamera presente un immagine retinale.
Il programma deve identificare tale immagine e dopo averla inserita in un
rettangolo di cut tagliarla per creare limmagine finale che verr utilizzata
per la fase di identificazione.
Nella funzione main viene richiamata la funzione
InitRetinaDetect(classifierbase)
la quale ha lo scopo di inizializzare la cascata di CLASSIFICATORI HAAR.
const int root_w = 400, root_h = 400;
int main( int argc, char **argv )
{
const char* facebaseopt="--facebase=";
char* classifierbase = 0;
char* aviname = 0;
int auto_run = 0;
if( argc > 1 && argv[argc-1][0] != '-' )
{
aviname = argv[argc-1];
auto_run = 1;
argc--;
}
if( argc > 1 && strncmp(argv[argc-1],facebaseopt,strlen(facebaseopt))==0 )
{
classifierbase=argv[argc-1] + strlen(facebaseopt);
argc--;
}

if( !InitRetinaDetect(classifierbase))
{
fprintf( stderr, "Could not locate face classifier base at %s\n"
"Use --facebase=<classifier base path> option to
specify the base\n",
classifierbase );
return -1;
}
cpu_freq = cvGetTickFrequency();
printf("Tick frequency (*10^-6): %g\n", cpu_freq );
Fl_Window* w;
{
Fl_Window* o = root_window = new Fl_Window( root_w, root_h );
w = o;
{
Fl_Tabs* o = new Fl_Tabs( 10, 10, root_w - 20, root_h - 100 );
// camera tab
{
Fl_Group* o = new Fl_Group( 10, 30, root_w - 20, root_h - 110,
"Face Detection" );
{
VideoWindow* o = new VideoWindow( 15, 35, root_w - 30,
root_h - 120 );
video_window = o;
o->box( FL_BORDER_BOX );
o->color(0);
}
o->end();
}
o->end();
Fl_Group::current()->resizable(o);
}
{
const int bwidth = 30, bheight = 30;
play_button = new Fl_Button( 10, root_h - 35, bwidth, bheight,
"@>" );
play_button->callback((Fl_Callback*)cb_PauseResume);
play_button->deactivate();
stop_button = new Fl_Button( 10 + bwidth, root_h - 35, bwidth,
bheight, "@square" );
stop_button->callback((Fl_Callback*)cb_Stop);
stop_button->deactivate();
video_button = new Fl_Button( 10 + bwidth*2, root_h - 35, bwidth,
bheight, "..." );
video_button->callback((Fl_Callback*)cb_Open);
cam_button = new Fl_Button( 10 + bwidth*3, root_h - 35, bwidth,
bheight, "[o]" );
cam_button->callback((Fl_Callback*)cb_StartCam);
video_pos = new Fl_Value_Slider( 10 + bwidth*4 + 10, root_h - 35,
200, 20, "Position" );
video_pos->type( FL_HOR_NICE_SLIDER );
record_button = new Fl_Button( 10 + bwidth*4 + 230, root_h - 35,
bwidth, bheight, "@circle" );
record_button->labelcolor(FL_RED);
record_button->callback((Fl_Callback*)cb_StartStopRecord );
record_button->deactivate();
fps_box = new Fl_Box( 10, root_h - 75, bwidth*4, bheight, "<No
data>" );
fps_box->box( FL_DOWN_BOX );

}
o->end();

}
Fl::visual(FL_RGB);
w->show(argc, argv);
if( auto_run )
Fl::add_timeout( 0.1, cb_AutoRun, aviname );
Fl::run();
cb_Exit(0,0);
return 0;

Come dicevamo
precedentemente il sistema
utilizza la trasformata di
GABOR e la trasformata
WAVELET per lanalisi delle
informazioni relative ad una
generica retina.
OpenCV utilizza le HAAR
WAVELET.
La precednte figura mostra
la funzione Harr Wavelet.
Limmagine successiva invece
mostra il procedimento
concettuale per ottenere il vettore delle di dimansioni
ottimizzate.
In questo H ed L indicano il filtre passa alto e quello passa
basso mentre HH indica il filtro passa alto utilizzato su ambedue
gli assi X e Y.
Limmagine a fianco si riferisce ad un immagine di esempio di
450x60.
Ciascuna delle 87
dimensioni possiedono un
valore reale compreso tra
-1 e 1.
Per ridurre lo spazio e il
tempo computazionale
necessario per la
manipolazione il valore
viene quantizzato in m,odo
molto semplice convertendo
il valore positivo in 1 e
quello negativo in 0.
In questo modo un immagine
di 450x60 pu essere
rappresentata in 87 bits.
I valori relativi alle
dimensioni dellimmagine
sono soltanto teoricizzati
in modalit puramente casuale in quanto la loro determinazione
precisa dovr essere eseguita mediante prove eseguite realmente.
Matematicamente la funzione di HAAR SCALING definita come segue:

Supponiamo che

sia definita da

Definiamo

come

Definiamo lo spazio del vettore Vj come

dove

denota lattraveramento lineare.

Lindice j si riferisce alla dilatazione mentre i si riferisce


alla translazione.

La costante

viene scelta tale a

Se uno considera la funzione di scala su un altro intervallo oltre


a [0,1], il fattore di normalizzazione potrebbe cambiare.
Volendo descrivere algoritmicamente la funzione di haarscaling
abbiamo :
function y=haarscale(i,j)
y=zeros(3000,1);

% 3000 is chosen to give a suitable resolution.

support_start = ceil(1000*i/2^j)+1;
support_end = floor(1000*(1+i)/2^j);

% Gives vector indices for where the


% support of the function starts/end.

for k=1000+support_start:1000+support_end
y(k) = 1;
end
norm_factor = sqrt(2^j);
y = norm_factor*y;

% Normalizing factor w.r.t. the L^2-norm.

Per quanto riguarda la funzione di HAAR WAVELET :

Supponiamo

sia definita da

Definiamo

come

Definiamo il vettore dello spazio Wj

dove

denota lattraversamento lineare.

La costante

scelta

Se uno considera la funzione di scala su un altro intervallo oltre


a [0,1], il fattore di normalizzazione potrebbe cambiare.
Algoritmicamente la HAARWAVELET
function y=haarwavelet(i,j)
y=zeros(3000,1);

% 3000 is chosen to give a suitable resolution.

support_start = ceil(1000*i/2^j)+1;
support_middle = floor(1000*(0.5+i)/2^j);
support_end = floor(1000*(1+i)/2^j);

% Gives vector indices representing


% the start/middle/end of
% the support of the wavelet.

for k=1000+support_start:1000+support_middle
y(k) = 1;
end
for k=1001+support_middle:1001+support_end
y(k) = -1;
end
norm_factor = sqrt(2^j);

% Normalizing factor w.r.t. the L^2-norm.

y = norm_factor*y;

La struttura di base di HAAR :

La libreria OPENCV utilizza le seguenti strutture per la gestione


delle funzioni cvHaar.
#define CV_HAAR_FEATURE_MAX

/* a haar feature consists of 2-3 rectangles with appropriate weights */


typedef struct CvHaarFeature
{
int tilted; /* 0 means up-right feature, 1 means 45--rotated feature */
/* 2-3 rectangles with weights of opposite signs and
with absolute values inversely proportional to the areas of the
rectangles.
if rect[2].weight !=0, then
the feature consists of 3 rectangles, otherwise it consists of 2 */
struct
{
CvRect r;
float weight;
} rect[CV_HAAR_FEATURE_MAX];
} CvHaarFeature;
/* a single tree classifier (stump in the simplest case) that returns the
response for the feature
at the particular image location (i.e. pixel sum over subrectangles of the
window) and gives out
a value depending on the responce */
typedef struct CvHaarClassifier
{
int count;
/* number of nodes in the decision tree */
CvHaarFeature* haarFeature;
/* these are "parallel" arrays. Every index i
corresponds to a node of the decision tree (root has 0-th index).
left[i] - index of the left child (or negated index if the left child is

a leaf)

right[i] - index of the right child (or negated index if the right child
is a leaf)
threshold[i] - branch threshold. if feature responce is <= threshold,
left branch
is chosen, otherwise right branch is chosed.
alpha[i] - output value correponding to the leaf. */
float* threshold; /* array of decision thresholds */
int* left; /* array of left-branch indices */
int* right; /* array of right-branch indices */
float* alpha; /* array of output values */
}
CvHaarClassifier;
/* a boosted battery of classifiers(=stage classifier):
the stage classifier returns 1
if the sum of the classifiers' responces
is greater than threshold and 0 otherwise */

typedef struct CvHaarStageClassifier


{
int count; /* number of classifiers in the battery */
float threshold; /* threshold for the boosted classifier */
CvHaarClassifier* classifier; /* array of classifiers */
}
CvHaarStageClassifier;
/* cascade of stage classifiers */
typedef struct CvHaarClassifierCascade
{
int count; /* number of stages */
CvSize origWindowSize; /* original object size (the cascade is trained for)
*/
CvHaarStageClassifier* stageClassifier; /* array of stage classifiers */
}
CvHaarClassifierCascade;

Come dicevamo nella funzione main viene richiamato


linizializzatore InitRetinaDetect :
int InitRetinaDetect( const char* classifier_cascade_path )
{
CvHaarClassifierCascade* cascade = cvLoadHaarClassifierCascade(
classifier_cascade_path && strlen(classifier_cascade_path) > 0 ?
classifier_cascade_path : "<default_face_cascade>",
cvSize( ORIG_WIN_SIZE, ORIG_WIN_SIZE ));
if( !cascade )
return 0;
hid_cascade = cvCreateHidHaarClassifierCascade( cascade );
cvReleaseHaarClassifierCascade( &cascade );
return 1;
}

Nella prima riga della funzione viene letta da disco la scascata


di classificatori HAAR.
I files letti hanno il seguente formato:
9
1
2
6 4 12 9 0 -1
6 7 12 3 0 3
haar_y3
-0.031512 0 -1
2.087538 -2.217210
1
2
6 4 12 7 0 -1
10 4 4 7 0 3
haar_x3
0.012396 0 -1
-1.863394 1.327205
1
2
3 9 18 9 0 -1
3 12 18 3 0 3
haar_y3
0.021928 0 -1
-1.510525 1.062573
1

2
8 18 9 6 0 -1
8 20 9 2 0 3
haar_y3
0.005753 0 -1
-0.874639 1.176034
1
2
3 5 4 19 0 -1
5 5 2 19 0 2
haar_x2
0.015014 0 -1
-0.779457 1.260842
1
2
6 5 12 16 0 -1
6 13 12 8 0 2
haar_y2
0.099371 0 -1
0.557513 -1.874300
1
2
5 8 12 6 0 -1
5 11 12 3 0 2
haar_y2
0.002734 0 -1
-1.691193 0.440097
1
2
11 14 4 10 0 -1
11 19 4 5 0 2
haar_y2
-0.018859 0 -1
-1.476954 0.443501
1
2
4 0 7 6 0 -1
4 3 7 3 0 2
haar_y2
0.005974 0 -1
-0.859092 0.852556
-5.042550

La sintassi della funzione che esegue la lettura :


CvHaarClassifierCascade*
cvLoadHaarClassifierCascade( const char*
directory="<default_face_cascade>",
CvSize origWindowSize=cvSize(24,24));
directory
Nome del file che contiene la dscrizione dei classificatori
creati mediante la fase di training.
origWindowSize
La dimsnione originale delloggetto sulla quel stato
eseguito il training.
La seconda funzionalit eseguita quella relativa alla creazione
della cascata di classificatori mediante lutilizzo dei parametri

letti.
/* hidden (optimized) representation of Haar classifier cascade */
typedef struct CvHidHaarClassifierCascade
CvHidHaarClassifierCascade;
CvHidHaarClassifierCascade*
cvCreateHidHaarClassifierCascade( CvHaarClassifierCascade*
cascade,
const CvArr* sumImage=0,
const CvArr* sqSumImage=0,
const CvArr* tiltedSumImage=0,
double scale=1 );
cascade
original cascade that may be loaded from file using
cvLoadHaarClassifierCascade.
sumImage
Integral (sum) single-channel image of 32-bit integer format.
This image as well as the two subsequent images are used for
fast feature evaluation and brightness/contrast
normalization. They all can be retrieved from the input 8-bit
single-channel image using function cvIntegral. Note that all
the images are 1 pixel wider and 1 pixel taller than the
source 8-bit image.
sqSumImage
Square sum single-channel image of 64-bit floating-point
format.
tiltedSumImage
Tilted sum single-channel image of 32-bit integer format.
scale
Initial scale (see cvSetImagesForHaarClassifierCascade).
In altre parole la funzione converte i parametri letti mediante la
funzione precedente in un formato interno pi veloce.
Una volta creata questa catena e assegnato lhandle alla variabile
di rappresentazione nel programma la cascata viene rilasciata.
cvReleaseHaarClassifierCascade( &cascade );
Sempre allinterno del MAIN il programma continua a richiamare la
funzione GetNextFrame dentro alla quale esiste il richiamo della
funzione alla quale demandata la funzionalit di individuare se
dentro allimmagine corrente giunta dalla telecamera esiste una
retina.
Questa funzione ha il compito di tracciare un rettangolo intorno a
questa.
void DetectAndDrawRetina( IplImage* img )
{
if( hid_cascade && img )
{
int scale = 2;

CvSize img_size = cvGetSize( img );


IplImage* temp =
cvCreateImage( cvSize(img_size.width/2,img_size.height/2), 8, 3 );
int i;
cvPyrDown( img, temp );
cvClearMemStorage( storage );
if( hid_cascade )
{
CvSeq* retinas = cvHaarDetectObjects( temp, hid_cascade, storage,
1.2, 2, 1 );
for( i = 0; i < (retinas ? retinas->total : 0); i++ )
{
CvRect r = *(CvRect*)cvGetSeqElem( retinas, i );
cvRectangle( img,
cvPoint(r.x*scale,/*img->height - */r.y*scale),
cvPoint((r.x+r.width)*scale,/*img->height - */
(r.y+r.height)*scale),
CV_RGB(255,0,0), 3 );
}
}
}

cvReleaseImage( &temp );

Lanalisi iniziale, quella relativa al training, stata eseguita


con il seguente campionamento dimmagini :
POSITIVE

NEGATIVE

Questi campioni sono stati scelti per lo scopo funzionale in


ufficio.
In altre parole non avendo una mucca in ufficio la sperimentazione
lho eseguita mostrando al programma come riconoscere un immagine
di una retina rappresentata da un immagine mescolata ad altri
oggetti.
Passando con la telecamera su un gruppo di oggetti il programma
doveva delimitare limmagine retinale.

Nellistante in cui lidentificazione dovr essere eseguita in


ambiente operativo reale, il training dovr essere rieseguito
catalogando una latra serie di immagini classificate come positive
e altre negative.

Il SISTEMA DI IDENTIFICAZIONE.
Tutto il sistema di identificazione basato sui modelli di Markov
nascosti implementati allinetrno della libreria OpenCV.
Le funzioni utilizzate per la gestione pretendono una serie di
parametri i quali sono i valori classici utilizzati nei sistemi
HIDDEN MARKOV MODELL.

Gli HMM sono una classe di strumenti matematici sempre pi


largamente utilizzata nel settore della elaborazione dei segnali e
della identificazione parametrica; questa locuzione inglese ha
certamente il vantaggio della compattezza rispetto alla pi
esplicativa perifrasi modelli di sistemi dinamici stocastici
parzialmente
osservati,
la
quale
tuttavia
ne
riunisce
efficacemente tutte le caratteristiche principali.
Essi descrivono infatti sistemi dinamici regolati da equazioni, in
generale non lineari, in cui stati ed osservazioni consistono in
processi aleatori piuttosto che in quantit deterministiche, ed in
cui non possibile una osservazione diretta del valore dello
stato corrente ma si ha accesso soltanto a versioni "corrotte" da
rumore stocastico dello stato stesso, regolate da una equazione di
misura.
Talvolta con questa terminologia si intende il pi ristretto caso
in cui sia gli stati del sistema che le osservazioni assumono
valori solamente in un insieme discreto, e le equazioni che
determinano levoluzione del sistema sono lineari.
MM pu essere trattata in maniera unitaria ricorrendo ai
reference probability methods o metodi del cambio di misura di
probabilit.

Un biettivo, una volta a disposizione i parametri definitivi del


modello, e quello di ottenere una stima della sequenza degli stati
attraversati dallHMM sollecitato dalla corrispondente sequenza
delle osservazioni.
Parlando concettualmente di HMM sono da prendere in considerazione
le stime in linea (forward estimates), quelle ottenute in base
alle
osservazioni
passate
(rispetto
ovviamente
allistante
corrente di stima), che, soprattutto, le pi significative stime a
posteriori (smoothed estimates) che tengono conto di tutte le
osservazioni passate, presenti e future.
Una difficolt importante che incorre nellutilizzo dei modelli
matematici per rappresentare un qualsiasi fenomeno fisico o
sistema dinamico consiste nel fatto che, solitamente, si hanno a
priori pochissime informazioni da poter sfruttare per stabilire i
valori dei parametri fondamentali dei modelli stessi, una volta
scelta la classe di formalismi da adottare.
Gli HMM hanno una straordinaria capacit di autoapprendimento di
questi
parametri
che
si
fonda
su
una
tecnica
detta
di
massimizzazione
dellaspettazione
(in
inglese
EM-expectation
maximization); essenziale a questo scopo lo stimare una serie di
altre quantit ausiliarie, oltre agli stati del modello, quali il
numero di transizioni tra i diversi stati nel corso della
sequenza, il numero di occupazioni degli stati stessi, ed altre
che vedremo in seguito.
interessante accennare al fatto che rientrano nella categoria
degli HMM anche formalismi ben noti come il filtro di Kalman, che
si riottiene dal modello generale nel caso di stati e osservazioni
continue ed equazioni lineari, insieme alla sua versione estesa in
cui si considerano equazioni generali di tipo non lineare;
sfruttando
i
reference
probability methods si ottengono
anche in questi ambiti soluzioni
molto eleganti.
Nel nostro caso utilizzeremo le
Embedded Hidden Markov Model
chiamate brevemente EHMM.
Le immagini che seguono
rappresentano il principio di
base degli HMM e le varie
diversit che esitono
nellambito degli stessi.
Limmagine rappresenta
graficamente lutilizzo degli
HMM nellambito di un
riconoscimento facciale.
Gli HMM vengono utilizzati in
diversi tipi di riconoscimento
come ad esempio in quello
vocale.
A seconda dellapplicazione il metodo di gestione diventa sempre
pi complesso.
La diversit riferita alla modalit di trattatamento degli

alberi su cui sono strutturati gli HMM.

I modelli marcoviani si basano anche su fattori statistici per la


valutazione dei percosri ottimali.
Matematicamente abbiamo :
S un set di stati. Il processo random {Xt|t=1,2.} una catena
di Markov se t, la variabile random Xt soddisfa la propriet di
MARKOV.
pij=P{Xt+1=j | Xt=i, Xt-1=it-1, Xt-2=it-2,.X1=i1}
= P{Xt+1=j | Xt=i }
Quindi, abbiamo :
P(X1X2.Xk-1Xk)=P(Xk | X1X2. Xk-1).P(X1X2.Xk-1)
=P(Xk | Xk-1).P(X1X2.Xk-1)
=P(X1). P(Xi+1 | Xi)
for i=1,k-1
pij chiamata matrice di transizione.

Markov Model=Markov Chain + Transition matrix.


HMM una catena di Markov con un numero finito di stati
osswervabili.
Questi stati hanno una probabilit di distribuzione associata con
il set di vettori di osservazione.

Cose necessarie per caratterizzare un HMM sono:


1. La matrice probabilistica di transazione di stato.
2. Uno stato iniziale relativo alla probabililit di
distribuzione dello stato.
3. Una funzione relativa alla probabile densit associata con
ogni stato di osservazione.
La seguente immagine mostra il flusso relativo al training di un
immagine.

Trattando gli HMM a livello di programmazione ci troviamo davanti


a concetti particolari come ad esempio gli stati, i superstati
ecc.
Questi vengono proiettati nella serie di valori che corrispondono
alla serie di parametri che le funzioni di OPENCV legate agli HMM
necessitano.
Gli HMM implementati nel nostro programma utilizzano le seguenti
strutture per la memorizzazione su disco.
CvHMM
Embedded HMM Structure
typedef struct _CvEHMM
{
int level;
int num_states;
float* transP;
float** obsProb;
union
{
CvEHMMState* state;

struct _CvEHMM* ehmm;


} u;
} CvEHMM;
level
Level of embedded HMM. If level ==0, HMM is most external. In 2D HMM there
are two types of HMM: 1 external and several embedded. External HMM has
level ==1, embedded HMMs have level ==0 .
num_states
Number of states in 1D HMM.
transP
State-to-state transition probability, square matrix
(num_statenum_state).
obsProb
Observation probability matrix.
state
Array of HMM states. For the last-level HMM, that is, an HMM without
embedded HMMs, HMM states are real.
ehmm
Array of embedded HMMs. If HMM is not last-level, then HMM states are not
real and they are HMMs.

E anche la seguente struttura


CvImgObsInfo
Image Observation Structure
typedef struct CvImgObsInfo
{
int obs_x;
int obs_y;
int obs_size;
float** obs;
int* state;
int* mix;
} CvImgObsInfo;
obs_x
obs_y

Number of observations in the horizontal direction.

Number of observations in the vertical direction.


obs_size
Length of every observation vector.
obs
Pointer to observation vectors stored consequently. Number of vectors is
obs_x*obs_y .
state
Array of indices of states, assigned to every observation vector.
mix

Mediante le funzioni di scrittura e di lettura che seguono vengono


eseguite le gestioni su disco di queste strutture di dati.
bool CContEHMM::Save( const char* filename )
{
FILE* file;
if (!m_hmm) return false;
file = fopen( filename, "wt" );

if (!file) return false;


// write topology
fprintf(file, "%s %d\n", "<NumSuperStates>", m_hmm->num_states );
fprintf(file, "%s ", "<NumStates>");
for( int i = 0; i < m_hmm->num_states; i++ )
{
fprintf(file, "%d ", m_hmm->u.ehmm[i].num_states );
}
fprintf(file, "\n");
fprintf(file, "%s ", "<NumMixtures>");
for( i = 0; i < m_hmm->num_states; i++ )
{
CvEHMM* ehmm = &(m_hmm->u.ehmm[i]);
for( int j = 0; j < ehmm->num_states; j++ )
{
fprintf(file, "%d ", ehmm->u.state[j].num_mix );
}

}
fprintf(file, "\n");

fprintf(file, "%s %d\n", "<VecSize>", m_vectSize);


//consequently write all hmms
CvEHMM* hmm = m_hmm;
for( i = 0; i < m_hmm->num_states + 1; i++ )
{
if (hmm->level == 0 )
fprintf(file, "%s\n", "<BeginEmbeddedHMM>");
else
fprintf(file, "%s\n", "<BeginExternalHMM>");
fprintf(file, "%s %d\n", "<NumStates>", hmm->num_states);
if (hmm->level == 0 )
{
for ( int j = 0; j < hmm->num_states; j++)
{
CvEHMMState* state = &(hmm->u.state[j]);
fprintf(file, "%s %d\n", "<State>", j);
fprintf(file, "%s %d\n", "<NumMixes>", state->num_mix);
float* mu = state->mu;
float* inv_var = state->inv_var;
for( int m = 0; m < state->num_mix; m++)
{
fprintf(file, "%s %d %s %f\n", "<Mixture>", m, "<Weight>",
state->weight[m] );
fprintf(file, "%s\n", "<Mean>");
for (int k = 0; k < m_vectSize; k++)
{
fprintf(file, "%f ", mu[0]);
mu++;
}

fprintf(file, "\n");
fprintf(file, "%s\n", "<Inverted_Deviation>");
for (k = 0; k < m_vectSize; k++)
{
fprintf(file, "%f ", inv_var[0]);
inv_var++;
}
fprintf(file, "\n");
fprintf(file, "%s %f\n", "<LogVarVal>", state>log_var_val[m] );
}
}

//write the transition probability matrix


fprintf(file, "%s\n", "<TransP>" );
float* prob = hmm->transP;
for (int j = 0; j < hmm->num_states; j++)
{
for (int k = 0; k < hmm->num_states; k++)
{
fprintf(file, "%f ", *prob);
prob++;
}
fprintf(file, "\n");
}
if( hmm->level == 0 )
fprintf(file, "%s\n", "<EndEmbeddedHMM>");
else
fprintf(file, "%s\n", "<EndExternalHMM>");
hmm = &(m_hmm->u.ehmm[i]);
}
fclose(file);
return true;
}
bool CContEHMM::Load( const char* filename )
{
FILE* file;
int num_states[128];
int num_mix[128];
char temp_char[128];
if (m_hmm) cvRelease2DHMM( &m_hmm);
file = fopen( filename, "rt" );
if (!file) return false;
// read topology
fscanf(file, "%s %d\n", temp_char, num_states);
fscanf(file, "%s ", temp_char);
for( int i = 0; i < num_states[0]; i++ )
{
fscanf(file, "%d ", num_states + i + 1 );

}
fscanf(file, "\n");
//compute total number of internal states
int total_states = 0;
for( i = 0; i < num_states[0]; i++ )
{
total_states += num_states[i+1];
}
//read number of mixtures
fscanf(file, "%s ", temp_char);
for( i = 0; i < total_states; i++ )
{
fscanf(file, "%d ", &num_mix[i] );
}
fscanf(file, "\n");
fscanf(file, "%s %d\n", temp_char, &m_vectSize);
m_hmm = cvCreate2DHMM( num_states, num_mix, m_vectSize);
//create HMM with known parameters
//!!!
cvCreate2DHMM( &m_hmm, num_states, num_mix, m_vectSize);
if (!m_hmm ) return false;
//consequently read all hmms
CvEHMM* hmm = m_hmm;
for( i = 0; i < num_states[0]+1; i++ )
{
fscanf(file, "%s\n", temp_char);
int temp_int;
fscanf(file, "%s %d\n", temp_char , &temp_int);
assert(temp_int==num_states[i]);
if ( i!= 0 )
{
for (int j = 0; j < num_states[i]; j++)
{
CvEHMMState* state = &(hmm->u.state[j]);
fscanf(file, "%s %d\n", temp_char, &temp_int); assert(temp_int
== j);
fscanf(file, "%s %d\n", temp_char, &temp_int); assert(temp_int
== state->num_mix);
float* mu = state->mu;
float* inv_var = state->inv_var;
for( int m = 0; m < state->num_mix; m++)
{
int temp_int;
fscanf(file, "%s %d %s %f\n", temp_char, &temp_int,
temp_char, &(state->weight[m]) );
assert( temp_int == m );
fscanf(file, "%s\n", temp_char );
for (int k = 0; k < m_vectSize; k++)
{
fscanf(file, "%f ", mu);

mu++;
}
fscanf(file, "\n");
fscanf(file, "%s\n", temp_char);
for (k = 0; k < m_vectSize; k++)
{
fscanf(file, "%f ", inv_var);
inv_var++;
}
fscanf(file, "\n");
>log_var_val[m]) );

fscanf(file, "%s %f\n", temp_char, &(state-

}
}

//read the transition probability matrix


fscanf(file, "%s\n", temp_char );
float* prob = hmm->transP;
for (int j = 0; j < hmm->num_states; j++)
{
for (int k = 0; k < hmm->num_states; k++)
{
fscanf(file, "%f ", prob);
prob++;
}
fscanf(file, "\n");
}
fscanf( file, "%s\n", temp_char );

hmm = &(m_hmm->u.ehmm[i]);
}
fclose(file);
return true;

Su disco le informazioni salvate possiedono la seguente forma.


<NumSuperStates> 5
<NumStates> 3 6 6 6
<NumMixtures> 3 3 3
<VecSize> 8
<BeginExternalHMM>
<NumStates> 5
<TransP>
-0.083382 -2.525729
-10000000000.000000
-10000000000.000000
-10000000000.000000
-10000000000.000000
0.000000
<EndExternalHMM>
<BeginEmbeddedHMM>
<NumStates> 3
<State> 0
<NumMixes> 3

3
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3

-10000000000.000000
-0.133531 -2.079442
-10000000000.000000
-10000000000.000000
-10000000000.000000

-10000000000.000000
-10000000000.000000
-0.133531 -2.079442
-10000000000.000000
-10000000000.000000

-10000000000.000000
-10000000000.000000
-10000000000.000000
-0.051293 -2.995732
-10000000000.000000

<Mixture> 0 <Weight> 0.769076


<Mean>
-20.972395 -1.199845 -9.647115 0.250141 -3.410903 0.394978 -3.123760 0.038134
<Inverted_Deviation>
0.014614 0.026245 0.022608 0.027866 0.058584 0.056817 0.068044 0.070711
<LogVarVal> 30.856710
<Mixture> 1 <Weight> 0.052209
<Mean>
473.255920 11.369661 18.953630 1.685655 -10.397700 1.066247 -1.562027 0.323794
<Inverted_Deviation>
0.007808 0.003862 0.070711 0.038979 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 31.478554
<Mixture> 2 <Weight> 0.178715
<Mean>
-212.370987 0.200874 -183.015442 4.668548 16.942772 -3.695751 15.826168 0.028298
<Inverted_Deviation>
0.011709 0.016380 0.012051 0.007986 0.017965 0.019467 0.020320 0.025447
<LogVarVal> 37.912323
<State> 1
<NumMixes> 3
<Mixture> 0 <Weight> 0.073511
<Mean>
-172.158371 -1.767324 -38.203041 4.343173 5.701460 0.570762 3.160259 0.587821
<Inverted_Deviation>
0.011865 0.014904 0.028598 0.030125 0.039158 0.070711 0.070711 0.070711
<LogVarVal> 31.463648
<Mixture> 1 <Weight> 0.122940
<Mean>
141.690094 -2.228673 9.012510 -2.404523 -2.345371 -0.013314 -3.915411 1.399009
<Inverted_Deviation>
0.013544 0.017109 0.021005 0.024648 0.035656 0.069890 0.070711 0.070711
<LogVarVal> 31.807859
<Mixture> 2 <Weight> 0.803549
<Mean>
-1.835994 -0.325710 -26.794296 0.163191 -0.147022 -2.228513 0.316842 -0.291369
<Inverted_Deviation>
0.023006 0.030401 0.026106 0.038078 0.063985 0.050297 0.070711 0.070711
<LogVarVal> 29.795156
<State> 2
<NumMixes> 3
<Mixture> 0 <Weight> 0.114630
<Mean>
187.055984 0.339832 -151.698074 -5.774115 12.813437 -3.688910 -12.469172
-1.845637
<Inverted_Deviation>
0.009355 0.015851 0.012498 0.008687 0.016760 0.025930 0.020683 0.024815
<LogVarVal> 37.839188
<Mixture> 1 <Weight> 0.864253
<Mean>
9.724429 1.652164 -2.467111 -0.952352 -1.476223 0.860395 1.329256 0.235927
<Inverted_Deviation>
0.020711 0.049239 0.022015 0.034982 0.067921 0.042441 0.053227 0.070711
<LogVarVal> 30.067411
<Mixture> 2 <Weight> 0.021116
<Mean>
-482.082367 181.330582 31.898191 -20.684919 -6.698741 0.991098 0.861548
-1.186828
<Inverted_Deviation>
0.006135 0.012554 0.070711 0.051096 0.031708 0.070711 0.070711 0.070711
<LogVarVal> 31.072191
<TransP>
-0.051505 -2.991724 -10000000000.000000

-10000000000.000000 -0.032199 -3.451890


-10000000000.000000 -10000000000.000000 0.000000
<EndEmbeddedHMM>
<BeginEmbeddedHMM>
<NumStates> 6
<State> 0
<NumMixes> 3
<Mixture> 0 <Weight> 0.061688
<Mean>
332.239349 -5.602865 -14.591848 25.645439 -13.951871 2.334095 -1.746691 0.068255
<Inverted_Deviation>
0.006121 0.004254 0.028941 0.021972 0.036400 0.070711 0.070711 0.070711
<LogVarVal> 33.755962
<Mixture> 1 <Weight> 0.847403
<Mean>
-22.062757 1.276612 -15.454859 -1.293295 -1.640206 -0.550484 -0.921772 -0.228833
<Inverted_Deviation>
0.016921 0.019725 0.031083 0.031985 0.051025 0.070711 0.070711 0.070711
<LogVarVal> 30.420504
<Mixture> 2 <Weight> 0.090909
<Mean>
-307.269745 -6.480935 -103.124588 -1.383479 24.288076 -4.534930 9.166031
2.136502
<Inverted_Deviation>
0.008850 0.007742 0.023042 0.011087 0.018224 0.070711 0.063117 0.042436
<LogVarVal> 35.016418
<State> 1
<NumMixes> 3
<Mixture> 0 <Weight> 0.200000
<Mean>
29.304270 -6.858905 -140.366104 4.895126 5.822843 -4.513440 -6.845486 1.737466
<Inverted_Deviation>
0.019348 0.034450 0.017851 0.027495 0.067659 0.018135 0.048407 0.070711
<LogVarVal> 31.892227
<Mixture> 1 <Weight> 0.757143
<Mean>
-8.627180 -3.913884 0.767934 1.317592 -1.155759 -5.062126 1.812659 -0.273500
<Inverted_Deviation>
0.024372 0.058368 0.027968 0.043595 0.070711 0.038213 0.070711 0.070711
<LogVarVal> 29.055836
<Mixture> 2 <Weight> 0.042857
<Mean>
224.008102 -40.747932 -22.446465 -15.536781 1.613000 -15.609318 -5.861628
1.234214
<Inverted_Deviation>
0.011552 0.029264 0.013270 0.021500 0.070711 0.055860 0.063269 0.070711
<LogVarVal> 31.676809
<State> 2
<NumMixes> 3
<Mixture> 0 <Weight> 0.657778
<Mean>
-16.048042 3.164254 -3.990868 -0.207382 0.337395 6.381185 -1.768695 -0.399991
<Inverted_Deviation>
0.024945 0.041299 0.011888 0.038374 0.067946 0.018147 0.070417 0.070711
<LogVarVal> 31.150278
<Mixture> 1 <Weight> 0.213333
<Mean>
-129.183838 -2.374153 58.049500 -3.579679 -2.807038 -12.895558 4.481920 0.560053
<Inverted_Deviation>
0.010968 0.021525 0.012028 0.029317 0.065543 0.026986 0.052524 0.070711
<LogVarVal> 32.813408
<Mixture> 2 <Weight> 0.128889

<Mean>
136.449188 15.072393 21.410343 0.304627 1.185207 1.440380 -1.137706 -0.825282
<Inverted_Deviation>
0.013488 0.012942 0.013954 0.020873 0.033767 0.026371 0.035187 0.069733
<LogVarVal> 34.407440
<State> 3
<NumMixes> 3
<Mixture> 0 <Weight> 0.348624
<Mean>
19.712467 3.993253 30.475801 13.512649 -4.126996 22.256044 -4.251338 -3.650669
<Inverted_Deviation>
0.023656 0.022237 0.016146 0.014870 0.050358 0.015594 0.036664 0.064872
<LogVarVal> 33.654343
<Mixture> 1 <Weight> 0.165138
<Mean>
128.506607 -7.856478 77.194824 -8.695423 -13.027184 -15.868305 -14.322035
4.716886
<Inverted_Deviation>
0.009385 0.022030 0.017512 0.022014 0.039319 0.040382 0.041146 0.070711
<LogVarVal> 33.208969
<Mixture> 2 <Weight> 0.486239
<Mean>
-41.485161 -2.108405 -60.229691 -0.742463 8.568585 2.393284 12.158751 1.010459
<Inverted_Deviation>
0.017285 0.032958 0.016136 0.020023 0.040785 0.023420 0.035068 0.060603
<LogVarVal> 33.194424
<State> 4
<NumMixes> 3
<Mixture> 0 <Weight> 0.901587
<Mean>
20.881762 -1.768637 -6.697111 -1.355091 -0.253182 -4.685781 -0.407999 -0.137556
<Inverted_Deviation>
0.027539 0.044917 0.022056 0.041149 0.070711 0.047551 0.070711 0.070711
<LogVarVal> 29.272205
<Mixture> 1 <Weight> 0.041270
<Mean>
200.368622 11.540556 45.782974 3.420283 -18.899872 4.935305 -8.701583 0.798857
<Inverted_Deviation>
0.012718 0.011904 0.028538 0.014262 0.033927 0.065217 0.070711 0.070711
<LogVarVal> 32.593105
<Mixture> 2 <Weight> 0.057143
<Mean>
-102.914154 2.887007 -68.612213 6.443975 14.543627 1.761935 9.705220 0.981170
<Inverted_Deviation>
0.015346 0.015776 0.024968 0.020148 0.044144 0.039683 0.060597 0.070711
<LogVarVal> 32.299801
<State> 5
<NumMixes> 3
<Mixture> 0 <Weight> 0.238411
<Mean>
215.408157 -2.994002 -67.357452 10.044118 17.727612 -3.746516 -5.952805 1.734724
<Inverted_Deviation>
0.011668 0.011538 0.018723 0.013939 0.020979 0.049652 0.048738 0.044163
<LogVarVal> 34.751118
<Mixture> 1 <Weight> 0.509934
<Mean>
-24.402103 17.768127 2.630338 -5.581841 0.374965 0.299918 0.793470 0.396344
<Inverted_Deviation>
0.009372 0.015522 0.061483 0.069743 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 29.463020
<Mixture> 2 <Weight> 0.251656
<Mean>

79.709373 9.802913 -22.161613 -8.858971 -14.081789 2.421615 2.427852 -1.545525


<Inverted_Deviation>
0.027740 0.017422 0.020816 0.020394 0.041865 0.043405 0.048802 0.070711
<LogVarVal> 31.958019
<TransP>
-0.053346 -2.957511 -10000000000.000000 -10000000000.000000 -10000000000.000000
-10000000000.000000
-10000000000.000000 -0.121361 -2.169054 -10000000000.000000 -10000000000.000000
-10000000000.000000
-10000000000.000000 -10000000000.000000 -0.073766 -2.643512 -10000000000.000000
-10000000000.000000
-10000000000.000000 -10000000000.000000 -10000000000.000000 -0.158748 -1.918759
-10000000000.000000
-10000000000.000000 -10000000000.000000 -10000000000.000000 -10000000000.000000
-0.052129 -2.979984
-10000000000.000000 -10000000000.000000 -10000000000.000000 -10000000000.000000
-10000000000.000000 0.000000
<EndEmbeddedHMM>
<BeginEmbeddedHMM>
<NumStates> 6
<State> 0
<NumMixes> 3
<Mixture> 0 <Weight> 0.024561
<Mean>
190.812363 -2.622171 13.340729 -1.307912 3.414199 0.007860 1.341246 -0.745841
<Inverted_Deviation>
0.010704 0.009503 0.032570 0.070711 0.065904 0.070711 0.070711 0.070711
<LogVarVal> 30.512737
<Mixture> 1 <Weight> 0.838597
<Mean>
-28.437531 2.835221 4.740308 -0.554232 -0.054382 -0.092343 -0.135304 0.087556
<Inverted_Deviation>
0.016608 0.028242 0.042271 0.063736 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 28.757013
<Mixture> 2 <Weight> 0.136842
<Mean>
-289.311829 -8.882969 33.374794 -11.372365 -1.015401 3.743531 -1.616095 0.227328
<Inverted_Deviation>
0.006962 0.007311 0.010206 0.022349 0.034598 0.032280 0.063989 0.070711
<LogVarVal> 35.045723
<State> 1
<NumMixes> 3
<Mixture> 0 <Weight> 0.033019
<Mean>
96.899635 -14.256236 42.901783 -11.595068 9.928827 -4.615505 -12.232879 3.404555
<Inverted_Deviation>
0.024551 0.014728 0.012893 0.038507 0.043853 0.034159 0.070711 0.070711
<LogVarVal> 31.913870
<Mixture> 1 <Weight> 0.759434
<Mean>
-22.718081 -2.177552 22.436535 0.733987 0.292511 -0.798927 -0.138575 -0.455182
<Inverted_Deviation>
0.023305 0.049659 0.020533 0.039577 0.070711 0.038835 0.070711 0.070711
<LogVarVal> 29.651739
<Mixture> 2 <Weight> 0.207547
<Mean>
-51.747776 -1.974753 183.901276 4.065125 -1.189533 -33.964375 6.775185 0.415048
<Inverted_Deviation>
0.016178 0.031040 0.015782 0.031070 0.066429 0.017635 0.057697 0.070711
<LogVarVal> 32.047115
<State> 2
<NumMixes> 3

<Mixture> 0 <Weight> 0.368984


<Mean>
-10.272039 11.030415 17.832426 2.757569 0.699663 -5.451602 0.950643 -1.928197
<Inverted_Deviation>
0.024173 0.016458 0.025588 0.032414 0.064883 0.068372 0.070711 0.070711
<LogVarVal> 30.219448
<Mixture> 1 <Weight> 0.139037
<Mean>
-119.390373 -16.832819 19.765537 -2.150642 6.756366 -5.849945 4.416418 2.809105
<Inverted_Deviation>
0.027897 0.013168 0.015439 0.037351 0.035009 0.049909 0.070711 0.070711
<LogVarVal> 31.594343
<Mixture> 2 <Weight> 0.491979
<Mean>
67.445679 -8.697609 40.084557 -11.676978 0.023525 -7.867779 0.510914 1.323853
<Inverted_Deviation>
0.014756 0.018779 0.019235 0.029185 0.042744 0.049894 0.066536 0.070711
<LogVarVal> 31.764666
<State> 3
<NumMixes> 3
<Mixture> 0 <Weight> 0.329412
<Mean>
-131.007034 1.681211 -57.504356 4.121988 28.122553 -0.132149 12.960434 4.076024
<Inverted_Deviation>
0.012016 0.012977 0.021044 0.014756 0.031071 0.049917 0.046628 0.063894
<LogVarVal> 33.707184
<Mixture> 1 <Weight> 0.358824
<Mean>
16.625938 16.799940 4.682932 23.613672 -2.290540 7.557913 -3.112319 -8.199980
<Inverted_Deviation>
0.020510 0.011215 0.021219 0.012174 0.031079 0.028879 0.033276 0.056163
<LogVarVal> 34.515877
<Mixture> 2 <Weight> 0.311765
<Mean>
166.596619 0.482895 61.800034 4.413245 -28.290314 0.981202 -13.898478 4.249887
<Inverted_Deviation>
0.011246 0.011284 0.015878 0.013644 0.030735 0.034372 0.040312 0.051504
<LogVarVal> 35.018291
<State> 4
<NumMixes> 3
<Mixture> 0 <Weight> 0.941634
<Mean>
34.391529 -1.193665 0.464204 -0.863615 -0.116536 -0.789280 -0.261378 -0.272957
<Inverted_Deviation>
0.050785 0.070711 0.050196 0.070711 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 26.445854
<Mixture> 1 <Weight> 0.031128
<Mean>
-91.545151 -49.904755 -40.684425 -39.325836 -10.901596 -9.738219 -4.026871
4.591399
<Inverted_Deviation>
0.019245 0.070711 0.042390 0.064079 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 27.683689
<Mixture> 2 <Weight> 0.027237
<Mean>
-4.087406 -16.898485 7.170161 -8.011189 -9.495887 -4.003296 0.647717 -0.724366
<Inverted_Deviation>
0.037639 0.058116 0.063505 0.070711 0.070711 0.065537 0.070711 0.070711
<LogVarVal> 26.782356
<State> 5
<NumMixes> 3
<Mixture> 0 <Weight> 0.248175

<Mean>
193.838730 -0.841311 -19.025002 -1.742880 2.352849 -0.728297 -8.807205 1.636138
<Inverted_Deviation>
0.016424 0.012148 0.010296 0.017934 0.026932 0.027708 0.038896 0.040834
<LogVarVal> 35.341076
<Mixture> 1 <Weight> 0.613139
<Mean>
12.877467 3.272102 2.626410 -0.844084 -1.496272 1.243824 2.790946 0.615524
<Inverted_Deviation>
0.034123 0.047279 0.047078 0.044948 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 27.763226
<Mixture> 2 <Weight> 0.138686
<Mean>
105.450005 6.511856 55.032810 4.647384 2.288129 -3.770526 1.748496 -4.205755
<Inverted_Deviation>
0.024094 0.016110 0.013488 0.014875 0.029750 0.029896 0.032855 0.048521
<LogVarVal> 34.413460
<TransP>
-0.057778 -2.879900 -10000000000.000000 -10000000000.000000 -10000000000.000000
-10000000000.000000
-10000000000.000000 -0.078472 -2.583997 -10000000000.000000 -10000000000.000000
-10000000000.000000
-10000000000.000000 -10000000000.000000 -0.089445 -2.458520 -10000000000.000000
-10000000000.000000
-10000000000.000000 -10000000000.000000 -10000000000.000000 -0.098846 -2.363210
-10000000000.000000
-10000000000.000000 -10000000000.000000 -10000000000.000000 -10000000000.000000
-0.064279 -2.776487
-10000000000.000000 -10000000000.000000 -10000000000.000000 -10000000000.000000
-10000000000.000000 0.000000
<EndEmbeddedHMM>
<BeginEmbeddedHMM>
<NumStates> 6
<State> 0
<NumMixes> 3
<Mixture> 0 <Weight> 0.079399
<Mean>
281.515320 5.062520 23.383995 -6.769423 -1.797396 -2.218050 -1.719421 0.938018
<Inverted_Deviation>
0.007033 0.005905 0.028191 0.037544 0.044815 0.068372 0.070711 0.070711
<LogVarVal> 32.605255
<Mixture> 1 <Weight> 0.831545
<Mean>
-9.736834 -1.911342 9.898236 0.245111 1.282359 -2.437490 -0.494635 -0.159903
<Inverted_Deviation>
0.016225 0.026268 0.026959 0.042149 0.070711 0.058875 0.070711 0.070711
<LogVarVal> 29.899328
<Mixture> 2 <Weight> 0.089056
<Mean>
-234.735886 2.390812 75.920830 5.356786 -11.480034 -6.676812 9.000673 0.603900
<Inverted_Deviation>
0.010240 0.011519 0.007933 0.010402 0.017930 0.036167 0.031687 0.033247
<LogVarVal> 37.223103
<State> 1
<NumMixes> 3
<Mixture> 0 <Weight> 0.068063
<Mean>
73.068626 4.208974 -108.522934 34.903324 7.557928 -18.668013 -4.114515 0.012024
<Inverted_Deviation>
0.006929 0.016683 0.015159 0.028884 0.037623 0.026465 0.039746 0.070711
<LogVarVal> 34.164383
<Mixture> 1 <Weight> 0.518325

<Mean>
-38.629051 5.369726 29.472923 7.114058 -0.945012 -12.123492 3.357934 -0.145340
<Inverted_Deviation>
0.009075 0.030282 0.013002 0.029454 0.065644 0.030550 0.070711 0.070711
<LogVarVal> 32.156143
<Mixture> 2 <Weight> 0.413613
<Mean>
134.908218 0.393828 113.515602 2.343509 -4.886399 -12.961477 -4.844072 -0.264280
<Inverted_Deviation>
0.007246 0.014630 0.011153 0.015511 0.031214 0.021559 0.046464 0.070711
<LogVarVal> 35.415352
<State> 2
<NumMixes> 3
<Mixture> 0 <Weight> 0.415414
<Mean>
-46.442204 2.625515 -39.037647 0.741800 0.109419 20.635923 1.452390 -0.398463
<Inverted_Deviation>
0.007425 0.019306 0.015365 0.019814 0.060124 0.012820 0.064612 0.070711
<LogVarVal> 34.082813
<Mixture> 1 <Weight> 0.204887
<Mean>
-25.423428 -1.808578 216.439011 -3.891537 -3.458281 27.054890 7.209569 -0.137808
<Inverted_Deviation>
0.015531 0.043220 0.010858 0.020908 0.060988 0.012498 0.039046 0.070711
<LogVarVal> 33.347191
<Mixture> 2 <Weight> 0.379699
<Mean>
28.790150 -4.355493 -174.831299 0.902964 3.188112 -11.479298 -6.226968 0.909172
<Inverted_Deviation>
0.010894 0.025712 0.012667 0.025213 0.062370 0.014423 0.047051 0.070711
<LogVarVal> 33.527699
<State> 3
<NumMixes> 3
<Mixture> 0 <Weight> 0.304348
<Mean>
86.820747 5.144733 -100.133652 -20.788786 13.970844 -1.287977 -22.528519
-0.661565
<Inverted_Deviation>
0.014922 0.025611 0.009903 0.013569 0.032377 0.014940 0.024406 0.039824
<LogVarVal> 35.933765
<Mixture> 1 <Weight> 0.462451
<Mean>
-37.885178 5.131439 33.009090 -11.834466 -1.668376 11.706657 8.278119 -1.887173
<Inverted_Deviation>
0.009802 0.020549 0.017517 0.012941 0.033183 0.015504 0.029852 0.047472
<LogVarVal> 35.612408
<Mixture> 2 <Weight> 0.233202
<Mean>
-55.076748 -2.452983 151.571808 3.877711 -13.306914 -3.478388 14.378208 2.829310
<Inverted_Deviation>
0.008172 0.019092 0.015081 0.013413 0.026307 0.017964 0.027104 0.051203
<LogVarVal> 36.087620
<State> 4
<NumMixes> 3
<Mixture> 0 <Weight> 0.529158
<Mean>
1.878654 -0.733471 33.537338 1.262277 -0.333027 7.590052 -0.060252 -1.090263
<Inverted_Deviation>
0.023968 0.055641 0.018797 0.023763 0.070711 0.017156 0.051693 0.070711
<LogVarVal> 31.238581
<Mixture> 1 <Weight> 0.276458
<Mean>

-21.736927 -2.542204 178.507385 4.930440 -3.906276 -7.275815 7.139390 1.955986


<Inverted_Deviation>
0.019527 0.070711 0.015019 0.023224 0.070711 0.014298 0.033285 0.070711
<LogVarVal> 32.073654
<Mixture> 2 <Weight> 0.194384
<Mean>
41.534611 0.298131 -100.676323 -7.546008 7.317516 13.963810 -7.074047 1.352842
<Inverted_Deviation>
0.020142 0.061396 0.014286 0.019261 0.051039 0.015684 0.029240 0.070711
<LogVarVal> 32.784084
<State> 5
<NumMixes> 3
<Mixture> 0 <Weight> 0.293725
<Mean>
65.476868 -4.344866 16.173809 1.581775 2.378810 -2.788371 -1.462082 1.073274
<Inverted_Deviation>
0.024989 0.019618 0.022597 0.028031 0.039386 0.055366 0.065933 0.070711
<LogVarVal> 31.060362
<Mixture> 1 <Weight> 0.626168
<Mean>
2.023335 1.580380 11.692266 0.814330 0.784113 -1.665624 1.021921 -0.508516
<Inverted_Deviation>
0.029082 0.035495 0.031096 0.042873 0.062136 0.070711 0.070711 0.070711
<LogVarVal> 28.801016
<Mixture> 2 <Weight> 0.080107
<Mean>
194.629745 3.963917 49.733593 7.044076 -14.174562 -5.307319 -8.042933 -0.596228
<Inverted_Deviation>
0.019450 0.010448 0.012431 0.018382 0.026960 0.053558 0.061205 0.059666
<LogVarVal> 33.617088
<TransP>
-0.043867 -3.148453 -10000000000.000000 -10000000000.000000 -10000000000.000000
-10000000000.000000
-10000000000.000000 -0.234994 -1.563394 -10000000000.000000 -10000000000.000000
-10000000000.000000
-10000000000.000000 -10000000000.000000 -0.078165 -2.587764 -10000000000.000000
-10000000000.000000
-10000000000.000000 -10000000000.000000 -10000000000.000000 -0.172097 -1.844510
-10000000000.000000
-10000000000.000000 -10000000000.000000 -10000000000.000000 -10000000000.000000
-0.090355 -2.448848
-10000000000.000000 -10000000000.000000 -10000000000.000000 -10000000000.000000
-10000000000.000000 0.000000
<EndEmbeddedHMM>
<BeginEmbeddedHMM>
<NumStates> 3
<State> 0
<NumMixes> 3
<Mixture> 0 <Weight> 0.764505
<Mean>
4.214235 -0.590245 -18.314932 -2.061201 1.134269 20.565706 -0.863436 0.251514
<Inverted_Deviation>
0.014439 0.022890 0.006650 0.021375 0.039824 0.013106 0.052829 0.070711
<LogVarVal> 34.600323
<Mixture> 1 <Weight> 0.075085
<Mean>
492.951630 -30.950453 -10.584294 2.464219 5.519246 -0.124828 -0.360903 -0.114343
<Inverted_Deviation>
0.015467 0.003779 0.070711 0.070711 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 30.221079
<Mixture> 2 <Weight> 0.160410
<Mean>

-135.749176 -2.787473 154.343948 -4.639666 -8.062235 3.051846 10.178977


-0.126230
<Inverted_Deviation>
0.012060 0.022278 0.008706 0.009491 0.022054 0.014707 0.019911 0.030878
<LogVarVal> 37.630077
<State> 1
<NumMixes> 3
<Mixture> 0 <Weight> 0.141401
<Mean>
-145.575256 -0.853109 43.739334 -3.194839 0.044414 -2.246252 4.633504 0.160951
<Inverted_Deviation>
0.012365 0.014624 0.008737 0.018236 0.038915 0.030116 0.035987 0.058581
<LogVarVal> 34.852570
<Mixture> 1 <Weight> 0.793631
<Mean>
4.161003 0.191693 24.924175 0.116371 0.253912 -0.735707 -0.397659 -0.206465
<Inverted_Deviation>
0.027827 0.031615 0.034241 0.052863 0.070711 0.070711 0.070711 0.070711
<LogVarVal> 28.525803
<Mixture> 2 <Weight> 0.064968
<Mean>
156.611725 10.981389 26.052437 4.576946 -3.795823 -1.734974 -1.815239 -0.578430
<Inverted_Deviation>
0.017771 0.011419 0.028703 0.050400 0.060905 0.070711 0.070711 0.070711
<LogVarVal> 30.366079
<State> 2
<NumMixes> 3
<Mixture> 0 <Weight> 0.084158
<Mean>
-18.079115 -3.193123 151.020554 -13.808769 -1.519674 5.422098 17.030935 2.441987
<Inverted_Deviation>
0.021982 0.041304 0.014502 0.016757 0.042436 0.014949 0.044584 0.058525
<LogVarVal> 33.217239
<Mixture> 1 <Weight> 0.193069
<Mean>
105.268265 1.056072 95.867630 6.613261 -8.230984 -4.027469 -9.223823 -1.890299
<Inverted_Deviation>
0.016956 0.025554 0.012963 0.014080 0.029512 0.020748 0.027063 0.040854
<LogVarVal> 35.137329
<Mixture> 2 <Weight> 0.722772
<Mean>
10.478083 2.190120 -32.570210 1.983625 2.397516 8.352318 -1.068018 0.576043
<Inverted_Deviation>
0.021509 0.058677 0.011861 0.032621 0.070711 0.019348 0.070711 0.070711
<LogVarVal> 31.003817
<TransP>
-0.067045 -2.735734 -10000000000.000000
-10000000000.000000 -0.024502 -3.721245
-10000000000.000000 -10000000000.000000 0.000000
<EndEmbeddedHMM>

La creazione di una HMM viene eseguita dal membro della classe


CContHMM.
bool CContEHMM::CreateHMM( int* num_states, int* num_mix, int vect_size )
{
if (m_hmm) cvRelease2DHMM( &m_hmm );
m_hmm = 0;
m_hmm = cvCreate2DHMM( num_states, num_mix, vect_size );
m_vectSize = vect_size;

return true;
//else return false;
}

Viene utilizzata la funzione di OpenCV :


Create2DHMM
Creates 2D embedded HMM
CvEHMM* cvCreate2DHMM( int* stateNumber, int* numMix, int
obsSize );
stateNumber
Array, the first element of the which specifies the number of
superstates in the HMM. All subsequent elements specify the
number of states in every embedded HMM, corresponding to each
superstate. So, the length of the array is stateNumber
[0]+1 .
numMix
Array with numbers of Gaussian mixture components per each
internal state. The number of elements in the array is equal
to number of internal states in the HMM, that is, superstates
are not counted here.
obsSize
Size of observation vectors to be used with created HMM.
Il progetto di base da cui derivato il sistema di riconoscimento
costituito dai seguenti files.
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
28/09/2003
21/04/2004
27/09/2003
27/09/2003
27/09/2003
27/09/2003
28/09/2003
22/01/2004
24/09/2003
28/09/2003
27/09/2003

08.34
08.32
08.32
08.32
09.39
08.32
09.35
09.27
20.22
08.08
08.15
08.12
15.57
12.44
12.50
23.39
23.29
21.36
10.25
20.40
09.38
18.56

9.517
899
1.503
597
8.466
1.631
8.871
1.638
5.181
1.045
5.805
1.238
43
723
386
3.180
802
13.874
117.760
919
4.635
1.007

EHMM.cpp
EHMM.h
EHMMObj.cpp
EHMMObj.h
EHMMObjDatabase.cpp
EHMMObjDatabase.h
EHMMObjRecognition.cpp
EHMMObjRecognition.h
ImgObj.cpp
ImgObj.h
ImgObjDatabase.cpp
ImgObjDatabase.h
lista.txt
Obj.cpp
Obj.h
RecognizeEHMM.cpp
RecognizeEHMM.h
RetinaRecognition.cpp
RetinaRecognition.ncb
RetinaRecognition.sln
RetinaRecognition.vcproj
Utils.cpp

27/09/2003

18.57

504 Utils.h

Il cuore in cui sono gestiti gli HMM descritto nei files che
possiedono EHMM nella parte iniziale.
E sta per EMBEDDED.
Il primo gruppo di files contiene le funzioni adatte alla lettura
e al salvataggio dei parametri visti nelle pagine precedenti.
#ifndef _EHMM_H_
#define _EHMM_H_
//Disable OpenCv type cast warning
#pragma warning( disable : 4312 )
#include <string>
#include "cv.h"
#include "ipl.h"
#pragma warning( default : 4312 )
class EHMM
{
public:
EHMM( );
virtual ~EHMM( );
void Create( int *noStates, int *noMix, int vecSize );
void Load( const std::string &fileName );
void Save( const std::string &fileName );
void Release( );
bool GetTrained( );
void SetTrained( bool trained );
void GetNoStates( int *noStates ) const;
void GetNoMix( int *noMix ) const;
int GetVecSize( ) const;
CvEHMM* GetCvEHMM( );
private:
//Hidden markov model
CvEHMM* _ehmm;
//Size of the vector that is used as input
int _vecSize;

};

//If the ehmm is trained or not


bool _trained;

#endif //_EHMM_H
#include "EHMM.h"
#include <stdio.h>
//*****************************************************************************
EHMM::EHMM( )
{
_ehmm = 0;
_vecSize = 0;
_trained = false;
}
//*****************************************************************************
EHMM::~EHMM( )
{
Release( );
}
//*****************************************************************************
void EHMM::Release( )
{
if ( _ehmm )
{
cvRelease2DHMM( &_ehmm );
}

_vecSize = 0;
_trained = false;

//*****************************************************************************
void EHMM::Create( int *noStates, int *noMix, int vecSize )
{
assert( _ehmm == 0 );
_ehmm = cvCreate2DHMM( noStates, noMix, vecSize );
assert( _ehmm != 0 );
_vecSize = vecSize;
_trained = false;
}
//*****************************************************************************
void EHMM::Load( const std::string &fileName )
{
const int MAX_STATES = 128;
FILE *file;
int states[ MAX_STATES ];
int gaussMix[ MAX_STATES ];
char tmpChar[ MAX_STATES ];
assert( _ehmm == 0 );

file = fopen( fileName.c_str( ), "rt" );


assert( file != 0 );
//Read topology
fscanf( file, "%s %d\n", tmpChar, states );
fscanf( file, "%s ", tmpChar );
for( int i = 0; i < states[ 0 ]; i++ )
{
fscanf( file, "%d ", states + i + 1 );
}
fscanf( file, "\n" );
//Compute total number of internal states
int total_states = 0;
for( i = 0; i < states[ 0 ]; i++ )
{
total_states += states[ i+1 ];
}
//Read number of mixtures
fscanf( file, "%s ", tmpChar );
for( i = 0; i < total_states; i++ )
{
fscanf( file, "%d ", &gaussMix[ i ] );
}
fscanf( file, "\n" );
fscanf( file, "%s %d\n", tmpChar, &_vecSize );
_ehmm = cvCreate2DHMM( states, gaussMix, _vecSize );
//Create HMM with known parameters
assert( _ehmm != 0 );
//Consequently read all hmms
CvEHMM* hmm = _ehmm;
for( i = 0; i < states[ 0 ]+1; i++ )
{
fscanf( file, "%s\n", tmpChar );
int temp_int;
fscanf( file, "%s %d\n", tmpChar , &temp_int );
assert( temp_int == states[ i ] );
if ( i!= 0 )
{
for ( int j = 0; j < states[ i ]; j++)
{
CvEHMMState* state = &( hmm->u.state[ j ] );
fscanf( file, "%s %d\n", tmpChar, &temp_int ); assert( temp_int
== j );

fscanf( file, "%s %d\n", tmpChar, &temp_int ); assert( temp_int


== state->num_mix );
float* mu = state->mu;
float* inv_var = state->inv_var;
for( int m = 0; m < state->num_mix; m++)
{
int temp_int;
fscanf( file, "%s %d %s %f\n", tmpChar, &temp_int,
tmpChar, &( state->weight[ m ]) );
assert( temp_int == m );
fscanf( file, "%s\n", tmpChar );
for ( int k = 0; k < _vecSize; k++)
{
fscanf( file, "%f ", mu );
mu++;
}
fscanf( file, "\n" );
fscanf( file, "%s\n", tmpChar );
for ( k = 0; k < _vecSize; k++)
{
fscanf( file, "%f ", inv_var );
inv_var++;
}
fscanf( file, "\n" );
fscanf( file, "%s %f\n", tmpChar, &( state>log_var_val[ m ]) );
}
}
}
//Read the transition probability matrix
fscanf( file, "%s\n", tmpChar );
float* prob = hmm->transP;
for ( int j = 0; j < hmm->num_states; j++)
{
for ( int k = 0; k < hmm->num_states; k++)
{
fscanf( file, "%f ", prob );
prob++;
}
fscanf( file, "\n" );
}
fscanf( file, "%s\n", tmpChar );
hmm = &( _ehmm->u.ehmm[ i ] );

}
fclose( file );
_trained = true;
}
//*****************************************************************************
void EHMM::Save( const std::string &fileName )
{
FILE* file;
int i;
int j;
int m;
int k;
assert( _ehmm != 0 );
file = fopen( fileName.c_str( ), "wt" );
assert( file != 0 );
//Write topology
fprintf( file, "%s %d\n", "<NumSuperStates>", _ehmm->num_states );
fprintf( file, "%s ", "<NumStates>" );
for( i = 0; i < _ehmm->num_states; i++ )
{
fprintf( file, "%d ", _ehmm->u.ehmm[ i ].num_states );
}
fprintf( file, "\n" );
fprintf( file, "%s ", "<NumMixtures>" );
for( i = 0; i < _ehmm->num_states; i++ )
{

CvEHMM* ehmm = &( _ehmm->u.ehmm[ i ] );


for( int j = 0; j < ehmm->num_states; j++ )
{
fprintf( file, "%d ", ehmm->u.state[ j ].num_mix );
}

}
fprintf( file, "\n" );

fprintf( file, "%s %d\n", "<VecSize>", _vecSize );


//Consequently write all hmms
CvEHMM* hmm = _ehmm;
{

for( i = 0; i < _ehmm->num_states + 1; i++ )


if ( hmm->level == 0 )
{
fprintf( file, "%s\n", "<BeginEmbeddedHMM>" );
}
else

{
fprintf( file, "%s\n", "<BeginExternalHMM>" );
}
fprintf( file, "%s %d\n", "<NumStates>", hmm->num_states );
if ( hmm->level == 0 )
{
for ( j = 0; j < hmm->num_states; j++)
{
CvEHMMState* state = &( hmm->u.state[ j ] );
fprintf( file, "%s %d\n", "<State>", j );
fprintf( file, "%s %d\n", "<NumMixes>", state->num_mix );
float* mu = state->mu;
float* inv_var = state->inv_var;
for( m = 0; m < state->num_mix; m++)
{
fprintf( file, "%s %d %s %f\n", "<Mixture>", m, "<Weight>",
state->weight[ m ] );
fprintf( file, "%s\n", "<Mean>" );
for ( k = 0; k < _vecSize; k++)
{
fprintf( file, "%f ", mu[ 0 ] );
mu++;
}
fprintf( file, "\n" );
fprintf( file, "%s\n", "<Inverted_Deviation>" );
for ( k = 0; k < _vecSize; k++)
{
fprintf( file, "%f ", inv_var[ 0 ] );
inv_var++;
}
fprintf( file, "\n" );
fprintf( file, "%s %f\n", "<LogVarVal>", state->log_var_val[
m ] );

}
}

//Write the transition probability matrix


fprintf( file, "%s\n", "<TransP>" );
float* prob = hmm->transP;
for ( int j = 0; j < hmm->num_states; j++)
{
for ( int k = 0; k < hmm->num_states; k++)
{
fprintf( file, "%f ", *prob );
prob++;
}
fprintf( file, "\n" );

}
if( hmm->level == 0 )
{
fprintf( file, "%s\n", "<EndEmbeddedHMM>" );
}
else
{
fprintf( file, "%s\n", "<EndExternalHMM>" );
}
}

hmm = &( _ehmm->u.ehmm[ i ] );

fclose( file );
}
//*****************************************************************************
bool EHMM::GetTrained( )
{
assert( _ehmm != 0 );
}

return _trained;

//*****************************************************************************
void EHMM::SetTrained( bool trained )
{
assert( _ehmm != 0 );
_trained = trained;
}
//*****************************************************************************
/*
*
Fill in the noStates vector with the number of states
*/
void EHMM::GetNoStates( int *noStates ) const
{
int i;
assert( _ehmm != 0 );
noStates[ 0 ] = _ehmm->num_states;

for( i = 0; i < _ehmm->num_states; i++ )


{
noStates[ i + 1 ] = _ehmm->u.ehmm[ i ].num_states;
}

//*****************************************************************************
/*
*
Fill in the noMix vector with the number of mixtures per state
*/
void EHMM::GetNoMix( int *noMix ) const
{
int i;

int j;
int c;
assert( _ehmm != 0 );
{

for( c = 0, i = 0; i < _ehmm->num_states; i++ )


CvEHMM* ehmm = &( _ehmm->u.ehmm[ i ] );
for( j = 0; j < ehmm->num_states; j++ )
{
noMix[ c++ ] = ehmm->u.state[ j ].num_mix;
}

//*****************************************************************************
int EHMM::GetVecSize( ) const
{
assert( _ehmm != 0 );
return _vecSize;
}
//*****************************************************************************
CvEHMM* EHMM::GetCvEHMM( )
{
assert( _ehmm != 0 );
}

return _ehmm;

Il secondo gruppo di files permette di creare degli oggetti relativi a dei


sistemi HMM.
Si tratta del riporto in formato OBJECT ORIENTED degli HMM.
/*
/*
/*
*/

Description: Implements a simple description


of an EHMMObj that is detected using EHMM

#ifndef _EHMMObj_H_
#define _EHMMObj_H_
#include "Obj.h"
#include "EHMM.h"
class EHMMObj : public Obj
{
public:
EHMMObj( );
virtual ~EHMMObj( );
void Create( const std::string &userName, int *noStates, int *noMix, int
vecSize );
void Load( const std::string &userName, const std::string &path );

void Save( const std::string &path );


EHMM& GetEHMM( );
private:

};

//Hidden markov model associated to the object


EHMM _ehmm;

#endif //_EHMMObj_H_
#include "EHMMObj.h"
using std::string;
//Standard extension to the user file
static const string EXTENSION = ".ehmm";
//*****************************************************************************
EHMMObj::EHMMObj( )
{
}
//*****************************************************************************
EHMMObj::~EHMMObj( )
{
}
//*****************************************************************************
void EHMMObj::Create( const string &userName, int *noStates,
int *noMix, int vecSize )
{
assert( _name == "" );
Obj::Create( userName );

//Create the ehmm for the object


_ehmm.Create( noStates, noMix, vecSize );

//*****************************************************************************
void EHMMObj::Load( const string &userName, const string &path )
{
const string EHMM_FILE = path + userName + EXTENSION;
assert( _name == "" );
Obj::Create( userName );

//Load the ehmm for each of the users


_ehmm.Load( EHMM_FILE );

//****************************************************************
void EHMMObj::Save( const string &path )
{

const string EHMM_FILE = path + _name + EXTENSION;


assert( _name != "" );

//Save the ehmm for the object


_ehmm.Save( EHMM_FILE );

//****************************************************************
EHMM& EHMMObj::GetEHMM( )
{
return _ehmm;
}
//****************************************************************

I seguenti files implementano un semplice database per la gestione


del riconoscimento
/*
/*
/*
*/

Description: Implements a simple database used


for the ehmm retina detection algorithm

#ifndef _EHMMObjDatabase_H_
#define _EHMMObjDatabase_H_
#include "EHMMObj.h"
#include <vector>
#include <iostream>
//Exception specification warning
#pragma warning( disable : 4290 )
class EHMMObjDatabase
{
private:
enum{ MAX_STATES = 128 };
public:
EHMMObjDatabase( );
virtual ~EHMMObjDatabase( );
void Create( const std::string &databaseName, int *noStates,
int *noMix, int vecSize );
void Load( const std::string &databaseName, const std::string &path );
void Save( const std::string &path );
void Release( );
void LoadObj( const std::string &userName, const std::string &path );
void AddObj( const std::string &userName );

void RemoveObj( size_t index );


void RemoveObj( const std::string &userName );
EHMMObj& GetObj( size_t index );
EHMMObj& GetObj( const std::string &userName ) throw( exception );
size_t GetNoObjs( ) const;
friend std::ostream& operator<<( std::ostream &os, EHMMObjDatabase &obj );
private:
void CheckEHMMConsistence( EHMMObj &ehmmObj );
//Stores all the users with their
//associated properties
std::vector< EHMMObj* > _objs;
//Total number of states/substates in the ehmm
int _numStates[ MAX_STATES ];
//Number of gaussian mixtures per state
int _numMix[ MAX_STATES - 1 ];
//Size of the input vector
int _vecSize;

};

//Name of the database file


std::string _databaseName;

std::ostream& operator<<( std::ostream &os, EHMMObjDatabase &obj );


#endif //_EHMMObjDatabase_H_
#include
#include
#include
#include
using
using
using
using
using
using
using
using
using

"EHMMObjDatabase.h"
<fstream>
<iostream>
<assert.h>

std::vector;
std::ifstream;
std::ofstream;
std::iostream;
std::ostream;
std::string;
std::cout;
std::endl;
std::cerr;

//*****************************************************************************
EHMMObjDatabase::EHMMObjDatabase( )
{
_databaseName = "";
memset( _numStates, 0, sizeof( _numStates ) );
memset( _numMix, 0, sizeof( _numMix ) );
_vecSize = 0;

}
//*****************************************************************************
EHMMObjDatabase::~EHMMObjDatabase( )
{
Release( );
}
//*****************************************************************************
/*
*
Create an empty data base
*/
void EHMMObjDatabase::Create( const string &databaseName, int *noStates,
int *noMix, int vecSize )
{
int i;
int totalNoInternalStates;
assert(
assert(
assert(
assert(

noStates != 0 );
noMix != 0 );
vecSize != 0 );
databaseName != "" );

//Save the name of the database


_databaseName = databaseName;
//Save the ehmm creation parameters
memcpy( _numStates, noStates, sizeof( int ) * ( noStates[ 0 ] + 1 ) );
//Count the number of internal states
for ( totalNoInternalStates = 0, i = 1; i < noStates[ 0 ] + 1; i++ )
{
totalNoInternalStates += noStates[ i ];
}
memcpy( _numMix, noMix, sizeof( int ) * totalNoInternalStates );
_vecSize = vecSize;
}
//*****************************************************************************
/*
*
Load a database from disk
*/
void EHMMObjDatabase::Load( const string &databaseName,
const string &path )
{
const string DATABASE_PATH = path + databaseName;
const int MAX_LINE_WIDTH = 255;
const int INITIAL_SIZE = 1000;
ifstream file( DATABASE_PATH.c_str( ) );
vector< EHMMObj* >::iterator objsIt;
char line[ MAX_LINE_WIDTH ];
assert( file.is_open( ) );
//Save the name of the database

_databaseName = databaseName;
_objs.reserve( INITIAL_SIZE );
while ( file.getline( line, sizeof( line ) ) )
{
_objs.push_back( new EHMMObj( ) );
try
{

_objs[ _objs.size( ) - 1 ]->Load( line, path );


}
catch( ... )
{
if ( _objs[ _objs.size( ) - 1 ] )
{
delete _objs[ _objs.size( ) - 1 ];
_objs[ _objs.size( ) - 1 ] = 0;
}
_objs.pop_back( );
cerr << "Exception occured when creating " << line << "." <<
endl;

}
}
//Save the ehmm creation parameters
if ( _objs.size( ) == 0 )
{
cerr << "Database is emtpy. Please use 'Create' before "
"adding new users" << endl;
}
else
{
_objs[ 0 ]->GetEHMM( ).GetNoStates( _numStates );
_objs[ 0 ]->GetEHMM( ).GetNoMix( _numMix );
_vecSize = _objs[ 0 ]->GetEHMM( ).GetVecSize( );
}
//Check to see if the db is consistent
for ( objsIt = _objs.begin( ); objsIt < _objs.end( ); objsIt++ )
{
CheckEHMMConsistence( *(*objsIt) );
}

}
//*****************************************************************************
/*
*
Save the database to disk. It will overwrite the previous data
*/
void EHMMObjDatabase::Save( const string &path )
{
const string DATABASE_PATH = path + _databaseName;
vector< EHMMObj* >::iterator objsIt;
ofstream file( DATABASE_PATH.c_str( ) );
assert( _databaseName != "" );
assert( file.is_open( ) );

for ( objsIt = _objs.begin( ); objsIt < _objs.end( ); objsIt++ )


{
file << (*objsIt)->GetName( ) << endl;
(*objsIt)->Save( path );
}
}
//*****************************************************************************
void EHMMObjDatabase::Release( )
{
vector< EHMMObj* >::iterator objsIt;
for ( objsIt = _objs.begin( ); objsIt < _objs.end( ); objsIt++ )
{
if ( *objsIt )
{
delete (*objsIt);
*objsIt = 0;
}
}
_databaseName = "";
}
//*****************************************************************************
/*
*
Load the object from an already trained ehmm
*/
void EHMMObjDatabase::LoadObj( const string &userName,
const string &path )
{
_objs.push_back( new EHMMObj( ) );
try
{
_objs[ _objs.size( ) - 1 ]->Load( userName, path );
//Check to see if the object is a correct one
CheckEHMMConsistence( *_objs[ _objs.size( ) - 1 ] );
}
catch( ... )
{
if ( _objs[ _objs.size( ) - 1 ] )
{
delete _objs[ _objs.size( ) - 1 ];
_objs[ _objs.size( ) - 1 ] = 0;
}
_objs.pop_back( );
cerr << "Exception occured when creating " << userName << "." <<
endl;

}
//*****************************************************************************
/*
*
*/

Add a new with not yet trained ehmm object

void EHMMObjDatabase::AddObj( const string &userName )


{
assert( _vecSize != 0 );
_objs.push_back( new EHMMObj( ) );
_objs[ _objs.size( ) - 1 ]->Create( userName, _numStates, _numMix,
_vecSize );
}
//*****************************************************************************
/*
*
Remove an object from the database by index
*/
void EHMMObjDatabase::RemoveObj( size_t index )
{
assert( _databaseName != "" );
assert( index < _objs.size( ) );
}

_objs.erase( _objs.begin( ) + index );

//*****************************************************************************
/*
*
Remove the first occurance of an object from the database by name
*/
void EHMMObjDatabase::RemoveObj( const string &userName )
{
const string EXCEPTION_STR = "EHMMObjDatabase::RemoveObj: " + userName + "
not found.";
vector< EHMMObj* >::iterator objsIt;
assert( _databaseName != "" );
for ( objsIt = _objs.begin( ); objsIt < _objs.end( ); objsIt++ )
{
if ( (*objsIt)->GetName( ) == userName )
{
_objs.erase( objsIt );
return;
}

throw exception( EXCEPTION_STR.c_str( ) );


}
//*****************************************************************************
EHMMObj& EHMMObjDatabase::GetObj( size_t index )
{
assert( _databaseName != "" );
assert( index < _objs.size( ) );
return *_objs[ index ];
}
//*****************************************************************************
/*

*
If more than one object has the same name the first object is returned
*/
EHMMObj& EHMMObjDatabase::GetObj( const string &userName )
{
const string EXCEPTION_STR = "EHMMObjDatabase::GetObj: " + userName + "
not found.";
vector< EHMMObj* >::iterator objsIt;
assert( _databaseName != "" );
for ( objsIt = _objs.begin( ); objsIt < _objs.end( ); objsIt++ )
{
if ( (*objsIt)->GetName( ) == userName )
{
return *(*objsIt);
}
}
}

throw exception( EXCEPTION_STR.c_str( ) );

//*****************************************************************************
size_t EHMMObjDatabase::GetNoObjs( ) const
{
assert( _databaseName != "" );
return _objs.size( );
}
//*****************************************************************************
/*
*
Check that the new loaded object has the same parameters
*
for his ehmm as the database
*/
void EHMMObjDatabase::CheckEHMMConsistence( EHMMObj &ehmmObj )
{
const string ERROR = "Database '" + _databaseName + "' is not consistent.
Terminating application.";
int noStates[ MAX_STATES ];
int noMix[ MAX_STATES - 1 ];
int totalNoInternalStates;
int i;
assert( _databaseName != "" );
ehmmObj.GetEHMM( ).GetNoStates( noStates );
ehmmObj.GetEHMM( ).GetNoMix( noMix );
//Count the number of internal states
for ( totalNoInternalStates = 0, i = 1; i < noStates[ 0 ] + 1; i++ )
{
totalNoInternalStates += noStates[ i ];
}
//Check vector size, states and mixtures
if ( ehmmObj.GetEHMM( ).GetVecSize( ) != _vecSize
|| memcmp( noStates, _numStates, _numStates[ 0 ] + 1 )
|| memcmp( noMix, _numMix, totalNoInternalStates ) )
{

cerr << ERROR;


abort( );

}
//*****************************************************************************
ostream& operator<<( ostream &os, EHMMObjDatabase &obj )
{
vector< EHMMObj* >::iterator objsIt;
int c;
assert( obj._databaseName != "" );
os << "Database of ehmms '" << obj._databaseName << "' contains..." <<
endl;
for ( c = 1, objsIt = obj._objs.begin( ); objsIt < obj._objs.end( );
objsIt++, c++ )
{
os << c << ")" << " " << (*objsIt)->GetName( ) << endl;
}
}

return os;

//*****************************************************************************

I seguenti files invece costituiscono il cuore degli HMM in quanto


nel codice implementato il training e il riconoscimento.
/*
/*
/*
*/

Description: Implements the training/recogniton with embedded


hidden markov model suing A. Nefian's algorithm

#ifndef _EHMMObjRecognition_H_
#define _EHMMObjRecognition_H_
//Forward declarations
class EHMMObj;
class ImgObj;
class EHMMObjDatabase;
class ImgObjDatabase;
//Disable OpenCv type cast warning
#pragma warning( disable : 4312 )
#include <vector>
#include "cv.h"
#include "ipl.h"
#pragma warning( default : 4312 )
class EHMMObjRecognition
{
public:
EHMMObjRecognition( );
virtual ~EHMMObjRecognition( );

void Create( int imgWidth, int imgHeight, int obsWidth, int obsHeight, int
noDCTCoeffX,
int noDCTCoeffY, int stepX, int stepY, bool suppressIntensity =
false );
void Release( );
void Train( ImgObj &imgObj, EHMMObj &ehmmObj );
void Train( ImgObjDatabase &imgObjDb, EHMMObjDatabase &ehmmObjDb );
float ComputeLikelihood( IplImage &img, EHMMObj &ehmmObj );
size_t ComputeLikelihood( IplImage &img, EHMMObjDatabase &ehmmObjDb,
std::vector< float > &likelihood );
private:
void CountObs( IplROI &roi, CvSize &winSize, CvSize &stepSize,
CvSize &noObs );
void ExtractDCT( float* src, float* dst, int num_vec, int dst_len );
//Controls the size of the observation vectors (the DCT window size)
CvSize _dctSize;
//Step of the DCT through the input image
CvSize _stepSize;
//Number of DCT coefficients to use
CvSize _noDCTCoeff;
//Controls how the image is resizes
int _imgWidth;
int _imgHeight;

};

//Keep or not the first DCT coefficient


bool _suppressIntensity;

#endif //_EHMMObjRecognition_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

"EHMMObjRecognition.h"
"EHMMObj.h"
"ImgObj.h"
"ImgObjDatabase.h"
"EHMMObjDatabase.h"
"Utils.h"
<iostream>
<cv.h>
<vector>
<math.h>
<float.h>

using std::cerr;
using std::endl;
using std::vector;
//*****************************************************************************
EHMMObjRecognition::EHMMObjRecognition( )
{

Release( );
}
//*****************************************************************************
EHMMObjRecognition::~EHMMObjRecognition( )
{
}
//*****************************************************************************
void EHMMObjRecognition::Create( int imgWidth, int imgHeight, int obsWidth,
int obsHeight, int noDCTCoeffX,
int noDCTCoeffY,
int stepX, int stepY, bool
suppressIntensity )
{
assert( _imgWidth == -1 );
assert( _imgHeight == -1 );
_dctSize = cvSize( obsWidth, obsHeight );
_stepSize = cvSize( stepX, stepY );
_noDCTCoeff = cvSize( noDCTCoeffX, noDCTCoeffY );
//They are used only for training
_imgWidth = imgWidth;
_imgHeight = imgHeight;
_suppressIntensity = suppressIntensity;
}
//*****************************************************************************
void EHMMObjRecognition::Release( )
{
assert( _imgWidth != -1 );
assert( _imgHeight != -1 );
_imgWidth = _imgHeight = -1;
}
//*****************************************************************************
/*
*
Train the CvEHMM using A. Nefian's algorithm (See OpenCV documentation)
*/
void EHMMObjRecognition::Train( ImgObj &imgObj, EHMMObj &ehmmObj )
{
const int MAX_ITER = 80;
const double STOP_STEP_ITER = 0.01;
CvImgObsInfo **obsInfoVec;
IplImage *iplImg;
int obsVecLen = _noDCTCoeff.width * _noDCTCoeff.height;
CvSize _noObs;
CvEHMM *tmplEhmm = ehmmObj.GetEHMM( ).GetCvEHMM( );
int numImages = (int)imgObj.GetNoImages( );
bool trained = false;
float oldLikelihood = 0;
int counter = 0;

int i;
assert( _imgWidth != -1 );
assert( _imgHeight != -1 );
if( _suppressIntensity )
{
//Suppress first DCT coefficient
obsVecLen--;
}
//Create the obsinfo array
obsInfoVec = new CvImgObsInfo*[ numImages ];
assert( obsInfoVec != 0 );
{

for( i = 0; i < numImages; i++ )


iplImg = imgObj.GetGrayScaleImage( i, _imgWidth, _imgHeight );
//Get how many DCT transforms we compute
CountObs( *iplImg->roi, _dctSize, _stepSize, _noObs );
//Create the observation for each of the transforms
obsInfoVec[ i ] = cvCreateObsInfo( _noObs, obsVecLen );

if( _suppressIntensity )
{
float *observations = new float[ _noObs.height * _noObs.width *
( obsVecLen + 1 ) ];
cvImgToObs_DCT( iplImg, observations, _dctSize, _noDCTCoeff,
_stepSize );
ExtractDCT( observations, obsInfoVec[ i ]->obs, _noObs.height
* _noObs.width, obsVecLen );

}
else
{

if ( observations )
{
delete( observations);
}

cvImgToObs_DCT( iplImg, obsInfoVec[ i ]->obs, _dctSize, _noDCTCoeff,

_stepSize );
}

cvUniformImgSegm( obsInfoVec[ i ], tmplEhmm );


}

cvReleaseImage( &iplImg );

cvInitMixSegm( obsInfoVec, numImages, tmplEhmm );

//Start the iterative training procedure


while( ( !trained ) && ( counter < MAX_ITER ) )
int j;
float likelihood = 0;
counter++;

cvEstimateHMMStateParams( obsInfoVec, numImages, tmplEhmm );


cvEstimateTransProb( obsInfoVec, numImages, tmplEhmm);
for( j = 0; j < numImages; j++ )
{
cvEstimateObsProb( obsInfoVec[ j ], tmplEhmm );
likelihood += cvEViterbi( obsInfoVec[ j ], tmplEhmm );
}
likelihood /= numImages * obsInfoVec[ 0 ]->obs_size;
cvMixSegmL2( &obsInfoVec[ 0 ], numImages, tmplEhmm );
trained = ( fabs( likelihood - oldLikelihood ) < STOP_STEP_ITER );
}

oldLikelihood = likelihood;
//Clear the observations
for( i = 0; i < numImages; i++ )
{
cvReleaseObsInfo( &(obsInfoVec[ i ]) );
}
delete []obsInfoVec;

ehmmObj.GetEHMM( ).SetTrained( trained );

//*****************************************************************************
/*
*
Apply the training algorithm on two databases of images and ehmms.
*
Train only for the users that appear in the ImgObjDatabase
*/
void EHMMObjRecognition::Train( ImgObjDatabase &imgObjDb, EHMMObjDatabase
&ehmmObjDb )
{
size_t i;
EHMMObj *tmpEHMMObj;
ImgObj *tmpImgObj;
//Train for every object in imgObjDb
for ( i = 0; i < imgObjDb.GetNoObjs( ); i++ )
{
try
{
tmpImgObj = &imgObjDb.GetObj( i );
tmpEHMMObj = &ehmmObjDb.GetObj( tmpImgObj->GetName( ) );
Train( *tmpImgObj, *tmpEHMMObj );
}
catch( exception &e )
{
cerr << e.what( ) << endl;
}
catch( ... )
{
abort( );
}

}
//Check that all users are trained
for ( i = 0; i < ehmmObjDb.GetNoObjs( ); i++ )
{
if ( !ehmmObjDb.GetObj( i ).GetEHMM( ).GetTrained( ) )
{
cerr << "Warning: Obj " << ehmmObjDb.GetObj( i ).GetName( )
<< " is not trained." << endl;
}
}
}
//*****************************************************************************
/*
*
Recognize with ehmm using A. Nefian's algorithm (See OpenCV documentation)
*
This function returns the match likelihood
*/
float EHMMObjRecognition::ComputeLikelihood( IplImage &img, EHMMObj &ehmmObj )
{
CvSize _noObs;
CvEHMM *tmplEhmm = ehmmObj.GetEHMM( ).GetCvEHMM( );
int obsVecLen = _noDCTCoeff.width * _noDCTCoeff.height;
CvImgObsInfo* info;
if( _suppressIntensity )
{
obsVecLen--;
}
//Get how many DCT transforms we compute
CountObs( *img.roi, _dctSize, _stepSize, _noObs );
info = cvCreateObsInfo( _noObs, obsVecLen );
assert( info != 0 );
if( _suppressIntensity )
{
float *observations = new float[ _noObs.height * _noObs.width *
( obsVecLen + 1 ) ];
cvImgToObs_DCT( &img, observations, _dctSize, _noDCTCoeff, _stepSize

);

ExtractDCT( observations, info->obs, _noObs.height * _noObs.width,


obsVecLen );
if ( observations )
{
delete( observations);
}
}
else
{

cvImgToObs_DCT( &img, info->obs, _dctSize, _noDCTCoeff, _stepSize );

}
cvEstimateObsProb( info, tmplEhmm );
return cvEViterbi( info, tmplEhmm );

}
//*****************************************************************************
/*
*
Apply the recognition algorithm on a database of ehmms. Returns all the
*
likelihoods for all the ehmms in teh database and the index of the best
match
*/
size_t EHMMObjRecognition::ComputeLikelihood( IplImage &img, EHMMObjDatabase
&ehmmObjDb,
vector< float
> &likelihood )
{
size_t i;
EHMMObj *tmpEHMMObj;
size_t maxLikelihoodPos = -1;
float maxLikelihood = -FLT_MAX;
//Train for every object in imgObjDb
for ( i = 0; i < ehmmObjDb.GetNoObjs( ); i++ )
{
try
{
tmpEHMMObj = &ehmmObjDb.GetObj( i );
if ( tmpEHMMObj->GetEHMM( ).GetTrained( ) )
{
likelihood.push_back( ComputeLikelihood( img,
*tmpEHMMObj ) );
maxLikelihood )

if ( likelihood[ likelihood.size( ) - 1 ] >


{

];

maxLikelihood = likelihood[ likelihood.size( ) - 1


maxLikelihoodPos = i;

}
}
catch( exception &e )
{
cerr << e.what( ) << endl;
}
catch( ... )
{
abort( );
}

return maxLikelihoodPos;
}
//*****************************************************************************
void EHMMObjRecognition::CountObs( IplROI &roi, CvSize &winSize, CvSize
&_stepSize,
CvSize &_noObs )
{
_noObs.width = ( roi.width - winSize.width + _stepSize.width ) /
_stepSize.width;
_noObs.height = ( roi.height - winSize.height + _stepSize.height ) /

_stepSize.height;
}
//*****************************************************************************
/*
*
Used to suppress the first DCT coefficient
*/
void EHMMObjRecognition::ExtractDCT( float* src, float* dst, int numVec, int
dstLen )
{
float *tmpSrc = src + 1;
float *tmpDst = dst;
int i;
for( i = 0; i < numVec; i++ )
{
memcpy( tmpDst, tmpSrc, dstLen * sizeof( float ) );
tmpSrc += dstLen + 1;
tmpDst += dstLen;
}
}
//*****************************************************************************
/*
/*
/*
*/

Description: Implements the recognition using embedded


hidden markov model for face recognition

#ifndef _RecognizeEHMM_H_
#define _RecognizeEHMM_H_
//Disable OpenCv type cast warning
#pragma warning( disable : 4312 )
#include <ipl.h>
#include <cv.h>
#pragma warning( default : 4312 )
//Forward declarations
class EHMMObj;
class EHMMObjDatabase;
float RecognizeEHMM( IplImage &img, EHMMObj &ehmmObj, int obsWidth, int
obsHeight,
int noDCTCoeffX, int noDCTCoeffY, int stepX, int
stepY,
bool suppressIntensity = false );
size_t RecognizeEHMM( IplImage &img, EHMMObjDatabase &ehmmObjDb, int obsWidth,
int obsHeight,
int noDCTCoeffX, int noDCTCoeffY, int stepX, int
stepY,
bool suppressIntensity = false );
#endif //_RecognizeEHMM_H_
#include "EHMMObj.h"
#include "EHMMObjDatabase.h"
#include "Utils.h"

#include
#include
#include
#include

"RecognizeEHMM.h"
<vector>
<iostream>
<float.h>

using std::cerr;
using std::endl;
using std::vector;
//*****************************************************************************
/*
*
Recognize with ehmm using A. Nefian's algorithm (See OpenCV documentation)
*
This function returns the match likelihood
*/
float RecognizeEHMM( IplImage &img, EHMMObj &ehmmObj, int obsWidth, int
obsHeight,
int noDCTCoeffX, int noDCTCoeffY, int stepX, int
stepY,
bool suppressIntensity )
{
CvSize noObs;
CvSize dctSize = cvSize( obsWidth, obsHeight );
CvSize stepSize = cvSize( stepX, stepY );
CvSize obsSize = cvSize( noDCTCoeffX, noDCTCoeffY );
CvEHMM *tmplEhmm = ehmmObj.GetEHMM( ).GetCvEHMM( );
int obsVecLen = noDCTCoeffX * noDCTCoeffY;
CvImgObsInfo* info;
if( suppressIntensity )
{
obsVecLen--;
}
//Get how many DCT transforms we compute
CountObs( *img.roi, dctSize.width, dctSize.height, stepSize.width,
stepSize.height,
noObs.width, noObs.height );
info = cvCreateObsInfo( noObs, obsVecLen );
assert( info != 0 );
if( suppressIntensity )
{
float *observations = new float[ noObs.height * noObs.width *
( obsVecLen + 1 ) ];
cvImgToObs_DCT( &img, observations, dctSize, obsSize, stepSize );
ExtractDCT( observations, info->obs, noObs.height * noObs.width,
obsVecLen );

}
else
{

if ( observations )
{
delete( observations);
}

cvImgToObs_DCT( &img, info->obs, dctSize, obsSize, stepSize );

cvEstimateObsProb( info, tmplEhmm );


return cvEViterbi( info, tmplEhmm );
}
//*****************************************************************************
/*
*
Apply the recognition algorithm on a database of ehmms
*/
size_t RecognizeEHMM( IplImage &img, EHMMObjDatabase &ehmmObjDb, int obsWidth,
int obsHeight, int noDCTCoeffX, int noDCTCoeffY,
int stepX,
int stepY, bool suppressIntensity )
{
size_t i;
EHMMObj *tmpEHMMObj;
vector< float > likelihood;
size_t maxLikelihoodPos = -1;
float maxLikelihood = -FLT_MAX;
//JUST FOR DEBUG
cerr << "Probability scores are" << endl;
//Train for every object in imgObjDb
for ( i = 0; i < ehmmObjDb.GetNoObjs( ); i++ )
{
try
{
tmpEHMMObj = &ehmmObjDb.GetObj( i );
if ( tmpEHMMObj->GetEHMM( ).GetTrained( ) )
{
likelihood.push_back( RecognizeEHMM( img, *tmpEHMMObj,
obsWidth, obsHeight,
noDCTCoeffX, noDCTCoeffY, stepX, stepY,
suppressIntensity ) );
if ( likelihood[ likelihood.size( ) - 1 ] >
maxLikelihood )

{
maxLikelihood = likelihood[ likelihood.size( ) - 1

];
}

maxLikelihoodPos = i;

//JUST FOR DEBUG


cerr << likelihood[ i ] << "

}
catch( exception &e )
{
cerr << e.what( ) << endl;
}
catch( ... )
{
abort( );
}

//JUST FOR DEBUG


cerr << endl;

";

return maxLikelihoodPos;

La gestione delle immagini invece viene eseguita da una classe


appositamente scritta.
/*
/*
/*
*/

Description: Implements a simple description of an ImgObj


that stores all the available images for that object

#ifndef _ImgObj_H_
#define _ImgObj_H_
#include
#include
#include
#include
#include
#include
#include

"Obj.h"
"EHMM.h"
<vector>
<string>
<ostream>
<ipl.h>
<cv.h>

class ImgObj : public Obj


{
public:
ImgObj( );
virtual ~ImgObj( );
void Create( const std::string &userName );
void Load( const std::string &userName, const std::string &path );
void Save( const std::string &path );
void Release( );
void AddImage( const std::string &imagePath );
= 0,

IplImage* GetGrayScaleImage( size_t index, int imgWidth = 0, int imgHeight


bool showImage = false );
size_t GetNoImages( );
friend std::ostream& operator<<( std::ostream &os, ImgObj &obj );

private:
//Stores the paths to the images
std::vector< std::string > _imagePaths;
int _imgWidth;
int _imgHeight;
};

std::ostream& operator<<( std::ostream &os, ImgObj &obj );


#endif //_ImgObj_H_
#include
#include
#include
#include
#include
using
using
using
using
using
using

"ImgObj.h"
<fstream>
<iostream>
<assert.h>
<highgui.h>

std::string;
std::vector;
std::ifstream;
std::ofstream;
std::endl;
std::ostream;

using std::cout;
//Standard extension to the user file
static const string EXTENSION = ".imobj";
//*****************************************************************************
ImgObj::ImgObj( )
{
}
//*****************************************************************************
ImgObj::~ImgObj( )
{
}
//*****************************************************************************
void ImgObj::Create( const string &userName )
{
assert( _name == "" );
Obj::Create( userName );
}
//*****************************************************************************
void ImgObj::Load( const string &userName, const string &path )
{
const int INITIAL_SIZE = 1000;
const int MAX_LINE_WIDTH = _MAX_PATH;
const string IMGUSER_FILE = path + userName + EXTENSION;
char line[ MAX_LINE_WIDTH ];
ifstream file( IMGUSER_FILE.c_str( ) );
assert( _name == "" );
assert( file.is_open( ) );
Obj::Create( userName );
_imagePaths.reserve( INITIAL_SIZE );
while ( file.getline( line, sizeof( line ) ) )

{
}

_imagePaths.push_back( line );

}
//*****************************************************************************
void ImgObj::Save( const string &path )
{
const string IMGUSER_FILE = path + _name + EXTENSION;
vector< string >::iterator imIt;
ofstream file( IMGUSER_FILE.c_str( ) );
assert( _name != "" );
assert( file.is_open( ) );
for ( imIt = _imagePaths.begin( ); imIt < _imagePaths.end( ); imIt++ )
{
file << (*imIt) << endl;
}
}
//*****************************************************************************
void ImgObj::Release( )
{
vector< string >::iterator imIt;
assert( _name != "" );
for ( imIt = _imagePaths.begin( ); imIt < _imagePaths.end( ); imIt++ )
{
(*imIt) = "";
}
_name = "";
}
//*****************************************************************************
void ImgObj::AddImage( const string &imagePath )
{
assert( _name != "" );
}

_imagePaths.push_back( imagePath );

//*****************************************************************************
/*
*
Load the image from the disk. Convert it to gray scale
*
because it is always loaded as a color image.
*
The IplImage will be returned by GetImage with
*
this dimensions no matter the dimensions
*
of image stored on disk. If one of the dimensions
*
is 0 then only the other dimension is used. If both
*
are 0 the image will not be rescaled
*/
IplImage* ImgObj::GetGrayScaleImage( size_t index, int imgWidth, int imgHeight,
bool showImage )
{

IplImage *imgDb;
IplImage *imgHdd;
IplImage *imgGray;
assert( _name != "" );
imgHdd = cvLoadImage( _imagePaths[ index ].c_str( ), 0 );
assert( imgHdd != 0 );
imgGray = cvCreateImage( cvSize( imgHdd->width, imgHdd->height ),
IPL_DEPTH_8U, 1 );
assert( imgGray != 0 );
//Resize the output image to the requested size
if ( imgWidth && imgHeight )
{
imgDb = cvCreateImage( cvSize( imgWidth, imgHeight ), IPL_DEPTH_8U,
1 );

}
else
if ( imgHeight )
{
imgDb = cvCreateImage( cvSize( imgHdd->width * imgHeight / imgHdd>height, imgHeight ),
IPL_DEPTH_8U, 1 );
}
if ( imgWidth )
{
imgDb = cvCreateImage( cvSize( imgWidth, imgHdd->height * imgWidth /
imgHdd->width ),
IPL_DEPTH_8U, 1 );
}
else
{
imgDb = cvCreateImage( cvSize( imgHdd->width, imgHdd->height ),
IPL_DEPTH_8U, 1 );
}
assert( imgDb != 0 );
//Check that the image is already grayscale
if ( imgHdd->nChannels > 1 )
{
//Make sure the output is gray scale
iplColorToGray( imgHdd, imgGray );
//Resize to the desired dimension
iplResizeFit( imgGray, imgDb, IPL_INTER_NN );
}
else
{
}

//Resize to the desired dimension


iplResizeFit( imgHdd, imgDb, IPL_INTER_NN );

//Set a roi to the full image


cvSetImageROI( imgDb, cvRect( 0, 0, imgDb->width, imgDb->height ) );
if ( showImage )
{
cvNamedWindow( _imagePaths[ index ].c_str( ), 0 );
cvShowImage( _imagePaths[ index ].c_str( ), imgDb );
cvWaitKey( 0 );

cvDestroyWindow( _imagePaths[ index ].c_str( ) );


}
//Delete all the temp images
cvReleaseImage( &imgHdd );
cvReleaseImage( &imgGray );
}

return imgDb;

//*****************************************************************************
size_t ImgObj::GetNoImages( )
{
assert( _name != "" );
return _imagePaths.size( );
}
//*****************************************************************************
ostream& operator<<( ostream &os, ImgObj &obj )
{
vector< string >::iterator imIt;
int c;
assert( obj._name != "" );
os << "Obj '" << obj._name << "' contains..." << endl << endl;
for ( c = 1, imIt = obj._imagePaths.begin( ); imIt <
obj._imagePaths.end( );
imIt++, c++ )
{
os << c << ")" << " " << (*imIt) << endl;
}
}

return os;

//*****************************************************************************

Lultima classe invece gestisce il database delle immagini.


/*
/*
*/

Description: Implements a simple database of imgusers

#ifndef _ImgObjDatabase_H_
#define _ImgObjDatabase_H_
#include "ImgObj.h"
#include <vector>
#include <iostream>
//Exception specification warning
#pragma warning( disable : 4290 )
class ImgObjDatabase
{
public:

ImgObjDatabase( );
virtual ~ImgObjDatabase( );
void Create( const std::string &databaseName );
void Load( const std::string &databaseName, const std::string &path );
void Save( const std::string &path );
void Release( );
void LoadObj( const std::string &userName, const std::string &path );
void AddObj( const std::string &userName );
void RemoveObj( size_t index );
void RemoveObj( const std::string &userName );
ImgObj& GetObj( size_t index );
ImgObj& GetObj( const std::string &userName ) throw( exception );
size_t GetNoObjs( ) const;
friend std::ostream& operator<<( std::ostream &os, ImgObjDatabase &obj );
private:
//Stores all the users with their
//associated properties
std::vector< ImgObj* > _objs;
//Name of the database file
std::string _databaseName;
};
std::ostream& operator<<( std::ostream &os, ImgObjDatabase &obj );
#endif //_ImgObjDatabase_H_
#include
#include
#include
#include
using
using
using
using
using
using
using
using
using

"ImgObjDatabase.h"
<fstream>
<iostream>
<assert.h>

std::vector;
std::ifstream;
std::ofstream;
std::iostream;
std::ostream;
std::string;
std::cout;
std::endl;
std::cerr;

//*****************************************************************************
ImgObjDatabase::ImgObjDatabase( )
{

_databaseName = "";
}
//*****************************************************************************
ImgObjDatabase::~ImgObjDatabase( )
{
Release( );
}
//*****************************************************************************
void ImgObjDatabase::Create( const string &databaseName )
{
assert( _databaseName == "" );
_databaseName = databaseName;
}
//*****************************************************************************
/*
*
Load a database from disk
*/
void ImgObjDatabase::Load( const string &databaseName, const string &path )
{
const string DATABASE_PATH = path + databaseName;
const int MAX_LINE_WIDTH = 255;
const int INITIAL_SIZE = 1000;
ifstream file( DATABASE_PATH.c_str( ) );
char line[ MAX_LINE_WIDTH ];
assert( file.is_open( ) );
_objs.reserve( INITIAL_SIZE );
while ( file.getline( line, sizeof( line ) ) )
{
//Create a new object
_objs.push_back( new ImgObj( ) );
try
{

//Create the corresponding object and load ehmm


_objs[ _objs.size( ) - 1 ]->Load( line, path );

}
catch( ... )
{
if ( _objs[ _objs.size( ) - 1 ] )
{
delete _objs[ _objs.size( ) - 1 ];
_objs[ _objs.size( ) - 1 ] = 0;
}
_objs.pop_back( );

cerr << "Exception occured when creating " << line << "." <<

endl;
}

//Save the name of the database


_databaseName = databaseName;

//*****************************************************************************
/*
*
Save the database to disk. It will overwrite the previous data
*/
void ImgObjDatabase::Save( const string &path )
{
const string DATABASE_PATH = path + _databaseName;
vector< ImgObj* >::iterator usersIt;
ofstream file( DATABASE_PATH.c_str( ) );
assert( _databaseName != "" );
assert( file.is_open( ) );

for ( usersIt = _objs.begin( ); usersIt < _objs.end( ); usersIt++ )


{
file << (*usersIt)->GetName( ) << endl;
(*usersIt)->Save( path );
}

//*****************************************************************************
void ImgObjDatabase::Release( )
{
vector< ImgObj* >::iterator usersIt;
assert( _databaseName != "" );
for ( usersIt = _objs.begin( ); usersIt < _objs.end( ); usersIt++ )
{
delete (*usersIt);
*usersIt = 0;
}
_databaseName = "";
}
//*****************************************************************************
/*
*
Load the object
*/
void ImgObjDatabase::LoadObj( const string &userName, const string &path )
{
_objs.push_back( new ImgObj( ) );
_objs[ _objs.size( ) - 1 ]->Load( userName, path );
}
//*****************************************************************************
/*
*
Add a new object to the database
*/
void ImgObjDatabase::AddObj( const string &userName )
{
_objs.push_back( new ImgObj( ) );

_objs[ _objs.size( ) - 1 ]->Create( userName );


}
//*****************************************************************************
/*
*
Remove an object from the database by index
*/
void ImgObjDatabase::RemoveObj( size_t index )
{
assert( _databaseName != "" );
assert( index < _objs.size( ) );
}

_objs.erase( _objs.begin( ) + index );

//*****************************************************************************
/*
*
Remove the first occurance of an object from the database by name
*/
void ImgObjDatabase::RemoveObj( const string &userName )
{
const string EXCEPTION_STR = "ImgObjDatabase::RemoveObj: " + userName + "
not found.";
vector< ImgObj* >::iterator usersIt;
assert( _databaseName != "" );
for ( usersIt = _objs.begin( ); usersIt < _objs.end( ); usersIt++ )
{
if ( (*usersIt)->GetName( ) == userName )
{
_objs.erase( usersIt );
return;
}

throw exception( EXCEPTION_STR.c_str( ) );


}
//*****************************************************************************
ImgObj& ImgObjDatabase::GetObj( size_t index )
{
assert( _databaseName != "" );
assert( index < _objs.size( ) );
return *_objs[ index ];
}
//*****************************************************************************
/*
*
If more than one object has the same name the first object is returned
*/
ImgObj& ImgObjDatabase::GetObj( const string &userName )
{
const string EXCEPTION_STR = "ImgObjDatabase::GetObj: " + userName + " not
found.";

vector< ImgObj* >::iterator usersIt;


assert( _databaseName != "" );
for ( usersIt = _objs.begin( ); usersIt < _objs.end( ); usersIt++ )
{
if ( (*usersIt)->GetName( ) == userName )
{
return *(*usersIt);
}
}
}

throw exception( EXCEPTION_STR.c_str( ) );

//*****************************************************************************
size_t ImgObjDatabase::GetNoObjs( ) const
{
assert( _databaseName != "" );
return _objs.size( );
}
//*****************************************************************************
ostream& operator<<( ostream &os, ImgObjDatabase &obj )
{
vector< ImgObj* >::iterator usersIt;
int c;
assert( obj._databaseName != "" );
os << "Database of images '" << obj._databaseName << "' contains..." <<
endl;
for ( c = 1, usersIt = obj._objs.begin( ); usersIt < obj._objs.end( );
usersIt++, c++ )
{
os << c << ")" << " " << (*usersIt)->GetName( ) << endl;
}
}

return os;

La gestione delle descrizioni viene trattata da :


/*
/*
*/

Description: Implements a simple description of an object

#ifndef _Obj_H_
#define _Obj_H_
#include <string>
#include <vector>
class Obj
{

public:
Obj( );
virtual ~Obj( );
void Create( const std::string &userName );
const std::string& GetName( ) const;
protected:

};

//Stores the name of the object


std::string _name;

#endif //_Obj_H_
#include "Obj.h"
#include <assert.h>
using std::string;
//*****************************************************************************
Obj::Obj( ) : _name("")
{
}
//*****************************************************************************
Obj::~Obj( )
{
}
//*****************************************************************************
void Obj::Create( const string &userName )
{
assert( userName != "" );
}

_name = userName;

//*****************************************************************************
const string& Obj::GetName( ) const
{
assert( _name != "" );
return _name;
}
//*****************************************************************************

Le funzioni legate alla gestione


utilizzate in questo modo nelle
programma.
Queste due sono precisamente :

delle HMM di Open CV sono


due parti fondamentali del

il training
il riconoscimento
Tutte le funzioni usate sono le seguenti :
Embedded Hidden Markov Models Functions
In order to support embedded models the user must
structures to represent 1D HMM and 2D embedded HMM model.
CvHMM

define

Embedded HMM Structure


typedef struct _CvEHMM
{
int level;
int num_states;
float* transP;
float** obsProb;
union
{
CvEHMMState* state;
struct _CvEHMM* ehmm;
} u;
} CvEHMM;
Level
Level of embedded HMM. If level ==0, HMM is most external. In
2D HMM there are two types of HMM: 1 external and several
embedded. External HMM has level ==1, embedded HMMs have
level ==0 .
num_states
Number of states in 1D HMM.
transP
State-to-state
transition
probability,
square
matrix
(num_statenum_state ).
obsProb
Observation probability matrix.
state
Array of HMM states. For the last-level HMM, that is, an HMM
without embedded HMMs, HMM states are real.
ehmm
Array of embedded HMMs. If HMM is not last-level, then HMM
states are not real and they are HMMs.
For representation
defined:
CvImgObsInfo

of

observations

Image Observation Structure

the

following

structure

is

typedef struct CvImgObsInfo


{
int obs_x;
int obs_y;
int obs_size;
float** obs;
int* state;
int* mix;
} CvImgObsInfo;
obs_x
Number of observations in the horizontal direction.
obs_y
Number of observations in the vertical direction.
obs_size
Length of every observation vector.
obs
Pointer to observation vectors stored consequently. Number of
vectors is obs_x*obs_y .
state
Array of indices of states, assigned to every observation
vector.
mix
Index of mixture component, corresponding to the observation
vector within an assigned state.
Create2DHMM
Creates 2D embedded HMM
CvEHMM*
cvCreate2DHMM(
obsSize );

int*

stateNumber,

int*

numMix,

int

stateNumber
Array, the first element of the which specifies the number of
superstates in the HMM. All subsequent elements specify the
number of states in every embedded HMM, corresponding to each
superstate. So, the length of the array is stateNumber
[0]+1 .
numMix
Array with numbers of Gaussian mixture components per each
internal state. The number of elements in the array is equal
to number of internal states in the HMM, that is, superstates
are not counted here.
obsSize
Size of observation vectors to be used with created HMM.
The function cvCreate2DHMM returns the created structure of the
type CvEHMM with specified parameters.
Release2DHMM

Releases 2D embedded HMM


void cvRelease2DHMM(CvEHMM** hmm );
hmm
Address of pointer to HMM to be released.
The function cvRelease2DHMM frees all the memory used by HMM and
clears the pointer to HMM.
CreateObsInfo
Creates structure to store image observation vectors
CvImgObsInfo* cvCreateObsInfo( CvSize numObs, int obsSize );
numObs
Numbers of observations in the horizontal and vertical
directions. For the given image and scheme of extracting
observations the parameter can be computed via the macro
CV_COUNT_OBS( roi, dctSize, delta, numObs ), where roi,
dctSize, delta, numObs are the pointers to structures of the
type CvSize . The pointer roi means size of roi of image
observed, numObs is the output parameter of the macro.
obsSize
Size of observation vectors to be stored in the structure.
The function cvCreateObsInfo creates new structures to store image
observation vectors. For definitions of the parameters roi,
dctSize, and delta see the specification of the function
cvImgToObs_DCT.
ReleaseObsInfo
Releases observation vectors structure
void cvReleaseObsInfo( CvImgObsInfo** obsInfo );
obsInfo
Address of the pointer to the structure CvImgObsInfo .
The
function
cvReleaseObsInfo
frees
all
memory
used
observations and clears pointer to the structure CvImgObsInfo .
ImgToObs_DCT

by

Extracts observation vectors from image


void cvImgToObs_DCT( IplImage* image, float* obs, CvSize dctSize,
CvSize obsSize, CvSize delta );
image
Input image.
obs
Pointer to consequently stored observation vectors.

dctSize
Size of image blocks for which DCT (Discrete Cosine
Transform) coefficients are to be computed.
obsSize
Number of the lowest DCT coefficients in the horizontal and
vertical directions to be put into the observation vector.
delta
Shift in pixels between two consecutive image blocks in the
horizontal and vertical directions.
The function cvImgToObs_DCT extracts observation vectors, that is,
DCT coefficients, from the image. The user must pass obsInfo.obs
as the parameter obs to use this function with other HMM functions
and use the structure obsInfo of the CvImgObsInfo type.
Calculating Observations for HMM
CvImgObsInfo* obs_info;
...
cvImgToObs_DCT( image,obs_info->obs, //!!!
dctSize, obsSize, delta );
UniformImgSegm
Performs uniform segmentation of image observations by HMM states
void cvUniformImgSegm( CvImgObsInfo* obsInfo, CvEHMM* hmm );
obsInfo
Observations structure.
hmm
HMM structure.
The function cvUniformImgSegm segments image observations by HMM
states uniformly (see Initial Segmentation for 2D Embedded HMM for
2D embedded HMM with 5 superstates and 3, 6, 6, 6, 3 internal
states of every corresponding superstate).
Initial Segmentation for 2D Embedded HMM
InitMixSegm
Segments all observations within every internal state of HMM by
state mixture components
void cvInitMixSegm(
CvEHMM* hmm );

CvImgObsInfo**

obsInfoArray,

int

numImg,

obsInfoArray
Array of pointers to the observation structures.
numImg
Length of above array.
hmm
HMM.
The function cvInitMixSegm takes a group of observations from
several training images already segmented by states and splits a
set of observation vectors within every internal HMM state into as
many clusters as the number of mixture components in the state.
EstimateHMMStateParams
Estimates all parameters of every HMM state
void cvEstimateHMMStateParams(
numImg, CvEHMM* hmm );

CvImgObsInfo**

obsInfoArray,

int

obsInfoArray
Array of pointers to the observation structures.
numImg
Length of the array.
hmm
HMM.
The
function
cvEstimateHMMStateParams
computes
all
parameters
of
every
HMM
state,
including
Gaussian
variances, etc.
EstimateTransProb

inner
means,

Computes transition probability matrices for embedded HMM


void cvEstimateTransProb( CvImgObsInfo** obsInfoArray, int numImg,
CvEHMM* hmm );
obsInfoArray
Array of pointers to the observation structures.
numImg
Length of the above array.
hmm
HMM.
The function cvEstimateTransProb uses current segmentation of
image observations to compute transition probability matrices for
all embedded and external HMMs.
EstimateObsProb
Computes probability of every observation of several images
void cvEstimateObsProb( CvImgObsInfo* obsInfo, CvEHMM* hmm );

obsInfo
Observation structure.
hmm
HMM structure.
The function cvEstimateObsProb computes Gaussian probabilities of
each observation to occur in each of the internal HMM states.
EViterbi
Executes Viterbi algorithm for embedded HMM
float cvEViterbi( CvImgObsInfo* obsInfo, CvEHMM* hmm );
obsInfo
Observation structure.
hmm
HMM structure.
The function cvEViterbi executes Viterbi algorithm for embedded
HMM. Viterbi algorithm evaluates the likelihood of the best match
between the given image observations and the given HMM and
performs segmentation of image observations by HMM states. The
segmentation is done on the basis of the match found.
MixSegmL2
Segments observations from all training
components of newly assigned states

images

by

mixture

void cvMixSegmL2( CvImgObsInfo** obsInfoArray, int numImg, CvEHMM*


hmm );
obsInfoArray
Array of pointers to the observation structures.
numImg
Length of the array.
hmm
HMM.
The function cvMixSegmL2 segments observations from all training
images by mixture components of newly Viterbi algorithm-assigned
states. The function uses Euclidean distance to group vectors
around the existing mixtures centers.
Tutta le teoria legata a queste funzionisi basa sulla creazione di
vettori di osservazione e sulle procedure eseguite mediante la
teoria di Markov inerente alla valutazione degli stati.
In altre parole alcune funzioni si interessando di creare degli
osservatori i quali sono rappresentati dalla struttura vista
allinizio della serie precedente di funzioni.
Questi osservatori sono indirizzati a rappresentare i particolari
di una certa immagine.

Altre funzioni sono in grado di collegare gli osservatori agli


stati interni degli HMM per fare in modo che questi possano
eseguire le valutazioni di attinenza.
In altre parole in fase di training il sistema crea di vettori di
osservazione tipici delle immagini usate nel training stesso.
Successivamente i vettori di osservazione di una nuova immagine
sono valutati per ottenere le probabilit che questi siano
riferiti a quelli di immagini trinate.
void EHMMObjRecognition::Train( ImgObj &imgObj, EHMMObj &ehmmObj )
{
const int MAX_ITER = 80;
const double STOP_STEP_ITER = 0.01;
CvImgObsInfo **obsInfoVec;
IplImage *iplImg;
int obsVecLen = _noDCTCoeff.width * _noDCTCoeff.height;
Tramite I coefficenti settati viene calcolata la lunghezza dei
vettori di osservazione.
CvSize _noObs;
CvEHMM *tmplEhmm = ehmmObj.GetEHMM( ).GetCvEHMM( );
Recupera il puntatore all EHMM.
int numImages = (int)imgObj.GetNoImages( );
Il numero delle immagini.
bool trained = false;
float oldLikelihood = 0;
int counter = 0;
int i;
assert( _imgWidth != -1 );
assert( _imgHeight != -1 );
if( _suppressIntensity )
{
obsVecLen--;
Elimina il primo coefficente DCT
}
obsInfoVec = new CvImgObsInfo*[ numImages ];
assert( obsInfoVec != 0 );

Crea larray per le informazioni relative all osservatore in


funzione del numero dimmagini.
A questo punto per ciascuna immagine vengono eseguite un certo
numero di funzioni:
for( i = 0; i < numImages; i++ )
{
iplImg
=
imgObj.GetGrayScaleImage(
_imgHeight );

i,

_imgWidth,

Si assicura che limmagine sia a 8 BITS in scale di grigio,


CountObs( *iplImg->roi, _dctSize, _stepSize, _noObs );
Riporta il numero di DCT (DISCRETE COSENE TRANSFORM) che sono
stati computati.
obsInfoVec[ i ] = cvCreateObsInfo( _noObs, obsVecLen );
Crea losservatore per ciascuna trasformata.
if( _suppressIntensity )
{
float *observations = new float[ _noObs.height *
_noObs.width * ( obsVecLen + 1 ) ];
Alloca il vettore
cvImgToObs_DCT(
_noDCTCoeff, _stepSize );

iplImg,

observations,

_dctSize,

Finalmente estrae I vettori di osservazione di un immagine.


ExtractDCT( observations, obsInfoVec[
_noObs.height * _noObs.width, obsVecLen );

]->obs,

Estrae I DCT
if ( observations )
{
delete( observations);
}
}
else
{
cvImgToObs_DCT( iplImg, obsInfoVec[ i ]->obs,
_dctSize, _noDCTCoeff, _stepSize );
}
cvUniformImgSegm( obsInfoVec[ i ], tmplEhmm );
Esegue

una

segmentazione

uniforma

basandosi

sullosservazione

degli stati HMM.


cvReleaseImage( &iplImg );
}
cvInitMixSegm( obsInfoVec, numImages, tmplEhmm );
Segmenta tutte le osservazioni utilizzando gli stati interni HMM.
In altre parole la funzione prende un gruppo di osservazioni da
immagine sulle quali stato eseguito il training gi segmentate
per stato e gli splitta un set di vettori di osservazione insieme
a ogni singolo stato HMM interno.
Lo split produce un insieme di cluster di numero uguale a quello
dei misture components degli stati.
while( ( !trained ) && ( counter < MAX_ITER ) )
{
int j;
float likelihood = 0;
counter++;
cvEstimateHMMStateParams(

obsInfoVec,

numImages,

tmplEhmm );
Valuta i parametri di ogni stato HMM
cvEstimateTransProb( obsInfoVec, numImages, tmplEhmm);
Computa la matrice probabilistica delle transazioni per gli EHMM
for( j = 0; j < numImages; j++ )
{
cvEstimateObsProb( obsInfoVec[ j ], tmplEhmm );
likelihood += cvEViterbi( obsInfoVec[ j ], tmplEhmm );
}
Per ciascuna immagine presente computa la probablit di ciascun
osservatore per ogni immagine.
La seconda funzione esegue lalgoritmo di VITERBI sugli EHMM.
Tale funzionalit finalizzata a valutare la verisomiglianza sul
losservatore migliore trovato relativo ad un immagine.
Inoltre viene eseguita la segmentazione.
likelihood /= numImages * obsInfoVec[ 0 ]->obs_size;
cvMixSegmL2( &obsInfoVec[ 0 ], numImages, tmplEhmm );
ricava losservatore segmentato da tutte le immagini trainate
attraverso i misture components dei nuovi stati assegnati.
trained

fabs(

likelihood

oldLikelihood

<

STOP_STEP_ITER );
oldLikelihood = likelihood;
}
for( i = 0; i < numImages; i++ )
{
cvReleaseObsInfo( &(obsInfoVec[ i ]) );
}
delete []obsInfoVec;
ehmmObj.GetEHMM( ).SetTrained( trained );
}
La funzione che calcola la verisomiglianza la seguente :
float
EHMMObjRecognition::ComputeLikelihood(
EHMMObj &ehmmObj )
{
CvSize _noObs;

IplImage

&img,

CvEHMM *tmplEhmm = ehmmObj.GetEHMM( ).GetCvEHMM( );


int obsVecLen = _noDCTCoeff.width * _noDCTCoeff.height;
CvImgObsInfo* info;
if( _suppressIntensity )
{
obsVecLen--;
}
//Get how many DCT transforms we compute
CountObs( *img.roi, _dctSize, _stepSize, _noObs );
info = cvCreateObsInfo( _noObs, obsVecLen );
assert( info != 0 );
if( _suppressIntensity )
{
float *observations =
_noObs.width * ( obsVecLen + 1 ) ];
cvImgToObs_DCT(
_noDCTCoeff, _stepSize );

&img,

ExtractDCT( observations,
_noObs.width, obsVecLen );
if ( observations )
{
delete( observations);
}
}

new

float[

_noObs.height

observations,
info->obs,

_dctSize,

_noObs.height

else
{
cvImgToObs_DCT( &img, info->obs, _dctSize, _noDCTCoeff,
_stepSize );
}
cvEstimateObsProb( info, tmplEhmm );
return cvEViterbi( info, tmplEhmm );
}
Un punto critico quello legato alla scelta dei valori legati ai
superstati come avviene nella funzione :
void
CEHMMRetinalIDDlg::CreateOpenRetinalEHMMDb(
const
string
&pathEhmmDb, const string &ehmmCvDbName )
{
EHMMObjDatabase ehmmDb;
int noStates[] = { 5, 4, 4, 4, 4, 4 };
int numMixtures[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
int vecSize = 15; //because we suppress the first coefficient
DCT_SIZE_X * DCT_SIZE_Y - 1
ehmmDb.Create( ehmmCvDbName, noStates, numMixtures, vecSize );
Il metodo Create della classe ehmmDb :
void EHMMObjDatabase::Create(
*noStates,

const

string

&databaseName,

int

int *noMix, int vecSize )


{
int i;
int totalNoInternalStates;
assert(
assert(
assert(
assert(

noStates != 0 );
noMix != 0 );
vecSize != 0 );
databaseName != "" );

//Save the name of the database


_databaseName = databaseName;
//Save the ehmm creation parameters
memcpy( _numStates, noStates, sizeof( int ) * ( noStates[ 0 ]
+ 1 ) );
//Count the number of internal states
for ( totalNoInternalStates = 0, i = 1; i < noStates[ 0 ] + 1;
i++ )
{

totalNoInternalStates += noStates[ i ];
}
memcpy(
_numMix,
totalNoInternalStates );

noMix,

sizeof(

int

_vecSize = vecSize;
}
Nel manuale, ad esempio, viene mostrata un immagine su cui vengono
applicati 5 superstati e 3, 6, 6, 6, 3 stati
interni.
Tale
scelta
di
valori
viene
graficamente
visualizzata su di un immagine come in quella a
fianco.
Nel caso della retina la scelta dovr essere fatta
in modo tale da riuscire a coprire nel modo
migliore i particolari dellimmagine stessa.
Una scelta di 5 superstati e 4,4,4,4,4 potrebbe
essere una soluzione equilibrata.

La valutazione relativa alla scelta dei


valori dovr avvenire mediante prove.
Nel
caso
dell'immagine
vista
precedentemente la scelta stata quella
relativa
ai
cinque
super
stati
e
successivamente
quella
degli
stati
interni
visti
in
quanto
le
caratteristiche
del
viso
venivano
settorizzate in modo ottimale mediante
queste valorizzazione.
Il passo successivo dovr essere quello
di implementare mediante le classi ADO gi sviluppate la gestione
dei database.

Le classi di gestione delle basi di dati


Il sistema didentificazione retinale necessita anche di una parte
legata alla gestione dei database.
MICROSOFT possiede diversi metodi per la gestione di questi tra i
quali il metodo ODBC, quello ADO, il metodo OLEDB ecc.
Ad ogni modo il sistema ADO tra quelli pi funzionali ed inoltre
facilmente inseribile allinterno di calssi indirizzate alla
gestione del sistema.
I files che gestiscono la classe ADO Database sono quelli che
seguono.
//
//
//

MODULE: Ado.h

#ifndef _ADO_H_
#define _ADO_H_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#include <afx.h>
#include <afxdisp.h>
#include <math.h>
#pragma warning (disable: 4146)
// CG : In order to use this code against a different version of ADO, the
appropriate
// ADO library needs to be used in the #import statement
#import "C:\Program Files\Common Files\System\ADO\msado15.dll"
rename_namespace("ADOCG") rename("EOF", "EndOfFile")
using namespace ADOCG;
#pragma warning (default: 4146)
#include "icrsint.h"
class CADOCommand;
struct CADOFieldInfo
{
char m_strName[30];
short m_nType;
long m_lSize;
long m_lDefinedSize;
long m_lAttributes;
short m_nOrdinalPosition;
BOOL m_bRequired;
BOOL m_bAllowZeroLength;
long m_lCollatingOrder;
};
CString IntToStr(int nVal);
CString LongToStr(long lVal);
CString ULongToStr(unsigned long ulVal);

CString DblToStr(double dblVal, int ndigits = 20);


CString DblToStr(float fltVal);
class CADODatabase
{
public:
enum cadoConnectModeEnum
{
connectModeUnknown = adModeUnknown,
connectModeRead = adModeRead,
connectModeWrite = adModeWrite,
connectModeReadWrite = adModeReadWrite,
connectModeShareDenyRead = adModeShareDenyRead,
connectModeShareDenyWrite = adModeShareDenyWrite,
connectModeShareExclusive = adModeShareExclusive,
connectModeShareDenyNone = adModeShareDenyNone
};
CADODatabase()
{
::CoInitialize(NULL);

m_pConnection = NULL;
m_strConnection = _T("");
m_strLastError = _T("");
m_dwLastError = 0;
m_pConnection.CreateInstance(__uuidof(Connection));
m_nRecordsAffected = 0;
m_nConnectionTimeout = 0;

virtual ~CADODatabase()
{
Close();
m_pConnection.Release();
m_pConnection = NULL;
m_strConnection = _T("");
m_strLastError = _T("");
m_dwLastError = 0;
::CoUninitialize();
}
BOOL Open(LPCTSTR lpstrConnection = _T(""), LPCTSTR lpstrUserID = _T(""),
LPCTSTR lpstrPassword = _T(""));
_ConnectionPtr GetActiveConnection()
{return m_pConnection;};
BOOL Execute(LPCTSTR lpstrExec);
int GetRecordsAffected()
{return m_nRecordsAffected;};
DWORD GetRecordCount(_RecordsetPtr m_pRs);
long BeginTransaction()
{return m_pConnection->BeginTrans();};
long CommitTransaction()
{return m_pConnection->CommitTrans();};
long RollbackTransaction()
{return m_pConnection->RollbackTrans();};
BOOL IsOpen();
void Close();
void SetConnectionMode(cadoConnectModeEnum nMode)

{m_pConnection->PutMode((enum ConnectModeEnum)nMode);};
void SetConnectionString(LPCTSTR lpstrConnection)
{m_strConnection = lpstrConnection;};
CString GetConnectionString()
{return m_strConnection;};
CString GetLastErrorString()
{return m_strLastError;};
DWORD GetLastError()
{return m_dwLastError;};
CString GetErrorDescription()
{return m_strErrorDescription;};
void SetConnectionTimeout(long nConnectionTimeout = 30)
{m_nConnectionTimeout = nConnectionTimeout;};
protected:
void dump_com_error(_com_error &e);
public:
_ConnectionPtr m_pConnection;
protected:
CString m_strConnection;
CString m_strLastError;
CString m_strErrorDescription;
DWORD m_dwLastError;
int m_nRecordsAffected;
long m_nConnectionTimeout;
};
class CADORecordset
{
public:
BOOL Clone(CADORecordset& pRs);
enum cadoOpenEnum
{
openUnknown = 0,
openQuery = 1,
openTable = 2,
openStoredProc = 3
};
enum cadoEditEnum
{
dbEditNone = 0,
dbEditNew = 1,
dbEdit = 2
};
enum cadoPositionEnum
{
positionUnknown = -1,
positionBOF = -2,
positionEOF = -3
};
enum cadoSearchEnum
{
searchForward = 1,
searchBackward = -1
};

enum cadoDataType
{
typeEmpty = adEmpty,
typeTinyInt = adTinyInt,
typeSmallInt = adSmallInt,
typeInteger = adInteger,
typeBigInt = adBigInt,
typeUnsignedTinyInt = adUnsignedTinyInt,
typeUnsignedSmallInt = adUnsignedSmallInt,
typeUnsignedInt = adUnsignedInt,
typeUnsignedBigInt = adUnsignedBigInt,
typeSingle = adSingle,
typeDouble = adDouble,
typeCurrency = adCurrency,
typeDecimal = adDecimal,
typeNumeric = adNumeric,
typeBoolean = adBoolean,
typeError = adError,
typeUserDefined = adUserDefined,
typeVariant = adVariant,
typeIDispatch = adIDispatch,
typeIUnknown = adIUnknown,
typeGUID = adGUID,
typeDate = adDate,
typeDBDate = adDBDate,
typeDBTime = adDBTime,
typeDBTimeStamp = adDBTimeStamp,
typeBSTR = adBSTR,
typeChar = adChar,
typeVarChar = adVarChar,
typeLongVarChar = adLongVarChar,
typeWChar = adWChar,
typeVarWChar = adVarWChar,
typeLongVarWChar = adLongVarWChar,
typeBinary = adBinary,
typeVarBinary = adVarBinary,
typeLongVarBinary = adLongVarBinary,
typeChapter = adChapter,
typeFileTime = adFileTime,
typePropVariant = adPropVariant,
typeVarNumeric = adVarNumeric,
typeArray = adVariant
};
enum cadoSchemaType
{
schemaSpecific = adSchemaProviderSpecific,
schemaAsserts = adSchemaAsserts,
schemaCatalog = adSchemaCatalogs,
schemaCharacterSet = adSchemaCharacterSets,
schemaCollections = adSchemaCollations,
schemaColumns = adSchemaColumns,
schemaConstraints = adSchemaCheckConstraints,
schemaConstraintColumnUsage = adSchemaConstraintColumnUsage,
schemaConstraintTableUsage = adSchemaConstraintTableUsage,
shemaKeyColumnUsage = adSchemaKeyColumnUsage,
schemaTableConstraints = adSchemaTableConstraints,
schemaColumnsDomainUsage = adSchemaColumnsDomainUsage,
schemaIndexes = adSchemaIndexes,
schemaColumnPrivileges = adSchemaColumnPrivileges,
schemaTablePrivileges = adSchemaTablePrivileges,

};
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL

schemaUsagePrivileges = adSchemaUsagePrivileges,
schemaProcedures = adSchemaProcedures,
schemaTables =adSchemaTables,
schemaProviderTypes = adSchemaProviderTypes,
schemaViews = adSchemaViews,
schemaProcedureParameters = adSchemaProcedureParameters,
schemaForeignKeys = adSchemaForeignKeys,
schemaPrimaryKeys = adSchemaPrimaryKeys,
schemaProcedureColumns = adSchemaProcedureColumns,
schemaDBInfoKeywords = adSchemaDBInfoKeywords,
schemaDBInfoLiterals = adSchemaDBInfoLiterals,
schemaCubes = adSchemaCubes,
schemaDimensions = adSchemaDimensions,
schemaHierarchies = adSchemaHierarchies,
schemaLevels = adSchemaLevels,
schemaMeasures = adSchemaMeasures,
schemaProperties = adSchemaProperties,
schemaMembers = adSchemaMembers,

SetFieldValue(int nIndex, int nValue);


SetFieldValue(LPCTSTR lpFieldName, int nValue);
SetFieldValue(int nIndex, long lValue);
SetFieldValue(LPCTSTR lpFieldName, long lValue);
SetFieldValue(int nIndex, unsigned long lValue);
SetFieldValue(LPCTSTR lpFieldName, unsigned long lValue);
SetFieldValue(int nIndex, double dblValue);
SetFieldValue(LPCTSTR lpFieldName, double dblValue);
SetFieldValue(int nIndex, CString strValue);
SetFieldValue(LPCTSTR lpFieldName, CString strValue);
SetFieldValue(int nIndex, COleDateTime time);
SetFieldValue(LPCTSTR lpFieldName, COleDateTime time);
SetFieldValue(int nIndex, bool bValue);
SetFieldValue(LPCTSTR lpFieldName, bool bValue);
SetFieldValue(int nIndex, COleCurrency cyValue);
SetFieldValue(LPCTSTR lpFieldName, COleCurrency cyValue);
SetFieldValue(int nIndex, _variant_t vtValue);
SetFieldValue(LPCTSTR lpFieldName, _variant_t vtValue);

BOOL SetFieldEmpty(int nIndex);


BOOL SetFieldEmpty(LPCTSTR lpFieldName);
void
BOOL
void
BOOL
BOOL

CancelUpdate();
Update();
Edit();
AddNew();
AddNew(CADORecordBinding &pAdoRecordBinding);

BOOL Find(LPCTSTR lpFind, int nSearchDirection =


CADORecordset::searchForward);
BOOL FindFirst(LPCTSTR lpFind);
BOOL FindNext();
CADORecordset();
CADORecordset(CADODatabase* pAdoDatabase);
virtual ~CADORecordset()
{
Close();
if(m_pRecordset)

m_pRecordset.Release();
if(m_pCmd)
m_pCmd.Release();
m_pRecordset = NULL;
m_pCmd = NULL;
m_pRecBinding = NULL;
m_strQuery = _T("");
m_strLastError = _T("");
m_dwLastError = 0;
m_nEditStatus = dbEditNone;

CString GetQuery()
{return m_strQuery;};
void SetQuery(LPCSTR strQuery)
{m_strQuery = strQuery;};
BOOL RecordBinding(CADORecordBinding &pAdoRecordBinding);
DWORD GetRecordCount();
BOOL IsOpen();
void Close();
BOOL Open(_ConnectionPtr mpdb, LPCTSTR lpstrExec = _T(""), int nOption =
CADORecordset::openUnknown);
BOOL Open(LPCTSTR lpstrExec = _T(""), int nOption =
CADORecordset::openUnknown);
BOOL OpenSchema(int nSchema, LPCTSTR SchemaID = _T(""));
long GetFieldCount()
{return m_pRecordset->Fields->GetCount();};
BOOL GetFieldValue(LPCTSTR lpFieldName, int& nValue);
BOOL GetFieldValue(int nIndex, int& nValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, long& lValue);
BOOL GetFieldValue(int nIndex, long& lValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, unsigned long& ulValue);
BOOL GetFieldValue(int nIndex, unsigned long& ulValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, double& dbValue);
BOOL GetFieldValue(int nIndex, double& dbValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, CString& strValue, CString
strDateFormat = _T(""));
BOOL GetFieldValue(int nIndex, CString& strValue, CString strDateFormat =
_T(""));
BOOL GetFieldValue(LPCTSTR lpFieldName, COleDateTime& time);
BOOL GetFieldValue(int nIndex, COleDateTime& time);
BOOL GetFieldValue(int nIndex, bool& bValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, bool& bValue);
BOOL GetFieldValue(int nIndex, COleCurrency& cyValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, COleCurrency& cyValue);
BOOL GetFieldValue(int nIndex, _variant_t& vtValue);
BOOL GetFieldValue(LPCTSTR lpFieldName, _variant_t& vtValue);
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
void

IsFieldNull(LPCTSTR lpFieldName);
IsFieldNull(int nIndex);
IsFieldEmpty(LPCTSTR lpFieldName);
IsFieldEmpty(int nIndex);
IsEof()
{return m_pRecordset->EndOfFile == VARIANT_TRUE;};
IsEOF()
{return m_pRecordset->EndOfFile == VARIANT_TRUE;};
IsBof()
{return m_pRecordset->BOF == VARIANT_TRUE;};
IsBOF()
{return m_pRecordset->BOF == VARIANT_TRUE;};
MoveFirst()
{m_pRecordset->MoveFirst();};

void MoveNext()
{m_pRecordset->MoveNext();};
void MovePrevious()
{m_pRecordset->MovePrevious();};
void MoveLast()
{m_pRecordset->MoveLast();};
long GetAbsolutePage()
{return m_pRecordset->GetAbsolutePage();};
void SetAbsolutePage(int nPage)
{m_pRecordset->PutAbsolutePage((enum PositionEnum)nPage);};
long GetPageCount()
{return m_pRecordset->GetPageCount();};
long GetPageSize()
{return m_pRecordset->GetPageSize();};
void SetPageSize(int nSize)
{m_pRecordset->PutPageSize(nSize);};
long GetAbsolutePosition()
{return m_pRecordset->GetAbsolutePosition();};
void SetAbsolutePosition(int nPosition)
{m_pRecordset->PutAbsolutePosition((enum PositionEnum)nPosition);};
BOOL GetFieldInfo(LPCTSTR lpFieldName, CADOFieldInfo* fldInfo);
BOOL GetFieldInfo(int nIndex, CADOFieldInfo* fldInfo);
BOOL AppendChunk(LPCTSTR lpFieldName, LPVOID lpData, UINT nBytes);
BOOL AppendChunk(int nIndex, LPVOID lpData, UINT nBytes);
BOOL GetChunk(LPCTSTR lpFieldName, CString& strValue);
BOOL GetChunk(int nIndex, CString& strValue);
BOOL GetChunk(LPCTSTR lpFieldName, LPVOID pData);
BOOL GetChunk(int nIndex, LPVOID pData);
CString GetString(LPCTSTR lpCols, LPCTSTR lpRows, LPCTSTR lpNull, long
numRows = 0);
CString GetLastErrorString()
{return m_strLastError;};
DWORD GetLastError()
{return m_dwLastError;};
void GetBookmark()
{m_varBookmark = m_pRecordset->Bookmark;};
BOOL SetBookmark();
BOOL Delete();
BOOL IsConnectionOpen()
{return m_pConnection != NULL && m_pConnection->GetState() !=
adStateClosed;};
_RecordsetPtr GetRecordset()
{return m_pRecordset;};
_ConnectionPtr GetActiveConnection()
{return m_pConnection;};
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL

SetFilter(LPCTSTR strFilter);
SetSort(LPCTSTR lpstrCriteria);
SaveAsXML(LPCTSTR lpstrXMLFile);
OpenXML(LPCTSTR lpstrXMLFile);
Execute(CADOCommand* pCommand);
Requery();

public:
_RecordsetPtr m_pRecordset;
_CommandPtr m_pCmd;
protected:
_ConnectionPtr m_pConnection;

int m_nSearchDirection;
CString m_strFind;
_variant_t m_varBookFind;
_variant_t m_varBookmark;
int m_nEditStatus;
CString m_strLastError;
DWORD m_dwLastError;
void dump_com_error(_com_error &e);
IADORecordBinding *m_pRecBinding;
CString m_strQuery;
protected:
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL

PutFieldValue(LPCTSTR lpFieldName, _variant_t vtFld);


PutFieldValue(_variant_t vtIndex, _variant_t vtFld);
GetFieldInfo(FieldPtr pField, CADOFieldInfo* fldInfo);
GetChunk(FieldPtr pField, CString& strValue);
GetChunk(FieldPtr pField, LPVOID lpData);
AppendChunk(FieldPtr pField, LPVOID lpData, UINT nBytes);

};
class CADOParameter
{
public:
enum cadoParameterDirection
{
paramUnknown = adParamUnknown,
paramInput = adParamInput,
paramOutput = adParamOutput,
paramInputOutput = adParamInputOutput,
paramReturnValue = adParamReturnValue
};
CADOParameter(int nType, long lSize = 0, int nDirection = paramInput,
CString strName = _T(""));
virtual ~CADOParameter()
{
m_pParameter.Release();
m_pParameter = NULL;
m_strName = _T("");
}
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
BOOL
void

SetValue(int nValue);
SetValue(long lValue);
SetValue(double dbValue);
SetValue(CString strValue);
SetValue(COleDateTime time);
SetValue(_variant_t vtValue);
GetValue(int& nValue);
GetValue(long& lValue);
GetValue(double& dbValue);
GetValue(CString& strValue, CString strDateFormat = _T(""));
GetValue(COleDateTime& time);
GetValue(_variant_t& vtValue);
SetPrecision(int nPrecision)
{m_pParameter->PutPrecision(nPrecision);};
void SetScale(int nScale)
{m_pParameter->PutNumericScale(nScale);};
void SetName(CString strName)

{m_strName = strName;};
CString GetName()
{return m_strName;};
int GetType()
{return m_nType;};
_ParameterPtr GetParameter()
{return m_pParameter;};
protected:
void dump_com_error(_com_error &e);
protected:
_ParameterPtr m_pParameter;
CString m_strName;
int m_nType;
CString m_strLastError;
DWORD m_dwLastError;
};
class CADOCommand
{
public:
enum cadoCommandType
{
typeCmdText = adCmdText,
typeCmdTable = adCmdTable,
typeCmdTableDirect = adCmdTableDirect,
typeCmdStoredProc = adCmdStoredProc,
typeCmdUnknown = adCmdUnknown,
typeCmdFile = adCmdFile
};
CADOCommand(CADODatabase* pAdoDatabase, CString strCommandText = _T(""),
int nCommandType = typeCmdStoredProc);
virtual ~CADOCommand()
{
m_pCommand.Release();
m_pCommand = NULL;
m_strCommandText = _T("");
}
void SetTimeout(long nTimeOut)
{m_pCommand->PutCommandTimeout(nTimeOut);};
void SetText(CString strCommandText);
void SetType(int nCommandType);
int GetType()
{return m_nCommandType;};
BOOL AddParameter(CADOParameter* pAdoParameter);
BOOL AddParameter(CString strName, int nType, int nDirection,
int nValue);
BOOL AddParameter(CString strName, int nType, int nDirection,
long lValue);
BOOL AddParameter(CString strName, int nType, int nDirection,
double dblValue, int nPrecision = 0, int nScale = 0);
BOOL AddParameter(CString strName, int nType, int nDirection,
CString strValue);
BOOL AddParameter(CString strName, int nType, int nDirection,
COleDateTime time);
BOOL AddParameter(CString strName, int nType, int nDirection,
_variant_t vtValue, int nPrecision = 0, int nScale = 0);
CString GetText()

long lSize,
long lSize,
long lSize,
long lSize,
long lSize,
long lSize,

{return m_strCommandText;};
BOOL Execute();
int GetRecordsAffected()
{return m_nRecordsAffected;};
_CommandPtr GetCommand()
{return m_pCommand;};
protected:
void dump_com_error(_com_error &e);
protected:
_CommandPtr m_pCommand;
int m_nCommandType;
int m_nRecordsAffected;
CString m_strCommandText;
CString m_strLastError;
DWORD m_dwLastError;
};
class CADOException : public CException
{
public:
enum
{
noError,
Unknown,

// no error
// unknown error

};
DECLARE_DYNAMIC(CADOException);
CADOException(int nCause = 0, CString strErrorString = _T(""));
virtual ~CADOException();
static int GetError(int nADOError);
public:
int m_nCause;
CString m_strErrorString;
protected:
};
void AfxThrowADOException(int nADOError = 1000, CString strErrorString =
_T(""));
#endif

Il file .CPP con il codice di gestione invece quello che segue.


//
//
//
//

MODULE: Ado.cpp

#include "ado.h"
#define ChunkSize 100
///////////////////////////////////////////////////////
//

// CADODatabase Class
//
DWORD CADODatabase::GetRecordCount(_RecordsetPtr m_pRs)
{
DWORD numRows = 0;
numRows = m_pRs->GetRecordCount();
if(numRows == -1)
{
if(m_pRs->EndOfFile != VARIANT_TRUE)
m_pRs->MoveFirst();
while(m_pRs->EndOfFile != VARIANT_TRUE)
{
numRows++;
m_pRs->MoveNext();
}
if(numRows > 0)
m_pRs->MoveFirst();
}
return numRows;
}
BOOL CADODatabase::Open(LPCTSTR lpstrConnection, LPCTSTR lpstrUserID, LPCTSTR
lpstrPassword)
{
HRESULT hr = S_OK;
if(IsOpen())
Close();
if(strcmp(lpstrConnection, _T("")) != 0)
m_strConnection = lpstrConnection;
ASSERT(!m_strConnection.IsEmpty());
try
{

if(m_nConnectionTimeout != 0)
m_pConnection->PutConnectionTimeout(m_nConnectionTimeout);
hr = m_pConnection->Open(_bstr_t(m_strConnection),
_bstr_t(lpstrUserID), _bstr_t(lpstrPassword), NULL);
return hr == S_OK;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
void CADODatabase::dump_com_error(_com_error &e)
{
CString ErrorStr;
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
ErrorStr.Format( "CADODataBase Error\n\tCode = %08lx\n\tCode meaning =
%s\n\tSource = %s\n\tDescription = %s\n",

e.Error(), e.ErrorMessage(), (LPCSTR)bstrSource,


(LPCSTR)bstrDescription);
m_strErrorDescription = (LPCSTR)bstrDescription ;
m_strLastError = _T("Connection String = " + GetConnectionString() + '\n'
+ ErrorStr);
m_dwLastError = e.Error();
#ifdef _DEBUG
AfxMessageBox(ErrorStr, MB_OK | MB_ICONERROR );
#endif
}
BOOL CADODatabase::IsOpen()
{
if(m_pConnection )
return m_pConnection->GetState() != adStateClosed;
return FALSE;
}
void CADODatabase::Close()
{
if(IsOpen())
m_pConnection->Close();
}
///////////////////////////////////////////////////////
//
// CADORecordset Class
//
CADORecordset::CADORecordset()
{
m_pRecordset = NULL;
m_pCmd = NULL;
m_strQuery = _T("");
m_strLastError = _T("");
m_dwLastError = 0;
m_pRecBinding = NULL;
m_pRecordset.CreateInstance(__uuidof(Recordset));
m_pCmd.CreateInstance(__uuidof(Command));
m_nEditStatus = CADORecordset::dbEditNone;
m_nSearchDirection = CADORecordset::searchForward;
}
CADORecordset::CADORecordset(CADODatabase* pAdoDatabase)
{
m_pRecordset = NULL;
m_pCmd = NULL;
m_strQuery = _T("");
m_strLastError = _T("");
m_dwLastError = 0;
m_pRecBinding = NULL;
m_pRecordset.CreateInstance(__uuidof(Recordset));
m_pCmd.CreateInstance(__uuidof(Command));
m_nEditStatus = CADORecordset::dbEditNone;
m_nSearchDirection = CADORecordset::searchForward;
}

m_pConnection = pAdoDatabase->GetActiveConnection();

BOOL CADORecordset::Open(_ConnectionPtr mpdb, LPCTSTR lpstrExec, int nOption)


{

Close();
if(strcmp(lpstrExec, _T("")) != 0)
m_strQuery = lpstrExec;
ASSERT(!m_strQuery.IsEmpty());
if(m_pConnection == NULL)
m_pConnection = mpdb;
m_strQuery.TrimLeft();
BOOL bIsSelect = m_strQuery.Mid(0, strlen("Select
")).CompareNoCase("select ") == 0 && nOption == openUnknown;
try
{

m_pRecordset->CursorType = adOpenStatic;
m_pRecordset->CursorLocation = adUseClient;
if(bIsSelect || nOption == openQuery || nOption == openUnknown)
m_pRecordset->Open((LPCSTR)m_strQuery,
_variant_t((IDispatch*)mpdb, TRUE),
adOpenStatic, adLockOptimistic,
adCmdUnknown);
else if(nOption == openTable)
m_pRecordset->Open((LPCSTR)m_strQuery,
_variant_t((IDispatch*)mpdb, TRUE),
adOpenKeyset, adLockOptimistic,
adCmdTable);
else if(nOption == openStoredProc)
{
m_pCmd->ActiveConnection = mpdb;
m_pCmd->CommandText = _bstr_t(m_strQuery);
m_pCmd->CommandType = adCmdStoredProc;
m_pConnection->CursorLocation = adUseClient;
}
else
{

m_pRecordset = m_pCmd->Execute(NULL, NULL, adCmdText);

TRACE( "Unknown parameter. %d", nOption);


return FALSE;
}
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}

return m_pRecordset != NULL;

BOOL CADORecordset::Open(LPCTSTR lpstrExec, int nOption)


{
ASSERT(m_pConnection != NULL);
ASSERT(m_pConnection->GetState() != adStateClosed);
return Open(m_pConnection, lpstrExec, nOption);
}
BOOL CADORecordset::OpenSchema(int nSchema, LPCTSTR SchemaID)
{
try

{
_variant_t vtSchemaID = vtMissing;
if(strlen(SchemaID) != 0)
vtSchemaID = SchemaID;
m_pRecordset = m_pConnection->OpenSchema((enum SchemaEnum)nSchema,
vtMissing, vtSchemaID);
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::Requery()
{
if(IsOpen())
{
try
{
m_pRecordset->Requery(adExecuteRecord);
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
return TRUE;
}
BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, double& dbValue)
{
double val = (double)NULL;
_variant_t vtFld;
try
{

vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
switch(vtFld.vt)
{
case VT_R4:
val = vtFld.fltVal;
break;
case VT_R8:
val = vtFld.dblVal;
break;
case VT_DECIMAL:
//Corrected by Jos Carlos Martnez Galn
val = vtFld.decVal.Lo32;
val *= (vtFld.decVal.sign == 128)? -1 : 1;
val /= pow(10, vtFld.decVal.scale);
break;
case VT_UI1:
val = vtFld.iVal;
break;
case VT_I2:
case VT_I4:

val = vtFld.lVal;
break;
case VT_INT:
val = vtFld.intVal;
break;
case VT_NULL:
case VT_EMPTY:
val = 0;
break;
default:
val = vtFld.dblVal;
}
dbValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(int nIndex, double& dbValue)


{
double val = (double)NULL;
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{

vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
switch(vtFld.vt)
{
case VT_R4:
val = vtFld.fltVal;
break;
case VT_R8:
val = vtFld.dblVal;
break;
case VT_DECIMAL:
//Corrected by Jos Carlos Martnez Galn
val = vtFld.decVal.Lo32;
val *= (vtFld.decVal.sign == 128)? -1 : 1;
val /= pow(10, vtFld.decVal.scale);
break;
case VT_UI1:
val = vtFld.iVal;
break;
case VT_I2:
case VT_I4:
val = vtFld.lVal;
break;
case VT_INT:
val = vtFld.intVal;
break;
case VT_NULL:
case VT_EMPTY:
val = 0;

break;
default:
val = 0;
}
dbValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, long& lValue)


{
long val = (long)NULL;
_variant_t vtFld;
try
{

vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
if(vtFld.vt != VT_NULL && vtFld.vt != VT_EMPTY)
val = vtFld.lVal;
lValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::GetFieldValue(int nIndex, long& lValue)
{
long val = (long)NULL;
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{

vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
if(vtFld.vt != VT_NULL && vtFld.vt != VT_EMPTY)
val = vtFld.lVal;
lValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, unsigned long& ulValue)
{

long val = (long)NULL;


_variant_t vtFld;
try
{
vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
if(vtFld.vt != VT_NULL && vtFld.vt != VT_EMPTY)
val = vtFld.ulVal;
ulValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(int nIndex, unsigned long& ulValue)


{
long val = (long)NULL;
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{
vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
if(vtFld.vt != VT_NULL && vtFld.vt != VT_EMPTY)
val = vtFld.ulVal;
ulValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, int& nValue)


{
int val = NULL;
_variant_t vtFld;
try
{
vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
switch(vtFld.vt)
{
case VT_BOOL:
val = vtFld.boolVal;
break;
case VT_I2:
case VT_UI1:
val = vtFld.iVal;
break;
case VT_INT:
val = vtFld.intVal;
break;

case VT_NULL:
case VT_EMPTY:
val = 0;
break;
default:
val = vtFld.iVal;
}
nValue = val;
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::GetFieldValue(int nIndex, int& nValue)
{
int val = (int)NULL;
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{

vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
switch(vtFld.vt)
{
case VT_BOOL:
val = vtFld.boolVal;
break;
case VT_I2:
case VT_UI1:
val = vtFld.iVal;
break;
case VT_INT:
val = vtFld.intVal;
break;
case VT_NULL:
case VT_EMPTY:
val = 0;
break;
default:
val = vtFld.iVal;
}
nValue = val;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, CString& strValue,


CString strDateFormat)
{
CString str = _T("");

_variant_t vtFld;
try
{

vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
switch(vtFld.vt)
{
case VT_R4:
str = DblToStr(vtFld.fltVal);
break;
case VT_R8:
str = DblToStr(vtFld.dblVal);
break;
case VT_BSTR:
str = vtFld.bstrVal;
break;
case VT_I2:
case VT_UI1:
str = IntToStr(vtFld.iVal);
break;
case VT_INT:
str = IntToStr(vtFld.intVal);
break;
case VT_I4:
str = LongToStr(vtFld.lVal);
break;
case VT_UI4:
str = ULongToStr(vtFld.ulVal);
break;
case VT_DECIMAL:
{
//Corrected by Jos Carlos Martnez Galn
double val = vtFld.decVal.Lo32;
val *= (vtFld.decVal.sign == 128)? -1 : 1;
val /= pow(10, vtFld.decVal.scale);
str = DblToStr(val);
}
break;
case VT_DATE:
{
COleDateTime dt(vtFld);
if(strDateFormat.IsEmpty())
strDateFormat = _T("%Y-%m-%d %H:%M:%S");
str = dt.Format(strDateFormat);
}
break;
case VT_EMPTY:
case VT_NULL:
str.Empty();
break;
case VT_BOOL:
str = vtFld.boolVal == VARIANT_TRUE? 'T':'F';
break;
default:
str.Empty();
return FALSE;
}
strValue = str;
return TRUE;

}
catch(_com_error &e)

{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(int nIndex, CString& strValue, CString


strDateFormat)
{
CString str = _T("");
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{

vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
switch(vtFld.vt)
{
case VT_R4:
str = DblToStr(vtFld.fltVal);
break;
case VT_R8:
str = DblToStr(vtFld.dblVal);
break;
case VT_BSTR:
str = vtFld.bstrVal;
break;
case VT_I2:
case VT_UI1:
str = IntToStr(vtFld.iVal);
break;
case VT_INT:
str = IntToStr(vtFld.intVal);
break;
case VT_I4:
str = LongToStr(vtFld.lVal);
break;
case VT_UI4:
str = ULongToStr(vtFld.ulVal);
break;
case VT_DECIMAL:
{
//Corrected by Jos Carlos Martnez Galn
double val = vtFld.decVal.Lo32;
val *= (vtFld.decVal.sign == 128)? -1 : 1;
val /= pow(10, vtFld.decVal.scale);
str = DblToStr(val);
}
break;
case VT_DATE:
{
COleDateTime dt(vtFld);
if(strDateFormat.IsEmpty())
strDateFormat = _T("%Y-%m-%d %H:%M:%S");
str = dt.Format(strDateFormat);
}
break;
case VT_BOOL:

str = vtFld.boolVal == VARIANT_TRUE? 'T':'F';


break;
case VT_EMPTY:
case VT_NULL:
str.Empty();
break;
default:
str.Empty();
return FALSE;
}
strValue = str;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, COleDateTime& time)


{
_variant_t vtFld;
try
{

vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
switch(vtFld.vt)
{
case VT_DATE:
{
COleDateTime dt(vtFld);
time = dt;
}
break;
case VT_EMPTY:
case VT_NULL:
time.SetStatus(COleDateTime::null);
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::GetFieldValue(int nIndex, COleDateTime& time)
{
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{
vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;

switch(vtFld.vt)
{
case VT_DATE:
{
COleDateTime dt(vtFld);
time = dt;
}
break;
case VT_EMPTY:
case VT_NULL:
time.SetStatus(COleDateTime::null);
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, bool& bValue)


{
_variant_t vtFld;
try
{

vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
switch(vtFld.vt)
{
case VT_BOOL:
bValue = vtFld.boolVal == VARIANT_TRUE? true: false;
break;
case VT_EMPTY:
case VT_NULL:
bValue = false;
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(int nIndex, bool& bValue)


{
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{

vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
switch(vtFld.vt)
{
case VT_BOOL:
bValue = vtFld.boolVal == VARIANT_TRUE? true: false;
break;
case VT_EMPTY:
case VT_NULL:
bValue = false;
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, COleCurrency& cyValue)


{
_variant_t vtFld;
try
{

vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
switch(vtFld.vt)
{
case VT_CY:
cyValue = (CURRENCY)vtFld.cyVal;
break;
case VT_EMPTY:
case VT_NULL:
{
cyValue = COleCurrency();
cyValue.m_status = COleCurrency::null;
}
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::GetFieldValue(int nIndex, COleCurrency& cyValue)
{
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try

{
vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
switch(vtFld.vt)
{
case VT_CY:
cyValue = (CURRENCY)vtFld.cyVal;
break;
case VT_EMPTY:
case VT_NULL:
{
cyValue = COleCurrency();
cyValue.m_status = COleCurrency::null;
}
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::GetFieldValue(LPCTSTR lpFieldName, _variant_t& vtValue)


{
try
{
vtValue = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::GetFieldValue(int nIndex, _variant_t& vtValue)
{
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{
vtValue = m_pRecordset->Fields->GetItem(vtIndex)->Value;
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::IsFieldNull(LPCTSTR lpFieldName)
{
_variant_t vtFld;

try
{
vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
return vtFld.vt == VT_NULL;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::IsFieldNull(int nIndex)
{
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{
vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
return vtFld.vt == VT_NULL;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::IsFieldEmpty(LPCTSTR lpFieldName)
{
_variant_t vtFld;
try
{
vtFld = m_pRecordset->Fields->GetItem(lpFieldName)->Value;
return vtFld.vt == VT_EMPTY || vtFld.vt == VT_NULL;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::IsFieldEmpty(int nIndex)
{
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
try
{
vtFld = m_pRecordset->Fields->GetItem(vtIndex)->Value;
return vtFld.vt == VT_EMPTY || vtFld.vt == VT_NULL;
}

catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::SetFieldEmpty(LPCTSTR lpFieldName)
{
_variant_t vtFld;
vtFld.vt = VT_EMPTY;
return PutFieldValue(lpFieldName, vtFld);
}
BOOL CADORecordset::SetFieldEmpty(int nIndex)
{
_variant_t vtFld;
vtFld.vt = VT_EMPTY;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
}

return PutFieldValue(vtIndex, vtFld);

DWORD CADORecordset::GetRecordCount()
{
DWORD nRows = 0;
nRows = m_pRecordset->GetRecordCount();
if(nRows == -1)
{
nRows = 0;
if(m_pRecordset->EndOfFile != VARIANT_TRUE)
m_pRecordset->MoveFirst();

while(m_pRecordset->EndOfFile != VARIANT_TRUE)
{
nRows++;
m_pRecordset->MoveNext();
}
if(nRows > 0)
m_pRecordset->MoveFirst();

return nRows;
}
BOOL CADORecordset::IsOpen()
{
if(m_pRecordset != NULL && IsConnectionOpen())
return m_pRecordset->GetState() != adStateClosed;
return FALSE;
}
void CADORecordset::Close()
{

if(IsOpen())
{
if (m_nEditStatus != dbEditNone)
CancelUpdate();
m_pRecordset->PutSort(_T(""));
m_pRecordset->Close();
}

BOOL CADODatabase::Execute(LPCTSTR lpstrExec)


{
ASSERT(m_pConnection != NULL);
ASSERT(strcmp(lpstrExec, _T("")) != 0);
_variant_t vRecords;
m_nRecordsAffected = 0;
try
{
m_pConnection->CursorLocation = adUseClient;
m_pConnection->Execute(_bstr_t(lpstrExec), &vRecords,
adExecuteNoRecords);
m_nRecordsAffected = vRecords.iVal;
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::RecordBinding(CADORecordBinding &pAdoRecordBinding)
{
HRESULT hr;
m_pRecBinding = NULL;
//Open the binding interface.
if(FAILED(hr = m_pRecordset->QueryInterface(__uuidof(IADORecordBinding),
(LPVOID*)&m_pRecBinding )))
{
_com_issue_error(hr);
return FALSE;
}

//Bind the recordset to class


if(FAILED(hr = m_pRecBinding->BindToRecordset(&pAdoRecordBinding)))
{
_com_issue_error(hr);
return FALSE;
}
return TRUE;

BOOL CADORecordset::GetFieldInfo(LPCTSTR lpFieldName, CADOFieldInfo* fldInfo)


{
FieldPtr pField = m_pRecordset->Fields->GetItem(lpFieldName);
return GetFieldInfo(pField, fldInfo);
}

BOOL CADORecordset::GetFieldInfo(int nIndex, CADOFieldInfo* fldInfo)


{
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
FieldPtr pField = m_pRecordset->Fields->GetItem(vtIndex);
return GetFieldInfo(pField, fldInfo);
}
BOOL CADORecordset::GetFieldInfo(FieldPtr pField, CADOFieldInfo* fldInfo)
{
memset(fldInfo, 0, sizeof(CADOFieldInfo));
strcpy(fldInfo->m_strName, (LPCTSTR)pField->GetName());
fldInfo->m_lDefinedSize = pField->GetDefinedSize();
fldInfo->m_nType = pField->GetType();
fldInfo->m_lAttributes = pField->GetAttributes();
if(!IsEof())
fldInfo->m_lSize = pField->GetActualSize();
return TRUE;
}
BOOL CADORecordset::GetChunk(LPCTSTR lpFieldName, CString& strValue)
{
FieldPtr pField = m_pRecordset->Fields->GetItem(lpFieldName);
}

return GetChunk(pField, strValue);

BOOL CADORecordset::GetChunk(int nIndex, CString& strValue)


{
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
FieldPtr pField = m_pRecordset->Fields->GetItem(vtIndex);
}

return GetChunk(pField, strValue);

BOOL CADORecordset::GetChunk(FieldPtr pField, CString& strValue)


{
CString str = _T("");
long lngSize, lngOffSet = 0;
_variant_t varChunk;
lngSize = pField->ActualSize;
str.Empty();
while(lngOffSet < lngSize)
{
try
{
varChunk = pField->GetChunk(ChunkSize);

str += varChunk.bstrVal;
lngOffSet += ChunkSize;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

lngOffSet = 0;
strValue = str;
return TRUE;
}
BOOL CADORecordset::GetChunk(LPCTSTR lpFieldName, LPVOID lpData)
{
FieldPtr pField = m_pRecordset->Fields->GetItem(lpFieldName);
}

return GetChunk(pField, lpData);

BOOL CADORecordset::GetChunk(int nIndex, LPVOID lpData)


{
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
FieldPtr pField = m_pRecordset->Fields->GetItem(vtIndex);
}

return GetChunk(pField, lpData);

BOOL CADORecordset::GetChunk(FieldPtr pField, LPVOID lpData)


{
long lngSize, lngOffSet = 0;
_variant_t varChunk;
UCHAR chData;
HRESULT hr;
long lBytesCopied = 0;
lngSize = pField->ActualSize;
while(lngOffSet < lngSize)
{
try
{
varChunk = pField->GetChunk(ChunkSize);
//Copy the data only upto the Actual Size of Field.
for(long lIndex = 0; lIndex <= (ChunkSize - 1); lIndex++)
{
hr= SafeArrayGetElement(varChunk.parray, &lIndex, &chData);
if(SUCCEEDED(hr))
{
//Take BYTE by BYTE and advance Memory Location
//hr = SafeArrayPutElement((SAFEARRAY FAR*)lpData,
&lBytesCopied ,&chData);
((UCHAR*)lpData)[lBytesCopied] = chData;
lBytesCopied++;
}

else
break;

lngOffSet += ChunkSize;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

lngOffSet = 0;
return TRUE;

BOOL CADORecordset::AppendChunk(LPCTSTR lpFieldName, LPVOID lpData, UINT nBytes)


{
FieldPtr pField = m_pRecordset->Fields->GetItem(lpFieldName);
}

return AppendChunk(pField, lpData, nBytes);

BOOL CADORecordset::AppendChunk(int nIndex, LPVOID lpData, UINT nBytes)


{
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
FieldPtr pField = m_pRecordset->Fields->GetItem(vtIndex);
return AppendChunk(pField, lpData, nBytes);
}
BOOL CADORecordset::AppendChunk(FieldPtr pField, LPVOID lpData, UINT nBytes)
{
HRESULT hr;
_variant_t varChunk;
long lngOffset = 0;
UCHAR chData;
SAFEARRAY FAR *psa = NULL;
SAFEARRAYBOUND rgsabound[1];
try
{

//Create a safe array to store the array of BYTES


rgsabound[0].lLbound = 0;
rgsabound[0].cElements = nBytes;
psa = SafeArrayCreate(VT_UI1,1,rgsabound);
while(lngOffset < (long)nBytes)
{
chData
= ((UCHAR*)lpData)[lngOffset];
hr = SafeArrayPutElement(psa, &lngOffset, &chData);
if(FAILED(hr))
return FALSE;
lngOffset++;

}
lngOffset = 0;
//Assign the Safe array to a variant.
varChunk.vt = VT_ARRAY|VT_UI1;
varChunk.parray = psa;
hr = pField->AppendChunk(varChunk);
if(SUCCEEDED(hr)) return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}

return FALSE;

CString CADORecordset::GetString(LPCTSTR lpCols, LPCTSTR lpRows, LPCTSTR lpNull,


long numRows)
{
_bstr_t varOutput;
_bstr_t varNull("");
_bstr_t varCols("\t");
_bstr_t varRows("\r");
if(strlen(lpCols) != 0)
varCols = _bstr_t(lpCols);
if(strlen(lpRows) != 0)
varRows = _bstr_t(lpRows);
if(numRows == 0)
numRows =(long)GetRecordCount();
varOutput = m_pRecordset->GetString(adClipString, numRows, varCols,
varRows, varNull);
return (LPCTSTR)varOutput;
}
CString IntToStr(int nVal)
{
CString strRet;
char buff[10];
itoa(nVal, buff, 10);
strRet = buff;
return strRet;
}
CString LongToStr(long lVal)
{
CString strRet;
char buff[20];
ltoa(lVal, buff, 10);
strRet = buff;
return strRet;
}

CString ULongToStr(unsigned long ulVal)


{
CString strRet;
char buff[20];
ultoa(ulVal, buff, 10);
strRet = buff;
return strRet;
}
CString DblToStr(double dblVal, int ndigits)
{
CString strRet;
char buff[50];
_gcvt(dblVal, ndigits, buff);
strRet = buff;
return strRet;
}
CString DblToStr(float fltVal)
{
CString strRet = _T("");
char buff[50];
_gcvt(fltVal, 10, buff);
strRet = buff;
return strRet;
}
void CADORecordset::Edit()
{
m_nEditStatus = dbEdit;
}
BOOL CADORecordset::AddNew()
{
m_nEditStatus = dbEditNone;
if(m_pRecordset->AddNew() != S_OK)
return FALSE;

m_nEditStatus = dbEditNew;
return TRUE;

BOOL CADORecordset::AddNew(CADORecordBinding &pAdoRecordBinding)


{
try
{
if(m_pRecBinding->AddNew(&pAdoRecordBinding) != S_OK)
{
return FALSE;
}
else
{
m_pRecBinding->Update(&pAdoRecordBinding);
return TRUE;
}

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::Update()
{
BOOL bret = TRUE;
if(m_nEditStatus != dbEditNone)
{
try
{
if(m_pRecordset->Update() != S_OK)
bret = FALSE;
}
catch(_com_error &e)
{
dump_com_error(e);
bret = FALSE;
}
if(!bret)
m_pRecordset->CancelUpdate();
m_nEditStatus = dbEditNone;
}
return bret;
}
void CADORecordset::CancelUpdate()
{
m_pRecordset->CancelUpdate();
m_nEditStatus = dbEditNone;
}
BOOL CADORecordset::SetFieldValue(int nIndex, CString strValue)
{
_variant_t vtFld;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
if(!strValue.IsEmpty())
vtFld.vt = VT_BSTR;
else
vtFld.vt = VT_NULL;
//Corrected by Giles Forster 10/03/2001
vtFld.bstrVal = strValue.AllocSysString();
}

return PutFieldValue(vtIndex, vtFld);

BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, CString strValue)


{
_variant_t vtFld;

if(!strValue.IsEmpty())
vtFld.vt = VT_BSTR;
else
vtFld.vt = VT_NULL;
//Corrected by Giles Forster 10/03/2001
vtFld.bstrVal = strValue.AllocSysString();
return PutFieldValue(lpFieldName, vtFld);
}
BOOL CADORecordset::SetFieldValue(int nIndex, int nValue)
{
_variant_t vtFld;
vtFld.vt = VT_I2;
vtFld.iVal = nValue;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
}

return PutFieldValue(vtIndex, vtFld);

BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, int nValue)


{
_variant_t vtFld;
vtFld.vt = VT_I2;
vtFld.iVal = nValue;
return PutFieldValue(lpFieldName, vtFld);
}
BOOL CADORecordset::SetFieldValue(int nIndex, long lValue)
{
_variant_t vtFld;
vtFld.vt = VT_I4;
vtFld.lVal = lValue;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
return PutFieldValue(vtIndex, vtFld);
}
BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, long lValue)
{
_variant_t vtFld;
vtFld.vt = VT_I4;
vtFld.lVal = lValue;
return PutFieldValue(lpFieldName, vtFld);
}
BOOL CADORecordset::SetFieldValue(int nIndex, unsigned long ulValue)

{
_variant_t vtFld;
vtFld.vt = VT_UI4;
vtFld.ulVal = ulValue;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
return PutFieldValue(vtIndex, vtFld);
}
BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, unsigned long ulValue)
{
_variant_t vtFld;
vtFld.vt = VT_UI4;
vtFld.ulVal = ulValue;
return PutFieldValue(lpFieldName, vtFld);
}
BOOL CADORecordset::SetFieldValue(int nIndex, double dblValue)
{
_variant_t vtFld;
vtFld.vt = VT_R8;
vtFld.dblVal = dblValue;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
return PutFieldValue(vtIndex, vtFld);
}
BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, double dblValue)
{
_variant_t vtFld;
vtFld.vt = VT_R8;
vtFld.dblVal = dblValue;
}

return PutFieldValue(lpFieldName, vtFld);

BOOL CADORecordset::SetFieldValue(int nIndex, COleDateTime time)


{
_variant_t vtFld;
vtFld.vt = VT_DATE;
vtFld.date = time;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
}

return PutFieldValue(vtIndex, vtFld);

BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, COleDateTime time)


{

_variant_t vtFld;
vtFld.vt = VT_DATE;
vtFld.date = time;
return PutFieldValue(lpFieldName, vtFld);
}

BOOL CADORecordset::SetFieldValue(int nIndex, bool bValue)


{
_variant_t vtFld;
vtFld.vt = VT_BOOL;
vtFld.boolVal = bValue;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
return PutFieldValue(vtIndex, vtFld);
}
BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, bool bValue)
{
_variant_t vtFld;
vtFld.vt = VT_BOOL;
vtFld.boolVal = bValue;
}

return PutFieldValue(lpFieldName, vtFld);

BOOL CADORecordset::SetFieldValue(int nIndex, COleCurrency cyValue)


{
if(cyValue.m_status == COleCurrency::invalid)
return FALSE;
_variant_t vtFld;
vtFld.vt = VT_CY;
vtFld.cyVal = cyValue.m_cur;
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
return PutFieldValue(vtIndex, vtFld);
}
BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, COleCurrency cyValue)
{
if(cyValue.m_status == COleCurrency::invalid)
return FALSE;
_variant_t vtFld;
vtFld.vt = VT_CY;
vtFld.cyVal = cyValue.m_cur;
return PutFieldValue(lpFieldName, vtFld);

}
BOOL CADORecordset::SetFieldValue(int nIndex, _variant_t vtValue)
{
_variant_t vtIndex;
vtIndex.vt = VT_I2;
vtIndex.iVal = nIndex;
}

return PutFieldValue(vtIndex, vtValue);

BOOL CADORecordset::SetFieldValue(LPCTSTR lpFieldName, _variant_t vtValue)


{
return PutFieldValue(lpFieldName, vtValue);
}
BOOL CADORecordset::SetBookmark()
{
if(m_varBookmark.vt != VT_EMPTY)
{
m_pRecordset->Bookmark = m_varBookmark;
return TRUE;
}
return FALSE;
}
BOOL CADORecordset::Delete()
{
if(m_pRecordset->Delete(adAffectCurrent) != S_OK)
return FALSE;
if(m_pRecordset->Update() != S_OK)
return FALSE;

m_nEditStatus = dbEditNone;
return TRUE;

BOOL CADORecordset::Find(LPCTSTR lpFind, int nSearchDirection)


{
m_strFind = lpFind;
m_nSearchDirection = nSearchDirection;
ASSERT(!m_strFind.IsEmpty());
if(m_nSearchDirection == searchForward)
{
m_pRecordset->Find(_bstr_t(m_strFind), 0, adSearchForward, "");
if(!IsEof())
{
m_varBookFind = m_pRecordset->Bookmark;
return TRUE;
}
}
else if(m_nSearchDirection == searchBackward)
{
m_pRecordset->Find(_bstr_t(m_strFind), 0, adSearchBackward, "");
if(!IsBof())
{

}
}
else
{

m_varBookFind = m_pRecordset->Bookmark;
return TRUE;

TRACE("Unknown parameter. %d", nSearchDirection);


m_nSearchDirection = searchForward;

}
return FALSE;

BOOL CADORecordset::FindFirst(LPCTSTR lpFind)


{
m_pRecordset->MoveFirst();
return Find(lpFind);
}
BOOL CADORecordset::FindNext()
{
if(m_nSearchDirection == searchForward)
{
m_pRecordset->Find(_bstr_t(m_strFind), 1, adSearchForward,
m_varBookFind);
if(!IsEof())
{
m_varBookFind = m_pRecordset->Bookmark;
return TRUE;
}
}
else
{
m_pRecordset->Find(_bstr_t(m_strFind), 1, adSearchBackward,
m_varBookFind);
if(!IsBof())
{
m_varBookFind = m_pRecordset->Bookmark;
return TRUE;
}
}
return FALSE;
}
BOOL CADORecordset::PutFieldValue(LPCTSTR lpFieldName, _variant_t vtFld)
{
if(m_nEditStatus == dbEditNone)
return FALSE;
try
{

m_pRecordset->Fields->GetItem(lpFieldName)->Value = vtFld;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::PutFieldValue(_variant_t vtIndex, _variant_t vtFld)

{
if(m_nEditStatus == dbEditNone)
return FALSE;
try
{

m_pRecordset->Fields->GetItem(vtIndex)->Value = vtFld;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::Clone(CADORecordset &pRs)


{
try
{
pRs.m_pRecordset = m_pRecordset->Clone(adLockUnspecified);
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::SetFilter(LPCTSTR strFilter)
{
ASSERT(IsOpen());
try
{

m_pRecordset->PutFilter(strFilter);
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::SetSort(LPCTSTR strCriteria)


{
ASSERT(IsOpen());
try
{

m_pRecordset->PutSort(strCriteria);
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADORecordset::SaveAsXML(LPCTSTR lpstrXMLFile)


{
HRESULT hr;
ASSERT(IsOpen());
try
{

hr = m_pRecordset->Save(lpstrXMLFile, adPersistXML);
return hr == S_OK;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
return TRUE;
}

BOOL CADORecordset::OpenXML(LPCTSTR lpstrXMLFile)


{
HRESULT hr = S_OK;
if(IsOpen())
Close();
try
{

hr = m_pRecordset->Open(lpstrXMLFile, "Provider=MSPersist;",
adOpenForwardOnly, adLockOptimistic, adCmdFile);
return hr == S_OK;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADORecordset::Execute(CADOCommand* pAdoCommand)
{
if(IsOpen())
Close();
ASSERT(!pAdoCommand->GetText().IsEmpty());
try
{
m_pConnection->CursorLocation = adUseClient;
m_pRecordset = pAdoCommand->GetCommand()->Execute(NULL, NULL,
pAdoCommand->GetType());
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
void CADORecordset::dump_com_error(_com_error &e)
{
CString ErrorStr;

_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
ErrorStr.Format( "CADORecordset Error\n\tCode = %08lx\n\tCode meaning =
%s\n\tSource = %s\n\tDescription = %s\n",
e.Error(), e.ErrorMessage(), (LPCSTR)bstrSource,
(LPCSTR)bstrDescription );
m_strLastError = _T("Query = " + GetQuery() + '\n' + ErrorStr);
m_dwLastError = e.Error();
#ifdef _DEBUG
AfxMessageBox( ErrorStr, MB_OK | MB_ICONERROR );
#endif
}
///////////////////////////////////////////////////////
//
// CADOCommad Class
//
CADOCommand::CADOCommand(CADODatabase* pAdoDatabase, CString strCommandText, int
nCommandType)
{
m_pCommand = NULL;
m_pCommand.CreateInstance(__uuidof(Command));
m_strCommandText = strCommandText;
m_pCommand->CommandText = m_strCommandText.AllocSysString();
m_nCommandType = nCommandType;
m_pCommand->CommandType = (CommandTypeEnum)m_nCommandType;
m_pCommand->ActiveConnection = pAdoDatabase->GetActiveConnection();
m_nRecordsAffected = 0;
}
BOOL CADOCommand::AddParameter(CADOParameter* pAdoParameter)
{
ASSERT(pAdoParameter->GetParameter() != NULL);
try
{

m_pCommand->Parameters->Append(pAdoParameter->GetParameter());
return TRUE;

}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADOCommand::AddParameter(CString strName, int nType, int nDirection, long


lSize, int nValue)
{
_variant_t vtValue;
vtValue.vt = VT_I2;
vtValue.iVal = nValue;
}

return AddParameter(strName, nType, nDirection, lSize, vtValue);

BOOL CADOCommand::AddParameter(CString strName, int nType, int nDirection, long


lSize, long lValue)
{
_variant_t vtValue;
vtValue.vt = VT_I4;
vtValue.lVal = lValue;
}

return AddParameter(strName, nType, nDirection, lSize, vtValue);

BOOL CADOCommand::AddParameter(CString strName, int nType, int nDirection, long


lSize, double dblValue, int nPrecision, int nScale)
{
_variant_t vtValue;
vtValue.vt = VT_R8;
vtValue.dblVal = dblValue;
return AddParameter(strName, nType, nDirection, lSize, vtValue,
nPrecision, nScale);
}
BOOL CADOCommand::AddParameter(CString strName, int nType, int nDirection, long
lSize, CString strValue)
{
_variant_t vtValue;
vtValue.vt = VT_BSTR;
vtValue.bstrVal = strValue.AllocSysString();
return AddParameter(strName, nType, nDirection, lSize, vtValue);
}
BOOL CADOCommand::AddParameter(CString strName, int nType, int nDirection, long
lSize, COleDateTime time)
{
_variant_t vtValue;
vtValue.vt = VT_DATE;
vtValue.date = time;
return AddParameter(strName, nType, nDirection, lSize, vtValue);
}
BOOL CADOCommand::AddParameter(CString strName, int nType, int nDirection, long
lSize, _variant_t vtValue, int nPrecision, int nScale)
{
try
{
_ParameterPtr pParam = m_pCommand>CreateParameter(strName.AllocSysString(), (DataTypeEnum)nType,
(ParameterDirectionEnum)nDirection, lSize, vtValue);
pParam->PutPrecision(nPrecision);
pParam->PutNumericScale(nScale);
m_pCommand->Parameters->Append(pParam);

return TRUE;
}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}
}
void CADOCommand::SetText(CString strCommandText)
{
ASSERT(!strCommandText.IsEmpty());

m_strCommandText = strCommandText;
m_pCommand->CommandText = m_strCommandText.AllocSysString();

void CADOCommand::SetType(int nCommandType)


{
m_nCommandType = nCommandType;
m_pCommand->CommandType = (CommandTypeEnum)m_nCommandType;
}
BOOL CADOCommand::Execute()
{
_variant_t vRecords;
m_nRecordsAffected = 0;
try
{
m_pCommand->Execute(&vRecords, NULL, adCmdStoredProc);
m_nRecordsAffected = vRecords.iVal;
return TRUE;
}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
void CADOCommand::dump_com_error(_com_error &e)
{
CString ErrorStr;
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
ErrorStr.Format( "CADOCommand Error\n\tCode = %08lx\n\tCode meaning =
%s\n\tSource = %s\n\tDescription = %s\n",
e.Error(), e.ErrorMessage(), (LPCSTR)bstrSource,
(LPCSTR)bstrDescription );
m_strLastError = ErrorStr;
m_dwLastError = e.Error();
#ifdef _DEBUG
AfxMessageBox(ErrorStr, MB_OK | MB_ICONERROR);
#endif
}
///////////////////////////////////////////////////////
//

// CADOParameter Class
//
CADOParameter::CADOParameter(int nType, long lSize, int nDirection, CString
strName)
{
m_pParameter = NULL;
m_pParameter.CreateInstance(__uuidof(Parameter));
m_strName = _T("");
m_pParameter->Direction = (ParameterDirectionEnum)nDirection;
m_strName = strName;
m_pParameter->Name = m_strName.AllocSysString();
m_pParameter->Type = (DataTypeEnum)nType;
m_pParameter->Size = lSize;
m_nType = nType;
}
BOOL CADOParameter::SetValue(int nValue)
{
_variant_t vtVal;
ASSERT(m_pParameter != NULL);
vtVal.vt = VT_I2;
vtVal.iVal = nValue;
try
{

if(m_pParameter->Size == 0)
m_pParameter->Size = sizeof(int);
m_pParameter->Value = vtVal;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::SetValue(long lValue)
{
_variant_t vtVal;
ASSERT(m_pParameter != NULL);
vtVal.vt = VT_I4;
vtVal.lVal = lValue;
try
{
if(m_pParameter->Size == 0)
m_pParameter->Size = sizeof(long);
m_pParameter->Value = vtVal;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);

return FALSE;
}

BOOL CADOParameter::SetValue(double dblValue)


{
_variant_t vtVal;
ASSERT(m_pParameter != NULL);
vtVal.vt = VT_R8;
vtVal.dblVal = dblValue;
try
{
if(m_pParameter->Size == 0)
m_pParameter->Size = sizeof(double);
m_pParameter->Value = vtVal;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADOParameter::SetValue(CString strValue)


{
_variant_t vtVal;
ASSERT(m_pParameter != NULL);
if(!strValue.IsEmpty())
vtVal.vt = VT_BSTR;
else
vtVal.vt = VT_NULL;
//Corrected by Giles Forster 10/03/2001
vtVal.bstrVal = strValue.AllocSysString();
try
{

if(m_pParameter->Size == 0)
m_pParameter->Size = sizeof(char) * strValue.GetLength();
m_pParameter->Value = vtVal;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::SetValue(COleDateTime time)
{
_variant_t vtVal;
ASSERT(m_pParameter != NULL);

vtVal.vt = VT_DATE;
vtVal.date = time;
try
{

if(m_pParameter->Size == 0)
m_pParameter->Size = sizeof(DATE);
m_pParameter->Value = vtVal;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::SetValue(_variant_t vtValue)
{
ASSERT(m_pParameter != NULL);
try
{

if(m_pParameter->Size == 0)
m_pParameter->Size = sizeof(VARIANT);
m_pParameter->Value = vtValue;
return TRUE;

}
catch(_com_error &e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::GetValue(int& nValue)
{
_variant_t vtVal;
int nVal = 0;
try
{

vtVal = m_pParameter->Value;
switch(vtVal.vt)
{
case VT_BOOL:
nVal = vtVal.boolVal;
break;
case VT_I2:
case VT_UI1:
nVal = vtVal.iVal;
break;
case VT_INT:
nVal = vtVal.intVal;
break;
case VT_NULL:
case VT_EMPTY:

nVal = 0;
break;
default:
nVal = vtVal.iVal;
}
nValue = nVal;
return TRUE;
}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::GetValue(long& lValue)
{
_variant_t vtVal;
long lVal = 0;
try
{

vtVal = m_pParameter->Value;
if(vtVal.vt != VT_NULL && vtVal.vt != VT_EMPTY)
lVal = vtVal.lVal;
lValue = lVal;
return TRUE;

}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::GetValue(double& dbValue)
{
_variant_t vtVal;
double dblVal;
try
{
vtVal = m_pParameter->Value;
switch(vtVal.vt)
{
case VT_R4:
dblVal = vtVal.fltVal;
break;
case VT_R8:
dblVal = vtVal.dblVal;
break;
case VT_DECIMAL:
//Corrected by Jos Carlos Martnez Galn
dblVal = vtVal.decVal.Lo32;
dblVal *= (vtVal.decVal.sign == 128)? -1 : 1;
dblVal /= pow(10, vtVal.decVal.scale);
break;
case VT_UI1:
dblVal = vtVal.iVal;
break;
case VT_I2:
case VT_I4:
dblVal = vtVal.lVal;

break;
case VT_INT:
dblVal = vtVal.intVal;
break;
case VT_NULL:
case VT_EMPTY:
dblVal = 0;
break;
default:
dblVal = 0;
}
dbValue = dblVal;
return TRUE;
}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::GetValue(CString& strValue, CString strDateFormat)
{
_variant_t vtVal;
CString strVal = _T("");
try
{

vtVal = m_pParameter->Value;
switch(vtVal.vt)
{
case VT_R4:
strVal = DblToStr(vtVal.fltVal);
break;
case VT_R8:
strVal = DblToStr(vtVal.dblVal);
break;
case VT_BSTR:
strVal = vtVal.bstrVal;
break;
case VT_I2:
case VT_UI1:
strVal = IntToStr(vtVal.iVal);
break;
case VT_INT:
strVal = IntToStr(vtVal.intVal);
break;
case VT_I4:
strVal = LongToStr(vtVal.lVal);
break;
case VT_DECIMAL:
{
//Corrected by Jos Carlos Martnez Galn
double val = vtVal.decVal.Lo32;
val *= (vtVal.decVal.sign == 128)? -1 : 1;
val /= pow(10, vtVal.decVal.scale);
strVal = DblToStr(val);
}
break;
case VT_DATE:
{
COleDateTime dt(vtVal);

if(strDateFormat.IsEmpty())
strDateFormat = _T("%Y-%m-%d %H:%M:%S");
strVal = dt.Format(strDateFormat);

}
break;
case VT_EMPTY:
case VT_NULL:
strVal.Empty();
break;
default:
strVal.Empty();
return FALSE;
}
strValue = strVal;
return TRUE;

}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}

BOOL CADOParameter::GetValue(COleDateTime& time)


{
_variant_t vtVal;
try
{

vtVal = m_pParameter->Value;
switch(vtVal.vt)
{
case VT_DATE:
{
COleDateTime dt(vtVal);
time = dt;
}
break;
case VT_EMPTY:
case VT_NULL:
time.SetStatus(COleDateTime::null);
break;
default:
return FALSE;
}
return TRUE;

}
catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}
}
BOOL CADOParameter::GetValue(_variant_t& vtValue)
{
try
{
vtValue = m_pParameter->Value;
return TRUE;
}

catch(_com_error& e)
{
dump_com_error(e);
return FALSE;
}
}
void CADOParameter::dump_com_error(_com_error &e)
{
CString ErrorStr;
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
ErrorStr.Format( "CADOParameter Error\n\tCode = %08lx\n\tCode meaning =
%s\n\tSource = %s\n\tDescription = %s\n",
e.Error(), e.ErrorMessage(), (LPCSTR)bstrSource,
(LPCSTR)bstrDescription );
m_strLastError = ErrorStr;
m_dwLastError = e.Error();
#ifdef _DEBUG
AfxMessageBox(ErrorStr, MB_OK | MB_ICONERROR);
#endif
}
IMPLEMENT_DYNAMIC(CADOException, CException)
CADOException::CADOException(int nCause, CString strErrorString) :
CException(TRUE)
{
m_nCause = nCause;
m_strErrorString = strErrorString;
}
CADOException::~CADOException()
{
}
int CADOException::GetError(int nADOError)
{
switch (nADOError)
{
case noError:
return CADOException::noError;
break;
default:
return CADOException::Unknown;
}
}
void AfxThrowADOException(int nADOError, CString strErrorString)
{
throw new CADOException(nADOError, strErrorString);
}

La creazione dei BARCODE


In alcune procedure vengono usate le codifiche a BARCODE per la
creazione delle etichette.
Di questi ne esistono diversi formati anche se quelli che possono
trattatare sia caratteri che numeri sono pochi come ad esempio il
CODE 39.
La seguente classe gestisce questo formato.
// Code39.cpp: implementation of the CCode39 class.
//
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Code39.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCode39::CCode39()
{
// code 39
m_nSymbology = CODE39;
}
CCode39::~CCode39()
{
}
////////////////////////////////////////////////////////////////////////////////
////
//
//
Name:
//
DrawBitmap()
//
//
Description:
//
draws a barcode using the previously loaded data
//
//
Arguments:
//
none
//
//
Return:
//
void
//
//
Called by:
//
public class interface
//
////////////////////////////////////////////////////////////////////////////////
////
void CCode39::DrawBitmap()
{
int
i;
CString
csCurrentPattern;
// draw start character, an asterisk

DrawPattern(RetrievePattern('*'));
// draw each character in the message
for (i=0;i<m_csMessage.GetLength();i++)
DrawPattern(RetrievePattern(m_csMessage.GetAt(i)));
// draw stop character, also an asterisk
DrawPattern(RetrievePattern('*'));
}

return;

////////////////////////////////////////////////////////////////////////////////
////
//
//
Name:
//
DrawPattern()
//
//
Description:
//
draws the passed character pattern at the end of the barcode
//
//
Arguments:
//
CString
csPattern
the bar pattern to draw
//
//
Return:
//
void
//
//
Called by:
//
CRationalCodabar::DrawBitmap()
//
////////////////////////////////////////////////////////////////////////////////
////
void CCode39::DrawPattern( CString csPattern )
{
int
i,nXPixel,nYPixel,nTempWidth;
CDC
oDC;
// attach to the device context
oDC.Attach(m_hDC);
// initialize X pixel value
nXPixel = m_nStartingXPixel;
for (i=0;i<csPattern.GetLength();i++)
{
// decide if narrow or wide bar
if (csPattern.GetAt(i)=='n')
nTempWidth = m_nNarrowBarPixelWidth;
else
nTempWidth = m_nWideBarPixelWidth;
// X value for loop
for
(nXPixel=m_nStartingXPixel;nXPixel<m_nStartingXPixel+nTempWidth;nXPixel++)
{
// Y value for loop
for
(nYPixel=m_nStartingYPixel;nYPixel<m_nStartingYPixel+m_nPixelHeight;nYPixel++)
{
// if this is a bar
if (i%2==0)

oDC.SetPixelV(nXPixel,nYPixel,COLORBLACK);
else
}

oDC.SetPixelV(nXPixel,nYPixel,COLORWHITE);

// advance the starting position


m_nStartingXPixel+= nTempWidth;

// detach from the device context


oDC.Detach();
}

return;

////////////////////////////////////////////////////////////////////////////////
////
//
//
Name:
//
RetrievePattern()
//
//
Description:
//
retrieves the bar pattern for a given character
//
//
Arguments:
//
char cInputCharacter
the input character to get the bar
pattern for
//
//
Return:
//
CString
the bar pattern for the input character
//
//
Called by:
//
CRationalCodabar::DrawBitmap()
//
////////////////////////////////////////////////////////////////////////////////
////
CString CCode39::RetrievePattern(char c)
{
CString
csCharPattern;
switch (c)
{
case '1':
csCharPattern
break;
case '2':
csCharPattern
break;
case '3':
csCharPattern
break;
case '4':
csCharPattern
break;
case '5':
csCharPattern
break;
case '6':
csCharPattern
break;

= "wnnwnnnnwn";
= "nnwwnnnnwn";
= "wnwwnnnnnn";
= "nnnwwnnnwn";
= "wnnwwnnnnn";
= "nnwwwnnnnn";

case '7':
csCharPattern
break;
case '8':
csCharPattern
break;
case '9':
csCharPattern
break;
case '0':
csCharPattern
break;
case 'A':
csCharPattern
break;
case 'B':
csCharPattern
break;
case 'C':
csCharPattern
break;
case 'D':
csCharPattern
break;
case 'E':
csCharPattern
break;
case 'F':
csCharPattern
break;
case 'G':
csCharPattern
break;
case 'H':
csCharPattern
break;
case 'I':
csCharPattern
break;
case 'J':
csCharPattern
break;
case 'K':
csCharPattern
break;
case 'L':
csCharPattern
break;
case 'M':
csCharPattern
break;
case 'N':
csCharPattern
break;
case 'O':
csCharPattern
break;
case 'P':
csCharPattern
break;
case 'Q':
csCharPattern

= "nnnwnnwnwn";
= "wnnwnnwnnn";
= "nnwwnnwnnn";
= "nnnwwnwnnn";
= "wnnnnwnnwn";
= "nnwnnwnnwn";
= "wnwnnwnnnn";
= "nnnnwwnnwn";
= "wnnnwwnnnn";
= "nnwnwwnnnn";
= "nnnnnwwnwn";
= "wnnnnwwnnn";
= "nnwnnwwnnn";
= "nnnnwwwnnn";
= "wnnnnnnwwn";
= "nnwnnnnwwn";
= "wnwnnnnwnn";
= "nnnnwnnwwn";
= "wnnnwnnwnn";
= "nnwnwnnwnn";
= "nnnnnnwwwn";

break;
case 'R':
csCharPattern
break;
case 'S':
csCharPattern
break;
case 'T':
csCharPattern
break;
case 'U':
csCharPattern
break;
case 'V':
csCharPattern
break;
case 'W':
csCharPattern
break;
case 'X':
csCharPattern
break;
case 'Y':
csCharPattern
break;
case 'Z':
csCharPattern
break;
case '-':
csCharPattern
break;
case '.':
csCharPattern
break;
case ' ':
csCharPattern
break;
case '*':
csCharPattern
break;
case '$':
csCharPattern
break;
case '/':
csCharPattern
break;
case '+':
csCharPattern
break;
case '%':
csCharPattern
break;

= "wnnnnnwwnn";
= "nnwnnnwwnn";
= "nnnnwnwwnn";
= "wwnnnnnnwn";
= "nwwnnnnnwn";
= "wwwnnnnnnn";
= "nwnnwnnnwn";
= "wwnnwnnnnn";
= "nwwnwnnnnn";
= "nwnnnnwnwn";
= "wwnnnnwnnn";
= "nwwnnnwnnn";
= "nwnnwnwnnn";
= "nwnwnwnnnn";
= "nwnwnnnwnn";
= "nwnnnwnwnn";
= "nnnwnwnwnn";

return csCharPattern;
}
////////////////////////////////////////////////////////////////////////////////
////
//
//
Name:
//
BitmapToClipboard()

//
//
Description:
//
puts the specified bitmap on the clipboard
//
//
Arguments:
//
none
//
//
Return:
//
void
//
//
Called by:
//
public class interface - called by users of the class
//
////////////////////////////////////////////////////////////////////////////////
////
void CCode39::BitmapToClipboard()
{
CDC
CBitmap

memDC;
oBitmap;

memDC.CreateCompatibleDC(NULL);
m_hDC = memDC.GetSafeHdc();
// create compatible, correctly sized bitmap
oBitmap.CreateCompatibleBitmap(&memDC,m_nFinalBarcodePixelWidth,m_nPixelHeight);
// select our bitmap into the device context
CBitmap * oldbm = memDC.SelectObject(&oBitmap);
// turn area white - stock black bitmap is selected
memDC.BitBlt(0,0,m_nFinalBarcodePixelWidth,m_nPixelHeight,NULL,0,0,WHITENESS);
// draw bitmap into memory device context
DrawBitmap();
// put bitmap on clipboard
::OpenClipboard(NULL);
::EmptyClipboard();
::SetClipboardData(CF_BITMAP, oBitmap.m_hObject);
::CloseClipboard();
//
deselect object out of device context
memDC.SelectObject(oldbm);
// make sure bitmap not deleted with CBitmap object
oBitmap.Detach();
}

return;

Il file di header :
// Code39.h: interface for the CCode39 class.
//
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_CODE39_H__6FE17747_EADF_4E89_9DCF_7688B04897BC__INCLUDED_)

#define AFX_CODE39_H__6FE17747_EADF_4E89_9DCF_7688B04897BC__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "Barcode.h"
class CCode39 : public CBarcode
{
public:
void BitmapToClipboard();
void DrawBitmap();
CCode39();
virtual ~CCode39();
private:
void DrawPattern(CString csPattern);
CString
RetrievePattern( char c );
};
#endif // !
defined(AFX_CODE39_H__6FE17747_EADF_4E89_9DCF_7688B04897BC__INCLUDED_)