Politecnico di Torino - elite.polito.itelite.polito.it/files/thesis/fulltext/garbo.pdf · Corso di...
Transcript of Politecnico di Torino - elite.polito.itelite.polito.it/files/thesis/fulltext/garbo.pdf · Corso di...
Politecnico di Torino
Facoltà di Ingegneria
Corso di Laurea in Ingegneria Informatica
Dipartimento di Automatica e Informatica
Studio e realizzazione di un sistema di
puntamento oculare
per l’accesso al computer
da parte di disabili motori gravi
Relatore: Ing. Fulvio CORNO
CoRelatore: Ing. Laura FARINETTI
Tesi di Laurea di: Alessandro GARBO
Anno Accademico 2002/2003
In tutti questi anni non ho mai capito in che misura la fortuna abbia influenzato l’andare
delle cose. Probabilmente anche in questo caso si deve utilizzare una sorta di teorema
sulla relatività. In quanto, ciò che per qualcuno è fortuna per altri è sfortuna oppure, ciò
che in un primo momento sembra malasorte col tempo si addolcisce e prende i contorni
di un’immensa fortuna.
Le avversità che ho incontrato costantemente sul mio cammino mi hanno fatto spesso
pensare alle relative fortune di altri che, puntavano al mio stesso obiettivo. Esami
superati con una preparazione insufficiente o voti elevati conseguiti solo per aver
studiato quello che veniva di solito chiesto all’esame facevano parte di un mondo a cui
io non appartenevo. I lunghissimi mesi passati a studiare l’intero programma di ogni
esame se non, in alcuni casi, accresciuto da testi ausiliari, mi portavano a maledire la
sorte che mi obbligava a sottostare a certe regole.
Vedere la strada semplice e soleggiata dove molti si avventuravano, mentre ero costretto
a percorrere un sentiero critico e irto d’insidie, mi ha insegnato a lottare per ottenere ciò
che volevo contro tutto e tutti.
Ora, a distanza di anni, vedo quelle avversità non come una prova inutile e
insignificante ma una possibilità d’inestimabile valore che mi è stata offerta. Il luogo
che ho raggiunto è simile solo in parte a quello raggiunto da chi ha sempre intrapreso il
percorso più semplice. In realtà quello che io ho raggiunto è il vero obiettivo che la
scuola ha cercato di dare a tutti noi. Ora ho un’istruzione sufficiente per parlare con
qualsiasi persona mi rivolga la parola, sia essa un fisico, un chimico, un elettronico, un
meccanico o, e questo è ancor più importante per me, un operaio o un religioso.
Per questa ragione dedico questa tesi alla sfortuna e alle avversità che mi hanno
accompagnato nel mio percorso. Senza di esse non sarei quello che sono ora. Inoltre il
lavoro qui svolto è dedicato a persone verso le quali le mie sfortune impallidiscono,
spero che questa tesi permetta a loro un pizzico di libertà in più.
Infine ringrazio il Professor Corno per la possibilità che mi ha dato e la famiglia per
l’aiuto indiretto costante negli anni.
Indice 1 Introduzione 1 2 Motivazione e obiettivi 5 2.1 Ausili 6 2.1.1 Sensori 6 2.1.2 Tastiere 7 2.1.3 Ausili per la comunicazione 8 2.1.4 Dispositivi di puntamento 9 2.1.5 Sistemi di puntamento oculare 10 2.2 Contesto 12 2.2.1 Definizione degli obiettivi 13 2.3 Analisi dei problemi 14 2.3.1 Ambiente di lavoro 15 2.3.2 Architettura del sistema 17 2.3.3 Acquisizione ed elaborazione dati 18 2.3.4 Integrazione nel sistema di applicazioni indipendenti 22 2.3.5 Calibrazione 23 3 BackGround 27 3.1 Paradigma di programmazione 28 3.1.1 Programmazione procedurale 28 3.1.2 Programmazione basata sugli oggetti 28 3.1.3 Programmazione orientata agli oggetti 29 3.2 Windows e i messaggi 29 3.2.1 Registrazione della window class 30 3.2.2 Creazione e visualizzazione della finestra 31 3.2.3 Ciclo dei messaggi 31 3.2.4 Window procedure 31 3.3 Multitasking e multithreading 32 3.4 Victor e le immagini 33 3.4.1 Codifica delle immagini 34 3.4.2 DIB 36 3.4.3 Victor 36 4 Architettura e algoritmi 39 4.1 Architettura 40 4.1.1 Acquisizione immagini 43 4.1.2 Scelta del riferimento 44 4.1.3 Motion capture 45 4.1.4 Definizione campo visivo 45 4.1.5 Indicazione dello sguardo 47 4.1.6 Gestione del sistema 48
4.2 Algoritmi 48 4.2.1 Scelta del riferimento 50 4.2.2 Cattura del movimento 53 4.2.3 Analisi del campo visivo 56 4.2.4 Definizione del punto fissato 58 5 Implementazione 61 5.1 Scelta del riferimento 63 5.1.1 Impostazione 64 5.1.2 Analisi immagine 65 5.1.3 Visualizzazione 68 5.1.4 Scelta 68 5.2 Cattura del movimento 69 5.2.1 Definizione parametri funzionali 71 5.2.2 Elaborazione svolta in una sequenza 72 5.2.3 Filtro 74 5.3 Definizione campo visivo 76 5.3.1 Introduzione di uno stato 77 5.3.2 HandShake 79 5.3.3 Elaborazione del singolo stato 79 5.3.4 Conclusione 81 5.3.5 Cablaggio manuale 81 5.4 Indicazione dello sguardo 82 5.4.1 Elaborazioni svolte in una sequenza 83 5.4.2 Messaggi alle applicazioni 86 6 Valutazioni sperimentali 87 6.1 I controlli 88 6.1.1 Controllo sulla velocità di esecuzione
del thread principale 89 6.1.2 Numero di catture per attuare il filtro 90 6.1.3 Clic temporale 91 6.1.4 Regione relativa al clic 92 6.1.5 Focus 93 6.2 Le applicazioni 94 6.2.1 Applicazione Uno: Addestramento 94 6.2.2 Applicazione Due: Tastiera 96 7 Sviluppi futuri 99 7.1 Legami esterni 100 7.2 Struttura 101 7.3 Algoritmi 102 7.4 Applicazioni 103 7.5 Conclusioni 106 8 Conclusioni 109
Appendice A 1 Elementi principali 1 Main 1 Dichiarazioni 6 Messaggi 11 Supervisore 14 Appendice B 25 Oggetti principali 25 Cattura 25 Palette 26 Intercetta 29 Decisore 35 Thread principale 37 Thread di calibrazione 38 Appendice C 39 Windows procedure delle sottofinestre 39 Tavolozza 39 Immagine 40 Controlli 41 Posiziona 47 Appendice D 51 Windows procedure delle applicazioni indipendenti 51 Applicazione Uno: addestramento 51 Applicazione Due: tastiera 52 Spaziatore 55 Applicazione Tre 56 Appendice E 59 Messaggi definiti da Windows 59 Resource 59 Oggetti relativi alla gestione 60 Menu 60 Errore 64 Finestre 64 Procedure di selezione comandi 68 Menu Cattura 68 Menu Intercetta 68 Menu Posiziona 69 Menu Applicazioni 69 Menu Finestre 69
1
1 Introduzione
Nel 1984 l’Ufficio del Servizio di Educazione Speciale e Riabilitazione del dipartimento
dell’Educazione statunitense insieme alla Casa Bianca prese l’iniziativa di condurre un
processo che portasse i produttori degli elaboratori, del software e gli utilizzatori a riflettere
sulla questione riguardante l’accesso e l’uso dell’elaboratore e del software da parte di
persone con disabilità. Il primo incontro di tale iniziativa si tenne nel Febbraio del 1984
presso la Casa Bianca.
Il risultato ottenuto fu il riconoscimento del problema dell’accessibilità ad un ambiente
informatico di comune utilizzo e la richiesta, da parte dei produttori, di ulteriori informazioni
inerenti ai tipi di disabilità, alle limitazioni che incontra una persona disabile nell’utilizzare un
elaboratore tradizionale, alle strategie che avrebbero potuto incrementare l’usabilità
dell’elaboratore stesso.
L’accesso al computer e ai programmi software deve garantire a chiunque, incluse le
persone disabili, la possibilità di usare vantaggiosamente la tecnologia in ambito lavorativo,
educativo e ricreativo. In seguito all’incontro di Febbraio, fu stesa la prima versione della
White Paper che venne distribuita come preparazione per un secondo incontro tenutosi
nell’Ottobre del 1985. Il risultato di questo secondo incontro fu la formazione di un gruppo di
2
lavoro volto ad identificare e documentare idee e considerazioni aventi lo scopo di
incrementare l’accessibilità degli elaboratori standard da parte sia di persone disabili, che
normodotate.
A grandi linee il problema generale può essere suddiviso in una serie di questioni più
semplici.
Una porzione importante della popolazione presenta delle disabilità (acquisite alla nascita,
o in seguito ad un incidente, a causa di una malattia o semplicemente dovute alla vecchiaia)
che impediscono loro di utilizzare i computer standard e i software tradizionali.
I costi per le modifiche necessarie da apportare agli elaboratori, per venire incontro alle
esigenze delle persone disabili, sarebbero non eccessivi o addirittura irrisori ed
incrementerebbero il numero di possibili utenti.
La caratteristica di accessibilità di un prodotto ne rende anche più semplice l’utilizzo da
parte delle persone non disabili, in quanto queste possono usufruire di benefici quali minor
fatica, maggiore velocità e minor probabilità di errore.
La maggior parte delle modifiche al design dovrebbero realizzarsi gratuitamente oppure ad
un prezzo contenuto, per ottenere un beneficio diretto sul mercato di massa.
La maggior parte dei cambiamenti necessari a rendere un prodotto accessibile dovrebbero
essere già inclusi direttamente nel prodotto stesso.
La White Paper rappresenta il primo passo compiuto dalle industrie del software per
andare incontro alle esigenze dei disabili. Lo scopo di questo documento è triplice. In primo
luogo si propone di informare le persone che operano nel campo della riabilitazione. In
secondo luogo di documentare l’importanza e le ragioni del software accessibile. Infine di
riuscire a sensibilizzare i produttori stimolandoli a lavorare, al fine di garantire alle persone
disabili la possibilità di usufruire di tutte le prestazioni in condizioni di adeguata affidabilità
ad autonomia.
Le ripercussioni scaturite dalla presentazione della White Paper si sono fatte sentire anche
in Europa. Nel Marzo del 1992 iniziò un progetto di elaborazione delle Nordic Guidelines for
Computer Accessibility da parte della NNH Nordic Committee an Disability. La NNH è una
cooperazione, sotto il Nordic Council of Minister, comprendente i governi danese, finlandese,
norvegese e svedese. Lo scopo dell’NNH è la realizzazione di obiettivi nazionali di piena
partecipazione nella società e di uguaglianza per le persone disabili. Il Nordic Guidelines for
Computer Accessibility è il risultato di un progetto riguardante l’informazione tecnologica
(IT). L’IT è una parte delle infrastrutture della società moderna.
3
Gli elaboratori sono ormai parte integrante nell’educazione e nel lavoro. È stato stimato
che il 70% del lavoro d’ufficio in Europa è svolto in posti di lavoro informatizzati.
La Cooperazione Internordica di Standardizzazione delle informazioni Tecnologiche
(INSTAT/IT) ha supportato questo progetto con la seguente dichiarazione:
“INSTAT/IT crede che si debba provvedere affinché le persone disabili
abbiano la possibilità di usare attrezzature IT. INSTAT/IT accoglie
favorevolmente il progetto e lo considera un buon inizio in questo settore.
Stiamo attendendo i risultati, sotto forma di guida, e ci aspettiamo di poterli
usare per un lavoro di standardizzazione regionale, nazionale ed
internazionale”.
Alla base di questa pubblicazione ci sono due documenti, il primo è la White Paper, mentre
l’altro è una specifica di requisiti prodotta in collaborazione dall’Agenzia Svedese per lo
Sviluppo Amministrativo e dall’Istituto Svedese di Handicap.
L’Organizzazione Mondiale della Sanità stima la percentuale di popolazione disabile
almeno nel 10%. Queste persone non costituiscono un gruppo omogeneo e statico, anche il
tipo di disabilità può essere temporaneo o permanente e variare d’intensità. Nei paesi
industrializzati si vive di più, ma le nascite diminuiscono. La persone hanno acquisito le
abitudini introdotte dalle attuali tecnologie, ma per alcuni l’accesso ad esse può essere
progressivamente ridotto a causa di limitazioni dovute all’età avanzata. Queste riflessioni
devono sicuramente aiutare a riflettere sull’importanza della questione dell’accessibilità
all’elaboratore. Con l’avvento e la diffusione della telematica, nuove possibilità di
integrazione economica e sociale si aprono per la popolazione disabile ed anziana. Tuttavia a
quest’interessante prospettiva si contrappongono problematiche che, se non opportunamente
considerate, possono aumentare l’isolamento di queste categorie di persone. Rendere un
terminale o servizio di facile uso per una persona disabile è garanzia di alta usabilità per ogni
utente. Per fare ciò è però necessario lo sviluppo di nuove aree di conoscenza, che includono i
tipi e il grado di disabilità, le conseguenze e le limitazioni che deve affrontare una persona
disabile, gli aspetti psicologici ed economici ed infine le strategie per incrementare l’usabilità
del prodotto.
Resta ancora da definire cosa si intenda per accessibilità. L’accessibilità si riferisce alla
progettazione dell’ambiente costruito, dei prodotti e dei servizi di uso generale in modo che
questi possano essere utilizzati agevolmente dalla più ampia percentuale di popolazione,
incluse le persone disabili od anziane. Più specificatamente per accessibilità informatica si
intende la possibilità, anche da parte di persone con impedita capacità motoria, di accedere ad
4
un ambiente informatico di comune utilizzo e di fruire di tutte le prestazioni in condizioni di
adeguata affidabilità ed autonomia.
Oggi in Europa 37 milioni di cittadini soffrono varie forme di handicap. L’Italia, in base ad
una stima della Divisione della popolazione dell’ONU, è fra i paesi a elevato tasso di
invecchiamento per la più alta percentuale di ultrasessantenni (24%) e la più bassa percentuale
(14%) di giovani di età inferiore ai 15 anni. In base alle previsioni dell’Istituto di ricerche
sulla popolazione del CNR in futuro nel nostro paese ci saranno 17 milioni di anziani e 9
milioni di giovani.
È evidente l’importanza crescente del tema della disabilità negli anni a venire. Il
progressivo invecchiamento della popolazione italiana, infatti rende necessari interventi,
anche attraverso tecnologie innovative, per migliorare le condizioni di vita delle persone che,
a causa dell’età, soffrono di disabilità di varia natura. La continua innovazione tecnologica è
riuscita a raggiungere risultati di rilievo, garantendo a molti disabili una vita uguale a quella
delle persone normodotate è possibile per loro studiare, lavorare, viaggiare e praticare perfino
sport a livello agonistico. In futuro la società si dovrà porre l’obiettivo di ricercare una sempre
migliore integrazione dei disabili.
Lo scopo di questa tesi è di apportare un piccolo contributo all’obiettivo appena citato. Tra
i numerosi campi in cui è possibile agire si è scelto di aiutare le persone affette da problemi
motori gravi. L’utilizzo da parte di queste persone del Personal Computer può essere
compromesso dall’impossibilità di utilizzare le varie periferiche di acquisizione dati come il
mouse e la tastiera. Tuttavia è possibile enfatizzare ed utilizzare le capacità motorie residue
dell’utente al fine di ottenere un metodo alternativo per indicare all’elaboratore i comandi da
eseguire. Grazie all’utilizzo di una videocamera connessa all’elaboratore si cercherà di carpire
il movimento del viso dell’utente per comprendere dove sia rivolto il suo sguardo. Sulla base
di una delle possibili soluzioni descritte verrà implementato un sistema che permetterà
all’utente di selezionare degli oggetti sullo schermo del Personal Computer. Per ottenere
questo risultato l’utente non dovrà far altro che rivolgere il viso nella direzione dell’oggetto.
Infine si discuteranno le prestazioni e i possibili sviluppi futuri del sistema.
5
2 Motivazione e obiettivi
Molte sono le barriere che una persona disabile deve affrontare e superare nella vita di tutti
i giorni. Si va da quelle architettoniche fino a quelle più subdole, nascoste negli apparecchi di
uso quotidiano. Spesso si dimentica come sia difficile, per non dire impossibile utilizzare gli
elettrodomestici se, per esempio, non si possono usare le mani per premere i pulsanti che li
controllano. Oppure leggere le istruzioni che regolano un dato apparecchio quando si hanno
notevoli problemi di vista.
Il problema più grave in cui ci si imbatte, quando si cercano delle soluzioni per migliorare
l’esistenza delle persone disabili è che purtroppo non esistono insiemi ben definiti di
disabilità. Esistono vari tipi di handicap che possono colpire una persona in modo più o meno
invalidante, non è possibile generalizzare ma è opportuno studiare i vari problemi uno alla
volta.
Questo sfortunatamente implica che, la disabilità che colpisce una gruppo numeroso di
persone sia studiata in maniera più estesa, mentre un handicap più raro venga accantonato in
quanto solo poche persone ne sono affette.
L’apporto che la tecnologia può riservare allo sviluppo di aiuti validi per le varie disabilità
è estremamente elevato. Ora più che mai, in quanto tutta la società moderna si appoggia ai
6
servigi resi dagli elaboratori. È possibile, grazie alla versatilità che offre l’informatica,
studiare ausili che aiutino in modo concreto tutti i portatori di handicap, trovando per ogni
disabilità la soluzione più consona.
2.1 Ausili L’ausilio è un oggetto che permette a chi lo utilizza di instaurare delle relazioni con le
persone che lo circondano. In questo sta la vera utilità dell’ausilio, dare alla persona disabile
la possibilità di entrare in relazione con gli altri, a casa, a scuola, al lavoro, etc. Questa
possibilità fa sì che la persona disabile abbia un’immagine di sé migliore, perché si sente parte
attiva nei suoi rapporti con gli altri e non un emarginato o un diverso. Anche l’ausilio
informatico è un oggetto, che serve a creare relazioni comunicative, che determinano
apprendimenti cognitivi che possono aumentare e facilitare contesti relazionali e sociali, tutto
ciò contribuisce al rafforzamento dell’identità della persona. Volendo dare una definizione
tecnica, l’ausilio è uno strumento che consente al disabile di fare ciò che altrimenti non
riuscirebbe a fare, oppure di farlo in maniera più sicura, veloce, meno faticosa, più accettabile
psicologicamente o infine per prevenire l’instaurarsi o l’aggravarsi di una disabilità.
2.1.1 Sensori
Un sensore (o pulsante, o switch) è un dispositivo che serve per trasformare una grandezza
fisica (pressione, spostamento, suono, soffio …) in una grandezza elettrica che viene utilizzata
per comandare dei dispositivi (giocattoli, computer …). In sostanza un sensore è un
dispositivo capace di inviare un comando (acceso, spento) ad un altro strumento.
È di grande utilità nei casi in cui l’utente non sia in grado di azionare un dispositivo
utilizzando i normali interruttori.
I sensori si dividono in due grandi categorie, da un lato si trovano i sensori singoli,
dall’altro i sensori multipli.
I sensori singoli sono sensori con un’unica funzione. Acceso oppure spento. Esistono
sensori con modalità di attivazione a testa, a tocco, pneumatici. Esistono sensori per chi è in
grado di muovere un solo dito, sensori che richiedono solo un movimento di pochi millimetri
o sensori che richiedono invece movimenti più ampi. I sensori singoli della AbleNet sono dei
pulsanti di diverse dimensioni azionabili premendo la superficie superiore. Il Big Red è un
pulsante di grandi dimensioni, attivabile tramite gli arti superiori ed inferiori, il Jelly Beam di
media dimensione, attivabile anche con il capo, ed infine lo Specs è un pulsante di piccole
dimensioni, attivabile anche col movimento delle labbra o del mento. Il sensore Jaggle della
Penny&Giles è caratterizzato dal fatto che la sua superficie può ruotare in senso orario o
7
antiorario facendo variare la resistenza del dispositivo (da un massimo di 1.5 Kg fino ad un
minimo di 200 g) con quindici diverse posizioni. Altri sensori singoli sono quelli della Tash.
Micra Light è un piccolo sensore molto sensibile e facilmente posizionabile. Left è un sensore
a stelo costituito da un’asta metallica ed un’estremità rigida ricoperta con un disco di spugna.
Quando si esercita la forza, per l’attivazione del sensore, l’estremità si flette leggermente. È
indicato per essere utilizzato con il capo da parte di chi abbia un buon controllo fine del
movimento del capo.
I sensori multipli sono sensori con più funzioni raggruppate in un unico strumento. Si
rilevano spesso ottime soluzioni perché in poco spazio racchiudono molte funzioni. Si
possono trovare sensori multipli due in uno (due sensori in uno) come nel Pneumatic Switch.
Questo è un sensore a pressione attiva o passiva (inspirazione o espirazione). Per attivare uno
switch si soffia, per attivare l’altro si inspira. Nel Racker Switch ci sono due sensori, uno per
ogni lato, che vengono attivati premendo ora sull’una ora sull’altra parte. Esistono poi sensori
5 a 1 come il Wafer il quale è costituito da cinque sensori a membrana disposti in sequenza.
Penta è formato da cinque piccoli sensori disposti a croce, per persone con un buon controllo
motorio fine. Nel Mini Joystick with Push quattro sensori vengono attivati col movimento
della leva nelle quattro direzioni, il quinto è attivato premendo la leva del joystick.
2.1.2 Tastiere
L’utilizzo della comune tastiera può essere complicato a causa di problemi nel controllo
degli arti superiori. A volte è sufficiente adottare delle modifiche alle impostazioni software
dell’elaborazione che si sta utilizzando, altre volte è possibile identificare delle soluzioni
alternative. A tale scopo esistono tastiere ridotte o espanse, tastiere a membrana,
riconfigurabili e a video su cui l’interazione avviene tramite un dispositivo di puntamento, o
tastiere con display, indipendenti dal computer. Un’altra soluzione consiste ne ricorrere ai
sistemi di controllo del computer a scansione, tramite l’uso di sensori esterni.
Le tastiere ridotte sono tastiere speciali caratterizzate da una dimensione ridotta e tasti
piccoli, ravvicinati e molto sensibili, al contrario le tastiere espanse sono tastiere di grandi
dimensioni, con tasti ingranditi. Un esempio di tastiera espansa è la Big Keys Color Plus della
Grayston Inc., in cui i tasti sono grandi il quadruplo di quelli di una tasiera normale. Un
esempio di tastiera ridotta è la WinMini della Tash Inc., in cui i tasti hanno un’area di 1.3 cm
quadrati e sono molto ravvicinati tra loro.
Nelle tastiere a membrana o riconfigurabili si può trovare la tastiera Discover Board della
Don Johnston Inc. Questa si presenta come una tavoletta su cui è possibile inserire delle
membrane plastificate e delle membrane cartacee personalizzate. Anche la tastiera espansa
8
dell’IntelliKeys, avente un’area maggiore rispetto a quella delle tastiere standard, è
riprogrammabile.
SoftType è una tastiera a video, che permette alle persone con difficoltà motorie, di inviare
comandi a qualsiasi applicazione di Windows. SoftType contiene un sistema di predizione di
parola che permette di ridurre il numero di tasti da premere. I clic del mouse possono essere
ottenuti usando la tecnica dell’Autoclic. Il clic del tasto sinistro del mouse è “realizzato” dopo
che il puntatore è rimasto immobile, per un intervallo di tempo programmabile, sul punto
desiderato. Si può avere accesso a qualsiasi applicazione o utilità Windows con l’uso
esclusivo di un sensore, di SoftType e di un sistema di puntamento col capo come
HeadMouse.
AlphaSmart è una tastiera indipendente dal computer con un display a quattro righe,
facilmente trasportabile ed ideale per chi ha problemi di scrittura.
Esistono infine sistemi a scansione come il Discover Switch della Don Johnston Inc..
Questo è un sistema integrato hardware e software per Personal Computer per l’utilizzo ed il
pieno controllo del computer attraverso la scansione. E possibile dare qualsiasi comando
all’elaboratore azionando un sensore (scansione automatica e selezione indiretta) o due
(scansione manuale e selezione diretta). Discover Switch consiste in un grosso sensore e in un
software di gestione. Una volta installato, il software riproduce, sul monitor, delle tabelle su
cui verrà attivata la scansione. Le tabelle sono personalizzabili.
2.1.3 Ausili per la comunicazione
Con il termine AAC (Comunicazione Aumentativi ed Alternativa) si trattano tutte le
strategie di comunicazione che sono alternative o incrementano la comunicazione verbale e
grafica. La AAC si pone come obiettivo la compensazione di una disabilità temporanea o
permanente del linguaggio espressivo. Vengono infatti create le condizioni affinché il disabile
abbia l’opportunità di comunicare in modo efficace, ovvero di tradurre il proprio pensiero in
una serie di segni intelligibili per l’interlocutore.
Esistono sia prodotti hardware che software per la comunicazione aumentativa. Si parla di
sistemi per la AAC con uscita in voce oppure senza uscita il voce.
Nei prodotti hardware si può trovare il Big Mack. Questo è un ausilio per la AAC con
uscita in voce, molto semplice. Attraverso la pressione di un grosso tasto si ottiene la
riproduzione di un messaggio precedentemente registrato. Al messaggio può essere associata
un’immagine o un simbolo. SpeakEasy è di nuovo un ausilio per la AAC con uscita in voce,
che permette di registrare e riprodurre dodici messaggi di durata variabile. Ad ogni messaggio
9
corrisponde un’area della tastiera sulla quale viene fissato un disegno associato al messaggio
registrato.
Comunica, al contrario, è un software di comunicazione con uscita in voce, ideato e
sviluppato dalla EasyLabs. Con Comunica è possibile costruire tabelle con celle di testo e di
immagine, cui è possibile associare file audio personalizzati e l’integrazione nel programma
della libreria completa dei simboli PCS (Picture Communication Symbols). Comunica è un
valido programma di comunicazione per persone che necessitano dell’uscita in voce.
I metodi di accesso vanno da quelli standard, quali mouse e tastiera, a quelli speciali come
dispositivi di puntamento col capo, tastiere alternative, trackball, sensori utilizzati insieme alle
modalità di scansione previste e touchscreen.
2.1.4 Dispositivi di puntamento
Esistono mouse speciali in alternativa al mouse standard per coloro che hanno difficoltà
nel controllare il movimento del puntatore e le funzioni dei tasti. Gli emulatori di mouse sono
dispositivi che permettono di controllare, attraverso sistemi diversi, il movimento del
puntatore e di svolgere tutte le funzioni del mouse. In alcuni casi si utilizza, per il movimento
del puntatore, un mouse standard e per la selezione si fa ricorso a sistemi che consentano di
realizzare il clic con l’ausilio di uno o più sensori esterni, tramite dispositivi d’interfaccia. In
altri casi è possibile ricorrere a soluzioni software per l’esecuzione dei comandi del mouse.
Dragger per Windows permette l’emulazione con un singolo clic delle funzioni del mouse.
Dragger può essere utilizzato insieme alla tecnica definita Autoclic. Chi presenta disabilità
motorie può avere facilmente accesso al mouse e alla maggior parte delle applicazioni
Windows grazie a Dragger e un qualsiasi dispositivo di puntamento.
L’accesso facilitato per Windows consente l’attivazione di un’opzione che permette di
utilizzare il tastierino numerico come emulatore di mouse.
Magic Touch è un pannello sensibile e trasparente che viene montato sullo schermo
dell’elaboratore. Per spostare il puntatore del mouse è sufficiente toccare la superficie
sensibile del pannello, in corrispondenza della posizione desiderata.
Il Palm Mouse rientra nella categoria dei mouse speciali. Questo è un mouse molto
piccolo, che consente di spostare il puntatore sul monitor utilizzando un minimo movimento
delle dita. È indicato soprattutto per distrofici e persone con sclerosi. I mouse della
Kensington, Expert mouse e Orbit Mouse, sono mouse di tipo trackball, in cui ruotando la
sfera posta sulla base è possibile spostare il puntatore sul monitor. Ai tasti funzione (quattro
nel primo e due nel secondo) è permesso associare funzioni specifiche.
10
I mouse speciali della Penny&Giles sono sia mouse a leva, sia Joystick, che Trackball.
Sono muniti di uno scudo che consente di appoggiare il polso e la mano sul piano del mouse,
impedendo di premere inavvertitamente i pulsanti abbinati alle funzioni del mouse.
NoHandsMouse della Hunter Digital consente di spostare il puntatore del mouse sul
monitor utilizzando il movimento dei piedi. Con un piede si controlla il movimento del
puntatore, con l’altro si esegue il clic.
HeadMouse della Origin Instruments consente di effettuare tutte le operazioni di un
normale mouse attraverso i movimenti del capo. È il mouse standard per persone che non
possono usare le mani, è infatti indicato per persone tetraplegiche e per chiunque sia in grado
di usare solo i movimenti del capo.
2.1.5 Sistemi di puntamento oculare
I dispositivi di puntamento rappresentano diversi modi per controllare il puntatore sul
monitor al fine di eseguire le azioni che si desiderano, tuttavia i dispositivi trattati nel
paragrafo precedente sono difficilmente accessibili a disabili motori per la difficoltà di
compiere due azioni distinte, lo spostamento sul piano e la pressione del tasto di selezione. In
questo, vengono in aiuto i sistemi basati sulla cattura dei movimenti dello sguardo per
direzionare il puntatore sullo schermo.
Questi dispositivi possono essere costituiti da una fascia o un caschetto che l’utente deve
indossare, oppure possono essere dispositivi da fissare sulla montatura di un paio di occhiali.
La soluzione migliore è però rappresentata da quei sistemi che non necessitano di alcun
dispositivo collegato all’utente, che quindi rimane più libero nei movimenti.
Il loro funzionamento si basa sulla
presenza di una telecamera e in alcuni casi di
un LED (emettitore di luce infrarossa), a
seconda del metodo utilizzato nella predizione
del punto di fissazione. Il sistema è costituito
da un monitor, sotto cui è montata una
telecamera ed un LED ad infrarossi. Il LED
emette dei raggi infrarossi a bassa potenza, che illuminano l’occhio. La superficie della cornea
riflette questi raggi. La riflessione genera il cosiddetto effetto occhi rossi. Questo effetto
migliora l’immagine della pupilla alla telecamera e rende più facile alle funzioni di
elaborazione delle immagini localizzare il centro pupilla. Il sistema calcola il punto fissato
dall’occhio basandosi sulla posizione relativa del centro pupilla e sulla riflessione della cornea
nell’immagine dell’occhio.
11
EyeGaze System della LC Technologies Inc. è un
sistema progettato per individuare su quale punto dello
schermo è fissato lo sguardo dell’utente. Il clic del
mouse è realizzato tramite una pausa di fissazione
dell’occhio sul punto desiderato. Le operazioni di
elaborazione delle immagini e di calcolo del punto
fissato vengono eseguite da un software apposito che
viene installato nell’elaboratore dell’utente. Le rilevazioni vengono effettuate ad una velocità
di campionamento di 60 Hertz. L’utilizzo di questo sistema richiede un buon controllo del
capo, l’utente deve essere in grado di mantenere lo sguardo fisso su di un punto per almeno
mezzo secondo, deve avere una buona vista e possedere adeguate capacità mentali. EyeGaze
deve essere utilizzato lontano dalle finestre e in presenza di un numero limitato di sorgenti di
luce infrarossa.
Quick Glace Eye-tracking System della Eye Tech Digital Sistem è un dispositivo che emula
il comportamento del mouse utilizzando il movimento degli occhi. Una telecamera montata
sotto il monitor del Personal Computer si focalizza sull’occhio dell’utente. Due LED a bassa
potenza sono montati ai lati del monitor. Quick Glace determina dove l’utente stia guardando
sul video, cioè il punto fissato, analizzando la posizione della riflessione della cornea e il
centro pupilla. Il cursore viene posizionato proprio sul punto calcolato in questo modo. Il clic
del mouse è realizzato con un battito di ciglia oppure con una pausa dell’occhio o ancora
tramite un sensore esterno. L’inseguimento dell’occhio avviene ad una velocità di 30
campioni al secondo.
ERICA System della ERICA Inc. permette di inseguire e
memorizzare il movimento degli occhi e la dilatazione della
pupilla di una persona, sullo schermo di un computer. ERICA
sta infatti per Eyegaze Response Interface Computer Aid
System. Inseguendo il movimento degli occhi di una persona il
sistema permette alla persona stessa di controllare il proprio
elaboratore utilizzando lo sguardo. Come i due prodotti
descritti sopra, anche ERICA non è un dispositivo intrusivo per
l’utente, infatti si ha di nuovo una telecamera ed un LED, questa volta posti in una scatola
sotto il monitor. Il sistema lavora alla velocità di 60 Hertz.
12
2.2 Contesto Prima di illustrare il contesto e con esso la strada che questa discussione intraprenderà nei
capitoli successivi è importante rilevare una caratteristica molto importante sui metodi
utilizzati per definire nuovi ausili utili alla comunità.
Come è già stato introdotto precedentemente è pressoché impossibile definire con esattezza
tutte le tipologie di handicap possibili. I disturbi sono così numerosi da rendere inutile la
ricerca di una soluzione unica per ogni disabilità. La comparsa di una minorazione può
associarsi ad altre e anche l’intensità con cui si presenta può variare notevolmente.
Due sono le possibili vie da percorre per ottenere un risultato apprezzabile nel superamento
delle barriere che l’handicap erige. La prima consiste nell’utilizzare a proprio favore la
caratteristica di eterogeneità nelle diverse forme di disabilità. Come l’handicap si presenta
sotto vari aspetti anche gli ausili devono proporre il più grande numero di soluzioni.
Progettare e produrre ausili che risolvano in modo diretto un solo e singolo problema ma che
non si ostacolino a vicenda e possano essere impiegati insieme. Questo permette di utilizzare,
o adattare, un certo numero di ausili in maniera concorrente per soddisfare il maggior numero
di utenti disabili, proponendo a ciascuno di loro il mix di ausili adatto alle loro necessità.
L’altra via percorribile è progettare e generare degli ausili la cui caratteristica principale sia
la versatilità, al fine di ottenere un numeroso bacino di utenza costituito da un gruppo
abbastanza eterogeneo di utenti disabili. Ciò significa puntare su un sistema unico che aiuti a
superare le diverse barriere erette da diversi handicap che hanno solo una radice comune, ma
che per intensità e caratteristiche marginali possono essere molto diversi fra loro.
Analizzando i diversi ausili presenti attualmente sul mercato balza subito all’occhio come
essi orbitino intorno al primo metodo descritto precedentemente. Tutti sono abbastanza
specifici e risolvono solo un problema alla volta, tuttavia l’utilizzo di un ausilio non preclude
l’uso concorrente di altri sistemi. Ciò nonostante è sempre possibile non tenere conto in fase
di progetto di importanti caratteristiche che il sistema deve possedere. Questo introduce un
aspetto abbastanza subdolo, che interviene quando più ausili vengono utilizzati insieme. Le
prestazioni globali sono vincolate al più modesto degli ausili utilizzati. Se, per esempio, uno
degli ausili impiegati in concorrenza non tiene conto della caratteristica relativa alla comodità
offerta all’utente è molto probabile che l’intero pacchetto venga menomato da questo
problema, anche se gli altri ausili sono ben progettati. Infine la versatilità di un certo ausilio
spesso è abbastanza rigida e non permette un impiego dell’ausilio diverso da quello
programmato.
13
L’utilizzo di un sistema unico e più completo, potrebbe essere in grado di eliminare questi
problemi. È infatti possibile avere sotto controllo tutti i passi nella progettazione e definire le
linee guida che determineranno i compiti che il sistema dovrà portare a termine. È possibile
discutere e riunire in fase di progettazione tutte le caratteristiche che faranno parte
dell’ausilio, senza dipendere da fattori esterni che potrebbero ostacolare le prestazioni del
sistema. Ovviamente una cattiva progettazione e la possibilità di trascurare fattori
determinanti possono essere deleteri al fine del risultato, alla pari o ancor peggio della
soluzione precedente.
2.2.1 Definizione degli obiettivi
È arrivato il momento di trasferire i concetti appena descritti nella pratica, per definire in
maniera completa il contesto che farà da sfondo per tutto il resto della discussione. A questo
punto molte sono le possibili vie da percorrere, ogni disabilità merita una corretta analisi,
tuttavia si deve compiere una scelta, non è possibile trattare in modo completo tutti i diversi
tipi di handicap. Si deve focalizzare un problema tra i tanti e cercare di risolverlo.
Quali risposte possono offrire le due correnti di pensiero se, per esempio, si vuole aiutare
le persone incapaci di utilizzare gli arti per comandare le periferiche di input del Personal
Computer come il mouse, ma che possiedono ancora un buon controllo del capo e una vista
sufficiente.
I seguaci della prima filosofia potrebbero consigliare a queste persone l’utilizzo del Magic
Touch unito all’utilizzo di un bastoncino da tenere in bocca per indicare al sistema le scelte
effettuate. Oppure l’utilizzo di uno dei tanti sistemi di puntamento oculare. Tuttavia sia l’una
che l’altra soluzione comportano numerosi difetti. Alla prima si può associare quanto detto in
precedenza sulla caratteristica di comodità, l’utente può affaticarsi se la sessione di lavoro si
protrae nel tempo. In quanto il Magic Touch non è stato progettato per questi scopi ma per un
utilizzo che includesse l’uso delle mani nella selezione. La seconda soluzione potrebbe
risolvere in modo corretto il problema, ma chi l’ha proposta non ha tenuto conto della
componente economica che spesso può costituire un aspetto importante.
Una soluzione in linea con la seconda corrente di pensiero potrebbe, in questo caso,
risolvere brillantemente i problemi che affliggono la soluzione precedente. La costituzione di
un sistema unico e progettato espressamente per questo gruppo di disabilità può essere la
risposta alla domanda. Avere la possibilità di progettare e costruire un sistema dalla base
permette di avere sotto controllo l’intero processo e definire con precisione tutte le
caratteristiche che l’ausilio deve possedere. In questo modo si può conservare ciò che di
buono ha introdotto la soluzione precedente eliminando nel contempo i difetti.
14
Utilizzare il movimento del capo dell’utente per selezionare gli oggetti sul terminale video
non è una cattiva idea, tuttavia si deve rimediare all’uso della bacchetta e nel contempo
cercare di eliminare il Magic Touch con una soluzione più confortevole ed economica. La
risposta si può ottenere dai sistemi di puntamento. Sarebbe ideale usare le potenzialità offerte
dalla cattura video per sostituire la bacchetta e il Magic Touch. Elaborare le immagini che
descrivono il volto dell’utente, al fine di cogliere il punto sul video dove l’utente ha rivolto il
viso. Infine, per la componente economica, attuare questa cattura con una WebCam.
Questa soluzione è estremamente confortevole per l’utente, in quanto quest’ultimo ha il
volto libero e deve solo muovere il capo nella direzione dell’oggetto sullo schermo che vuole
selezionare. Inoltre è possibile sfruttare in modo completo le potenzialità di un sistema
progettato dalle fondamenta, rendendolo assai versatile. Questa caratteristica permette di
utilizzare il sistema con vati tipi di handicap, in quanto è possibile spingersi fino alle
prestazioni di un sistema di puntamento oculare. Ciò significa poter utilizzare l’ausilio anche
per gli utenti che non hanno più la capacità motoria del capo ma che possiedono ancora un
buon movimento oculare. Il sistema in questo caso elabora solo la parte di immagine relativa
all’occhio dell’utente, divenendo una specie di versione economica di un sistema Eye-
tracking.
In conclusione l’obiettivo proposto è cercare di simulare il comportamento di un mouse
standard per Personal Computer utilizzando una camera economica come per esempio una
WebCam che cattura immagini relative al volto dell’utente. Questo per aiutare il maggior
numero di persone che, per vari motivi, non possono utilizzare le mani per imprimere i
comandi ad un normale mouse. Infine si può intravedere anche un utilizzo da parte di persone
normodotate che per qualche ragione hanno le mani occupate e devono poter utilizzare il
Personal Computer.
2.3 Analisi dei problemi Purtroppo in un contesto del genere non esistono sistemi già esistenti a cui si possa fare
riferimento e a cui ci si possa appoggiare per sviluppare un ausilio con le caratteristiche
determinate in precedenza. In questo caso l’ausilio deve essere creato dal nulla e perciò nella
determinazione dei problemi bisogna tenere conto anche di questo fatto. Questo porta ad una
prima generale divisione dei problemi in due gruppi diversi, da un lato ci sono le incertezze
dovute alla definizione di un nuovo sistema, dall’altro le incognite insite nella fase di sviluppo
che si accompagnano sempre ad un’operazione di miglioramento di un dato sistema in
crescita. Inoltre questi due insiemi di problemi non sono separati, ma interagiscono fra di loro
15
e la soluzione di un dato dubbio può influire positivamente o in modo negativo sulla
trattazione di un altro problema. Quindi per risolverli occorre avere sempre in mente il
disegno generale o contesto in cui il sistema si deve collocare.
Un’ulteriore suddivisione dei problemi si intravede nella sostanza stessa dell’ausilio, esso
infatti, oltre ad avere incertezze legate esclusivamente alla sua costituzione e di attinenza
riservata agli sviluppatori, ha al suo interno un’ulteriore peculiarità. Deve, infatti, rendere un
servizio ad una certa classe di utenza che, per sua natura, porta con sé alcune caratteristiche da
prendere seriamente in esame. Questi utenti non interagiscono con i sistemi e gli ausili
informatici allo stesso modo della maggioranza dei consumatori. Oltre ai normali problemi di
generazione di un nuovo sistema, si vengono a creare delle incognite relative alle capacità
residue e limitate degli utenti a cui questo ausilio deve dare il massimo aiuto possibile, per
questo motivo il fine ultimo nella progettazione e a cui dobbiamo sempre fare riferimento,
sarà ottimizzare le capacità residue di un numero limitato di utenti.
In accordo con quanto appena descritto è possibile dividere la parte successiva del
paragrafo in sezioni separate che descrivono i vari problemi che si incontreranno. Per quanto
riguarda le incertezze relative alla definizione di un nuovo sistema, si possono individuare la
scelta dell’ambiente di lavoro e la definizione dell’architettura del sistema. Per i problemi
relativi alla parte di sviluppo si incontrano i dubbi relativi al numero, all’utilizzo e al
posizionamento delle periferiche di acquisizione dati e la difficoltà di innestare nel sistema
applicazioni indipendenti. Un’ulteriore incognita risiede nella possibilità di calibrare il
sistema per le sessioni di lavoro. Quest’ultimo deve essere posto nell’insieme di incertezze
legate all’ausilio stesso in quanto per risolverlo è necessario tenere in considerazione le
capacità limitate dell’utente finale.
È d’obbligo aggiungere che in questo capitolo non si può fare altro che elencare e analizzare i
vari problemi, mentre la scelta delle varie opzioni possibili e la soluzione dei problemi è
demandata ai capitoli successivi.
2.3.1 Ambiente di lavoro
Il primo e più importante problema, la cui soluzione porrà le basi per lo sviluppo
successivo, è la scelta del Sistema Operativo che verrà usato da piattaforma software per
sviluppare e rendere attivo l’ausilio informatico. Decidere quale sia l’ambiente di lavoro non è
un’impresa facile, infatti le qualità che dovranno caratterizzare l’ausilio sono spesso in
contrasto fra di loro. Da una parte c’è il desiderio degli sviluppatori di avere una piattaforma
software completamente raggiungibile e analizzabile come per esempio una piattaforma Unix.
16
Dall’altra c’è la necessità di ottenere il maggior numero di utenti possibile, e per far questo,
bisogna orientare il lavoro verso ambienti Microsoft.
Sia l’una che l’altra possibilità offrono vantaggi e svantaggi. Se si orienta lo sviluppo verso
un Sistema Operativo della famiglia Unix come per esempio Linux, lo sviluppatore può avere
sotto controllo tutto il sistema e quindi verificare autonomamente l’intera catena di
acquisizione dati. Inoltre tale sistema offre delle librerie software estremamente potenti che
permettono allo sviluppatore di creare una struttura complessa, ottimizzata ed elegante da un
punto di vista architetturale, per esempio Unix nasce, tra le altre cose, con il supporto IPC
(Inter Process Comunication) che permette agli sviluppatori la possibilità di progettare
sistemi che lavorano in modalità concorrente e quindi, se il sistema viene progettato in
maniera corretta, ottimizzata. Tuttavia esistono degli svantaggi: per esempio la maggioranza
delle periferiche di acquisizione video non vengono fornite, dalla casa costruttrice, con i
relativi driver per questa piattaforma, questo implica che la scelta sui dispositivi di cattura
video è limitata. Questo Sistema Operativo, inoltre, viene visto come un sistema per gli
“addetti ai lavori” e non ha incontrato ancora l’approvazione del grande pubblico che lo
reputa una piattaforma difficile da usare.
Anche l’ambiente Microsoft presenta pregi e difetti, ad esempio la sua vastissima
diffusione, fa sì che un ausilio ideato per questa famiglia di Sistemi Operativi possa
raggiungere un numero molto grande di utenti ed inoltre la maggioranza delle periferiche di
acquisizione video sono fornite con gli elementi necessari all’installazione su questa
piattaforma. Oltre a ciò numerosi sono gli ambienti di sviluppo, estremamente validi, che
accompagnano questo ambiente, il che vuol dire avere un aiuto energico in fase di sviluppo e
di controllo degli errori. Dall’altra parte, utilizzando queste piattaforme, è obbligatorio
scendere a compromessi per quanto riguarda la visibilità sul sistema, infatti molte parti di esso
sono vincolate dal copyright e quindi non permettono allo sviluppatore di accedere in modo
libero al sorgente, questo può causare dei fastidi se si vuole andare molto in profondità nello
sviluppo della catena di acquisizione dati. Inoltre questa famiglia di Sistemi Operativi non
agevola lo sviluppo di applicazioni con strutture informatiche complesse come per esempio
l’IPC di Unix, tuttavia permette agli sviluppatori di creare sistemi con una struttura ottima ed
elegante anche se più semplice.
Per affrontare in modo completo ed esauriente i dubbi sulla scelta dell’ambiente di lavoro,
entrano in gioco anche gli obiettivi che abbiamo descritto a proposito del contesto a cui
l’ausilio deve adattarsi. La possibilità di rendere il sistema molto versatile e raggiungibile dal
più grande numero di utenti possibile indica la direzione da intraprendere per definire il
17
Sistema Operativo che farà da piattaforma software per in nostro sistema, infatti le garanzie
per ottenere un simile risultato le può offrire solamente la famiglia Microsoft. Quindi si arriva
al compromesso nella scelta dell’ambiente di sviluppo: a fronte di un sistema più semplice ma
ugualmente ben strutturato ed elegante c’è la possibilità di raggiungere una popolazione di
utenti enormemente vasta.
In conclusione il sistema sviluppato permetterà il suo utilizzo sui seguenti Sistemi
Operativi : Windows 98 e Windows 2000.
2.3.2 Architettura del sistema
Scelta la piattaforma software la strada da percorrere per raggiungere il gli obiettivi
preposti è, per così dire, solamente tracciata, i successivi passi sono l’individuazione di un
adeguato linguaggio di programmazione e la definizione dell’architettura del sistema. Per
quanto riguarda il primo dubbio in questa sezione è possibile dare già una soluzione, tuttavia
sull’architettura in questo contesto non si può far altro che definire le varie possibilità e
indicare quali caratteristiche il progetto deve possedere, sarà compito dei capitoli successivi
descrivere le soluzioni che si sono adottate.
La scelta del linguaggio di programmazione è nella maggior parte dei casi fortemente
influenzata dalle preferenze soggettive dello sviluppatore, tuttavia questa peculiarità può
indirizzare la scelta su un linguaggio non adatto che non è in grado cioè di soddisfare le
proprietà che la struttura del sistema richiede. Infatti le caratteristiche che un sistema deve
possedere variano a seconda del contesto in cui ci si ritrova ad operare, per esempio un
programma che risiede in una pagina Web ha bisogno di un linguaggio di programmazione
quale Java o JavaScript che permette a chi sviluppa di utilizzare al massimo le potenzialità
che la rete offre. Un sistema che deve interrogare un Data Base e che vuole visualizzare in
una finestra Windows i suoi risultati può, con successo, utilizzare Visual Basic e interrogare
la banca dati con comandi SQL embedded oppure ODBC (OpenDataBaseConnectivity). Nel
caso in cui all’utilizzo delle finestre Windows si unisce l’obbligo di definire una struttura
complessa il linguaggio più adatto alle è il C++. Quest’ultimo offre la possibilità di utilizzare
le librerie MFC, svariate altre potenzialità come la programmazione concorrente e le librerie
Video for Windows che permettono la cattura e l’elaborazione delle immagini grazie alle
videocamere.
Definire l’architettura del sistema è analogo a progettare le fondamenta di un grattacielo, se
il progetto non rispetta certi modelli o vincoli c’è il pericolo che tutta la struttura divenga
poco solida e rischi di crollare quando diviene molto complessa ed elaborata. Questo esempio
fa capire come una sua definizione accurata sia indispensabile per il sistema stesso.
18
Una prima qualità che la struttura deve possedere è la semplicità, un’architettura semplice
porta innumerevoli vantaggi sia agli sviluppatori che al sistema stesso. Infatti, i primi possono
godere di un codice sorgente facile, elegante e preciso da scrivere e da leggere, che permetta
una chiara localizzazione degli errori e la capacità di comprendere velocemente in che punti
agire per integrare nuove soluzioni. Inoltre nel concetto di semplicità è insita anche un’altra
qualità estremamente importante per un sistema votato a crescere nel tempo. La capacità di
permettere enormi sviluppi futuri senza sovraccaricare la struttura. Evitando, in questo modo,
agli sviluppatori di arrivare al punto in cui non è più possibile aggiungere nuove potenzialità
al sistema, senza stravolgere la struttura stessa.
Anche la versatilità è un’importante qualità, infatti un’architettura che rispetta questa
imposizione permette ai suoi sviluppatori di modificarne i contenuti, senza sconvolgerli, al
fine di superare i problemi che vengono alla luce mentre l’implementazione procede. Inoltre
se l’architettura è ben strutturata è possibile modificare gli obiettivi del sistema mentre il
sistema stesso viene creato, questo significa che le finalità iniziali, spesso limitate, possono
venire corrette o addirittura ampliate nella fase di sviluppo del sistema.
2.3.3 Acquisizione ed elaborazione dati
Contrariamente alle due sezioni precedenti nel quale si discutevano incertezze che
accompagnano sempre la fase di costituzione di una nuova applicazione e che perciò tendono
a vedere l’ausilio nella sua totalità e nel suo fine ultimo, qui vengono descritti e analizzati i
problemi dovuti alle caratteristiche dell’ausilio stesso, in pratica come l’applicazione dovrà
operare.
Tenendo ben presente quanto osservato nella parte relativa al contesto, l’ausilio deve
essere in grado, tramite la cattura di immagini dell’utente posizionato davanti allo schermo
del Personal Computer, di indicare quale zona del video ella stia osservando. Questa
mansione porta con sé svariati problemi che, per una corretta osservazione, devono essere
suddivisi in varie categorie.
Nascono per esempio domande su quante videocamere possano essere utilizzate, se sia più
utile usare motion capture intrusiva o non intrusiva, la posizione delle sorgenti video ed infine
come i dati acquisiti debbano essere elaborati.
La prima categoria di incognite, legate al numero delle videocamere, introduce un
problema abbastanza spinoso. Infatti, non è possibile definire con precisione il numero esatto
di videocamere ottimale per il sistema. Solo lo sviluppo può rispondere a questo dubbio, in
questo contesto è possibile indicare solamente il massimo numero di videocamere collegabili
al Personal Computer. Risulta evidente che più videocamere il sistema è in grado di utilizzare,
19
maggiore sarà l’insieme di dati che esso potrà elaborare. Tuttavia, si deve tener conto del fatto
che un maggior numero di dati non è indice di maggior precisione. Sarà compito
dell’implementazione gestire al meglio i dati ottenuti, anche con l’utilizzo di un numero
minore di videocamere, al fine di ottenere buoni risultati. Inoltre c’è un limite fisico al numero
di camere collegabili al sistema. Infatti, oltre al numero limitato di porte USB che un Personal
Computer di solito supporta (in genere due), c’è il limite imposto dalla banda massima del
Bus che collega le porte alla scheda madre. È questo il limite più restrittivo sui moderni
elaboratori. Questo significa che, sebbene ci sia la possibilità di installare due o più camere
grazie a diverse porte USB, fornite dal Personal Computer, quest’ultime non potranno
acquisire ed elaborare i dati in maniera concorrente. Questo perché la banda disponibile
risulta insufficiente per più di una camera. Anche l’elaborazione dei dati, attuata dal
processore, potrebbe essere inscritta tra le limitazioni al numero di sorgenti video. Tuttavia
una struttura del sistema ottimizzata e dei buoni algoritmi per l’acquisizione permettono di
superare questa incertezza. Quindi l’unico limite fondamentale al numero delle camere è dato
dal collo di bottiglia localizzato nel Bus dati. Se questo, in futuro, sarà più capiente ci sarà la
possibilità di aggiungere ed elaborare più sorgenti video. Tuttora si arriva all’utilizzo di non
più di due videocamere nei PC più recenti, mentre la maggioranza degli elaboratori ne
supporta, in concorrenza, solo una.
A questo punto è d’obbligo discutere come il sistema interpreta e percepisce il movimento
nelle immagini catturate. Due sono le possibili strade da intraprendere per arrivare ad una
soluzione, utilizzare motion capture non intrusiva oppure intrusiva. La tecnica non intrusiva
ha il vantaggio di rendere più comoda la sessione di lavoro all’utente. In quanto quest’ultimo
non ha l’obbligo di indossare nessun elemento estraneo. La cattura del movimento viene
totalmente affidata all’elaboratore. Tuttavia ha lo svantaggio di essere poco precisa, onerosa
per l’elaborazione ed estremamente suscettibile alle condizioni ambientali di luce. Questo
comporta che, nella maggioranza dei casi, si propenda per la seconda ipotesi, cioè utilizzare
motion capture intrusiva. Il vantaggio più evidente è la semplicità con cui essa porta a termine
il suo compito. Grazie all’utilizzo di un riferimento colorato, per esempio un pallino fissato
sull’elemento di cui si vuole conoscere il movimento, l’elaborazione risulta più semplice.
Vengono evitati molti problemi relativi alla definizione delle immagini e ridotti quelli
sull’illuminazione. Questo comporta una precisione maggiore e una gestione di errori
relativamente più contenuti. Tuttavia ha lo svantaggio di costringe l’utente ad indossare un
elemento estraneo che permetta la cattura del movimento.
20
Occorre, prima di parlare della collocazione delle videocamere, definire la posizione
ottimale che l’utente utilizza per accedere all’ausilio. Più la posizione è ergonomia più
l’utente può affrontare lunghe sessioni di lavoro, senza affaticarsi. Questo problema non
comporta un esame laborioso, infatti non sono molte le posizioni che l’utente può avere
davanti al video di un Personal Computer, l’unico obiettivo è l’obbligo di rendere comoda la
sessione. Anche se questa decisione è molto soggettiva sembra ovvia la scelta di posizionarsi
in asse con il centro dello schermo ed avere il bordo superiore dello stesso pressappoco alla
medesima altezza degli occhi.
Per discutere, ora, la collocazione delle videocamere è necessario definire i pregi e difetti
di ogni possibile posizione, infatti ogni ubicazione valorizza certi aspetti mentre ne limita
altri. Per esempio, se disponiamo di una sola videocamera, una posizione centrata con l’asse
verticale dello schermo rende l’elaborazione dei dati simmetrica e lineare. Questo perché
l’asse verticale dell’utente stesso è centrato su quello del video. Tuttavia esiste ancora un
grado di libertà che porta a due ubicazioni differenti: sopra o sotto il video. Ognuna di queste
posizioni porta con sé diverse caratteristiche. Infatti la collocazione inferiore consente alla
camera di avere sempre sotto controllo la zona dell’occhio dell’utente. Comunque il
riferimento colorato, se si usa motion capture intrusiva, subisce degli spostamenti non lineari
e difficili da compensare in fase di elaborazione. La soluzione superiore d’altra parte
intercetta movimenti lineari e costanti del riferimento ma rende precaria l’identificazione
dell’occhio. Se per esempio, l’utente sta guardando verso il basso, l’occhio può venire coperto
dal sopracciglio.
Tuttavia l’utilizzo di una sola videocamera posta in asse con l’utente non offre al sistema
dati sufficienti per comprendere tutti i movimenti che l’utilizzatore è libero di fare. Questo
porta ad errori sulla valutazione e l’interpretazione di rotazioni o spostamenti laterali eseguite
da chi usa il sistema. In questo caso diviene essenziale l’uso concorrente di almeno due
videocamere per l’acquisizione delle immagini al fine di compensare questi errori di
valutazione. Infatti la seconda videocamera posta lateralmente all’utente può fornire al
sistema il modo con cui capire qualsiasi movimento che l’utilizzatore compie davanti allo
schermo. Tuttavia anche questa soluzione porta con sé dei problemi, in questo caso puramente
pratici, sulla collocazione fisica della videocamera. Difatti essa deve essere posta lateralmente
all’altezza del capo dell’utente e raramente in questa posizione si può appoggiare qualcosa in
quanto c’è aria libera.
Un’ulteriore soluzione che utilizza due videocamere, nata grazie allo sviluppo stesso del
sistema e che risulta abbastanza ingegnosa, è quella di posizionare la prima sopra il video, la
21
seconda sotto ed entrambe sull’asse verticale dello schermo. Una tale soluzione anche se non
permette al sistema di elaborare strane rotazioni, fa sì che si abbia sempre sotto controllo il
riferimento e la zona dell’occhio dell’utente. La videocamera posta in alto può, grazie al
riferimento e alla motion capture intrusiva, definire con una buona precisione il punto dello
schermo verso il quale l’utente ha rivolto il viso. La videocamera in basso può definire più
accuratamente dove si posa lo sguardo, al fine di simulare le prestazioni di un sistema eye-
tracking. Oppure, non elaborare informazioni relative ai movimenti dell’occhio, ma attuare
una sorta di selezione (clic) agendo sul riconoscimento della palpebra abbassata per alcuni
istanti. Inoltre non ci sono problemi per quanto riguarda la posizione fisica nello spazio delle
due videocamere, si ha sempre un base dove appoggiarle, la prima sul ripiano superiore del
video, la seconda sul tavolo dove risiede il video stesso.
Restano, a questo punto, da trattare le incertezze relative all’elaborazione delle immagini
che le videocamere sono in grado di catturare. Anche in questo caso ci sono varie strade
percorribili e ognuna ha pregi e difetti. C’è la possibilità di utilizzare i dati forniti dalle
videocamere per generare un modello virtuale della posizione nello spazio dell’utente, per
calcolare in maniera geometrica il punto fissato da quest’ultimo. Tuttavia una soluzione che
adotta un tale sistema porta con sé più svantaggi che vantaggi. Diviene pressoché impossibile
cablare il sistema in maniera corretta a causa dei parametri variabili che entrano in gioco. Per
esempio devono essere definite le posizioni nello spazio delle camere, dello schermo e
dell’utente nonché le sue dimensioni fisiche, cosicché le equazioni geometriche aumentano in
maniera non controllabile. Quindi a fronte di una trattazione più rigorosa ed esatta dal punto
di vista matematico e geometrico c’è il pericolo reale di introdurre nel sistema errori troppo
grandi. I quali possono pregiudicare in maniera devastante l’elaborazione dei dati acquisiti
grazie alle videocamere. Un altro percorso, al contrario, permette di evitare un tale salto nel
buio e, sebbene porti ad una soluzione meno elaborata da un punto di vista matematico,
promette allo sviluppatore di poter raggiungere una possibile risoluzione del problema. L’idea
di base che caratterizza questa soluzione sta nello sfruttare la natura statica delle videocamere.
Si può utilizzare un sistema di riferimento geometrico riconducibile ad una finestra da
sovrapporre all’immagine stessa. Questa finestra delimiterà il campo d’azione del riferimento
che è, se si usa motion capture intrusiva, solidale con il volto dell’utente. Se, per esempio,
quest’ultimo fissa la parte superiore dello schermo, l’immagine catturata dalla camera rileverà
che il riferimento è in prossimità del lato superiore della finestra e così di seguito, anche se
rovesciati come in uno specchio, tutti gli altri punti fissati dall’utente sul video. Questa
soluzione inoltre, ha il pregio estremamente importante di non creare difficoltà e rendere la
22
fase di calibrazione più leggera. Per rendere il sistema in grado di elaborare i dati relativi al
riferimento, nella fase di taratura, l’utente non deve fare altro che definire, fissando gli estremi
del video, la finestra utilizzata come sistema di riferimento. Ovviamente è in questa fase che
si deve tener conto delle non linearità apportate dalla posizione scelta della videocamera. Se la
posizione è quella relativa alla zona superiore del video, si otterranno dati che comportano
meno problemi. La soluzione che prevede la videocamera al di sotto del terminale video
invece apporta le non linearità che il sistema dovrà poi compensare.
In conclusione, i problemi relativi all’acquisizione e all’elaborazione dei dati sono
molteplici, tuttavia in questa, e nelle altre sezioni del capitolo, non si è fatto altro che esporli e
descriverli in maniera esauriente. Solo nel capitolo successivo dedicato all’architettura e agli
algoritmi si cercherà di definire delle soluzioni per questi problemi.
2.3.4 Integrazione nel sistema di applicazioni indipendenti
Ha preso piede, ormai da parecchi anni, il concetto di programmazione orientata o basata
sugli oggetti. Oltre ad avere un sorgente più ordinato e facile da comprendere, questa tecnica
ha sconvolto il modo di concepire la struttura di qualsiasi sistema informatico. Infatti ha
introdotto l’idea per cui un oggetto può essere utilizzato in molteplici modi, senza dover
conoscere come le operazioni sono svolte al suo interno o gli algoritmi che il suo creatore ha
utilizzato per il funzionamento. L’oggetto è visto come una scatola nera che risponde a
determinate domande. La rivoluzione sta nel fatto che uno sviluppatore può utilizzare oggetti
creati da altri, per produrre oggetti a loro volta utili agli sviluppatori.
Anche codesto sistema dovrebbe seguire questa logica per attuare una sorta di
stratificazione tra il sistema vero e proprio e le applicazioni ad esso correlate. Questo al fine di
separare i due compiti e permettere a futuri sviluppatori di inserire le loro applicazioni
all’interno dalla struttura in modo semplice. Evitando a quest’ultimi l’obbligo di dover
comprendere il funzionamento interno del sistema come la catena di acquisizione dati o gli
algoritmi di elaborazione, ma richiedere solamente l’elaborazione di pochi segnali che lo
strato del sistema manda alle loro applicazioni.
Una struttura che permette l’esistenza di questa caratteristica porta con sé un ulteriore
vantaggio, in quanto è possibile differenziare il lavoro di sviluppo su due fronti distinti.
Infatti, mantenendo fissi i messaggi che i due strati si scambiano, si è in grado di permettere
ad un potenziale gruppo di sviluppatori di lavorare su applicazioni indipendenti tra di loro.
Mentre sul versante opposto il sistema stesso può venire sviluppato da altri, per esempio
ottimizzando e rendendo più complessi gli algoritmi di elaborazione dati utilizzati, senza
compromettere o rendere inservibile il lavoro sulle applicazioni.
23
2.3.5 Calibrazione
I dubbi insiti nella calibrazione dell’ausilio devono essere analizzati, come spiegato
precedentemente, tenendo sempre in considerazione le capacità relativamente limitate
dell’utente finale.
Occorre, prima di tutto, definire con esattezza cosa si intende per calibrazione. Tutte le
periferiche che si possono installare su un Personal Computer, siano esse di acquisizione che
di visualizzazione dati, richiedono, subito dopo la fase di installazione, una fase di
calibrazione dei parametri di funzionamento. Per esempio un mouse possiede alcuni parametri
che devono essere definiti sulla base delle sensazioni che l’utente vuole avere quando utilizza
l’ausilio. Una di queste può essere l’accelerazione che la freccia subisce quando si muove il
mouse, oppure il tempo che trascorre tra un click e il successivo per definire il doppio click o
ancora l’opportunità di rendere l’utilizzo più semplice a una persona mancina, agendo sulla
possibilità di invertire i pulsanti. Tutti questi ed altri parametri vengono definiti grazie ad
un’applicazione fornita dalla casa costruttrice della periferica. Dopodiché è possibile usare
l’ausilio, senza più cablarlo, nelle sessioni di lavoro che l’utente realizza successivamente sul
Personal Computer.
Inoltre esiste un’ulteriore concetto che deve essere considerato prima di poter procedere
nell’analisi di questo ausilio. Le periferiche utilizzano due principali tecniche per interpretare
i dati, da un lato ci sono gli ausili che utilizzano un sistema di riferimento relativo, per
elaborare e rendere coerenti gli input che ottengono. Dall’altro, quelli che usano un sistema
assoluto di riferimento. Del primo insieme fanno parte la maggioranza delle periferiche di
acquisizione dati, per esempio, un mouse utilizza questo metodo per visualizzare gli
spostamenti della freccia sul video. Infatti ogni spostamento viene calcolato come distanza
vettorizzata tra la posizione iniziale e finale che il mouse occupa nello spazio. Tuttavia esso
non possiede un sistema di riferimento fisso perché l’utente è in grado di annullare il suo
funzionamento, semplicemente sollevandolo dal tavolo. Questo implica che, ad ogni
sollevamento esso modifica il suo sistema di riferimento. Un joystick, al contrario fa parte del
secondo insieme perché utilizza un sistema di riferimento fisso e assoluto, centrato nella sua
posizione di riposo.
Ora è possibile analizzare con facilità le caratteristiche che definiscono la calibrazione
dell’ausilio. Il problema può essere visto come due operazioni da svolgere in sequenza. Il
primo passo consiste nel preparare il sistema alle condizioni ambientali in cui si troverà ad
operare. Ciò significa che si devono impostare i parametri caratteristici dell’immagine, per
esempio la luminosità, il contrasto e il colore che l’immagine catturata deve possedere, per
24
una buona elaborazione da parte del sistema. Questa prima fase di impostazione porta con sé
alcuni dubbi. Infatti sarebbe ideale dover affrontare questa operazione una volta sola, subito
dopo la fase di installazione del sistema sul Personal Computer. Tuttavia il funzionamento
dell’ausilio è fortemente vincolato, in quanto elabora immagini del mondo reale, alle
condizioni di luminosità insite intorno ad esso. Quindi, se per esempio, il sistema viene usato
con la luce del giorno, potrebbe richiedere un ulteriore settaggio dei parametri di luminosità se
successivamente viene usato di sera grazie ad un’illuminazione artificiale. Questo porta al
problema di rendere semplice e poco dispendioso il compito di cablare le periferiche di
acquisizione dati.
La natura stessa dell’ausilio porta alla definizione dei compiti che il sistema deve
affrontare per completare il secondo, ed ultimo, passo della calibrazione. Come spiegato
precedentemente gli ausili utilizzano due correnti di pensiero differenti per elaborare i dati che
si procurano, nel nostro caso c’è l’obbligo di usare un sistema di riferimento assoluto. Su
questa asserzione non ci sono dubbi infatti, una volta cablato, l’ausilio utilizza il suo sistema
di riferimento per tutta la sessione di lavoro. Tuttavia, sebbene si sia definita in modo univoco
la natura del sistema di riferimento, la fase del suo settaggio porta con sé alcune incertezze
che devono essere analizzate prima di poter proseguire.
Per studiare in maniera esauriente il problema si potrebbe iniziare col pensare a come
l’utente utilizzerà l’ausilio. Si può immaginare che quest’ultimo venga posizionato davanti
allo schermo del Personal Computer grazie all’aiuto di un assistente, dopodiché il
collaboratore dovrà, tramite le normali periferiche, attivare il sistema. A questo punto verrà
richiesto all’utente di definire il sistema di riferimento dell’ausilio e alla conclusione di questa
fase ci sarà la possibilità di accedere alle applicazioni. Il problema di maggior rilievo per
definire questo passo è rappresentato dal fatto che ad ogni sessione di lavoro l’utente non sarà
mai nella posizione esatta in cui era nella sessione precedente. Tra un utilizzo e il successivo
dell’ausilio può trascorrere parecchio tempo nel quale l’utente ha la facoltà di cambiare
posizione nello spazio tramite l’aiuto degli assistenti.
Grazie a questo possibile scenario sull’utilizzo del sistema, si osserva che questa fase deve
essere estremamente semplice dal punto di vista dell’utente, in quanto deve essere ripetuta ad
ogni sessione di lavoro e quindi non deve affaticare o essere onerosa per chi usa l’ausilio.
Dopo questa trattazione si arriva alla conclusione che ambedue le fasi della calibrazione
devono essere estremamente semplici. La prima deve concedere all’assistente la possibilità di
variare i parametri per l’acquisizione dell’immagine in modo corretto e veloce in quanto c’è la
probabilità non del tutto remota che essi possano variare da una sessione di lavoro all’altra. La
25
seconda fase deve tenere inoltre in considerazione le caratteristiche dell’utente e fare in modo
che non sia gravoso il lavoro che costui deve compiere.
27
3 Background
I problemi che sono apparsi non appena si è tentato di creare un ausilio utile per il
prossimo, e che hanno caratterizzato tutto il capitolo precedente hanno reso chiarissimo il
concetto che il sistema che si vuole creare non è così semplice come poteva sembrare a prima
vista. Tuttavia non si è indifesi nel combattere questi problemi, come supporto ci sono i
decenni spesi nella ricerca informatica, i quali hanno portato a risultati che possono
contribuire alla risoluzione delle incertezze.
Una prima freccia per il nostro arco è costituita dalla scelta del linguaggio di
programmazione, il C++ infatti consente una programmazione orientata agli oggetti che
permette di scrivere un codice sorgente molto elegante e facile da comprendere, inoltre grazie
al C++ è possibile utilizzare gli oggetti predefiniti di Windows e la capacità di scrivere
applicazioni multithreaded.
Per completare la nostra faretra non può mancare l’aiuto che ci viene dato dalle procedure
Video For Windows per la cattura delle immagini provenienti dalle camere e il pacchetto
software della Victor che permette di elaborarle, senza dimenticare l’aiuto dato dalla
RamDisk.
A questo punto, come avrebbe detto Manzoni :
28
“Son cose che chi conosce la storia le deve sapere; ma siccome, per un giusto
sentimento di noi medesimi, dobbiam supporre che quest’opera non possa essere letta se
non da ignoranti, così non sarà male che ne diciamo qui quanto basti per infarinare chi
n’avesse bisogno”
Perciò seguono varie sezioni dove si cerca di spiegare a grandi linee gli ausili informatici che
ci hanno sorretto e aiutato nella costituzione del sistema.
3.1 Paradigma di programmazione Con questo termine si indica la relazione esistente tra gli algoritmi e i dati, che
costituiscono le due componenti fondamentali di un programma. Entrambi sono rimasti, nella
breve storia dell’informatica, aspetti invarianti, mentre si è evoluta la relazione esistente fra di
loro.
All’inizio si parlava di programmazione procedurale, poi col passare del tempo, verso gli
anni settanta l’attenzione si è spostata dal paradigma procedurale a quello dei tipi di dato
astratto ora noto come programmazione basata sugli oggetti, infine con il concetto di
ereditarietà e di collegamento dinamico si è arrivati ad una programmazione orientata agli
oggetti.
Il C++ è un linguaggio multiparadigma, pur essendo considerato principalmente un
linguaggio orientato agli oggetti, esso è di supporto anche per la programmazione procedurale
e per quella basata sui tipi di dato astratto. Il vantaggio è che è possibile fornire una soluzione
più adatta al problema: in pratica, infatti, nessun paradigma rappresenta la soluzione finale di
un problema. Lo svantaggio sta nel fatto che ciò implica un linguaggio più grande e più
complesso.
3.1.1 Programmazione procedurale
In questo caso il problema è illustrato direttamente mediante un insieme di algoritmi o da
una serie di procedure. I dati sono memorizzati separatamente, ed è possibile accede ad essi in
una posizione globale oppure passandoli alle procedure. Tra i più importanti linguaggi
procedurali ci sono il FORTRAN, il C e il Pascal. Come menzionato precedentemente il C++
supporta tutte le capacità offerte dal suo predecessore C.
3.1.2 Programmazione basata sugli oggetti
Secondo questo paradigma il problema è modellato direttamente da un insieme di
astrazioni sui dati: nel C++ queste astrazioni sono dette classi. Ad esempio, il sistema di
gestione per una biblioteca con questo paradigma è rappresentato come l’interazione fra
29
oggetti che sono istanze di classi quali Libro, Utente, Prestito e cosi via, che rappresentano le
astrazioni possibili in una biblioteca. Gli algoritmi associati a ogni classe sono chiamati
interfaccia pubblica della classe. I dati sono memorizzati in maniera privata all’interno di
ogni oggetto e non è possibile accedere da essi dal programma generale. CLU, Ada, Modula-2
e il C++ sono solo alcuni dei linguaggi che supportano questo paradigma.
3.1.3 Programmazione orientata agli oggetti
In questo caso i tipi di dato astratto vengono estesi mediante i meccanismi di ereditarietà e
di collegamento dinamico, il cui significato è riutilizzare un’implementazione oppure
un’interfaccia pubblica già esistente. Quest’ultima caratteristica verrà ampliamente descritta
nella sezione dedicata agli oggetti predefiniti di Windows.
In questo modo sono rese disponibili relazioni speciali tipo/sottotipo fra tipi
precedentemente indipendenti. Un libro, una videocassetta, un disco, sono tutti una sorta di
materiale di biblioteca, anche se ciascuno ha le sue regole per il prestito. L’interfaccia
pubblica condivisa e i dati privati sono posti in una classe astratta MaterialeBiblioteca. Ogni
singolo materiale eredita il comportamento comune dalla classe astratta MaterialeBiblioteca e
deve fornire soltanto gli algoritmi e i dati che supportano il suo comportamento. Tre
importanti linguaggi, escluso il C++, che supportano il paradigma sono Simula, Smalltalk e
Java.
3.2 Windows e i messaggi Quando si programma per Windows, si esegue un tipo di programmazione orientata agli
oggetti. Per rendersene conto, basta osservare l’oggetto con cui si lavora più spesso in
Windows: la finestra. Le finestre più ovvie che spiccano sul Desktop sono quelle delle
applicazioni. Queste finestre comprendono una barra del titolo che riporta il nome del
programma, un menu ed eventualmente una barra degli strumenti e una barra di scorrimento.
L’utente vede queste finestre come oggetti sullo schermo e interagisce direttamente con
esse utilizzando la tastiera o il mouse. Come fa l’applicazione a capire che l’utente sta
cercando di comunicare con la finestra stessa? Per i programmatori abituati solo alla
convenzionale programmazione in modo caratteri, non esiste mezzo che consenta al Sistema
Operativo di trasmettere all’utente informazioni di questo tipo. Ne consegue che questa
domanda è essenziale per comprendere l’architettura di Windows. Quando l’utente interagisce
con una finestra per esempio modificandone le dimensioni, Windows invia un messaggio al
programma per indicare le nuove dimensioni della finestra. Il programma può quindi regolare
il contenuto della finestra per adattarlo alle nuove dimensioni.
30
L’affermazione “Windows invia un messaggio al programma” significa che Windows
chiama una funzione all’interno del programma, una funzione che deve essere scritta e che
costituisce una parte essenziale del programma. I parametri di questa funzione descrivono il
messaggio specifico che viene inviato da Windows e ricevuto dal programma. Questa
procedura all’interno del programma è detta window procedure.
L’idea che un programma effettui chiamate al Sistema Operativo è familiare a tutti.
Questo, per esempio, è il metodo utilizzato per aprire un file su disco. Al contrario, l’idea di
un Sistema Operativo che effettua chiamate a un programma può risultare più estranea e
tuttavia è fondamentale per l’architettura di Windows.
Ogni finestra creata da un programma ha una window procedure associata. Quest’ultima è
una funzione che potrebbe trovarsi nel programma stesso o in una libreria. Windows invia un
messaggio a una finestra chiamando la window procedure, che provvede a eseguire la
necessaria elaborazione e al termine restituisce il controllo a Windows.
Più precisamente, una finestra viene sempre creata in base a una window class. La window
class identifica la window procedure che elabora i messaggi rivolti alla finestra.
Nella programmazione orientata agli oggetti, un oggetto è una combinazione di codice e
dati: la finestra è l’oggetto, il codice costituisce una window procedure, mentre i dati sono le
informazioni conservate dalla window procedure e quelle conservate da Windows per ogni
finestra e window class che esiste nel sistema.
Quando un programma Windows inizia l’esecuzione, Windows crea una coda dei
messaggi, dove vengono archiviati i messaggi indirizzati a tutte le finestre che un programma
potrebbe creare. In ogni applicazione Windows esiste uno spezzone di codice definito ciclo
dei messaggi avente lo scopo di prelevare i messaggi della coda e trasmetterli alla window
procedure appropriata. Altri messaggi vengono inviati direttamente alla window procedure
senza essere inseriti nella coda.
3.2.1 Registrazione della window class
Una finestra viene sempre creata sulla base di una window class. La window class
identifica la window procedure che elabora i messaggi indirizzati alla finestra.
È possibile creare più finestre basate sulla medesima window class. Quest’ultima definisce
non solo la window procedure ma anche altre caratteristiche delle finestre create sulla base di
detta classe. Prima di poter creare una finestra dell’applicazione, occorre registrare una
window class chiamando RegisterClass. Grazie a questa funzione siamo in grado di iscrivere
nel sistema la nostra applicazione definendo in maniera univoca le caratteristiche che la
finestra dovrà avere.
31
3.2.2 Creazione e visualizzazione della finestra
La window class definisce le caratteristiche generali di una finestra, per cui consente di
utilizzare la stessa window class per creare numerose finestre diverse. Quando si crea una
finestra chiamando CreateWindow, si specificano informazioni più dettagliate sulla finestra.
In Windows, ogni finestra è dotata di un handle. Il programma utilizza quest’ultimo per
riferirsi alla finestra, molte funzioni di Windows lo richiedono come argomento affinché
Windows capisca a quale finestra si riferisce la funzione. Se un programma crea svariate
finestre, ciascuna avrà un handle differente.
Quando la chiamata della funzione CreateWindow ha restituito un risultato, significa che la
finestra è stata creata internamente a Windows. Ma la finestra non compare ancora sullo
schermo. Sono infatti necessarie ancora due chiamate: ShowWindow e UpdateWindow.
3.2.3 Ciclo dei messaggi
Dopo la chiamata a UpdateWindow, la finestra viene completamente visualizzata sullo
schermo. A questo punto, il programma deve prepararsi a leggere gli input della tastiera e del
mouse operati dall’utente. Windows gestisce una coda di messaggi per ogni programma
attualmente in esecuzione. Quando si verifica un evento di input, Windows traduce l’evento in
un messaggio che viene inserito nella coda dei messaggi del programma.
Un programma preleva i messaggi dalla coda eseguendo un blocco di codice conosciuto
come ciclo dei messaggi
3.2.4 Window procedure
Tutto quanto è stato descritto fino a questo punto non è che una semplice impostazione. La
window class è stata registrata, la finestra è stata creata ed è stata visualizzata sullo schermo e
il programma ha avviato un ciclo di messaggi per prelevare i messaggi dalla coda. L’azione
vera e propria si verifica nella window procedure, che determina che cosa la finestra debba
visualizzare al suo interno e in che modo la finestra debba rispondere all’input dell’utente.
In genere, i programmi non chiamano le window procedure direttamente. La window
procedure viene quasi sempre chiamata da Windows stesso. Un programma può chiamare
indirettamente la propria window procedure grazie alla funzione denominata SendMessage.
Al momento della creazione di una finestra, Windows chiama la window procedure e la
richiama anche quando la finestra viene eliminata. Inoltre, Windows richiama questa
procedura quando la finestra viene ridimensionata, spostata o ridotta a icona, quando un
utente fa clic sulla finestra con il mouse, quando vengono digitati dei caratteri utilizzando la
tastiera, quando è stata scelta una voce da un menu, quando viene utilizzata una barra di
32
scorrimento e quando l’area deve essere ridisegnata. Tutte queste chiamate alla window
procedure avvengono sotto forma di messaggi. Nella maggior parte dei programmi in
Windows, gran parte del codice è dedicato alla gestione di questi messaggi. In genere, i
messaggi che Windows può inviare a un programma sono identificati da nomi che cominciano
con le lettere WM e sono definiti nel file di intestazione WINUSER.H.
L’idea di una routine all’interno del programma che venga chiamata dall’esterno dello
stesso non è una novità nella programmazione in modo caratteri. In Unix, la funzione signal è
in grado di intercettare un’interruzione Ctrl-C o altri interrupt emessi dal Sistema Operativo. I
programmi più vecchi scritti per l’MS-DOS intercettavano spesso gli interrupt hardware. In
Windows questo concetto è stato esteso a tutto. Tutto ciò che interessa una finestra viene
trasferito alla window procedure sotto forma di messaggio. Dopodiché quest’ultima risponde
al messaggio in qualche modo o passa il messaggio a DefWindowProc per un’elaborazione di
default.
3.3 Multitasking e multithreading Il multitasking è la capacità di un Sistema Operativo di eseguire più programmi
simultaneamente. In linea di principio, il Sistema Operativo utilizza un clock hardware per
allocare intervalli di tempo a ogni processo in esecuzione. Se gli intervalli di tempo sono
sufficientemente piccoli e la macchina non è sovraccaricata da troppi programmi che tentano
di fare qualcosa, l’utente ha l’impressione che tutti i programmi vengano eseguiti
simultaneamente.
Il multitasking non è niente di nuovo. Sui grandi mainframe, il multitasking è un dato di
fatto. A questi mainframe sono spesso collegati centinaia di terminali e ogni utente che lavora
su un terminale ha l’impressione di godere di un accesso esclusivo a tutte le risorse della
macchina. Il multitasking sui Personal Computer ha impiegato un tempo molto maggiore
prima di divenire realtà. Le versioni a 32 bit di Windows supportano tutte sia il multitasking
vero e proprio sia il multithreading.
Il multithreading è la capacità di un programma di implementare il multitasking
internamente. Il programma può dividersi in thread o thread di esecuzione separati che in
apparenza vengono eseguiti contemporaneamente.
La progettazione, la scrittura e il collaudo di un’applicazione multithreaded complessa è
uno dei compiti più difficili per un programmatore Windows. Poiché un sistema multitasking,
nella sua versione preemptive, può interrompere un thread in qualsiasi punto per passare il
33
controllo ad un altro thread, eventuali interazioni non desiderabili tra due thread possono
rivelarsi occasionalmente, come se si verificassero casualmente.
Il race condition, un bug comune in un programma multithreaded, si verifica quando un
programmatore presume che un thread finisca di svolgere un determinato lavoro (per esempio,
la preparazione di alcuni dati) prima che un altro thread richieda quei dati. Per aiutare a
coordinare l’attività dei thread, i Sistemi Operativi richiedono varie forme di
sincronizzazione. Una di queste viene chiamata semaforo e consente al programmatore di
fermare l’esecuzione di un thread in un determinato punto del codice fino a quando un altro
thread segnala che è possibile riprendere l’esecuzione. Ci sono tuttavia delle sezioni critiche,
cioè porzioni di codice che non possono essere interrotte. I semafori inoltre introducono un
altro bug comune nell’uso dei thread, che viene chiamato deadlock. Ciò si verifica quando
due thread hanno fermato reciprocamente la loro esecuzione e possono sbloccarla solo
procedendo.
La funzione API per creare un nuovo thread di esecuzione si chiama CreateThread.
Tuttavia, la maggior parte dei programmatori in Windows preferiscono utilizzare una libreria
runtime del C chiamata _beginthread che viene dichiarata nel file di intestazione
PROCESS.H.
Infine i thread hanno bisogno di una loro temporizzazione, in quanto la loro elaborazione
potrebbe avvenire troppo velocemente. La soluzione è fornita dalla funzione Sleep. Un thread
può chiamare la funzione Sleep per sospendere volontariamente la sua esecuzione. L’unico
parametro richiesto è un periodo di tempo espresso in millisecondi. La funzione Sleep non
ripassa il controllo fino al termine del tempo specificato. Durante quel periodo, il thread viene
sospeso e non vengono più allocate porzioni di tempo, sebbene ovviamente, il thread necessiti
ancora di piccole quantità di tempo di elaborazione durante i clic del timer, quando il sistema
deve determinare il thread che deve essere ripreso. Un argomento uguale a 0 passato a Sleep
fa perdere al thread la parte di tempo rimanente assegnatagli.
Quando un thread chiama la funzione Sleep, solo quel thread viene sospeso per il periodo
di tempo specificato. Il sistema continua l’esecuzione degli altri thread, sia nello stesso
processo sia in un altro.
3.4 Victor e le Immagini È essenziale, al fine di una corretta analisi sulla libreria Victor Image Processing prodotta
dalla Catenary Systems, portare l’attenzione sui metodi utilizzati per definire, conservare e
visualizzare le immagini su un moderno Personal Computer. Solo grazie a questa discussione
34
saremo in grado di studiare in maniera corretta le funzioni Victor in grado di elaborare le
immagini.
3.4.1 Codifica delle immagini
Diversi problemi devono trovare soluzione per ottenere questo risultato, per esempio si
deve discutere in che modo possano essere caratterizzati i colori, oppure quale sia la struttura
più adatta per contenere un’immagine.
La natura stessa dei colori sostiene la ricerca della soluzione attinente al primo quesito.
Infatti in natura qualsiasi colore è scomponibile nei tre colori primari rosso, verde e blu
(RGB). Con questa tecnica è possibile definire qualunque sfumatura si voglia ottenere. Quindi
non si deve far altro che definire una struttura che contenga i pesi dei tre colori primari per
ottenere abbastanza informazioni sulla sfumatura desiderata.
Di solito il peso di ogni colore primario è contenuto in un byte che permette un scala da 0 a
255, cosicché, per esempio il nero sarà definito da una struttura che contiene 0 in ogni campo.
Una sfumatura di giallo avrà un valore molto alto sia nel campo attinente al rosso sia nel
verde mentre conterrà un valore tendente a 0 nel campo relativo al blu. Infine un grigio avrà
un valore costante e simile in tutti i tre campi dove, valori molto bassi porteranno ad un grigio
scuro, mentre valori elevati produrranno grigi chiari. Con questa tecnica è possibile definire
più di sedici milioni di colori quindi abbiamo a nostra disposizione una gamma estremamente
valida per descrivere immagini del mondo reale.
Per definire una struttura relativamente semplice che ci permetta di visualizzare le
immagini in modo corretto possiamo sfruttare le tecniche utilizzate dai terminali video per il
loro funzionamento. Quest’ultimi utilizzano dei punti, chiamati pixel, per dare la sensazione
di colore all’occhio umano. Ogni pixel utilizza il metodo descritto prima, nel quale ci sono tre
aree molto piccole e ravvicinate. Ciascuna delle quali può modulare l’intensità di un colore
primario cosicché, da lontano, l’occhio percepisce una sola sfumatura, risultato della fusione
dei tre colori primari. Più pixel formano la superficie dello schermo più elevata è la
risoluzione dello stesso.
L’immagine non è altro che una matrice di pixel organizzati in righe e colonne che
definiscono i colori di ogni singolo punto. Quindi, per esempio, un’immagine indicata
480X640 significa che contiene 480 righe e 640 colonne di pixel. I pixel vengono
immagazzinati, una riga dopo l’altra partendo dall’angolo in basso a sinistra, in un vettore.
Varie sono le tecniche utilizzate per relazionare i pixel dell’immagine con l’informazione
sul loro colore. Tuttavia, solo un paio di queste sembrano aver acquisito una notevole
importanza ed entrambe hanno sia pregi che difetti.
35
La prima impone la creazione di una palette, che è paragonabile alla tavolozza che il
pittore usa per contenere i colori che utilizza sulla tela. Questa palette è un vettore di strutture
RGB che contengono al loro interno i colori primari per ogni sfumatura dell’immagine. Con
questa tecnica il vettore che contiene i dati relativi ad ogni pixel dell’immagine può essere
visto come un insieme di indici utilizzati per ritrovare i vari colori nella palette.
Grazie a questo metodo è possibile indicare la definizione dell’immagine. Infatti con un
solo bit per l’indice si può indirizzare solo due colori nella palette, uno con lo 0 e l’altro con
1, di solito sono il bianco e il nero. In questo caso ogni pixel viene caratterizzato da un solo
bit nella struttura che li conserva. Quindi un byte contiene il colore di 8 pixel sulla stessa riga.
Se si utilizza un byte per definire l’indice, si può indirizzare 256 colori nella palette e
quindi nella struttura che contiene i pixel ognuno di essi è caratterizzato da 8 bit.
Il vantaggio nell’utilizzare questa tecnica sta nel fatto di poter compattare in maniera
corretta le informazioni sul colore dei pixel, in quanto pixel vicini, che potrebbero avere lo
stesso colore, indicano lo stesso indice nella palette, con un considerevole risparmio di spazio,
un byte al posto di tre. Tuttavia se si utilizza per esempio una palette con 256 colori, non è
possibile avere immagini con più di 256 sfumature al loro interno, anche se quest’ultime
possono reperire una sfumatura diversa in un insieme di sedici milioni.
Per prevenire questo svantaggio si adotta la seconda tecnica che, a discapito di un vettore più
capiente per contenere l’informazione sul colore dei pixel, permette di avere immagini con
sedici milioni e più di colori, contemporaneamente al loro interno.
Le immagini che utilizzano questa tecnica abbandonano la palette e i tre byte che
corrispondono ai tre colori primari di ogni sfumatura vengono stoccati nel vettore che
contiene i colori dei pixel, cosicché ogni pixel occupa tre byte in questo vettore. Queste
immagini vengono, per questo, chiamate a 24 bit.
È importante notare che numerose sono le variazioni che si possono ottenere. Per esempio
si possono avere immagini caratterizzate da sfumature di grigio ottenute in quanto nella
palette ci sono 256 grigi differenti che vanno dal più chiaro al più scuro. Oppure è possibile
avere immagini che utilizzano quattro e non tre byte per definire i tre colori primari, in questo
caso si privilegia il verde donandogli più bit, in quanto l’occhio umano percepisce
maggiormente le sfumature di verde.
Tuttavia per risolvere i problemi descritti nel capitolo precedente si utilizzano immagini a
24 bit, quindi la struttura che si andrà a trattare non avrà la palette ma ogni pixel verrà definito
grazie a tre byte nel vettore che ha il compito di contenerli tutti.
36
3.4.2 DIB
Un argomento di grande interesse che non è stato ancora trattato ma che riveste un ruolo
importante nell’analisi, è come Windows identifichi le immagini. Quest’ultimo utilizza un
oggetto chiamato DIB (Bitmap Indipendente dal Dispositivo) nel quale trovano collocazione
le strutture che abbiamo analizzato precedentemente.
La DIB è stata introdotta in Windows 3.0 allo scopo di fornire quel formato tanto
necessario per lo scambio di immagini. In realtà, altri formati di file come GIF e JPEG sono
molto più comuni su Internet e questo soprattutto perché i formati GIF e JPEG implementano
schemi di compressione in grado di ridurre in maniera significativa i tempi di download.
Sebbene sia stato definito uno schema di compressione anche per le DIB, questo viene usato
di rado. Nella maggior parte delle DIB, i bit della Bitmap conservano la loro forma non
compressa. Questo è senza dubbio un vantaggio se si desidera manipolare i bit della Bitmap
nel proprio programma. Contrariamente ai file GIF e JPEG, la DIB è direttamente supportata
dalla API di Windows. Se in memoria c’è una DIB, è possibile associarle puntatori come
argomenti a svariate funzioni che consentono di visualizzarla.
Un oggetto DIB comprende quattro sezioni principali: un’intestazione dell’oggetto,
un’intestazione informativa, una tabella dei colori RGB (ma non sempre) e i bit per pixel della
Bitmap. È possibile pensare alle prime due parti come a strutture di dati del C e alla terza
parte come a un array di strutture di dati. Queste strutture sono documentate nel file di
intestazione di Windows WINGDI.H. Una DIB salvata in memoria nel formato DIB
compresso comprende tre sezioni: un’intestazione informativa, la palette (ma non sempre) e i
bit per pixel della Bitmap.
La parte più interessante che descrive l’immagine è contenuta nell’intestazione informativa
è qui, infatti, che sono descritte le caratteristiche della Bitmap. Per esempio ci sono dei campi
che contengono la dimensione dell’immagine in pixel e un campo che indica il numero di bit
per pixel. Per le DIB questo può essere 1, 4, 8 oppure 24, che caratterizzano rispettivamente
una DIB a 2, 16, 256 oppure full-color. Nei primi tre casi l’intestazione informativa e seguita
da una palette, che invece non esiste per le DIB a 24 bit. La palette è un array di strutture
RGB da tre byte, una per ciascun colore dell’immagine.
3.4.3 Victor
Le funzioni Victor possono operare su un sotto gruppo delle DIB. La differenza sta nella
compressione, nei bit per pixel e nella palette. Una DIB può essere compressa o non
compressa. Le funzioni della librerie Victor possono operare solo su DIB non compresse.
37
Questo permette alle funzioni di lavorare sulle sezioni della DIB. Inoltre questa caratteristica
è di vitale importanza nel sistema che si va a costituire. Infatti gli algoritmi di cattura ed
elaborazione dati che si occuperanno di manipolare le immagini devono operare direttamente
sulle sezioni della DIB. Windows permette alle DIB di avere 1, 4, 8 oppure 24 bit per pixel.
Le funzioni Victor possono operare solo su DIB con 1, 8 o 24 bit per pixel. Le differenze sulla
palette non vengono contemplate in quanto, per gli scopi prefissati, si utilizzano immagini a
24 bit che non impiegano la tabella dei colori.
La più importante struttura nel pacchetto Victor è il descrittore dell’immagine. Tutte le
informazioni necessarie alle funzioni, per operare su un’immagine sono contenute nel
descrittore dell’immagine. Quest’ultimo è definito in VICDEFS.H ed è un argomento
richiesto per molte funzioni Victor.
Le potenzialità offerte dalla libreria Victor sono numerose. Ci sono funzioni che
manipolano i colori dell’immagine per ottenere gli effetti più vari. Dall’effetto neve all’effetto
metallico all’effetto bianco e nero. Esistono inoltre funzioni che permettono di analizzare
l’immagine per produrre diagrammi relativi alle caratteristiche cromatiche o luminose. Infine
è possibile utilizzare funzioni che operano sulle dimensioni dell’immagine oppure sono in
grado di modificare il numero di bit per pixel.
Per poter utilizzare le funzioni contenute nella libreria si devono effettuare alcune
operazioni preliminari. Per prima cosa si deve allocare memoria per l’immagine, dopodiché si
carica l’immagine. Solo a questo punto è possibile manipolarla grazie alle funzioni Victor.
Un’altra caratteristica, che rende interessante l’oggetto incaricato da Victor di contenere
l’immagine, è la possibilità di manipolare direttamente i bit che contengono il colore dei
pixel. Quest’ultima opportunità avrà enorme rilievo nell’implementazione degli algoritmi che
supportano l’elaborazione dell’immagine. È possibile aggiungere, già da ora, che questa
operazione sarà l’unica effettuata sulle immagini contenute nell’oggetto Victor. Infatti per
conseguire gli obiettivi preposti non è importante manipolare le immagini per ottenere
qualche strano effetto. L’unica caratteristica della libreria Victor che viene sfruttata in questo
contesto è la facilità con cui si accede ai dati che descrivono i pixel dell’immagine.
Per allocare memoria si utilizza la funzione allocimage, per caricare nella memoria appena
definita un’immagine si utilizza loadbmp. Infine per liberare la memoria, dopo l’elaborazione,
si utilizza la funzione freeimage.
39
4 Architettura e algoritmi
Grazie ai capitoli precedenti è possibile avere di fronte un disegno generale nel quale
l’ausilio si colloca, si sono definiti sia un punto di partenza che di arrivo, i problemi che si
devono affrontare, nonché gli aiuti esterni che ci possono sostenere nel nostro compito. Ora la
strada è stata tracciata e possediamo i mezzi per costruirla, non dobbiamo fare altro che
iniziare con i lavori. In questo capitolo si intraprenderà la discussione delle soluzioni proposte
nel sistema. Tuttavia a questo punto dell’analisi le soluzioni non possono essere che teoriche,
in quanto alla pratica si dovrà attendere il capitolo successivo relativo all’implementazione,
infatti solo in esso compariranno le soluzioni concrete adottate nel sorgente.
Il titolo di questo capitolo contiene già al suo interno un’informazione importante ed
essenziale per il suo assetto. Infatti la discussione si dividerà su due aspetti fondamentali in
stretta relazione fra di loro. Il primo tratta delle scelte intraprese al fine di risolvere i dubbi,
che sono apparsi quando l’analisi si è rivolta all’architettura che il sistema deve possedere, in
quel particolare momento sono emerse in realtà alcune caratteristiche che tutti i complessi
devono ottenere per il loro buon funzionamento. Dall’altra parte devono essere discusse le
soluzioni che sono state contemplate per risolvere i problemi relativi alla natura stessa
40
dell’ausilio, in questo caso gli algoritmi che sono stati definiti per portare a termine i compiti
che il sistema deve offrire agli utenti.
4.1 Architettura Vengono trattate in questa sezione le soluzioni adottate per definire la struttura che il
sistema deve possedere. La discussione successiva non ha altro scopo che produrre una base
solida e stabile per la costruzione degli algoritmi che verranno trattati nella prossima sezione e
che permettono al sistema di portare a termine il suo compito.
Grazie al C++, il linguaggio di programmazione scelto per scrivere questo sistema, è
possibile definire una architettura che utilizza gli oggetti come base per la sua struttura. È
all’interno di questi oggetti che vengono incapsulati gli algoritmi, un singolo algoritmo per
ogni oggetto. Quindi ogni oggetto porterà al suo interno determinate caratteristiche che lo
collocheranno in un ruolo ben preciso all’interno del sistema.
Varie sono le tipologie che si possono esaminare. Si può, per esempio, definire una
struttura nella quale tutti gli oggetti sono sullo stesso piano, oppure delineare una struttura
stratificata dove gli oggetti si trovano ad operare a diversi livelli. Infine si può scegliere di
operare con una struttura master slave dove un oggetto è a capo di tutti gli altri. Ogni
soluzione presenta sia vantaggi che svantaggi.
Un’architettura che pone tutti gli oggetti sullo stesso livello porta con sé il privilegio di
poterli fare comunicare liberamente fra di loro. Costituendo una struttura estremamente
complessa, veloce e compatta. Tuttavia si rischia di produrre un sistema impossibile da
comprendere e da manipolare. Questo perché le relazioni che intercorrono tra gli oggetti
possono diventare troppo complesse ed intricate da poterle seguire facilmente e in un attimo,
eliminando una relazione o modificandola, si può compromettere l’intero sistema. Questa
struttura porta anche un difetto più subdolo da analizzare, infatti rende inutile una
programmazione orientata agli oggetti e il paradigma di programmazione si sposta su una
programmazione procedurale. Infatti gli algoritmi contenuti negli oggetti vengono elaborati
uno dopo l’altro in sequenza rendendo vano lo sforzo di divedere i compiti su più oggetti, è
come se gli algoritmi fossero in sequenza temporale all’interno di un programma monolitico.
In più una tale soluzione entra in netto contrasto con un Sistema Operativo quale Windows
nel quale si adottano messaggi per la comunicazione tra oggetti che devono essere ben
definiti. Infine si genera un sistema nel quale uno sviluppatore non può estenderne una sola
parte senza capire come funziona l’intera struttura.
41
La stratificazione degli oggetti potrebbe essere una soluzione più conveniente, oltre a
rendere la struttura più semplice e intuitiva, permette agli oggetti di riacquistare il proprio
valore e inserirsi, grazie alle loro caratteristiche, a livelli ben definiti all’interno della
struttura. Tuttavia entra in gioco un’ulteriore domanda: quanti strati deve avere la struttura?
Infatti c’è il pericolo di sovrastimare il numero di strati e rendere il sistema troppo lento nel
rispondere agli stimoli esterni prodotti dall’utente, o per esempio, nella cattura e
riconoscimento dei movimenti. Se il sistema è obbligato a tradurre le operazione da svolgere
attraverso numerosi passaggi tra uno strato e l’altro, l’elaborazione può risultare complessa e
divenire lenta nella sua globalità.
L’ultima tipologia da discutere non è stata rilegata alla fine senza motivo, infatti questa è la
soluzione che si adotterà per il sistema. La struttura master slave risponde alla domanda di
poco fa sugli strati che deve possedere l’architettura, ogni oggetto che incapsula al suo interno
un algoritmo diviene schiavo di un oggetto che presiede al controllo e alla gestione di tutto il
sistema. In pratica la struttura si divide su due soli livelli, il livello più basso contiene
l’oggetto che controlla l’intero sistema, mentre al livello superiore risiedono gli oggetti che
contengono gli algoritmi per l’elaborazione dei dati. Un oggetto slave dello strato superiore,
per poter comunicare con un oggetto dello stesso livello deve chiamare il master al livello
inferiore informandolo sull’operazione che vuole svolgere, sarà quest’ultimo ad impartire gli
ordini all’oggetto servo destinatario.
Questo permette di trattare gli oggetti servi come delle scatole nere, utile nello sviluppo
futuro del sistema in quanto gruppi di sviluppatori diversi possono interessarsi solo ad alcuni
aspetti del sistema tralasciandone altri, senza entrare in conflitto. Per gestire le funzioni dello
strato master viene incaricato un oggetto che, oltre a regolare gli oggetti schiavi, pensa ad
organizzare e tradurre le azioni che l’utente fa sul sistema, visualizzando i risultati su schermo
grazie ad un rapporto stretto con le funzioni Windows.
Esiste un solo difetto che colpisce questa tipologia di struttura ed è rappresentato dalla
velocità. Si è già incontrato questo aggettivo quando si discutevano i vantaggi di una struttura
in cui tutti gli oggetti erano posti sullo stesso livello. Quindi potrebbe rivelarsi una strada
valida da percorrere, cercare di portare ed inserire nella struttura le qualità che promettono la
caratteristica della velocità. Per ottenere questo traguardo, si deve purtroppo infrangere una
regola importante che costituisce una struttura master slave, e cioè, permettere che gli oggetti
schiavi possano comunicare fra di loro eliminando il vincolo che li obbliga a parlare
solamente al master.
42
In questo caso, si privilegia la caratteristica di velocità a dispetto della coerenza nella
struttura. Tuttavia non si deve abusare di questa singolarità, essa è ammessa solo nei casi
strettamente legati al tempo di esecuzione, permettendo agli oggetti servi di interagire
direttamente senza l’ausilio del master.
A questo punto è possibile contemplare la struttura del sistema nella sua interezza. Essa
sarà gestita da un oggetto che farà le veci del master, quest’oggetto avrà il compito di
supervisore in tutte le operazioni che il sistema dovrà essere in grado di compiere. Gli oggetti
predisposti per interpretare i comandi decisi dall’utente dovranno comunicare al supervisore
quale operazione è stata richiesta, quest’ultimo impartirà gli ordini agli oggetti su cui ricade
quella operazione e i risultati ottenuti saranno visualizzati grazie ad altri oggetti sempre gestiti
dal supervisore.
È importante ancora discutere le qualità e definire le caratteristiche che ogni oggetto slave
deve possedere. Per far questo è possibile iniziare col pensare a come l’ausilio opererà. La
scala che permette di raggiungere il nostro obbiettivo è composta da numerosi scalini, ogni
scalino è composto da uno o più oggetti servi che ne definiscono la natura. Tutta la scala,
infine, viene tenuta insieme dal cemento costituito dal master o supervisore.
Ogni scalino, e quindi gli oggetti che lo costituiscono, deve essere pensato come il telaio
nel quale incastonare gli algoritmi che verranno presentati nel paragrafo successivo, sono
infatti quest’ultimi la vera risorsa del sistema, sono essi a permettere il funzionamento
dell’ausilio. Qui con la tipologia di struttura master slave prima e con l’analisi degli oggetti
fra poco, non si fa altro che porre le fondamenta per accogliere gli algoritmi che manipolano i
dati e che portano alla soluzione del problema.
Vengono ora presentati grazie a sezioni separate tutti gli scalini che permettono di arrivare
all’obiettivo.
43
4.1.1 Acquisizione immagini
Una delle qualità più importanti che il sistema deve possedere è la capacità di agganciare e
ottenere immagini dalle videocamere collegate al computer. Il sistema ha incaricato, per
questa potenzialità, un singolo oggetto al quale è addossata l’intera responsabilità di questa
operazione. Il suo compito non è solo quello di prelevare le immagini dalle videocamere ma
deve gestire tutto quello che è attinente alle camere stesse. Sotto l’interrogazione del master,
che d’ora in poi verrà chiamato Supervisore, deve fornire dei risultati attinenti alle domande
oppure rispondere indicando un errore se qualcosa non può essere fatto. Questo oggetto non
contiene al suo interno particolari algoritmi per l’elaborazione dei dati, è solo il tramite e il
mezzo che il sistema utilizza verso il mondo delle videocamere. I risultati più importanti che
l’oggetto deve essere in grado di ottenere sono l’aggancio delle camere nel sistema, la cattura
di singoli fotogrammi, la possibilità di modificare parametri quali la luce e il contrasto delle
immagini e infine poter rilasciare l’aggancio delle camere prima che il sistema venga
terminato.
La caratteristica del poter modificare alcuni importanti parametri relativi all’immagine si
ricollega al discorso intrapreso nei capitoli precedenti sul problema di rendere semplice
l’utilizzo del sistema da parte dell’utente e del suo assistente. Infatti, utilizzando questa
caratteristica è possibile modificare i parametri dell’immagine direttamente dall’ausilio senza
dover usare altri programmi forniti dalla casa costruttrice della videocamera. Questo permette
di velocizzare queste operazioni e rendere meno pesante il lavoro richiesto all’utente.
È importante notare che il sistema ha un forte bisogno di questo oggetto. Infatti grazie ad
esso non si deve preoccupare di come le camere vengono gestite. Questo implica che uno
44
sviluppatore a cui non interessa questa parte del sistema non è assolutamente obbligato a
capire come è implementato al suo interno quest’oggetto, ne deve soltanto richiedere le
funzionalità. Al contrario uno sviluppatore interessato al miglioramento di questa parte del
sistema può agire liberamente, con il solo vincolo di rispettare la coerenza sulle risposte che
l’oggetto può dare al Supervisore rispetto alle versioni precedenti.
4.1.2 Scelta del riferimento
In questa sezione si risponde ad un problema che era stato analizzato nel capitolo relativo
al contesto dell’ausilio. In quella sezione era stata trattata la differenza tra motion capture
intrusiva o non intrusiva elencando i pregi e i difetti di ciascuna soluzione. La nostra scelta
non può che ricadere su un sistema che utilizza motion capture intrusiva, in quanto si cerca di
privilegiare un sistema che introduca il minor numero di errori nell’elaborazione
dell’immagine. Questo può essere fatto solo utilizzando la tecnica intrusiva, non priva di
errori ovviamente, ma più sicura di quella non intrusiva.
Tuttavia non si deve dimenticare il fatto che, grazie alla programmazione orientata agli
oggetti è sempre possibile implementare una soluzione che incorpora la tecnica non intrusiva.
Anche se passare da una soluzione all’altra possa essere visto come riedificare una parte delle
fondamenta e quindi un operazione abbastanza complessa, tutto il lavoro si focalizza solo su
pochi oggetti i quali sono adibiti a questa elaborazione all’interno del sistema.
Ora grazie alla scelta caduta sulla motion capture intrusiva è possibile definire le
caratteristiche che deve possedere l’oggetto o gli oggetti a cui è demandato il compito di
intercettare il riferimento colorato nelle immagini catturate grazie all’oggetto descritto
precedentemente.
Gli oggetti che costituiscono questo passo, se interrogati dal Supervisore devono essere in
grado di indicare quali colori sono presenti all’interno delle immagini catturate e dare la
possibilità all’utente di scegliere il colore da intercettare, quest’ultimo sarà il colore che più si
avvicina al colore del riferimento fisso sul volto dell’utente.
Anche in questo caso il sistema non ha bisogno di conoscere come queste operazioni
vengono effettuate all’interno degli oggetti, il Supervisore non deve fare altro che ottenere il
colore scelto dall’utente e passarlo agli oggetti che permettono al sistema di proseguire nella
sua elaborazione. Questo, come è gia stato ribadito più volte è il grande vantaggio di poter
operare con gli oggetti.
45
4.1.3 Motion capture
Gli oggetti che appartengono a questa parte del sistema sono il cuore della struttura stessa,
è infatti in questo luogo che il sistema riesce a intuire il movimento del volto dell’utente
grazie al riferimento fisso sulla sua fronte. Gli algoritmi contenuti all’interno di questi oggetti
rendono possibile tutto questo e per questa ragione sono i più importanti di tutto il sistema.
Ottenuto il colore del riferimento dal Supervisore che li gestisce e comanda in tutte le
azioni che possono attuare, gli oggetti a cui è demandato quest’incarico devono essere in
grado di fornire al sistema un’indicazione, la più precisa possibile, di dove si trovi il
riferimento su ogni immagine acquisita dalle videocamere. A questo punto catturando
un’immagine dopo l’altra è possibile tenere traccia dei movimenti del volto dell’utente. È qui
che entra in gioco la singolarità che impone alla struttura di venir meno alle leggi imposte tra
master e slave. Infatti, il fattore velocità diventa una carta importante da giocare in questa
parte dell’elaborazione. Per privilegiare la rapidità si deve permettere agli oggetti che
contengono gli algoritmi che elaborano le immagini catturate di poter comunicare
direttamente con l’oggetto che è incaricato della cattura stessa. Scavalcando, in questo modo,
la mediazione costituita dal Supervisore. Il vantaggio che risiede nella velocità di
elaborazione supera di gran lunga lo svantaggio di destabilizzare la coerenza della struttura
stessa.
Infatti l’obbligo, da parte degli oggetti che costituiscono questa parte del sistema, di
comunicare solo con il Supervisore e chiedere a quest’ultimo di caricare ogni immagine porta
ad un overhead di operazioni troppo costoso e inutile. Inoltre lasciando liberi questi oggetti di
comunicare si arriva ad una implementazione più chiara da comprendere per i futuri
sviluppatori.
Ciò non toglie che solo in questo caso si può attuare questa politica, per tutto il resto del
sistema le regole stabilite da una struttura master slave devono essere strettamente rispettate,
qui si è agito in questo modo solo perché nella lista di priorità si è posta la velocità prima di
tutto il resto e quindi anche delle regole che determinano la struttura. Tuttavia nessun altra
eccezione può essere fatta in quanto subito dopo la prontezza nell’elaborazione la lista delle
priorità accoglie il dovere di rispettare diligentemente le regole che sanciscono la struttura.
4.1.4 Definizione campo visivo
A questo punto del cammino il Supervisore deve gestire degli oggetti che custodiscono al
loro interno gli algoritmi che permettono di associare ad una data posizione del riferimento la
46
direzione in cui è rivolto il viso dell’utente. Sono questi oggetti che contengono tutte le
tematiche che si sono analizzate mentre si parlava della calibrazione del sistema.
Una volta in grado di intercettare il movimento del riferimento nelle immagini catturate
dalle videocamere, in che modo si può tradurre questa informazione in un dato utile?
Il discorso già intrapreso nel capitolo attinente al contesto nel quale si parlava del
cablaggio ha introdotto un concetto interessante quale il sistema di riferimento assoluto. È
possibile infatti, definire un sistema di riferimento assoluto basato sull’immagine stessa
catturata. Essa nasce dal sistema ottico di una videocamera che per definizione è fissa nello
spazio. Quest’ultima viene appoggiata per esempio sul bordo superiore dello schermo e in
quella posizione resta per tutto il tempo in cui il sistema è al lavoro senza essere più toccata.
Ciò significa che sia lo schermo del Personal Computer sia la videocamera sono fermi uno
rispetto all’altro. Questa caratteristica viene sfruttata per ricavarne la constatazione che se
l’utente muove solo la testa e non tutto il corpo fissando il punto in alto a destra dello
schermo, il riferimento solidale al suo volto, si troverà in una posizione ben precisa nel
sistema assoluto dell’immagine. Tutte le volte che l’utente fisserà quel punto il riferimento si
troverà sempre nella stessa posizione.
Il trucco sta nel far fissare all’utente i quattro angoli dello schermo e da questi si ottiene
un’area rettangolare nel sistema di riferimento assoluto dell’immagine nel quale il riferimento
solidale al volto dell’utente ha libertà di movimento. Se, per esempio, l’utente rivolge il viso
verso il centro dello schermo, il riferimento si troverà al centro dell’area rettangolare definita
nel sistema di riferimento solidale con l’immagine. Mentre se quest’ultimo esce dall’area
appena determinata significa che l’utente non ha il viso rivolto verso lo schermo.
La determinazione di quest’area, scalata nel sistema di riferimento solidale con l’immagine
catturata dalla videocamera, è il compito a cui gli oggetti che risiedono a questo punto del
cammino verso il nostro obiettivo sono obbligati a compiere. O meglio, gli algoritmi
contenuti in questi oggetti devono portare al sistema questo risultato.
Quando si è discusso nei capitoli precedenti sulla disposizione delle videocamere, l’analisi
non si è spinta oltre l’esame dei pregi e difetti di ogni soluzione. Precedentemente si è indicata
la parte superiore dello schermo come una potenziale posizione della videocamera, ebbene
questa sarà la posizione definitiva che la camera principale occuperà nel sistema. È stata
scelta questa posizione per minimizzare al massimo i problemi di linearità insiti nella
geometria spaziale che incorpora schermo, utente e videocamera.
Infatti la soluzione che vedeva la camera posta al di sotto dello schermo del video porta
all’introduzione di trasformazioni curvilinee tra riferimento e punto fissato difficili da
47
compensare, per esempio il rettangolo dell’area definita dal cablaggio non è più un rettangolo
ma una famiglia di curve sempre più accentuate.
Questo è dovuto al fatto che l’asse tra la camera e la fronte dell’utente dove risiede il
riferimento è praticamente orizzontale nella soluzione nella quale la camera è sopra lo
schermo, mentre è obliquo se la videocamera è posta sotto lo schermo ed è questo fatto a
introdurre le non linearità nella relazione tra riferimento e punto fissato dall’utente.
4.1.5 Indicazione dello sguardo
Si è quasi raggiunta la soluzione del problema, ora il Supervisore non deve fare altro che
gestire un oggetto che ha il compito di riferire in quale direzione l’utente abbia rivolto il viso.
Questo oggetto ha il compito di elaborare dati che sono già stati filtrati e a cui si è già dato un
significato attraverso i numerosi oggetti precedentemente descritti. In pratica questo oggetto
non deve fare altro che controllare la posizione del riferimento in ogni immagine catturata,
confrontarla con l’area ottenuta nella calibrazione e, in base ad un algoritmo ben preciso,
avvertire il Supervisore del punto sullo schermo sul quale l’utente ha rivolto lo sguardo.
Entra ora in gioco l’analisi che è stata fatta sulla possibilità di connettere al sistema
applicazioni indipendenti. In quella discussione era emerso che sarebbe stata un’ottima
caratteristica poter inserire nel sistema applicazioni che non avevano nulla da condividere con
l’ausilio, se non sfruttare le sue potenzialità per i loro obiettivi privati. La struttura che si è
definita in queste pagine permette di risolvere questo problema in maniera egregia.
Le applicazioni vengono trattate dal sistema come oggetti che appartengono allo strato
superiore, dove risiedono tutti gli oggetti che sono stati definiti slave. In quest’ottica è
possibile suddividere questo strato in due insiemi separati di oggetti schiavi, da un lato ci sono
tutti gli oggetti che concorrono alla soluzione del problema, quindi tutti gli oggetti che si sono
descritto fino ad ora. Nell’altro insieme risiedono gli oggetti che contengono al loro interno
un’applicazione indipendente. Questo gruppo ha al suo interno solo un oggetto attivo alla
volta ed è quest’ultimo che contiene l’applicazione attiva, cioè quella che riceve
l’informazione sulla posizione del viso dell’utente.
L’unico compito aggiuntivo per uno sviluppatore di applicazioni, che incorporano al loro
interno le potenzialità dell’ausilio, sarà quello di iscrivere nella lista delle applicazioni tenuta
dal Supervisore la sua applicazione e informare quest’ultimo che, tra le tante applicazioni
esistenti all’interno del sistema, quella attiva è la sua. A questo punto il Supervisore, quando
otterrà il punto fissato dal volto dell’utente grazie all’ultimo oggetto nella scala
dell’elaborazione dei dati, avviserà con questa informazione l’applicazione che in quel dato
istante è attiva.
48
Questa è la parte più elegante di tutta la struttura ed è al fine di raggiungere questa
potenzialità che il sistema è stato concepito, questo è il fine ultimo dell’ausilio, tutto
all’interno di esso spinge per rendere attiva questa possibilità.
È per questo motivo che si è deciso di utilizzare questa tipologia di struttura in quanto se
tutti gli oggetti fossero stati posti sullo stesso piano, il compito di capire quale applicazione
indipendente era attiva sarebbe stato di difficile soluzione. Grazie al Supervisore, che tiene
traccia di tutte le applicazioni all’interno del sistema e conosce con precisione quale delle
tante sia in quell’istante attiva questo problema viene risolto in maniera elementare.
4.1.6 Gestione del sistema
Il Supervisore non ha solo il compito di controllare tutta la catena di acquisizione ed
elaborazione dati, nonché la gestione di tutte le applicazioni indipendenti iscritte nel sistema,
ma deve gestire anche degli oggetti che non concorrono direttamente alla soluzione finale.
Questi oggetti risiedono, come tutti gli altri, nello strato superiore descritto come slave.
Possono quasi essere visti come un terzo insieme tra quelli descritti in precedenza,
quest’ultimi si occupano di tutta la gestione indiretta del sistema, dalla gestione del menu, alla
gestione degli errori, alla visualizzazione delle finestre attive e infine alla gestione delle liste
che contengono le applicazioni. Possiamo vederli come oggetti che lavorano dietro le quinte e
che aiutano il Supervisore nei suoi compiti più pesanti ma che esulano dall’elaborazione più
importante.
Il loro compito principale è quello di rendere l’architettura semplice e ben ordinata, per
permettere una facile lettura del codice sorgente e per non affaticare in maniera pesante le
operazioni svolte dal Supervisore. Quest’ultimo demanda i compiti più pesanti e noiosi a
questi oggetti trattenendo per sé le elaborazioni più ricche di significato per il sistema.
Nel corso della trattazione è possibile riferirsi a questi oggetti in maniera superficiale, ma
solamente nelle appendici verranno esaminati nel loro complesso e nella loro totalità.
4.2 Algoritmi Ora, finalmente, non si cammina più su un terreno inesplorato. Ora esiste una base a cui si
è cercato di dare un’impostazione più solida possibile. Le fondamenta, descritte nel paragrafo
precedente, su cui nascerà tutto il resto sembrano robuste, ora non resta da fare altro che
definire degli algoritmi che sfruttino al massimo le potenzialità offerte dall’architettura del
sistema.
La parola algoritmo contiene al suo interno un concetto estremamente importante per il
mondo annesso all’informatica e all’elaborazione compiuta dal cosiddetto cervello
49
elettronico. Chi non ha mai avuto contatti con questo mondo reputa questa disciplina fredda e
a volte oscura, tanto da tenersi alla larga e a trattarla con diffidenza. Tuttavia nulla come
l’informatica dà prova delle potenzialità su cui può contare la mente umana. Questa disciplina
deve essere collocata al pari di molte altre, nelle conquiste che la razza umana ha ottenuto nei
milioni di anni combattuti per l’evoluzione. Il calore e l’emozione che può dare, per esempio,
l’arte musicale producendo una meravigliosa sonata devono essere percepiti anche nelle
creazioni imputabili alla nostra arte, infatti ambedue sono opere dell’intelletto umano e
contribuiscono a rendere particolare ciò che noi siamo. L’algoritmo, è per molti versi il soffio
di vita che percorre l’elemento inanimato costituito dal cervello elettronico, è la traccia
lasciata da un essere umano in un oggetto inorganico. È per questa ragione che non si deve
temere questa disciplina, in essa possiamo ritrovare solo il lavoro di noi stessi, come
qualunque altra cosa prodotta dall’uomo.
Gli algoritmi che vengono presentati in questo paragrafo non recano neppure lontanamente
l’ambizione descritta precedentemente, sono solo granelli di sabbia in una spiaggia
vastissima, ma nel loro piccolo cercano di migliorare la vita di qualche essere umano che può
trovare giovamento da questo umile ausilio.
A questo punto possiamo iniziare l’analisi dei vari algoritmi che si incontreranno negli
oggetti che costituiscono la struttura del sistema. Essi verranno divisi e studiati in varie
sezioni che si sovrappongono alle sezioni trattate nel paragrafo precedente.
Come già accennato quando si è parlato dell’oggetto candidato alla cattura delle immagini,
nessun algoritmo è contenuto al suo interno, questo è dovuto al fatto che il suo solo compito è
prelevare le immagini dalla camera. Il primo algoritmo che si incontrerà permette all’utente di
scegliere il colore del riferimento per le catture successive, poi verrà analizzato l’algoritmo
che permette di catturare il movimento del volto dell’utente. A questo punto si incontrerà
quello che aiuta a calibrare il sistema e infine verrà presentato l’algoritmo che risiederà
nell’oggetto, il cui incarico è dare l’informazione sul punto fissato sullo schermo dall’utente,
al Supervisore.
È di vitale importanza comprendere che in questo capitolo avviene l’analisi teorica degli
algoritmi, quindi si discute la catena logica di avvenimenti che rendono possibile
l’elaborazione dati. Ogni algoritmo viene discusso in maniera isolata dagli altri e l’unico
punto di contatto sono i risultati che l’algoritmo a monte dona alla procedura che sta a valle.
Tuttavia, come si vedrà nel prossimo capitolo l’implementazione è più complessa dell’analisi
teorica. In quanto, escluso l’algoritmo che permette all’utente di scegliere il riferimento, gli
altri sono estremamente correlati e potrebbero essere trattati quasi come un unico algoritmo.
50
La struttura che li organizza è ancora quella descritta in questo capitolo ma, alcuni oggetti
incapsulano più di un algoritmo al loro interno.
4.2.1 Scelta del riferimento
Il problema, che deve essere risolto da questo algoritmo, riguarda la possibilità, da parte
dell’utente, di scegliere il colore più conveniente al fine di riconoscere il riferimento fisso
sulla sua fronte.
La motion capture intrusiva ci permette di rendere relativamente semplice la risoluzione di
questo problema. Prima di tutto dobbiamo scegliere un colore, da applicare alla fronte
dell’utente, che sia particolare, cioè non troppo comune. Infatti esso deve risaltare
nell’immagine catturata dalla camera nella quale, presumibilmente, verranno visualizzati oltre
al volto dell’utente anche lo sfondo del luogo dove si trova e i capi di abbigliamento che
coprono le sue spalle. Per esempio si può scegliere un rosso acceso oppure un verde forte.
Tuttavia il sistema dovrebbe essere in grado di intercettare in modo corretto più colori per
risultare abbastanza versatile. Questa caratteristica è molto importante in quanto ci potremmo
ritrovare in situazioni strane, dove si cerca di intercettare un rosso in una stanza con la
tappezzeria rossa, quindi risulterebbe positivo, poter cambiare il colore del riferimento per
esempio in un verde ed evitare i problemi di intercettazione. Questo accade perché non siamo
in grado di prevedere dove il sistema verrà utilizzato e quindi non possiamo imporre dei
determinati colori allo sfondo e ai vestiti dell’utente.
A questo punto, affrontando l’analisi dell’algoritmo in maniera superficiale e senza introdurre
tutte le problematiche che entrano in gioco realmente, potremmo definire con facilità le
operazioni che esso deve compiere. La procedura non dovrebbe fare altro che, elencare i vari
colori che appaiono in un’immagine catturata presa come modello. Da questa lista l’utente
dovrebbe selezionare il colore che più si avvicina al colore del riferimento e l’algoritmo
dovrebbe concludere informando il Supervisore del colore scelto.
Purtroppo l’algoritmo, per ottenere un risultato valido e utilizzabile nelle elaborazioni
successive, non può essere così semplice. Ci sono fattori che ci costringono a produrre una
procedura più complessa di quanto possa sembrare. L’unico punto in comune tra la soluzione
appena descritta e l’algoritmo vero e proprio e il passo iniziale nel quale viene preso come
modello un’immagine catturata. Quest’ultima può essere vista come la prima immagine, del
mondo reale, catturata dal sistema, essa è il modello su cui si baseranno tutte le immagini
catturate successivamente. Infatti solo piccoli spostamenti del volto dell’utente faranno la
differenza tra questa e tutte le altre immagini.
51
Per cominciare, un fattore che preclude completamente la possibilità di costituire un algoritmo
semplice come è stato descritto è la definizione delle immagini che vengono elaborate dal
sistema. Nel nostro caso utilizziamo immagini a 24 bit che ammettono più di sedici milioni di
colori. Risulta ovvio che, sarebbe impossibile per l’utente poter eseguire una scelta sensata in
una lista così grande.
Inoltre, esiste un problema ancora più subdolo che è quasi paragonabile ad un errore. Se
catturiamo due immagini, anche vicine temporalmente, dello stesso oggetto, non otterremo
mai gli stessi colori dei pixel che lo definiscono. Questo è causato dall’illuminazione che può
variare, dall’angolazione differente dell’oggetto nelle due immagini rispetto alla sorgente di
luce, da imprecisioni dovute alla periferica di cattura ed errori nell’ottica di quest’ultima.
Quindi due immagini che l’occhio umano vede come pressoché identiche possono avere
differenze notevoli a livello di colore dei singoli pixel, basta una leggera sfumatura diversa
per identificare nell’elaborazione colori completamente differenti. Può anche capitare che
nell’immagine in alcune zone molto chiare o molto scure vengano catturati colori che non
hanno senso. L’algoritmo deve accorgersi di queste incongruenze e cercare di correggerle
oppure informarne l’utente.
Tenendo conto di questi fattori, che tentano di destabilizzare il nostro algoritmo, quest’ultimo
deve proporre delle contromisure valide per ottenere il risultato che ci prefiggiamo. Prima di
tutto la procedura non analizza più i colori singolarmente ma attua una sorta di filtro,
introducendo un concetto nuovo quale la sfumatura. Quest’ultima è una sorta di insieme di
colori che hanno una caratteristica in comune, sono cioè molto simili nella loro gradazione,
per esempio è una sfumatura il rosso scuro o il verde molto chiaro. Questo permette di
aggirare il problema della lista dei colori molto grande, infatti limitando il numero delle
sfumature che il sistema riconosce possiamo presentare all’utente un numero limitato di
opzioni nel quale può scegliere e rendere così più semplice il suo compito.
Per la definizione di un gruppo di colori in una sfumatura ci soccorre la discussione che
abbiamo intrapreso in un capitolo precedente riguardo ai colori e come essi sono interpretati
dall’elaboratore. Grazie alla scissione di un dato colore nei tre colori primari rosso,verde e
blu, siamo in grado, per esempio, di definire un rosso come un colore dove la componente
rossa è molto elevata mentre sia la componente verde che quella blu sono contenute. Quindi
possiamo definire una sfumatura, quell’insieme di colori che si discostano poco nel rapporto
tra le tre componenti.
Per riprendere l’esempio di poco fa, una sfumatura di rosso acceso conterrà tutti i colori che
hanno una componente di rosso prossima a 255 (ricordiamo che ogni componente in una
52
struttura RGB è definita da un byte che permette un range da 0 a 255), mentre avranno un
numero prossimo allo 0 nelle altre due componenti. Per questo motivo due colori
caratterizzati rispettivamente da 250,10,10 e 252,8,8 faranno parte entrambi della stessa
sfumatura in quanto possono essere entrambi definiti dei rossi accesi anche se il secondo è
leggermente più brillante del primo.
Per risolvere l’incongruenza prodotta dal secondo fattore viene introdotta un’ulteriore idea
che ci può aiutare. Ogni sfumatura porta con sé l’informazione del luogo nel quale è collocata
nell’immagine. Per fare questo si introduce un sistema di riferimento solidale all’immagine,
che reca l’origine nell’angolo in basso a sinistra della figura con gli assi diretti parallelamente
ai bordi della stessa, l’unità di misura è il pixel quindi le ascisse hanno un range che va da 0 al
numero di colonne dell’immagine, mentre le ordinate vanno da 0 al numero di righe. Con
questo metodo possiamo indicare e tenere traccia di tutti i punti che costituiscono l’immagine
stessa.
Grazie a questo sistema di riferimento possiamo indicare in che area è localizzata una certa
sfumatura. Per esempio se il nostro riferimento è di un rosso acceso, tra le molte sfumature
che il sistema può riconoscere ce ne sarà una che indica il rosso acceso e l’area nella quale
risiede combacerà sull’immagine nel punto esatto dove il riferimento si trova. Questo è stato
fatto per evitare che l’utente scelga sfumature che non sono corrette e che non intercettano il
riferimento in maniera corretta.
Può capitare infatti, che più di una sfumatura possa essere ricondotta al colore del riferimento.
Questo avviene perché il sistema non definisce una sola sfumatura per il colore ma ne indica
alcune. Per esempio il sistema riconosce varie sfumature di rosso dal più brillante al più scuro
e cupo al fine di permettere al sistema di funzionare sia con una luce perfetta sia con una
carenza nell’illuminazione dell’utente. Quindi non si deve fare altro che, scegliere la
sfumatura che ricalca più fedelmente il contorno del riferimento in quanto le altre potrebbero
essere guastate da colori che rientrano nella sfumatura ma che non hanno niente a che fare con
il riferimento. Si attua in pratica una specie di filtro che permette al sistema di individuare e
scartare gli errori nella colorazione dei pixel nell’immagine catturata.
Ora, grazie a questi concetti e alle idee analizzate fino a questo punto possiamo definire i
passi che l’algoritmo deve compiere per arrivare al suo completamento. Per prima cosa si
deve scorrere l’array che contiene l’informazione sul colore dei pixel scegliendo la sfumatura
adatta per ognuno. Tutte le volte che viene trovata una sfumatura nuova, che il sistema può
intercettare, si indica la zona che localizza la sfumatura come l’area che incorpora la
posizione di quel pixel. Se vengono, successivamente trovati altri pixel che appartengono a
53
quella sfumatura, l’area viene modificata per contenere anche quel punto. Tutte le sfumature
che vengono intercettate sono poste in una lista che praticamente indica una palette filtrata dei
colori che compongono l’immagine. Ovviamente la maggioranza dei colori che costituiscono
l’immagine deve essere scartata in quanto indicano zone della stessa di poco interesse, i colori
non bene identificabili come i grigi o colori molto scuri vengono inseriti in una sfumatura che
essenzialmente fa da cestino dei rifiuti. La nostra attenzione è focalizzata sulle sfumature che
il nostro riferimento può adottare, quindi sfumature di rosso, verde o blu.
A questo punto la procedura deve visualizzare questa lista di sfumature al fine di far scegliere
all’utente il colore migliore. Per aiutare quest’ultimo nella sua scelta il sistema deve indicare
l’area di competenza sull’immagine catturata di ogni sfumatura, si sceglierà quella che
possiede un’area che combacia meglio con i contorni del riferimento. L’algoritmo termina
come nella sua versione più semplice, informando il Supervisore della scelta attuata
dall’utente.
4.2.2 Cattura del movimento
Prima di analizzare come questa operazione possa essere attuata dall’algoritmo contenuto a
questo punto della catena di acquisizione ed elaborazione dati, dobbiamo discutere un
problema che a prima vista può sembrare estraneo al nostro scopo ma che invece riveste una
fondamentale importanza.
Dobbiamo descrivere le caratteristiche fisiche che deve possedere il riferimento. Questa
discussione è strettamente correlata alla capacità della camera di riprodurre fedelmente i
colori del mondo reale che si appresta a catturare.
A seconda di un riferimento lucido od opaco oppure con una superficie piana o curvilinea il
sistema si comporterà in maniera differente. Questa caratteristica è dovuta al modo di
riflettere il colore se il riferimento viene colpito dalla luce da diverse angolazioni.
Se, per esempio, utilizziamo un riferimento con una superficie piana c’è il rischio che, in
alcune angolazioni, la luce si rifletta come su uno specchio, inibendo la capacità della camera
di catturare il colore. Questo effetto può sembrare strano, ma non lo è, se si pensa che
abbiamo a che fare con camere che non sono molto potenti e che possono essere ingannate da
simili eventi. Mentre una superficie curvilinea è sempre in grado di avere almeno una zona
che colpisce la luce nella stessa angolatura e permette al riferimento di indicare lo stesso
colore anche se in maniera parziale sulla superficie.
Anche una superficie opaca può portare a problemi, infatti abbiamo bisogno di rendere il più
brillante possibile il riferimento, per sfruttare le situazioni di poca luce al meglio. Dunque un
54
riferimento opaco potrebbe essere difficile da intercettare quando ci troviamo ad operare con
un’illuminazione carente.
Potrebbe sembrare una soluzione valida adottare un riferimento dotato di luce propria come,
per esempio, un led colorato. Tuttavia c’è il pericolo, non del tutto remoto, che la camera
venga, per così dire, abbagliata dalla luce e indicare nell’immagine una zona bianca invece
che colorata.
Per i nostri scopi quindi, il riferimento dovrebbe avere una superficie lucida per riflettere il
più possibile il colore e non avere una superficie piana ma curvata per evitare la riflessione
totale del colore.
La natura del riferimento, unita ai fattori destabilizzanti introdotti nella sezione precedente
rendono questo algoritmo estremamente complesso anche se, la sua funzione di tenere traccia
degli spostamenti del volto dell’utente è un’operazione abbastanza semplice.
Questo è dovuto al fatto che, l’algoritmo utilizza, per intercettare il riferimento, la sfumatura
definita grazie alla procedure del passo precedente e non ha la possibilità di scegliere una
sfumatura differente per ogni immagine utilizzando sempre la migliore per una data
angolazione della luce. In tal modo si possono catturare immagini dove la sfumatura scelta
non ricalca fedelmente i contorni del riferimento ma ne intercetta solo alcuni, cambiando
forma ad ogni immagine. In più entrano in gioco anche gli errori nell’ottica della camera che
potrebbero inserire nell’immagine colori attinenti alla sfumatura cercata ma in zone che non
hanno niente a che vedere con il riferimento. Questa possibilità produce delle zone nel quale è
contenuta la sfumatura troppo grandi che non permettono di indicare con una certa precisione
il riferimento.
A questo punto, dopo aver delineato tutti i problemi che l’algoritmo deve cercare di evitare o
almeno ridimensionare, possiamo analizzare la procedura attuata per raggiungere il nostro
scopo. È logico pensare che, per avere la sensazione di movimento dobbiamo catturare
un’immagine dietro l’altra del volto dell’utente. L’algoritmo deve andare ad intercettare su
ogni figura la zona dove risiede la sfumatura scelta precedentemente che indica il riferimento.
Tuttavia non è possibile ottenere un risultato apprezzabile elaborando direttamente questa
informazione ma siamo costretti a utilizzare una specie di filtro per ottenere una soluzione
accettabile.
Questo viene realizzato attendendo la cattura di un certo numero di immagini che vengono
elaborate per presentare una soluzione. Precisamente sono le zone dove risiede la sfumatura
ad essere analizzate, scartando le zone che sembrano troppo grandi per contenere il
riferimento e mediando le altre per ottenere una zona che si avvicina il più possibile ai
55
contorni del riferimento. Scartare le zone più grandi serve per ridurre al minimo gli errori
descritti in precedenza sulle ottiche in quanto vengono scartate zone che comprendono gli
errori nei pixel e che non sono attinenti al riferimento. Mediare significa evitare i problemi
riguardo al riferimento stesso dove ci possono essere delle riflessioni che modificano troppo
velocemente la zona della sfumatura che sì, indica il riferimento, ma ne indica solo alcuni
contorni per volta. In pratica si devono mediare tanti piccoli rettangoli che indicano il punto
del riferimento ma che variano troppo la loro forma da una cattura all’altra, producendo in
questo caso un rettangolo che dovrebbe definire in maniera abbastanza corretta i contorni del
riferimento.
Questo metodo porta ovviamente dei ritardi nell’indicazione della posizione del riferimento,
tuttavia è essenziale per ottenere un risultato accettabile per le elaborazioni future. Agire sulla
velocità della cattura delle immagini può essere una valida alternativa per ottenere delle
indicazioni che si avvicinano all’elaborazione real time ed indicare più prontamente la
posizione del riferimento. Anche diminuire il numero di campioni può essere un fattore per
avvicinarsi all’elaborazione in tempo reale tuttavia ridurlo di molto inficia l’utilità del filtro.
Quest’ultimo metodo è utile se possiamo utilizzare il sistema con un’illuminazione ottima che
rende possibile definire zone precise sull’immagine della sfumatura del riferimento. Se
possiamo usufruire di questo vantaggio non siamo costretti a mediare o scartare molte
immagini e otteniamo un’elaborazione molto veloce e precisa.
Un ultima funzione delegata dal sistema all’algoritmo impone che quest’ultimo produca una
sorta di controllo sui dati che elabora. Se la zona che descrive la sfumatura diventa troppo
piccola nella sequenza di immagini catturate e contiene pochi pixel che hanno un colore
corretto, l’algoritmo deve informare il Supervisore di questo fatto. Questa operazione ha
l’utilità di avvertire l’utente che forse la sfumatura che ha scelto non è la più corretta ed è
conveniente ripetere la valutazione sul colore del riferimento.
Infine, l’algoritmo deve proporre delle soluzioni che permettano un’elaborazione più
efficiente dei dati a disposizione, questo può essere fatto sfruttando la natura del riferimento.
Ciò significa che il volto dell’utente non subisce spostamenti repentini e di grande ampiezza,
cosicché è possibile ricercare la zona della sfumatura, non su tutta l’immagine, ma su una
zona definibile grazie alla posizione del riferimento nell’immagine precedente. Questo
permette di diminuire in maniera significativa il lavoro del processore che può dedicare il suo
tempo a operazioni più importanti. Tuttavia questo implica che l’algoritmo possa perdere
l’aggancio col riferimento, se ciò avviene non resta che cercare di nuovo la sfumatura su tutta
l’immagine.
56
Il Supervisore deve venir informato anche se l’algoritmo perde l’aggancio con il riferimento e
non riesce più ad individuarlo nell’immagine. Questo può essere causato da uno spostamento
eccezionalmente grande dell’utente davanti alla camera che perde dal suo campo visivo
l’immagine del riferimento.
4.2.3 Analisi del campo visivo
È importante far notare che d’ora in poi si presume di lavorare con dati che non sono più
affetti da errori o almeno gli algoritmi vengono informati se il sistema non riesce a controllarli
in maniera corretta. Tra i due algoritmi presentati nelle sezioni precedenti e quelli che
verranno analizzati in questa e nella successiva si intravede una netta divisione sulla loro
natura. Mentre le procedure che sono state descritte avevano a che fare con il mondo reale e
dovevano essere in grado di elaborare immagini che da questo mondo provenivano, gli
algoritmi che ci apprestiamo a osservare lavorano su dei dati che sono, per così dire, un
modello matematico del mondo reale, ci troviamo ad un livello di astrazione superiore. Questa
caratteristica permette una più facile analisi delle procedure qui di seguito presentate in
quanto i dati che devono elaborare non sono più complessi come i dati che erano tenuti ad
elaborare gli algoritmi precedenti.
Guardando il cammino che dobbiamo ancora percorrere ed appoggiandoci a quanto detto nel
paragrafo precedente dove si analizzava l’architettura del sistema, ci troviamo di fronte
all’algoritmo che permette al sistema di definire il campo visivo che l’utente utilizza per
spostare lo sguardo sul terminale video del Personal Computer.
Il compito assegnato a questa procedura è quello di cablare il sistema ed ottenere dei dati
sufficienti per l’ultimo algoritmo che permette di indicare in che posizione l’utente ha rivolto
il viso.
Per ottenere questo risultato la procedura deve assicurarsi una specie di zona, sempre indicata
tramite il sistema di riferimento fisso sull’immagine, nel quale il riferimento ha libertà di
movimento ed ogni posizione occupata all’interno di quest’area è relazionabile ad una
posizione fissa del volto dell’utente. Come già descritto nel paragrafo precedente se, per
esempio, l’utente rivolge il viso verso il centro dello schermo, il riferimento si deve trovare al
centro della zona ottenuta con questo algoritmo.
È a questo punto che entra in gioco l’analisi che è stata fatta nel capitolo nel quale si
discutevano i vari problemi legati all’ausilio e particolarmente, sul problema di tenere
fortemente in considerazione le capacità residue dell’utente. Infatti, mentre il lavoro richiesto
all’utente dai precedenti algoritmi è praticamente nullo, è l’assistente a fare il grosso del
lavoro mentre l’utente non deve fare altro che indossare il riferimento e posizionarsi davanti
57
allo schermo del Personal Computer. In questo caso è l’utilizzatore che riveste un ruolo
importante ed è per questa ragione che all’algoritmo viene chiesta una caratteristica notevole
che sta nel rendere le richieste all’utente semplici e non gravose. Questo, come è già stato
analizzato nel capitolo riguardante il contesto, è dovuto al fatto che l’operazione di cablaggio,
per la natura dell’ausilio, deve essere riproposta ad ogni sessione di lavoro e quindi l’utente
deve ripetere ciò che l’algoritmo gli indica ogni volta che vuole utilizzare l’ausilio.
L’algoritmo ottiene il suo obiettivo richiedendo all’utente di fissare per alcuni istanti delle
zone ben definite dello schermo. Grazie alla posizione intercettata, dalla procedura descritta
precedentemente del riferimento, questo algoritmo permette di delineare un’area che contiene
al suo interno tutte queste zone. In pratica l’algoritmo chiede all’utente di rivolgere il viso sui
contorni dello schermo e in particolare negli angoli di quest’ultimo.
Tutte le volte che l’utente ha il desiderio di cablare il sistema per poterlo utilizzare richiede
all’ausilio le funzionalità attribuite a questo algoritmo. Quest’ultimo inizia avvertendo l’utente
che la fase di cablaggio è in corso dopodiché presenta un pallino colorato che l’utente deve
fissare con tutto il viso. Ogni volta che l’algoritmo è sicuro di aver intercettato ed
immagazzinato la posizione della sfumatura grazie al sistema di riferimento solidale con
l’immagine, il pallino colorato cambia posizione sullo schermo e percorre con questa tecnica
i punti che si ritengono più importanti per definire l’area dell’immagine che definisce i
contorni del video.
Una funzionalità che è estremamente importante è garantire l’informazione sulla stabilità
della cattura del riferimento da parte del sistema. Per ottenere questo l’algoritmo deve sempre
informare l’utente dell’aggancio sul riferimento, in quanto se si perde la traccia del
riferimento la procedura di cablaggio è incapace di portare a termine il suo compito.
È importante far notare, infine, che l’algoritmo analizzato nella sezione precedente continua a
lavorare anche quando la procedura qui descritta è in corso. Ciò significa che le informazioni
dell’algoritmo che permette l’intercettazione del riferimento vengono utilizzate per gli scopi
di cablaggio quindi se l’algoritmo che intercetta la sfumatura del riferimento avvisa il sistema
che l’aggancio è scarso oppure è stato perso, la procedura di cablaggio deve informare
l’utente di questo fatto.
Mentre il sistema veniva creato si è incappati in una caratteristica che a prima vista non era
stata analizzata ma che permette all’algoritmo di essere più versatile e rendere il lavoro
dell’utente estremamente semplice. Invece di obbligare l’utente a seguire il pallino per
ottenere il cablaggio possiamo ottenere lo stesso risultato permettendo all’utilizzatore di
fissare liberamente gli angoli del video. Questo ovviamente deve essere fatto da un utente che
58
ha già in mente i punti da fissare, se il sistema viene usato per la prima volta è consigliabile
eseguire il cablaggio seguito passo a passo dal sistema grazie al pallino colorato da fissare.
Se l’utente è esperto e sa già come il sistema funziona può utilizzare questa scorciatoia.
Avvisando il sistema che ha intenzione di cablarlo e poi definendo l’area che delimita il
campo d’azione del riferimento in modo manuale fissando i quattro angoli del video e
controllando l’effettivo ridimensionamento dell’area direttamente sull’immagine e sul sistema
di riferimento ad essa relativo.
In questo caso si parla di cablaggio manuale mentre la procedura di cablaggio gestita dal
sistema grazie al pallino colorato prende il nome di cablaggio automatico.
4.2.4 Definizione del punto fissato
A questo punto, nella catena di acquisizione ed elaborazione dei dati, siamo arrivati nel luogo
più alto e quindi più astratto dell’intero sistema. Questo significa che l’algoritmo presentato in
questa sezione si trova ad elaborare dati che rappresentano un modello matematico e
geometrico della realtà ma che con essa non hanno più niente a che vedere. Si presume che a
questo punto i dati che ci troviamo ad elaborare siano privi di errori o almeno subiscano in
modo lieve i danni di quest’ultimi.
Il fine ultimo di questa procedura è avvertire il Supervisore circa la zona dello schermo sul
quale l’utente ha rivolto il viso. Per fare questo utilizza i dati che i precedenti algoritmi hanno
prodotto. Grazie all’area che definisce il campo visivo e all’uso dell’algoritmo che intercetta il
riferimento questa procedura ricava il punto fissato.
Tutte le volte che la procedura che individua il riferimento fisso sul volto dell’utente produce
dei risultati stabili nei quali si sono filtrati gli errori, quindi è in grado di definire una zona ben
precisa nel sistema di riferimento solidale con l’immagine, entra in gioco questo algoritmo.
Le operazioni svolte non sono estremamente complesse, il suo compito sta nel prendere
questa precisa zona e correlarla con il campo visivo ottenuto dalla procedura precedente.
L’unica trappola sta nella specularità delle immagini catturate, ciò significa che la camera
cattura un’immagine che è paragonabile alla figura riflessa da uno specchio. Quindi
l’algoritmo deve compensare questo evento e produrre dei dati che abbiano senso. Per
esempio, se l’utente fissa il punto in alto a destra dello schermo, la procedura si ritroverà ad
elaborare una zona della sfumatura scelta che, nel sistema di riferimento solidale
all’immagine, si troverà nelle vicinanze dell’angolo in alto a sinistra della zona che definisce
il campo visivo. Quindi non deve fare altro che invertire il riferimento nella zona del campo
visivo per ottenere il punto corretto fissato. Fatta questa operazione l’algoritmo conclude
59
avvisando il Supervisore della zona in cui l’utente ha rivolto il viso, sarà quest’ultimo ad
avvisare l’applicazione che in quell’istante è attiva.
Prima di concludere questa sezione e con essa il capitolo dobbiamo analizzare una
caratteristica del sistema che non è ancora stata discussa. Per simulare il comportamento di un
mouse dobbiamo incorporare nel sistema un metodo per riprodurre il clic, questo per
permettere all’utente di attuare una sorta di selezione degli oggetti che fissa sullo schermo.
I metodi per attuare questa operazione sono strettamente legati alla posizione delle camere e
al loro numero. Principalmente due sono le possibilità, attuare un clic basandosi sul tempo in
cui l’utente rimane a fissare un certo oggetto, oppure utilizzare la palpebra abbassata come
indicatore del clic. Il primo metodo è attuabile se possiamo utilizzare una sola camera, in
quanto la scelta di posizionarla al di sopra dello schermo non permette di analizzare in
maniera corretta l’occhio. Questo perché, come abbiamo spiegato nel capitolo che analizzava
i problemi, se l’utente fissa un punto in basso dello schermo l’angolatura non permette alla
camera di vedere in maniera corretta l’occhio in quanto quest’ultimo è coperto dal sopracilio.
Tuttavia se c’e la possibilità di utilizzare due camere la seconda può essere posta sotto lo
schermo ed in questa posizione siamo in grado di avere sempre l’aggancio sulla zona
dell’occhio, potendo in questo modo attuare il secondo metodo per identificare i clic.
Il sistema è stato studiato per attuare entrambe le soluzioni tuttavia, a causa del collo di
bottiglia identificabile sul Bus dati, la maggior parte dei Personal Computer tuttora sul
mercato non permettono di utilizzare in concorrenza più di una camera. Per questo motivo il
sistema, per ora, simula il clic valutando il tempo che l’utente passa a fissare un certo punto
sullo schermo.
Questa operazione viene svolta dall’algoritmo analizzato in questa sezione. Esso tiene traccia
degli spostamenti del riferimento e se essi si concentrano in un punto per un determinato
valore del tempo la procedura informa il Supervisore che c’è stato un clic. Sarà quest’ultimo
ad informare l’applicazione attiva di questo evento.
61
5 Implementazione
Potrà sembrare strano e, sicuramente il lettore si sarà chiesto per quale motivo un
documento che dovrebbe studiare un'applicazione informatica non abbia ancora analizzato
una sola riga di codice. Questa domanda, seppur legittima, trova risposta nella volontà di
mettere il lettore nella posizione più comoda e proficua per comprendere appieno il contenuto
di questo capitolo. Il lavoro fin qui svolto ha un enorme significato, in quanto ha permesso di
comprendere in maniera completa l’analisi teorica degli algoritmi che costituiscono il sistema.
I precedenti capitoli hanno risposto alle domande sul perché è nata la necessità di tali
procedure e hanno fornito risposte adeguate a come gli algoritmi devono provvedere ai
compiti riservati a loro dal sistema.
Sono gli algoritmi la risorsa più importante contenuta nell’ausilio, è da ricercare in essi la
scintilla di ingegno che permette al sistema di portare a termine il suo compito. La struttura
che si trova in secondo piano e che ha il compito di incastonare le pietre preziose costituite
dalle procedure, assume contorni indefiniti negli scopi che si sono prefissi, per non
appesantire in maniera sproporzionata la discussione. Tuttavia la sua ombra è sempre presente
nello studio che si intraprenderà sull’implementazione dei vari algoritmi. Ignorare
l’implementazione della struttura nel contesto in cui ci si ritrova costa un caro prezzo in
62
quanto questo risultato è stato raggiunto con dei grandi sforzi paragonabili a quelli compiuti
per ottenere gli algoritmi. Quindi è con immensa pena che nel presente capitolo non viene
affrontata l’analisi dettagliata sulla gestione del sistema, mentre tutta l’attenzione si focalizza
su come le procedure sono state implementate.
A questo punto, prima di analizzare in maniera dettagliata l’implementazione degli
algoritmi, è importante riprendere un concetto che era stato solo introdotto nel capitolo
precedente. In quel contesto si era detto che, per semplificare l’analisi, la discussione sugli
algoritmi seguiva un filo logico che legava quest’ultimi come in una catena. I risultati ottenuti
da una procedura erano utilizzati come base di partenza per l’algoritmo che stava a valle.
Tuttavia, in realtà, nell’implementazione ciò avviene solo per il primo algoritmo, che permette
di scegliere la sfumatura del riferimento. Le restanti procedure sono talmente correlate tra di
loro che potrebbero essere analizzate insieme. Infatti quest’ultime sono implementate negli
stessi oggetti e rappresentano una sorta di algoritmo monolitico che è l’unione omogenea delle
tre procedure divise logicamente nel capitolo precedente.
Quindi sono due le strade che si possono percorrere per analizzare l’implementazione. Si
può enfatizzare la logica che regola i rapporti tra gli algoritmi oppure privilegiare
l’implementazione stessa. Nel primo caso l’analisi risulta semplice e lineare anche se si è
obbligati a descrivere separatamente il sorgente analizzando solo alcune parti di esso per ogni
algoritmo. Scegliendo la seconda via è possibile analizzare totalmente le procedure contenute
negli oggetti sminuendo il discorso logico fin qui fatto. La scelta non può che ricadere sul
primo metodo, infatti la logica che regola gli algoritmi è un punto di forza dell’intera
trattazione e non può essere abbandonata proprio a questo punto.
Resta ancora da affrontare una tematica che abbraccia nel complesso la trattazione di
questo capitolo. Di solito si è tentati di utilizzare tutte le potenzialità che ci vengono offerte
sia dal Sistema Operativo che dal linguaggio di programmazione. Tuttavia agire in questo
modo non sempre porta a risultati soddisfacenti. Non significa che se il linguaggio di
programmazione permette di utilizzare costrutti molto compatti ma estremamente complessi
per scrivere il sorgente essi debbano per forza essere utilizzati. Se lo stesso risultato può
essere raggiunto scrivendo un sorgente più semplice non c’è motivo nell’utilizzare il costrutto
più complesso. Per questa ragione molte potenzialità offerte dal C++ non sono state utilizzate.
Per esempio la caratteristica dell’ereditarietà, i template di funzione e classe e il sovraccarico
degli operatori non compaiono nel sorgente. Questo non è indice di incompetenza oppure di
inesperienza da parte di chi scrive il sorgente, ma rispecchia l’obbligo di rendere quest’ultimo
scorrevole e semplice da comprendere.
63
Nulla ora vieta la possibilità di analizzare efficacemente come gli algoritmi sono stati
implementati. Il capitolo avrà una struttura simile al precedente nel quale ogni paragrafo
studierà un algoritmo, conservando l’ordine imposto dalla logica di elaborazione dati descritta
in precedenza.
5.1 Scelta del riferimento Come analizzato dettagliatamente nel capitolo precedente questo algoritmo deve indicare
al sistema quale colore è stato scelto dall’utente per intercettare il riferimento. Per ottenere
questa informazione la procedura si divide in quattro passaggi intermedi. Per prima cosa il
sistema definisce i parametri essenziali dell’oggetto, che ha il compito di contenere le
procedure che implementano l’algoritmo, successivamente si compone la tavolozza di
sfumature intercettabili nell’immagine. Come terzo passo viene visualizzata sullo schermo
questa palette per informare l’utente dei colori disponibili. Infine si permette all’utente di
scegliere il colore e viene informato il sistema della scelta.
Prima di analizzare i quattro passi è importante descrivere una caratteristica alquanto
particolare insita nel codice scritto. Si potranno notare in esso dei nomi di variabili piuttosto
insolite, che potrebbero suonare strane. Questo è stato fatto per rendere il sorgente più
semplice da leggere. Per esempio, i compiti del Supervisore, che è stato descritto ampliamente
nel paragrafo sull’architettura, sono incapsulati in una classe definita grazie al C++ chiamata a
64
sua volta Supervisore. L’istanza di tale classe è un oggetto che prende il nome di Angelo. È
stato scelto questo nome per ricordare le caratteristiche del Supervisore, in quanto esso
presiede a tutte le operazioni eseguibili del sistema. Altri strani nomi verranno analizzati
quando ci imbatteremo in essi nel corso della discussione.
5.1.1 Impostazione
Come si è visto, l’obbligo di focalizzare la discussione solamente sull’implementazione
degli algoritmi, porta a dover analizzare punti nel codice che non sono ordinati
temporalmente. Ciò significa che il sistema, o meglio la struttura che incapsula gli algoritmi,
ha già svolto del lavoro ed è in grado di fornire alla procedura il materiale adatto per la sua
elaborazione. Per esempio, in questo caso, il sistema ha già ottenuto l’aggancio alla camera
per la cattura delle immagini grazie alla libreria Video For Windows e all’oggetto incaricato
di questa mansione. Inoltre la prima immagine, utilizzata come modello, è stata catturata e il
sistema la offre, grazie alla libreria Victor, alla procedura che la elaborerà.
Prima di passare all’elaborazione vera e propria il Supervisore informa gli oggetti che
incorporano le funzionalità dell’algoritmo che presto verrà richiesto il loro apporto e per
questo motivo chiede a quest’ultimi di definire i parametri e le strutture di elaborazione dati
che l’algoritmo utilizza per i suoi scopi. L’istanza della classe Palette chiamata Tavolozza si
serve di due liste doppiamente concatenate per contenere l’elaborazione della procedura.
65
La prima contiene l’insieme delle sfumature intercettabili sull’immagine catturata. Ogni
elemento è costituito dalla struttura seguente, LC significa Lista Complessa:
typedef struct CELLA_COLORE_LC{
COLORE Colore;SCOSTAMENTO Scostamento;UINT TipoColore;RECT Zona;int Bersagli;int Posizione;struct CELLA_COLORE_LC *Precedente,*Successivo;
};
Sono indicate molte informazioni, ci sono i contorni dove è localizzata la sfumatura nel
sistema di riferimento relativo all’immagine (Zona), il numero di pixel dell’immagine che
fanno parte della sfumatura (Bersagli) e i parametri che identificano il tipo di sfumatura
(Colore, TipoColore e Scostamento). Per le sfumature non intercettabili che includono tutti i
colori che appartengono all’immagine ma che non hanno valore per la nostra elaborazione
viene utilizzato il primo elemento della lista che rappresenta una sorta di cestino dei rifiuti.
La seconda è utilizzata come merce di scambio tra i vari oggetti che concorrono alla
riuscita dell’algoritmo e contiene solo i tipi di sfumature intercettati, con i valori dei tre colori
primari del colore identificativo della sfumatura, LS indica Lista Semplice.
typedef struct CELLA_COLORE_LS{
BYTE Blu,Verde,Rosso;UINT TipoColore;struct CELLA_COLORE_LS *Precedente,*Successivo;
};
Una volta definite queste strutture dati e generato i vari puntatori che permettono di
utilizzarle l’oggetto Tavolozza è pronto a partire con l’elaborazione vera e propria
dell’algoritmo. Esiste anche la possibilità di eliminare le liste se per caso sono già state
richieste le funzionalità dell’algoritmo e si decide di riproporlo con un’altra immagine presa
come modello.
5.1.2 Analisi immagine
È in questa sezione che viene analizzata la parte più importante della procedura per la
determinazione del colore del riferimento.
L’oggetto Tavolozza ha a sua disposizione il puntatore alla variabile Victor che contiene
l’immagine catturata. Non deve fare altro che scorrere il vettore dove sono contenuti i colori
primari di ogni pixel, si ricorda che vengono elaborate immagini a 24 bit che non utilizzano
una palette ma conservano direttamente il colore di ogni pixel. Grazie al ciclo:
66
i = j = 0;X = Y = 0;while (i < ((int) Immagine->bmh->biSizeImage)){
Colore.Blu = Immagine->ibuff[i];Colore.Verde = Immagine->ibuff[i+1];Colore.Rosso = Immagine->ibuff[i+2];this->In(Colore);i = i + 3;j++;X = j % 320;Y = j / 320;
}
si può scorrere tutto il vettore. Ad ogni passaggio viene conservato il colore di un pixel nella
struttura Colore, non altro che l’unione di tre byte. Dopodiché si lancia la procedura che
elabora le caratteristiche del colore, del quale identifica la sfumatura di appartenenza e la
inserisce nella lista se non è stata già incontrata.
Le variabili i e j servono per scorrere il vettore, mentre le variabili X e Y identificano il
pixel nel sistema di riferimento solidale con l’immagine e permettono di definire i contorni
della sfumatura, si utilizza il valore 320 perché le immagini elaborate sono 480X320.
Passando ora ad analizzare la procedura In() notiamo che essa rappresenta il cuore di tutta
l’implementazione dell’algoritmo:
void Palette::In(COLORE Colore){// Creo la Cella Colore Lista Complessa
TempLC = new(CELLA_COLORE_LC);TempLC->Colore.Blu = Colore.Blu;TempLC->Colore.Verde = Colore.Verde;TempLC->Colore.Rosso = Colore.Rosso;TempLC->Scostamento.BV = Colore.Blu - Colore.Verde;TempLC->Scostamento.VR = Colore.Verde - Colore.Rosso;TempLC->Scostamento.RB = Colore.Rosso - Colore.Blu;TempLC->TipoColore = TipoColore(TempLC);TempLC->Posizione = Elementi;if (!Trovato(TempLC)){// Inserisco la Cella in coda alla Lista Complessa
TempLC->Precedente = CodaLC;TempLC->Successivo = NULL;CodaLC->Successivo = TempLC;CodaLC = TempLC;
// Creo la Cella Colore Lista SempliceTempLS = new(CELLA_COLORE_LS);TempLS->Blu = Colore.Blu;TempLS->Verde = Colore.Verde;TempLS->Rosso = Colore.Rosso;TempLS->TipoColore = TempLC->TipoColore;
// Inserisco la Cella in coda alla Lista SempliceTempLS->Precedente = CodaLS;TempLS->Successivo = NULL;CodaLS->Successivo = TempLS;CodaLS = TempLS;
67
Elementi += 1;}else
delete TempLC;}
La procedura genera le due liste che contengono le informazioni sulle sfumature intercettabili
nell’immagine. Per produrre questo risultato riserva, nella memoria di massa, spazio per un
elemento della Lista Complessa dove verranno indicate tutte le qualità del colore passato
come parametro. Grazie alle procedure TipoColore() e Trovato() analizza rispettivamente in
che sfumatura risiede il colore e se è già stata incontrata questa sfumatura. Se è stata
intercettata una nuova sfumatura la procedura la inserisce in coda alla Lista Complessa e
genera un nuovo elemento della Lista Semplice (LS) inserendolo in coda a quest’ultima.
Mentre se abbiamo a che fare con un colore che appartiene ad una sfumatura già intercettata,
lo scartiamo e liberiamo, grazie alla delete, lo spazio in memoria riservato all’elemento della
Lista Complessa.
La procedura chiamata TipoColore() permette di determinare a che sfumatura appartiene il
colore del pixel. Grazie alla scomposizione nei tre colori primari si è in grado di definire
qualsiasi sfumatura ma la procedura identifica solo poche sfumature focalizzate sui colori che
hanno un nesso col colore del riferimento. Si hanno sei sfumature differenti di rosso, verde e
blu che vanno dal più brillante al più scuro. Il sistema è in grado di intercettare anche alcune
sfumature di colori composti come il giallo, l’azzurrino e il violetto, nonché diverse sfumature
di grigio.
La procedura Trovato() merita una discussione più dettagliata:
bool Palette::Trovato(CELLA_COLORE_LC *Cella){
IndiceLC = TestaLC;while(IndiceLC != NULL){
if(IndiceLC->TipoColore == Cella->TipoColore){
if(X < IndiceLC->Zona.left)IndiceLC->Zona.left = X;
if(Y > IndiceLC->Zona.top)IndiceLC->Zona.top = Y;
if(X > IndiceLC->Zona.right)IndiceLC->Zona.right = X;
if(Y < IndiceLC->Zona.bottom)IndiceLC->Zona.bottom = Y;
IndiceLC->Bersagli++;return true;
}IndiceLC = IndiceLC->Successivo;
}Cella->Zona.left = Cella->Zona.right = X;Cella->Zona.top = Cella->Zona.bottom = Y;Cella->Bersagli = 1;
68
return false;}
Il compito svolto da questa funzione è lievemente più complesso di quello svolto dalle
precedenti. Prima di tutto la procedura ricerca nella Lista Complessa un elemento che
possieda lo stesso TipoColore dell’elemento passato come parametro. Se questa ricerca va a
buon fine significa che la sfumatura era già stata precedentemente individuata dall’algoritmo.
A questo punto non resta che modificare i contorni della zona che indica la posizione della
sfumatura nel sistema di riferimento solidale con l’immagine e incrementare il contatore del
numero di pixel appartenenti alla sfumatura. Se si arriva alla fine della Lista Complessa senza
aver trovato la sfumatura significa che il sistema ha intercettato una nuova sfumatura e
l’elemento deve essere incluso nella lista. Vengono definiti i contorni della sfumatura come le
coordinate del pixel e il conto dei pixel relativi viene posto a 1. Quindi si avvisa la procedura
chiamante In() che l’elemento deve essere inserito in coda alla Lista Complessa.
5.1.3 Visualizzazione
Ora la parte di elaborazione può ritenersi conclusa. Non resta che visualizzare sul terminale
video i risultati e aspettare la scelta dell’utente. È a questo punto che entra in gioco la seconda
lista denominata Semplice. Quest’ultima viene prelevata dall’Angelo e indirizzata alla
window procedure della finestra preposta alla visualizzazione dei risultati di questo algoritmo.
Una dopo l’altra le varie sfumature intercettate nell’immagine, usata come modello, vengono
indicate sullo schermo sotto forma di piccoli rettangoli colorati posti in fila. Dopodiché la
procedura si pone in attesa della scelta dell’utente.
5.1.4 Scelta
Quando l’utente si posiziona sul piccolo rettangolo che contraddistingue la sfumatura e
preme il pulsante sinistro del mouse indica alla procedura che è stata selezionata una
sfumatura. Questo evento genera una serie di operazioni che avvengono in cascata. La
window procedure che gestisce la finestra dove vengono visualizzate le sfumature avvisa
l’Angelo della scelta. Quest’ultimo compie la sua mansione di supervisore chiedendo le
coordinate della zona nel quale risiede la sfumatura direttamente all’oggetto Tavolozza
istanza di Palette, grazie alla procedura:
RECT* Palette::GetZonaColore(UINT TipoColore){
IndiceLC = TestaLC;while(IndiceLC != NULL){
if(IndiceLC->TipoColore == TipoColore)return &IndiceLC->Zona;
69
IndiceLC = IndiceLC->Successivo;}return NULL;
}
Quindi invia un messaggio alla window procedure relativa alla finestra dove viene
visualizzata l’immagine. Questo messaggio porta con sé i contorni della zona della sfumatura
che vengono sovrapposti all’immagine stessa. Se la sfumatura scelta è adatta i suoi contorni
dovrebbero intercettare il riferimento.
È importante analizzare perché l’algoritmo si comporti in questo modo quando potrebbe
concludere indicando solamente all’Angelo la sfumatura scelta. Ci troviamo di fronte questo
comportamento per implementare ciò che è stato detto nel capitolo precedente. Si richiedeva
alla procedura di aiutare l’utente nella scelta del colore del riferimento, non solo per facilitare
il compito dell’utilizzatore ma far si che non venisse scelta una sfumatura che poteva
condurre all’errore. Grazie a questo accorgimento l’algoritmo è in grado di identificare la
zona dell’immagine dove risiede la sfumatura. Questo permette all’utente di scegliere quella
che più si adatta ai contorni del riferimento inquadrato nell’immagine, evitando le sfumature
simili ma che introducono gli errori elencati nel capitolo precedente. In pratica l’utente può
scegliere la sfumatura che meglio si adatta ai contorni del riferimento, provando a selezionare
le varie sfumature presentate grazie ai piccoli rettangoli colorati.
5.2 Cattura del movimento Si apre con questo paragrafo la discussione dell’algoritmo monolitico descritto in
precedenza. Quest’ultimo è costituito dalle tre procedure logiche analizzate nel capitolo
precedente che permettono la cattura del movimento, la definizione del campo visivo e
l’indicazione dello sguardo. La prima, discussa in questo contesto, è la più importante in
quanto costituisce la base sulla quale le altre due verranno costituite. Tuttavia essendo
estremamente correlata, in quanto parte dell’algoritmo monolitico, con le altre, alcune sue
parti non potranno essere analizzate in questo paragrafo ma si dovrà attendere il momento
propizio per farlo. Solo alla conclusione del capitolo si avrà sotto controllo il disegno generale
del sistema.
Prima di poter analizzare a fondo questa procedura è essenziale introdurre un’ulteriore
caratteristica che contraddistingue gli algoritmi. Un aspetto importante che è stato tralasciato
nella discussione fin qui svolta è il tempo che impiega la procedura per elaborare i dati iniziali
al fine di ottenere dei risultati. Per esempio, l’algoritmo che è stato appena discusso
impiegava un breve intervallo di tempo per portare a termine la sua elaborazione. Tuttavia
non sempre si ha a che fare con procedure di questo tipo. L’algoritmo che viene presentato in
70
questo paragrafo è di natura completamente differente. Non è stato pensato per offrire un
risultato singolo alla fine dell’elaborazione ma, il suo compito è fornire al sistema
informazioni continue sul mondo reale che lo circonda. Quindi non è possibile a priori
stabilire quanto tempo occorra alla sua elaborazione.
Questo porta ad una diversa struttura in grado di sorreggerlo. La funzione principale
affidata al Supervisore è la cattura e l’elaborazione in tempo utile di tutti i messaggi che
arrivano all’applicazione sia dal Sistema Operativo sia dall’utente stesso. Per esempio, la
scelta di una voce dal menu oppure il ridimensionamento della finestra che include
l’applicazione sono operazioni che non devono essere rallentate oppure posticipate ma, hanno
estrema urgenza di essere eseguite. Quindi, mentre il Supervisore poteva permettersi di
visionare direttamente gli algoritmi che promettevano tempi di elaborazione estremamente
compatti non può assolutamente addossarsi l’onere di controllare un tale algoritmo, dove
l’elaborazione si protrae per un tempo indeterminato.
Per questo motivo si utilizzano le potenzialità offerte dalla programmazione concorrente.
Si lascia libero il Supervisore di rispondere non appena arriva una richiesta, mentre il lavoro
attuato dall’algoritmo viene visionato da un thread che utilizza l’elaboratore in modo
concorrente al sistema principale. In questo modo al Supervisore restano poche e veloci
operazioni da compiere in quanto deve solo attivare, terminare il thread ed utilizzare le
informazioni che regolarmente quest’ultimo gli fornisce. Per analizzare in maniera adeguata
questa procedura, quindi, si devono abbandonare le vecchie convinzioni sugli algoritmi che
avevano un inizio ed una fine ma pensare che le operazioni fatte rappresentano un continuo.
Entra in gioco ora l’analisi svolta nel capitolo precedente sulle caratteristiche che doveva
possedere la procedura. Si era detto che non era possibile indicare ad ogni cattura la posizione
del riferimento, in quanto esistevano errori che potevano vanificare l’elaborazione. Era quindi
necessaria una specie di filtro che scartasse i dati incoerenti e producesse informazioni utili al
sistema, prive o almeno con un’attenuazione sugli errori. Questo viene implementato
introducendo il concetto di sequenza e sfruttando la natura del thread. Ogni cattura ed
elaborazione di un’immagine è inserita in una sequenza, che rappresenta le operazioni svolte
dalla procedura ogni volta che il thread ottiene dal Sistema Operativo l’accesso alla CPU.
Tuttavia in questa fase l’algoritmo non informa il sistema sulla posizione del riferimento ma
si limita a conservare le informazioni relative ai contorni dello stesso senza produrre nessun
risultato. L’informazione, che rappresenta lo scopo della procedura, viene costituita quando si
ottengono un certo numero di catture dunque quando si sono effettuati svariate sequenze. A
71
questo punto l’algoritmo attua il filtro e scarta i dati che ritiene inconsistenti mediando gli altri
per ottenere un risultato apprezzabile.
5.2.1 Definizione parametri funzionali
Sebbene la procedura qui descritta sia completamente differente da un punto di vista
concettuale rispetto a quella descritta in precedenza, entrambe utilizzano una prima fase dove
è possibile indicare ai vari oggetti che incapsulano l’algoritmo i parametri di funzionamento
utili all’elaborazione che si apprestano a compiere. È in questa fase che si istruisce l’algoritmo
su quale sfumatura deve essere intercettata, utilizzando i risultati ottenuti dalla procedura
precedente. Per far questo si utilizza una struttura atta a contenere tutte queste informazioni:
typedef struct LIMITI_COLORE{
bool ColoreAttivo;bool ColoreTrovato;UINT TipoColore;RECT Zona;RECT Dominio;RECT Focus;RECT Lento;RECT FocusEye;RECT ZonaEye;short RossoMin,RossoMax,VerdeMin,VerdeMax,BluMin,BluMax;
};
Per esempio, nel campo TipoColore viene inserita l’informazione ottenuta grazie alla scelta
dell’utente, nel campo Zona risiede l’indicazione dei contorni della sfumatura e nei campi
RossoMin, RossoMax ecc… ci sono i limiti espressi nei colori primari che identificano la
sfumatura. Gli altri campi sono utilizzati per l’elaborazione e verranno introdotti e analizzati
successivamente.
Questa fase si conclude con la definizione dei parametri che permettono al thread un
corretto funzionamento. Per ottenere questo risultato si utilizza una zona di memoria
condivisa dove il thread è in grado di reperire le informazioni che il sistema gli comunica. In
questo caso si utilizza una struttura composta in questo modo:
typedef struct{
UINT Tipo;HWND hwnd;Supervisore* Angelo;Intercetta* Mago;Decisore* Oz;imgdes* Immagine;imgdes* ImmagineEye;RETTANGOLI* Rettangoli;UINT Stato;
72
bool Ucciso;} PARAMS, *PPARAMS;
I due campi più importanti sono Ucciso, nel quale il Supervisore comunica al thread che è
conclusa la sua elaborazione e Angelo, che indica al thread la possibilità di comunicare con il
Supervisore. È importante notare come all’oggetto thread si offra la possibilità di utilizzare
direttamente gli altri oggetti (Mago e Oz) aggirando l’obbligo di comunicare solamente con
l’Angelo. Questo è da attribuirsi all’analisi fatta nei capitoli precedenti in cui era emerso che
il sistema, in alcuni casi, prediligeva la velocità di elaborazione ad una struttura rigida e
strettamente regolamentata.
5.2.2 Elaborazione svolta in una sequenza
Prima di poter analizzare in maniera capillare il codice che permette l’implementazione
dell’algoritmo è importante discutere la relazione che lega il thread al sistema. Entra in gioco
a questo punto il discorso fatto all’inizio del capitolo. Il thread fa parte dell’implementazione
globale dell’algoritmo monolitico e per questa ragione non deve essere solamente ricondotto
all’implementazione dell’algoritmo logico descritto in questo paragrafo. Questo significa che
tutte e tre le procedure logiche che costituiscono l’algoritmo globale utilizzano il thread per i
loro scopi. Infatti questa caratteristica è visibile nella struttura stessa del codice sorgente che
implementa il thread. Sia la natura unitaria sia l’elemento logico sono evidenti nella sua
struttura. Il thread è composto da un grande ciclo che rispecchia la natura dell’algoritmo
monolitico. Mentre l’impronta logica è data da cicli minori che permettono al thread di
svolgere svariati compiti utili agli algoritmi.
La natura monolitica è esaltata anche dal fatto che il Supervisore attiva il thread all’inizio
di questa procedura e lo disattiva solo quando l’utente decide di fermare l’algoritmo logico
che identifica lo sguardo. Questo significa che i tre algoritmi logici descritti in questo e nei
prossimi due paragrafi sono visti dal Supervisore come un’unica entità.
Il compito dei cicli minori è implementare una specie di clock per le funzioni che
descrivono i tre algoritmi. Il thread dà il ritmo e permette di creare la sensazione che gli
algoritmi elaborino in modo continuo le immagini del mondo reale. È il cuore pulsante ed il
motore per la vita delle procedure.
Ogni ciclo minore si compone di quattro passaggi fondamentali. Per prima cosa interroga
gli oggetti che incapsulano le funzioni che caratterizzano l’algoritmo, dopodiché analizza i
risultati e avvisa il sistema se ci sono dei cambiamenti o errori. Prima di rilasciare la CPU,
visualizza i dati ottenuti nella finestra dell’applicazione.
Il ciclo minore che permette l’elaborazione discussa in questo paragrafo è il seguente:
73
while ((!pparams->Ucciso) && (pparams->Stato == STATO_ZERO)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);
}
Il primo passo, fatto dal ciclo minore, è attivare le funzione che permette l’elaborazione
relativa all’algoritmo descritto in questo paragrafo. Dopodiché il thread controlla se
l’algoritmo non presenta errori come, per esempio, la perdita dell’aggancio sulla sfumatura
del riferimento. Infine, prima di “addormentarsi” e rilasciare la CPU avverte l’applicazione
che dei nuovi dati sono pronti per essere visualizzati.
Ad ogni battito del ciclo minore il thread richiama la procedura:
UINT Intercetta::GetPosizioneI(){// Carica una nuova immagine
Camera->GetFoto();Camera->GetFile();loadbmp(NomeFile,&Immagine);if(Colore.ColoreAttivo){
74
// Setto la zona per accogliere le hitColore.Zona.left = 320;Colore.Zona.right = 0;Colore.Zona.bottom = 240;Colore.Zona.top = 0;Hit[Indice].Bersagli = 0;
// Setto il Dominio e il Focus per questa chiamataif (Colore.ColoreTrovato)
Colore.ColoreTrovato = TrovaColoreFocus();else
Colore.ColoreTrovato = TrovaColoreDominio();// Metto la zona trovata nel vettore delle catture
CopyRect(&Hit[Indice].Zona,&Colore.Zona);// Dopo NUMERO_CATTURE dò dei risultati più stabili
if(Indice >= (NUMERO_AQUISIZIONI - 1))return SetLento();
elseIndice++;
}return StatoCattura;
}
Il compito riservato a questa funzione è quello di richiedere direttamente all’oggetto che si
occupa della camera di catturare una nuova immagine per l’elaborazione, anche in questo
caso gli oggetti comunicano direttamente fra di loro senza passare dall’Angelo, questo per
ottenere un’elaborazione estremamente più veloce.
Quando si attiva il thread per la prima volta e si inizia l’elaborazione di questa procedura
grazie al ciclo minore, la funzione GetPosizioneI() non lavora ancora a pieno regime. Infatti
per operare al massimo delle sue possibilità ha bisogno di dati che solo il prossimo algoritmo
può fornirgli. Questo implica che per ottenere una spiegazione esauriente si deve attendere il
paragrafo successivo. Nel contesto in cui ci si ritrova è sufficiente indicare che il compito
affidato all’algoritmo viene comunque portato a termine. Grazie al campo della struttura che
identifica la sfumatura, l’algoritmo tiene traccia della posizione del riferimento. Il campo
Zona indica i contorni della sfumatura in ogni immagine catturata. Per ottenere questo
risultato non si deve fare altro che ricercare nel vettore che identifica i pixel i colori che
rientrano nei parametri che delineano la sfumatura.
5.2.3 Filtro
L’implementazione svolta dell’algoritmo permette, a questo punto, di attuare in maniera
abbastanza semplice l’operazione di filtro sui dati ottenuti dall’elaborazione. Ogni volta che la
funzione GetPosizioneI() conclude la sua sequenza di operazioni e quindi intercetta i contorni
della sfumatura, quest’ultimi vengono salvati in un vettore nel quale ogni elemento è una
struttura del tipo:
typedef struct HIT{
75
int Bersagli;RECT Zona;RECT ZonaEye;
};
che conservano sia la zona indicata nel sistema di riferimento solidale con l’immagine sia il
numero di pixel che appartengono alla sfumatura.
Quando sono avvenute tante sequenze quanti sono gli elementi del vettore si attiva la
funzione che permette all’algoritmo di produrre dei dati più stabili da poter offrire al sistema
per le elaborazioni successive. Avviene quello che è stato chiamato filtro:
UINT Intercetta::SetLento(){short i,Miss,Less;int Lunghezza,Larghezza;short Cont = 0;RECT Sum;
Miss = Less = 0;Indice = 0;Sum.left = Sum.right = Sum.top = Sum.bottom = 0;Larghezza = Colore.Lento.right - Colore.Lento.left;Lunghezza = Colore.Lento.top - Colore.Lento.bottom;for(i=0; i<NUMERO_AQUISIZIONI ; i++){
if(((Hit[i].Zona.right - Hit[i].Zona.left) <= (Larghezza + 10))&&
((Hit[i].Zona.top - Hit[i].Zona.bottom) <= (Lunghezza + 10))&&(Hit[i].Zona.left < Hit[i].Zona.right))
{Sum.left = Sum.left + Hit[i].Zona.left;Sum.right = Sum.right + Hit[i].Zona.right;Sum.bottom = Sum.bottom + Hit[i].Zona.bottom;Sum.top = Sum.top + Hit[i].Zona.top;Cont++;
}if((Hit[i].Bersagli > 0) && (Hit[i].Bersagli < MASSIMO_LESS))
Less++;if(Hit[i].Bersagli == 0)
Miss++;}if(Cont > 0){
Colore.Lento.left = (Sum.left / Cont);Colore.Lento.right = (Sum.right / Cont);Colore.Lento.bottom = (Sum.bottom / Cont);Colore.Lento.top = (Sum.top / Cont);CopyRect(&Rettangoli.Lento,&Colore.Lento);
}if(Miss == NUMERO_AQUISIZIONI)
StatoCattura = STATO_CATTURA_ROSSO;if(((Miss > 2) && (Miss < NUMERO_AQUISIZIONI)) || (Less > 5))
StatoCattura = STATO_CATTURA_GIALLO;if((Miss <= 2) && (Less < 5))
StatoCattura = STATO_CATTURA_VERDE;return StatoCattura;
}
76
Il campo che contiene l’informazione filtrata è Lento. Per ottenere questo risultato si elabora il
vettore nel quale risiedono i contorni del riferimento acquisiti nelle varie sequenze proposte
dal ciclo minore. L’analisi compiuta nel capitolo precedente riguardo al filtro in questa
funzione trova la sua corretta implementazione. Prima di tutto si scartano i dati che
evidenziano dei contorni che si discostano troppo dalle dimensioni normali del riferimento.
Questo potrebbe essere dovuto al fatto che l’algoritmo ha intercettato un pixel che appartiene
alla sfumatura ma che è il risultato di un errore sulle ottiche della camera e che non ha niente
a che vedere con il riferimento. I dati che passano questo controllo vengono mediati per
ottenere un’indicazione più stabile sulla posizione del riferimento. Questo serve per
compensare gli effetti che può dare la luce riflessa sul riferimento. In quanto, se si utilizza
come base una superficie curvilinea per il colore la luce può cambiare repentinamente il
colore e ingannare l’algoritmo. Tuttavia i piccoli rettangoli definiti da Zona sono tutti molto
vicini fra loro sull’immagine e la loro media permette di intercettare in modo corretto il
riferimento.
Un’altro incarico importante implementato da questa funzione è quello di dare una sorta di
controllo sulla cattura ed elaborazione dell’immagine. Se il numero di pixel attinenti alla
sfumatura diminuisce in maniera notevole l’algoritmo deve informare il sistema che non è più
in grado di fornire dati corretti. Quest’ultimo indica all’utente che probabilmente la sfumatura
scelta non è valida e si dovrebbero ripetere le operazioni offerte dalla procedura descritta nel
paragrafo precedente.
In conclusione il sistema è ora in grado di avere sempre sotto controllo la posizione del
riferimento nel sistema di riferimento solidale con l’immagine. Ovviamente l’elaborazione per
ricavare dati attendibili non può essere in tempo reale, tuttavia il ritardo che apporta
l’algoritmo è accettabile e verrà ampliante discusso nel capitolo successivo.
5.3 Definizione campo visivo Questa procedura è sicuramente quella più dinamica tra le tre che costituiscono l’algoritmo
monolitico. Il suo compito è di ottenere la dimensione del campo visivo dell’utente. Questa
zona fissa nel sistema di riferimento solidale con l’immagine indica l’insieme di coordinate
dove il riferimento ha libertà di movimento. In pratica si indicano le posizioni del volto che
hanno un’attinenza con i punti del terminale video. Se il riferimento esce dai contorni definiti
dal campo visivo significa che l’utente non ha il viso rivolto verso lo schermo.
La procedura deve chiedere all’utente di fissare i lati dello schermo e definire in questo
modo, appoggiandosi all’algoritmo descritto nel paragrafo precedente, il campo visivo.
77
Per ottenere questo risultato l’algoritmo utilizza l’elaborazione di un’ulteriore thread che
lavorando in simbiosi con il precedente ricava i risultati voluti. La comunicazione tra i due
thread è demandata all’Angelo.
L’algoritmo si evolve per stati, ogni stato rappresenta una posizione sullo schermo che
l’utente deve fissare con il viso. Per rendere la fase di cablaggio il più veloce e semplice
possibile per l’utente, si sono ridotti al minimo i punti da fissare. Quest’ultimi sono disposti
sui lati dell’applicazione e sono rappresentati da un pallino colorato, quando la finestra
dell’applicazione è distesa su tutto lo schermo i punti combaciano con i lati del terminale
video e questo permette di ricavare il campo visivo. In ogni stato l’algoritmo deve essere in
grado di catturare un certo numero di immagini al fine di ottenere un’informazione adeguata e
filtrata dagli errori che si sono già discussi.
Il thread introdotto in questo paragrafo ha la funzione di scandire i vari stati e con essi le
posizioni che l’utente deve fissare. Quando il thread principale ha ottenuto un risultato
accettabile riguardo ad un punto fissato avvisa il Supervisore che è pronto a passare ad un
altro stato. Quest’ultimo fa in modo che il thread che scandisce gli stati passi al successivo e
l’elaborazione si ripete.
Un altro motivo per cui si deve attendere la cattura di più immagini per ogni stato è il
periodo di assestamento che il viso dell’utente ha nello spostarsi da un punto dello schermo
all’altro. Questo comporta che alcune immagini debbano essere scartate in quanto indicano
zone intermedie tra i due punti che identificano stati differenti.
L’analisi dettagliata non avviene per oggetti, ma si studierà l’intero procedimento che
definisce un singolo stato. Si inizierà con l’analizzare il thread che scandisce gli stati, le
richieste che questo fa all’Angelo, l’elaborazione fatta dal thread principale ed infine i
messaggi che permettono di passare allo stato successivo. Ovviamente l’elaborazione
completa dell’algoritmo è la somma dell’elaborazione avvenuta nei vari stati.
5.3.1 Introduzione di uno stato
L’algoritmo prende vita nella procedura che implementa il thread che scandisce i vari stati.
Quest’ultimo non ha altro compito che avvertire il sistema e gli oggetti che prendono parte
all’elaborazione dello stato in cui ci si ritrova:
void ThreadPosiziona(PVOID pvoid){UINT TemporaneoStato;PPARAMS_POSIZIONA pparams = (PPARAMS_POSIZIONA) pvoid;
Sleep(1000);while(!pparams->Ucciso){
78
InvalidateRect(pparams->hwnd,NULL,true);if(pparams->Stato <= STATO_NOVE){
TemporaneoStato = pparams->Stato;pparams->Angelo->HandShake(AN_ASK_INIZIO_STATO,
AN_THREAD_POSIZIONA,pparams->Stato);
while((!pparams->Ucciso) &&(pparams->Stato == TemporaneoStato))Sleep(200);
}else{
Sleep(2000);pparams->Angelo->HandShake(AN_ASK_FINE_SETTAGGIO,
AN_THREAD_POSIZIONA,0);pparams->Ucciso = true;
}}pparams->Stato = STATO_ZERO;_endthread();
}
Finché l’Angelo non lo avverte che lo stato si è concluso il thread, dopo aver inviato un
messaggio all’Angelo, si pone in attesa. La sottofinestra correlata col thread utilizza
l’informazione sullo stato raggiunto per indicare, tramite un pallino colorato, la posizione
sullo schermo su cui l’utente deve rivolgere il viso. La comunicazione tra l’Angelo e il thread
avviene grazie ad una struttura non dissimile da quella usata dal thread principale, nel quale il
parametro più importante è il campo Ucciso che permette al Supervisore di terminare il thread
e con esso questo algoritmo.
79
5.3.2 HandShake
Il messaggio che il thread invia all’Angelo fa partire l’elaborazione vera e propria che
contraddistingue uno stato. Quest’ultimo viene elaborato dal Supervisore grazie al codice:
case AN_ASK_INIZIO_STATO:SendMessage(Finestre>GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_STATO_MUTATO,0,(long) Parametro);
break;
Il quale trasferisce al thread principale l’informazione sullo stato raggiunto. In questo
modo si rispettano in maniera completa le regole che sono state definite nei capitoli
precedenti. È il supervisore a gestire i rapporti fra gli oggetti. In quanto in questo caso non è
importante la velocità di esecuzione e allo stesso tempo l’elaborazione svolta dall’Angelo non
compromette il suo compito principale nel rispondere ai messaggi che provengono
dall’esterno.
5.3.3 Elaborazione del singolo stato
Torna qui di attualità l’analisi svolta nel paragrafo precedente sui cicli ed i cicli minori
contenuti nel codice sorgente del thread principale. Per asservire i compiti specifici di questa
procedura si utilizza un altro ciclo minore che imprime all’algoritmo la sensazione di
continuità e scandisce il ritmo delle catture delle immagini che vengono utilizzate per ricavare
l’informazione voluta.
if((pparams->Stato >= STATO_UNO) && (pparams->Stato <= STATO_NOVE)){
pparams->Mago->UnLock(pparams->Stato);TemporaneoStato = pparams->Stato;StatoProgresso = STATO_PROGRESSO_INIZIO;
// Ciclo Minorewhile ((!pparams->Ucciso) && (pparams->Stato == TemporaneoStato)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo>Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}TemporaneoStatoProgresso = pparams->Mago->GetProgresso();if(StatoProgresso != TemporaneoStatoProgresso){
StatoProgresso = TemporaneoStatoProgresso;if(StatoProgresso == STATO_PROGRESSO_FINE){
pparams->Angelo->HandShake(AN_ASK_FINE_STATO,AN_THREAD_INTERCETTA,pparams->Stato);
pparams->Mago->Lock();StatoProgresso = STATO_PROGRESSO_INIZIO;
80
}pparams->Angelo->Messaggio(AN_ASK_STATO_PROGRESSO_MUTATO,
pparams->Tipo,StatoProgresso);}Sleep(SLEEP_THREAD_FRONTALE);
}}
Anche in questo caso si possono trovare le fasi che contraddistinguono ogni ciclo minore. Per
prima cosa vengono interrogati gli oggetti che permettono la cattura e l’elaborazioni delle
immagini. Dopodiché si passa a controllare se ci sono errori oppure se lo stato è arrivato alla
sua conclusione, infine, prima di rilasciare la CPU, si avverte il Supervisore se è necessario
procedere all’elaborazione dello stadio successivo o si è giunti alla conclusione della
procedura.
La fase che permette l’interrogazione degli oggetti che contengono il sorgente atto
all’elaborazione svolta da questo algoritmo potrebbe risultare, a prima vista, molto simile a
quella discussa nel paragrafo precedente. Tuttavia in essa si nasconde una rilevante
differenza. Per comprendere appieno l’elaborazione svolta in questo contesto, si deve
introdurre il campo Dominio, contenuto nella struttura che identifica la sfumatura, che non è
stato ancora trattato. Nel Dominio risiede il rettangolo che descrive il campo visivo, nel
sistema di riferimento solidale con l’immagine. Una proprietà estremamente importante che
accompagna il Dominio è la possibilità di modificarlo solo quando il sistema permette questa
operazione, per il resto il Dominio rimane fisso ed è impossibile, per le procedure,
ridimensionarlo.
Nel ciclo minore preso qui in esame si può notare come, prima di iniziare la cattura e
l’elaborazione delle immagini, si dà la possibilità di modificare i contorni del Dominio grazie
alla funzione UnLock(). A questo punto la funzione GetPosizioneI() non si limita
all’elaborazione discussa nel paragrafo precedente ma modifica anche il Dominio e decide
quando l’analisi dello stato è conclusa bloccando il ridimensionamento del Dominio grazie
alla funzione Lock().
L’analisi dettagliata di tutte le funzioni che permettono le operazioni descritte
precedentemente e contenute negli oggetti che fanno da supporto per l’algoritmo non vengono
qui descritte. Questo perché la loro discussione potrebbe appesantire in modo estremo il
discorso fin qui fatto. Per un’analisi completa del sorgente si rimanda alle appendici.
L’unica caratteristica che merita una discussione approfondita e che completa il discorso
introdotto nel paragrafo precedente è il procedimento utilizzato da GetPosizioneI() per
elaborare le immagini. Si era detto che nell’algoritmo precedente la funzione non lavorava in
maniera completa ma solo parzialmente. Ora grazie al Dominio la funzione inizia a operare al
81
massimo regime. È di vitale importanza capire che ogni sequenza, che elabora un’immagine
catturata, non è isolata. Viene influenzata dalla precedente e influenza a sua volta la
successiva. Per ottimizzare la ricerca della sfumatura non si analizza tutta l’immagine ma solo
parte di essa. Grazie al campo Focus si riduce l’elaborazione ad un sottoinsieme di pixel. Nel
funzionamento normale, ogni volta che si richiama GetPosizioneI(), si ricerca la sfumatura
nella zona delimitata da Focus, tuttavia se, per esempio, l’utente cambia repentinamente la
posizione del volto c’è la possibilità di perdere l’aggancio con il riferimento. Se si avvera
questa possibilità la funzione non ricerca il riferimento su Focus ma sul Dominio ed in questo
modo si è sicuri che, se l’utente rivolge il viso allo schermo, sicuramente il riferimento si
troverà nella zona delineata dal Dominio. Quindi se una sequenza perde l’aggancio del
riferimento, avvisa la successiva che si deve ricercare il riferimento nel Dominio. Questo
comunque ottimizza ancora la ricerca in quanto il Dominio rappresenta solo una parte
dell’immagine completa.
5.3.4 Conclusione
Quando l’elaborazione di uno stato si conclude il ciclo minore avverte l’Angelo.
Quest’ultimo non deve fare altro che informare il thread che scandisce gli stati. Due sono le
possibilità che si incontrano a questo punto. La prima indica la conclusione di uno stato
intermedio, mentre l’altra indica la conclusione dell’ultimo stato che determina la fine
dell’algoritmo preso in esame in questo paragrafo. Se a concludere è uno stato intermedio la
procedura inizia un’elaborazione simile a quella descritta, con l’unica differenza che nella
finestra di applicazione il pallino colorato ha cambiato posizione. Se, invece, si è concluso
l’ultimo stato, l’Angelo termina il thread che scandisce gli stati e riporta il sistema nella
configurazione che aveva prima che l’utente attivasse questo algoritmo. L’unica differenza è
che a questo punto il Supervisore ha in mano un Dominio valido per le elaborazioni
successive.
5.3.5 Cablaggio manuale
Per facilitare il più possibile il compito dell’utente il sistema propone anche un metodo
alternativo per affrontare la fase di cablaggio. Invece di seguire passo a passo le istruzioni che
compaiono nella finestra e seguire il pallino colorato nelle varie posizioni sullo schermo, c’è
la possibilità di delineare in maniera manuale il campo visivo. Questo metodo, comunque, è
da preferire solamente se l’utente ha già utilizzato il sistema ed è in grado di prevenire le
richieste dell’applicazione.
82
Questa soluzione è da ricondurre all’analisi svolta nei capitoli precedenti, in quanto
permette di cablare il sistema in maniera più veloce e intuitiva, aiutando l’utente in un
compito che deve affrontare obbligatoriamente ogni volta che utilizza l’ausilio.
Anche in questo caso c’è un ciclo minore che dà vita alla procedura, nel thread principale:
if(pparams->Stato == STATO_MANUALE){
pparams->Mago->UnLock(pparams->Stato);while ((!pparams->Ucciso) && (pparams->Stato == STATO_MANUALE)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);
}}
Come si può notare, prima di iniziare il ciclo minore si dà la possibilità di modificare i
contorni del Dominio grazie alla già discussa funzione UnLock(). Dopodiché, anche in questo
caso, si evidenziano le quattro fasi che costituiscono il ciclo. Si richiama la funzione che
permette la cattura e l’elaborazione dell’immagine, si controlla che i dati ottenuti siano privi
d’errore ed infine, prima di rilasciare il controllo della CPU si informa il sistema dei risultati
conseguiti.
Una volta attivata la procedura manuale grazie al menu, l’utente non deve fare altro che
fissare per un breve periodo di tempo i quattro angoli dello schermo e controllare nelle
finestre dell’applicazione se effettivamente il rettangolo che delimita il Dominio modifica le
sue dimensioni.
Per concludere è necessario notare che questa procedura non termina automaticamente
come la precedente, quindi deve essere l’utente che ne decreta la conclusione utilizzando i
comandi che reperisce nel menu. A questo punto è il Supervisore che impedisce la possibilità
di modificare il Dominio con la funzione Lock().
5.4 Indicazione dello sguardo Questa procedura conclude sia l’analisi degli algoritmi descritti teoricamente nel capitolo
precedente sia lo studio dell’algoritmo che è stato chiamato monolitico. Tra quelle presentate
fino ad ora risulta essere la più semplice, in quanto si trova ad elaborare dati che
rappresentano sostanzialmente un modello matematico e geometrico della realtà che circonda
83
l’applicazione. Il suo compito è di indicare al sistema dove sia rivolto il viso dell’utente. Per
ottenere questo risultato relaziona la posizione del riferimento con il campo visivo ottenuto in
precedenza.
Considerando l’analisi che è stata svolta nel capitolo relativo all’architettura e agli
algoritmi è possibile discutere senza esitazioni l’implementazione pratica della procedura.
Come le precedenti, anche in questo caso l’algoritmo si basa su un ciclo minore contenuto nel
thread principale per elaborare le operazioni che fanno parte di una sequenza. Inoltre è in
grado di avvertire il sistema se l’utente fissa un punto sullo schermo per un certo periodo di
tempo al fine di simulare il clic del mouse.
5.4.1 Elaborazioni svolte in una sequenza
Anche in quest’ultimo caso la sensazione di continuità nell’elaborazione è data da un ciclo
minore contenuto nel thread principale:
while ((!pparams->Ucciso) && (pparams->Stato == STATO_LAVORO)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;
84
pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,pparams->Tipo,StatoCattura);
}switch (pparams->Oz->GetDecisione()){case AN_RET_NO_DECISIONE:
break;case AN_RET_SI_DECISIONE:
pparams->Angelo->Messaggio(AN_ASK_DECISIONE_AVVENUTA);break;
case AN_RET_CLICK_TEMPORALE:pparams->Angelo->Messaggio(AN_ASK_CLICK_TEMPORALE_AVVENUTA);break;
}Sleep(SLEEP_THREAD_FRONTALE);
}
L’unica fase del ciclo che si discosta dalle precedenti e merita un’analisi dettagliata è la parte
in cui si avvisa l’Angelo se avviene un evento degno di nota. L’elaborazione svolta in
GetDecisione() si basa sul campo Lento descritto in precedenza, quindi un risultato accettabile
non è pronto ad ogni sequenza ma si deve attendere che la procedura che intercetta il
movimento deliberi le informazioni contenute in quel campo. In questo modo solo dopo un
certo numero di sequenze viene inviato un messaggio al Supervisore.
La procedura GetDecisione():
UINT Decisore::GetDecisione(){
Contatore++;if(Contatore >= (NUMERO_AQUISIZIONI - 1)){
Contatore = 0;// Trovo il punto mediano di Lento:
X = (float)((Rettangoli->Lento.right + Rettangoli->Lento.left)/ 2);
Y = (float)((Rettangoli->Lento.top + Rettangoli->Lento.bottom)/ 2);// Controllo se il Punto mediano di Lento è nelle "vicinanze" del Dominio:
if((X > (Rettangoli->Dominio.left - BORDO_DOMINIO)) &&(X < Rettangoli->Dominio.left))
X = (float)Rettangoli->Dominio.left;if((X < (Rettangoli->Dominio.right + BORDO_DOMINIO)) &&
(X > Rettangoli->Dominio.right))X = (float)Rettangoli->Dominio.right;
if((Y > (Rettangoli->Dominio.bottom - BORDO_DOMINIO)) &&(Y < Rettangoli->Dominio.bottom))
Y = (float)Rettangoli->Dominio.bottom;if((Y < (Rettangoli->Dominio.top + BORDO_DOMINIO)) &&
(Y > Rettangoli->Dominio.top))Y = (float)Rettangoli->Dominio.top;
// Se il Punto mediano di Lento è contenuto nel Dominio:if((X >= Rettangoli->Dominio.left) &&
(X <= Rettangoli->Dominio.right) &&(Y >= Rettangoli->Dominio.bottom) &&(Y <= Rettangoli->Dominio.top))
{// Lo setto nel SdR del Dominio:
X = X - Rettangoli->Dominio.left;
85
Y = Y - Rettangoli->Dominio.bottom;// Lo traslo sulla Finestra Applicazione e lo metto nell'area publica:
Punto.X = (1 - (X / NumeroDiColonne));Punto.Y = (1 - (Y / NumeroDiLinee));
// Controllo se il punto è attinente al Click:if((X >= Click.Regione.left) &&
(X <= Click.Regione.right) &&(Y >= Click.Regione.bottom) &&(Y <= Click.Regione.top))
{Click.Attinenze++;if(Click.Attinenze > NUMERO_AQUISIZIONI_AREA_CLICK){
Click.Attinenze = 0;SetRect(&this->Click.Regione,
(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,
(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);
return AN_RET_CLICK_TEMPORALE;}
}else{
Click.Attinenze = 0;SetRect(&this->Click.Regione,
(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,
(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);
}return AN_RET_SI_DECISIONE;
}}return AN_RET_NO_DECISIONE;
}
Questa funzione oltre che a compensare l’effetto specchio delle immagini catturate dalla
camera decide se il riferimento è rimasto nello stesso luogo per un certo periodo di tempo. Se
questo avviene l’algoritmo avvisa l’Angelo che l’utente molto probabilmente vuole simulare
il clic.
Si parla di clic temporale in quanto si utilizza il tempo come parametro di decisione per
avvisare il sistema del clic. Per ora il sistema utilizza solo questo metodo per simulare il clic
ma ci sono svariate possibilità per attuare questa opzione. Se ci fosse la possibilità, in futuro,
di connettere più camere si potrebbe simulare il clic semplicemente abbassando la palpebra e
incaricare una camera di catturare questo movimento.
Per concludere questa sezione è importante notare che le coordinate del punto fissato sono
indicate con valori che vanno da 0 a 1. In questo modo, per esempio, l’angolo in basso a
destra è indicato dalla coppia 1, 1 e il punto centrale della finestra dell’applicazione viene
risolto con 0.5 e 0.5 e così via. Questo metodo permette di separare completamente gli oggetti
che descrivono questo algoritmo dagli oggetti che identificano le applicazioni indipendenti, in
86
quanto la procedura non deve sapere la dimensione effettiva dell’applicazione ma rilascia
valori assoluti che poi, verranno convertiti in coordinate relative alla finestra.
5.4.2 Messaggi alle applicazioni
Anche se questa discussione esula dall’algoritmo vero e proprio qui descritto è importante
descrivere i messaggi che le applicazioni indipendenti ricevono. Oltre ai messaggi che invia il
Sistema Operativo le window procedure devono elaborare altri messaggi che arrivano
direttamente dal sistema.
Per prima cosa il sistema invia all’applicazione il messaggio WM_CREATE_PUNTO che
permette all’applicazione indipendente di agganciare la zona di memoria dove può reperire
con facilità i dati appena elaborati dall’algoritmo, indicanti le coordinate del punto fissato.
Con il messaggio WM_NON_VALIDO si avverte l’applicazione indipendente che
l’algoritmo ha dei dati nuovi è più recenti sul punto fissato.
Infine, per quanto riguarda i messaggi relativi all’elaborazione di questa procedura il
Supervisore invia all’applicazione il messaggio WM_CLICK che indica il clic temporale.
Per concludere il sistema invia un’ulteriore messaggio che permette all’applicazione
indipendente di indicare all’utente se la cattura e l’aggancio sul riferimento sono elaborati
correttamente oppure se il sistema non è più in grado di intercettare il riferimento. Questo
avviene con l’invio del messaggio WM_STATO_CATTURA_MUTATO.
Il Supervisore si limita ad inviare questi messaggi, è compito dell’applicazione
indipendente elaborarli in maniera corretta. Il sistema è stato concepito in questo modo per
lasciare completa libertà d’azione agli sviluppatori delle applicazioni. Infatti varia da
applicazione ad applicazione la reazione richiesta per ogni messaggio. Un’applicazione può
persino scartare e non elaborare parte dei messaggi che il sistema gli propone, tutto dipende
dalle caratteristiche e dal fine che vuole ottenere l’applicazione indipendente.
87
6 Valutazioni sperimentali
L’analisi fin qui svolta non può ritenersi conclusa se non si discutono le prestazioni che il
sistema è in grado di fornire. È importante studiare e comprendere se gli algoritmi, descritti
nei capitoli precedenti, sono validi e portano a termine gli obiettivi che l’ausilio si proponeva
di conseguire. Nell’implementazione delle procedure si è cercata la soluzione migliore ad
ogni problema ma, solo l’analisi delle prestazioni può rispondere correttamente ai dubbi sulla
validità della soluzione.
Il fattore più importante che influenza le prestazioni in modo pressoché completo sfugge
purtroppo dal controllo del sistema. Gli algoritmi che compongono la catena di acquisizione
ed elaborazione dati entrano in contatto con il mondo reale. Questo implica che le immagini
catturate siano solo una rappresentazione della realtà. Questa rappresentazione è afflitta, come
è già stato descritto, da numerosi errori che vengono in qualche modo soppressi o attenuati dal
lavoro delle procedure. Tuttavia la caratteristica che da sola decide sul funzionamento del
sistema è l’illuminazione dell’utente quando si trova davanti al terminale video. Se questa non
è sufficiente, gli errori insiti nelle immagini vengono enfatizzati e risulta difficile se non
impossibile agli algoritmi compensare per ottenere un risultato utile. L’illuminazione non è un
elemento che il sistema possa controllare, quindi, le prestazioni del sistema sono legate
88
direttamente a questo fattore. Se l’illuminazione è ottima anche le prestazioni saranno ottime,
mentre se la luce che colpisce l’utente è mediocre, mediocri saranno le prestazioni del
sistema.
Non è possibile in questo contesto seguire la catena di passi utilizzata per i precedenti due
capitoli. Mentre non ha significato analizzare le prestazioni di ogni singolo algoritmo, è di
grande utilità focalizzare invece l’attenzione sull’algoritmo monolitico precedentemente
introdotto. Quest’ultimo rappresenta l’unità elaborativa centrale di tutto il sistema ed è grazie
ad essa che l’ausilio può portare a termine il suo compito.
Prima di poter studiare in profondità le prestazioni che questo algoritmo monolitico offre,
occorre tuttavia discutere ed analizzare una caratteristica estremamente importante che
accompagna il sistema. È rilevante notare che l’elaborazione delle immagini catturate dalla
camera non avviene in modo rigido e fissato ma, il sistema permette all’utente di modificare
l’elaborazione mentre questa si sta compiendo. I parametri di funzionamento dell’algoritmo
monolitico vengono controllati direttamente dall’utente grazie ad una sottofinestra visibile
nella finestra dell’applicazione. Agendo su questi controlli, l’utilizzatore è in grado di adattare
il sistema alle condizioni ambientali che lo circondano. Quindi è possibile migliorare
l’elaborazione anche se l’illuminazione risulta carente.
Solo dopo aver analizzato questi controlli, è possibile studiare le prestazioni che il sistema
può offrire. Per fare questo si è implementata una coppia di applicazioni indipendenti, il cui
unico scopo è testare il sistema. Infatti non esiste altro modo per verificare se l’ausilio
raggiunge gli obiettivi che si erano posti all’inizio.
6.1 I controlli La maggior parte dei sistemi, inclusi i sistemi informatici, devono possedere una sorta di
controllo per portare a termine i loro compiti. Non è possibile lasciare che l’elaborazione
avvenga in modo predefinito, senza modificarsi in base alle condizioni ambientali che
circondano il sistema. Solo se il sistema si trova a lavorare in un ambiente isolato non è
irragionevole, ma possibile tralasciare la caratteristica di controllo, in quanto non esistono
azioni esterne che possano turbare l’attività del sistema. In questo caso, al contrario, l’ausilio
è estremamente vincolato all’ambiente che lo circonda ed è impensabile non attuare una
specie di controllo. Se per assurdo, il sistema non attuasse un controllo ed elaborasse in
maniera fissa i dati che ottiene dall’ambiente circostante, i risultati che si otterrebbero
potrebbero essere completamente errati e privi di significato. È per questa ragione che si è
data all’utente la possibilità di modificare i parametri che definiscono il comportamento
89
dell’elaborazione dell’immagine. Grazie a semplici ed intuitivi controlli attivabili in una
sottofinestra dell’applicazione principale è possibile agire in maniera diretta sui parametri che
regolano l’algoritmo monolitico che rappresenta la mente di tutto il sistema.
È da considerarsi inoltre l’implementazione come prototipo per l’ausilio che si vuole
produrre. Questo comporta che molti controlli non sono automatici in quanto gli algoritmi che
lo compongono non sono così complessi da attuare una sorta di retroazione al loro interno.
Nel contesto in cui si trova ad operare il fine ultimo è ottenere una soluzione che possa
concretizzare gli obiettivi eletti dall’ausilio e non un’ottimizzazione degli stessi.
Prima di analizzare i vari controlli singolarmente è utile aggiungere che il sistema è stato
testato su un Personal Computer che monta una scheda madre Intel con processore Celeron da
2.0 Ghz. con 128 Mb di Ram e si sono utilizzate due WebCam 5 della Creative per la cattura
delle immagini, connesse alle porte USB dell’elaboratore.
6.1.1 Controllo sulla velocità di esecuzione del thread principale
È importante aprire una piccola parentesi sul thread e la funzione Sleep() da lui richiamata,
prima di passare alla discussione vera e propria del controllo.
Come è già stato studiato nel capitolo precedente il thread principale utilizza dei piccoli
cicli, detti cicli minori, per rappresentare le varie procedure che costituiscono l’algoritmo
monolitico. Ogni ciclo minore ha, per ultima fase, la chiamata alla funzione Sleep(). Questa
operazione è fortemente correlata alle operazioni svolte dal Sistema Operativo per assegnare
la CPU ai vari processi. Se il ciclo minore non utilizzasse la funzione Sleep() la CPU verrebbe
sopraffatta dall’elaborazione del thread. Infatti alla conclusione della sequenza il Sistema
Operativo porrebbe nella coda dei processi attivi il thread pronto per una sequenza successiva.
Se per caso, ma la probabilità è alta, non ci fossero altri processi in attesa, il thread otterrebbe
di nuovo l’utilizzo della CPU. Questo porterebbe alla saturazione completa della CPU per
l’elaborazione del thread e si avrebbe anche un rallentamento importante nelle altre funzioni
svolte dal Sistema Operativo, come l’apertura di finestre, menu e così via.
L’utilizzo della funzione Sleep() evita tutto questo. Il thread, quando conclude il ciclo
minore, avverte il Sistema Operativo che è pronto a rilasciare la CPU. Quando il Sistema
Operativo vede che il thread ha richiamato la funzione Sleep() non lo mette più nella coda
relativa ai processi attivi in attesa della CPU finché non è trascorso il tempo indicato nella
Sleep(). Solo quando, un timer impostato sul tempo di attesa avverte il Sistema Operativo
della scadenza, il thread viene di nuovo posto nella coda dei processi attivi è può così
riottenere la CPU.
90
Il controllo non fa altro che variare l’argomento della Sleep(). Cosicché è possibile, dalla
finestra di applicazione, modificare il tempo di esecuzione dell’algoritmo monolitico. La
gamma di possibilità va da 1 a 100 millisecondi. Più il tempo di attesa si porta verso il
millisecondo più le catture delle immagini e l’elaborazione delle stesse avvengono con un
ritmo accelerato. In un Personal Computer potente come quello su cui si è testato il sistema,
attese prossime al millisecondo ingannano l’occhio umano che non è più in grado di percepire
l’attesa tra un’immagine e l’altra, mentre la CPU non viene utilizzata più del 15%. Tuttavia in
elaboratori meno evoluti potrebbe non essere possibile arrivare a certi livelli, ed è per questo
motivo che si è mantenuta una gamma abbastanza cospicua per poter agire su questo
parametro.
Per concludere è importante notare che il tempo di attesa non definisce in modo completo
il tempo utilizzato da ogni ciclo minore per l’elaborazione. Infatti è possibile scomporre il
tempo utilizzato da un ciclo in tre parti distinte. Per prima cosa c’è il tempo che il ciclo
occupa a catturare l’immagine dalla camera, dopodiché c’è l’intervallo di tempo utilizzato per
l’elaborazione e solo a questo punto sopraggiunge il tempo di attesa implementato grazie alla
Sleep(). Solo gli ultimi due possono essere modificati e sono sotto il controllo del sistema. Il
tempo di attesa è direttamente modificabile dall’utente, mentre il tempo di elaborazione può
essere migliorato implementando algoritmi più complessi ed ottimizzati. In un elaboratore
recente il tempo di elaborazione non supera le frazioni del millisecondo in quanto le
operazioni svolte in una sequenza non sono molte. Detto questo si potrebbe pensare che
limitando il tempo di attesa al millisecondo si potrebbe arrivare ad elaborare un migliaio di
sequenze al secondo. Purtroppo ciò non accade perché si è tralasciato il tempo che il ciclo
minore passa per catturare e caricare l’immagine dalla camera. Questo ritardo non dipende dal
sistema ma e imputabile alla velocità del Bus che collega la porta USB alla scheda madre ed è
maggiore di alcuni ordini di grandezza rispetto agli altri due.
Precedentemente si è scritto che se il controllo della velocità di elaborazione del thread
viene posto al millisecondo, la CPU non ha un utilizzo maggiore del 15%. Questo perché
anche il processore deve attendere che l’immagine venga trasportata dal Bus prima di poterla
elaborare. C’è il cosiddetto collo di bottiglia che interessa il trasporto dell’immagine dalla
camera al processore. Questo implica che non si possano elaborare più di una decina di
immagini al secondo anche con il tempo di attesa che sfiora il millisecondo.
6.1.2 Numero di catture per attuare il filtro
Come si è già detto all’inizio di questo capitolo il sistema è fortemente legato all’ambiente
che lo circonda. Il controllo descritto in questa sezione non fa altro che assecondare
91
l’illuminazione che colpisce il volto dell’utente. Inoltre non si deve pensare ai controlli come
entità separate ma, l’azione su uno di essi deve essere bilanciata con l’utilizzo di altri
controlli. Tutti i controlli concorrono al miglioramento delle prestazioni del sistema. Se, per
esempio, l’illuminazione è scarsa occorre elaborare più immagini per ottenere un risultato
accettabile. Questo comporta che, se la velocità del thread resta invariata, si dovrà attendere
un tempo maggiore prima di vedere i risultati dell’elaborazione e quindi il sistema risulterà
più lento. Tuttavia se a fronte di un filtro che elabora più immagini si accelera l’elaborazione
del thread è possibile ottenere le stesse prestazioni anche se il sistema è obbligato, per via
dell’illuminazione, ad elaborare più immagini.
Stessa analisi si può effettuare se le immagini catturate presentano un’illuminazione
ottima. Se il riferimento viene intercettato dal sistema con facilità non è indicato attuare un
filtro con molte immagini. Il controllo in questo caso può essere utilizzato per diminuire le
catture utilizzate dal filtro per ottenere un’elaborazione più veloce ma sempre precisa. In
questo caso, se il processore è sovraccarico, è possibile diminuire la velocità di elaborazione
del thread senza ridurre le prestazioni.
Questo controllo utilizzato in simbiosi con il precedente assicura al sistema una reazione in
base alle condizioni ambientali presenti intorno all’utente ed al suo viso. Ovviamente nulla è
possibile fare se l’illuminazione è estremamente carente. Se il riferimento non è intercettabile
il filtro scarterà tutte le immagini e anche se il controllo imporrà al filtro l’elaborazione di 30
immagini, limite superiore del range, non si otterranno risultati utilizzabili.
Per concludere è importante descrivere una caratteristica piuttosto singolare del controllo.
Il suo range va da 1 a 30 immagini catturate per l’elaborazione del filtro. Se si sceglie solo
un’immagine catturata il filtro cessa la sua funzione ed il sistema lavora come se ogni
immagine portasse ad un rilevamento. Per utilizzare le variabili descritte nel capitolo
precedente si può dire che il Lento si va a sovrapporre al campo Zona.
6.1.3 Clic temporale
Per analizzare in modo approfondito questo controllo si deve riprendere la discussione fatta
nel capitolo precedente. In quel contesto si era studiata la tecnica utilizzata dal sistema per
rilevare, o meglio, simulare il clic. Dopo un certo numero ben definito di rilevamenti, attuati
dall’algoritmo monolitico, nello stesso punto del video, si era in grado di avvertire il sistema
che era avvenuto un clic temporale. Dall’esperienza maturata con l’implementazione
dell’ausilio si è posto a circa due secondi il tempo in cui l’utente deve fissare un punto per
ottenere il clic temporale. Si è scelto questo intervallo di tempo per rendere l’utilizzo del
sistema, da parte dell’utente, il più semplice e comodo possibile. Infatti un intervallo
92
maggiore potrebbe, alla lunga, affaticare l’utilizzatore, mentre una pausa più ristretta potrebbe
ingannare il sistema e rilevare un clic anche se l’utente sta solo ricercando le informazioni sul
video e rallenta gli spostamenti del volto.
Per queste ragioni entra in gioco il controllo qui descritto. Se vengono modificati, grazie ai
controlli precedentemente descritti, la velocità delle catture e il numero di rilevamenti per
unità di tempo è molto probabile che il limite dei due secondi per rilevare il clic non sia più
rispettato. Se, per esempio, viene aumentata la velocità delle catture anche il numero di
rilevamenti per secondo aumenta proporzionatamente. Questo implica che occorreranno meno
di due secondi per far indicare dal sistema all’applicazione indipendente l’evento del clic
temporale. Ecco che con l’aiuto di questo controllo è possibile ristabilire il margine di tempo
voluto, in base alla velocità delle catture e alla velocità di elaborazione del filtro. Questo è
fatto aumentando il numero di rilevamenti che devono avere le stesse coordinate nel sistema
di riferimento solidale con l’immagine. In questo modo si dilatano i tempi per decidere se si è
di fronte ad un clic temporale, riequilibrando nel contempo il sistema sui fatidici due secondi
per clic.
6.1.4 Regione relativa al clic
Nella sezione precedente non si è tenuto conto del fatto che il filtro, purtroppo, non è una
barriera insormontabile per tutti gli errori. Questo causa degli effetti collaterali che possono
alterare il funzionamento dell’ausilio. In particolare c’è un effetto che risulta estremamente
collegato all’ambiente che circonda il sistema. Più l’illuminazione è carente più si evidenzia
questo fenomeno. Nel funzionamento normale dell’ausilio si nota che il punto che indica sullo
schermo la zona fissata dall’utente oscilla. L’oscillazione è tanto più marcata tanto più
l’illuminazione del viso dell’utente è insufficiente. Se l’illuminazione risulta ottima
l’oscillazione è quasi impercettibile. Questo implica che, per definire un clic, non si possa
attuare un’elaborazione semplice.
Come è già stato discusso nel capitolo precedente si utilizza una zona che racchiude il
punto fissato, se il rilevamento fatto dal filtro nella sequenza successiva è contenuto ancora in
questa zona è alta la probabilità che l’utente stia effettuando un clic temporale. Se un numero
di rilevamenti successivi, pari al valore definito grazie al controllo precedentemente descritto,
è incluso in questa zona allora il sistema avverte l’applicazione indipendente che è avvenuto
un clic temporale.
Il controllo qui descritto permette di modificare le dimensioni della zona che aiuta il
sistema nel capire se l’utente sta fissando un punto preciso dello schermo. In questo modo si
possono assecondare le oscillazioni dovute agli errori non trattenuti dal filtro. Questo è fatto
93
perché se le oscillazioni sono troppo marcate e la zona ha dimensioni inferiori all’ampiezza
dell’oscillazione stessa, non sarà possibile al sistema intercettare i clic temporali. Mentre è
utile ridurre le dimensioni, se l’illuminazione è buona, per ottenere una definizione maggiore
dei punti fissati nell’applicazione indipendente. Questo porta ad avere un sistema che si
avvicina nelle prestazioni ad un mouse collegato al Personal Computer.
Il sistema, a questo punto dello sviluppo, riesce a intercettare, in condizione di luce buona,
oggetti dalle dimensioni di pochi centimetri (1.5-2.0) come per esempio le icone poste nel
DeskTop.
6.1.5 Focus
Questo controllo permette al sistema un’elaborazione più veloce. Si è già discusso di come
l’algoritmo monolitico utilizzi una tecnica particolare per attuare meno elaborazioni quando si
trova davanti una nuova immagine. Invece di cercare su tutta l’immagine la sfumatura da
intercettare la si cerca in un sottoinsieme di pixel indicato dal rettangolo Focus, elaborato
dalla sequenza precedente. In questo modo si accelera la ricerca e si è relativamente sicuri che
la sfumatura si troverà in questo rettangolo se l’utente non ha eseguito movimenti repentini
con il volto.
Grazie a questo controllo è possibile modificare la dimensione del Focus. Più si restringe
quest’area più l’elaborazione è veloce, viceversa se si aumentano le dimensioni, l’algoritmo
dovrà processare più pixel e l’elaborazione ne risulterà rallentata.
Dall’esperienza fatta è utile sovradimensionare i contorni del Focus nella fase di cablaggio
del sistema. In quanto l’algoritmo monolitico non lavora ancora a pieno regime e
l’intercettazione del riferimento si basa esclusivamente sul Focus. Questo implica che, se
l’utente ha un movimento repentino, c’è la possibilità che il sistema perda l’aggancio. Con un
rettangolo del Focus sovradimensionato questa possibilità diviene del tutto remota. Conclusa
la fase di cablaggio è possibile ridurre i contorni del Focus, in quanto, ora, l’algoritmo
monolitico funziona a pieno regime e se anche l’utente attua un movimento veloce il sistema
non perde l’aggancio con il riferimento. Se l’utente continua a rivolgere il viso allo schermo
l’algoritmo può ritrovare il riferimento grazie al rettangolo definito dal Dominio che identifica
il campo visivo.
In un elaboratore potente, questo artifizio può sembrare evitabile, infatti sono
relativamente poche le operazioni che vengono eluse. Non c’è grossa differenza tra
l’elaborare un’immagine completa o solo parte di essa. Tuttavia il fine del sistema è offrire
un’elaborazione ottimizzata e il più possibile veloce, quindi ogni metodo per processare meno
operazioni è ben accetto. Inoltre c’è la possibilità che l’ausilio venga eseguito su un Personal
94
Computer meno potente ed in questo caso ogni aiuto possibile può ripercuotersi sulle
prestazioni finali.
6.2 Le applicazioni A questo punto l’analisi si può spostare sulla discussione delle due applicazioni
implementate per testare le prestazioni del sistema. Entrambe hanno solamente un carattere
dimostrativo. Oltre a costituire una base di studio sulle potenzialità del sistema sono state
utilizzate da quest’ultimo per capire quali fossero i messaggi più convenienti per una
comunicazione adeguata. Grazie alla loro implementazione è stato possibile separare i compiti
svolti sui due fronti. Da un lato si è capito cosa dovesse fare il sistema e dall’altra parte si è
compreso quali fossero i doveri dell’applicazione indipendente.
Grazie al lavoro svolto si è definito l’insieme dei messaggi che sono stati discussi nel
capitolo precedente. Come si è avuto modo di vedere le operazioni svolte dal Supervisore
sono limitate all’invio dei messaggi e tutta l’elaborazione viene demandata all’applicazione.
Questo modello nel concepire la comunicazione fra Supervisore e applicazioni indipendenti è
dovuto a numerosi motivi. Prima di tutto, se parte dell’elaborazione fosse sotto il controllo del
Supervisore, gli sviluppatori di applicazioni indipendenti dovrebbero conoscere, oltre ai
messaggi scambiati, anche le operazioni svolte dal Supervisore sulle loro applicazioni. Questo
dissolverebbe una delle regole più importanti imposte dall’architettura del sistema e cioè la
netta separazione tra sistema di elaborazione delle immagini e applicazioni indipendenti.
Inoltre le ipotetiche operazioni svolte dal Supervisore sulle applicazioni potrebbero essere
valide per un certo tipo di applicazioni, mentre potrebbero rendere impossibile
l’implementazioni di altre. Infine agendo in questo modo gli sviluppatori hanno la possibilità
di implementare codice che può reagire in qualsiasi maniera ai messaggi che il Supervisore gli
spedisce.
6.2.1 Applicazione Uno: Addestramento
Tenendo ben presente lo studio fatto nel capitolo relativo al contesto, l’utente che si
appresta ad utilizzare il sistema è dotato di capacità limitate. Questa caratteristica amplifica
ancor di più il dovere, da parte del sistema, di offrire il maggior aiuto possibile all’utente.
Un’operazione importante non ancora discussa è la fase di apprendimento. Infatti, come per
qualsiasi utensile prodotto per l’uomo, anche in questo caso ci sarà una fase nel quale l’utente
dovrà prendere confidenza con il sistema. Solo grazie all’esperienza è possibile utilizzare
l’ausilio in maniera ottimizzata.
95
Questa applicazione è stata implementata al fine di servire l’operazione appena descritta.
Si dà la possibilità all’utente di imparare a gestire i movimenti del viso per assecondare e
prevenire le reazioni del sistema. Inoltre l’impostazione appresa grazie a questa applicazione
sarà utile in tutte le applicazioni che entreranno a far parte dell’ausilio in futuro. Tutto si
svolge come nel gioco del tiro a segno, nel quale si deve cercare di colpire l’area delimitata da
un rettangolo. Al fine di imparare a selezionare gli oggetti visualizzati nelle applicazioni si
deve fissare il rettangolo per alcuni istanti per indicare al sistema che un clic temporale è stato
eseguito. Solo se il clic è attinente all’area delimitata dal rettangolo è possibile procedere
all’intercettazione di un altro obiettivo.
La caratteristica che colpisce maggiormente è che, in un sistema appena creato, solo al
momento dell’utilizzo è possibile definire il comportamento da seguire per un buon utilizzo.
Grazie all’esperienza fatta con questa applicazione si è in grado di elencare alcuni
atteggiamenti che facilitano l’apprendimento sull’uso dell’ausilio.
Per prima cosa si deve pensare che la visualizzazione del punto fissato arriva con un certo
ritardo, modificabile grazie ai controlli descritti nel paragrafo precedente. A condizioni di luce
sufficiente, un’informazione più accurata è accompagnata da un ritardo maggiore
nell’elaborazione. Questo implica che i movimenti del viso dell’utente sono rappresentati sul
terminale video del Personal Computer con un ritardo dovuto all’elaborazione. Si consiglia,
per questo motivo di fare sempre movimenti fluidi, armonici e progressivi senza scatti
improvvisi.
Se, per esempio, si deve intercettare un rettangolo in alto a destra e il viso è rivolto verso il
basso a sinistra è possibile, muovere velocemente il viso verso la posizione in alto a destra.
Tuttavia tanto più ci si avvicina alla posizione del rettangolo tanto più i movimenti del viso
devono essere fluidi e contenuti. Si deve pensare al percorso che unisce i due punti e
percorrerlo con il movimento del volto, assecondando il punto che si vede sullo schermo,
lavorando in simbiosi con il sistema. Se si passa con un movimento repentino dal punto in
basso a sinistra a quello in alto a destra è possibile che si inneschino strane oscillazioni dovute
al ritardo attuato dall’elaborazione. In pratica si cerca di posizionare il riferimento all’interno
del rettangolo tramite movimenti veloci e a scatti che pregiudicano il funzionamento. Si vedrà
il punto che indica il riferimento oscillare sui contorni esterni del rettangolo senza poterlo
intercettare in modo preciso. E importante rallentare e compiere movimenti dolci e contenuti
non appena si arriva in prossimità dell’oggetto che si vuole selezionare, in questo caso il
rettangolo colorato.
96
Solo grazie all’esperienza l’utente sarà in grado di imparare ad usare il sistema
correttamente, tuttavia il tempo richiesto per questa fase non è estremamente lungo. Nel giro
di pochi minuti si può ottenere un ottimo feeling con l’ausilio e passare alle applicazioni più
complesse senza problemi.
6.2.2 Applicazione Due: Tastiera
L’implementazione di una applicazione indipendente più complessa rispetto alla
precedente, permette di analizzare altre caratteristiche importanti che possono essere rilevate
solo con una sperimentazione diretta.
Una proprietà al quale lo sviluppatore deve sempre porre grande attenzione è la capacità,
da parte dell’applicazione, di lavorare con qualsiasi condizione ambientale. Sia con
un’illuminazione ottima sia con una mediocre l’applicazione deve sempre fornire un servizio.
Non si può accettare il fatto che l’ausilio funzioni solo con condizioni ambientali ottime.
L’applicazione deve assecondare le prestazioni del sistema. Tanto più l’illuminazione risulta
mediocre, tanto più la possibilità di intercettare oggetti di dimensione ridotta si affievolisce.
Quindi è indispensabile tenere conto di questo fatto quando si implementano nuove
applicazioni indipendenti. È fondamentale attuare una certa flessibilità sulle dimensioni degli
oggetti selezionabili, al fine di rendere l’applicazione utile con qualsiasi condizione
ambientale. Ovviamente se le condizioni di luce sono talmente scarse da inibire addirittura il
funzionamento del sistema nulla può essere richiesto all’applicazione, semplicemente l’ausilio
non è utilizzabile.
L’applicazione discussa in questa sezione implementa una semplice tastiera sulla quale
l’utente può comporre delle frasi. Grazie al compito svolto da questa applicazione è possibile
analizzare il comportamento descritto precedentemente. La rappresentazione della tastiera non
è fissa ma l’utente, o meglio l’assistente, può scegliere diverse alternative per utilizzare al
meglio le prestazioni del sistema. Lo spazio riservato all’applicazione sullo schermo del
Personal Computer deve essere ottimizzato per contenere i pulsanti. Più caratteri possono
essere utilizzati per comporre le frasi, più piccole saranno le dimensioni di ogni pulsante che
rappresenta un carattere. L’applicazione permette di definire cinque tastiere differenti, ognuna
delle quali conta un diverso numero di pulsanti e quindi di caratteri. Si và da una tastiera che
contiene solo le lettere dell’alfabeto ad una tastiera che contiene tutti i possibili caratteri
reperibili su una tastiera collegata al Personal Computer. Se le prestazioni del sistema lo
permettono è possibile utilizzare la tastiera con più caratteri, in quanto l’utente è in grado di
selezionare i pulsanti, anche se la loro dimensione è ridotta. Tuttavia se le prestazioni sono
mediocri in quanto rispecchiano condizioni ambientali mediocri, l’applicazione può essere
97
ancora utilizzata selezionando una tastiera che contiene meno caratteri ma che è composta da
pulsanti di dimensioni maggiori. Il servizio minimo viene garantito anche se il sistema si
trova ad elaborare dati non buoni.
Questa è la caratteristica che differenzia un’applicazione scritta per essere collegata
all’ausilio da una normale applicazione Windows. Lo sviluppatore di applicazioni
indipendente deve prendere in considerazione questa peculiarità se vuole ottenere applicazioni
che rispondano in maniera versatile alle prestazioni del sistema e offrano anche in condizioni
limite un servizio minimo.
99
7 Sviluppi futuri
Tutto sembra semplice quando si conosce la strada che conduce nel luogo dove si vuole
andare, non esiste niente di più facile che seguire un itinerario che si è già percorso o si
compie il cammino in compagnia di qualcuno, che ha ben preciso in mente le strade da
percorrere. Purtroppo il compito si complica notevolmente se si ignora il modo di arrivare alla
meta. L’unica soluzione è partire e cercare in qualche modo di arrivare a destinazione,
lasciarsi andare e compiere il balzo nel vuoto che si apre davanti ai nostri piedi. Molte saranno
le strade possibili per arrivare a destinazione, indubbiamente esisteranno strade facili da
percorrere, scorciatoie oppure difficili valichi impraticabili. Tuttavia, in un modo o nell’altro,
si potranno ottenere dei risultati.
La soluzione proposta per questo ausilio non è altro che una fra le tante possibili.
L’informatica, d’altro canto, è caratterizzata dal fatto di offrire numerose soluzione per
qualsiasi problema. Come i percorsi per arrivare a destinazione le soluzioni potranno essere
facili, complesse oppure presentare delle scorciatoie che semplificano di molto
l’implementazione del sistema.
Il salto nel buio compiuto è visibile nel codice che implementa l’ausilio. Se lo si osserva
attentamente è facile notare che il percorso intrapreso non è lineare e preciso fino alla meta,
100
ma presenta itinerari appena accennati e non ulteriormente sviluppati che si discostano dal
cammino principale e si perdono nell’oscurità. Quest’ultimi rappresentano possibili soluzioni
che non sono state approfondite quando si sono trovate, sul percorso, due strade ugualmente
percorribili. Tuttavia ciò non vuol dire che la scelta scartata sia impraticabile o inutile ma
significa solamente che si sono fatte delle valutazioni su certi obiettivi da raggiungere a
discapito di altri. Questi sentieri appena accennati non sono stati eliminati completamente dal
sorgente perché rappresentano le porte dalle quali si potrà, in futuro, sviluppare nuove
potenzialità del sistema.
Gli obiettivi delineati all’inizio dell’analisi spingevano sulla ricerca di una soluzione, per
questo motivo non si sono studiate a fondo tutte le scelte possibili ma solo quelle che
promettevano dei risultati concreti. Tuttavia, a questo punto dello sviluppo, occorre
approfondire questi sentieri singolarmente e cercare di comprendere se possono portare ad un
miglioramento delle prestazioni del sistema.
7.1 Legami esterni Mai, come in questo caso, la frase “il fine giustifica i mezzi” è appropriata. Dimostrare la
possibilità di creare un ausilio che assecondasse tutti gli obiettivi descritti nei capitoli
precedenti vantava una priorità maggiore di qualsiasi obiettivo sull’ottimizzazione. Ora che
questo fine è stato raggiunto, è compito dello sviluppo mettere in primo piano
l’ottimizzazione.
Questo significa che, gli aiuti ricevuti dall’esterno, erano giustificati fintanto che il fine era
la creazione del sistema, tuttavia a questo punto dello sviluppo si deve pensare anche
all’ottimizzazione. Per questa ragione i legami che vincolano il sistema con l’esterno devono
essere recisi. Il sistema deve svilupparsi al punto di gestire al suo interno le operazioni che,
per ora, gli vengono fornite dalle librerie della Victor e dalla RamDisk implementata dalla
Cenatec.
Questo passo è imposto da svariate ragioni, prima fra tutte la semplificazione delle fasi di
installazione del sistema sul Personal Computer dell’utente. Non è ammissibile, infatti, che
l’utente, o meglio l’assistente, debba addossarsi un elevato numero di operazioni per rendere
operativo l’ausilio. In quanto già l’installazione del software relativo al funzionamento della
camera implica un notevole lavoro.
Un’altra ragione è puramente economica. Sia le librerie della Victor sia la RamDisk della
Cenatec vengono fornite a pagamento, quindi non è possibile addossare all’utente una spesa
che si aggiunge al costo della camera. Una soluzione di ripiego potrebbe essere quella di
101
utilizzare le versioni di valutazione gratuite reperibili in rete, tuttavia quest’ultime hanno una
scadenza che impone un periodico download.
Infine, un sistema che è autosufficiente rispecchia una soluzione più elegante rispetto ad un
sistema che per lavorare ha bisogno di aiuti esterni.
Per questi motivi, prima di sviluppare qualsiasi altra sezione del codice, è importante
rimuovere gli aiuti esterni, in quanto il loro compito ha perso d’importanza ed inoltre sono
diventati d’impaccio per il sistema stesso.
7.2 Struttura Come è già stato analizzato nel capitolo relativo all’architettura è importante creare una
base solida su cui costruire. Se le fondamenta sono salde anche la struttura, che su di esse
appoggerà, sarà robusta. Se il percorso fin qui fatto rappresenta solo una prima tappa di un
cammino molto più lungo e articolato, è necessario sviluppare e rafforzare ancor di più la
struttura che si trova alla base del sistema. Ciò non significa che il lavoro svolto fino a questo
punto sia da scartare ma, come per il resto del sistema, deve evolversi e migliorarsi. È
impensabile tralasciare lo sviluppo di questa parte del sistema per dedicarsi allo sviluppo della
struttura che su di essa poggia. L’architettura implementata a questo punto dello sviluppo è
più che ottima per gli obiettivi che si sono delineati all’inizio, tuttavia rappresenta solo il
modello da seguire nello sviluppo futuro.
Molto deve essere ancora fatto per rendere reale la soluzione teorica che è stata analizzata
nei capitoli precedenti. Per ora la divisione dei compiti tra oggetti differenti non è ancora
completa. Anche se concettualmente la separazione è evidente, purtroppo l’implementazione
non rispecchia a fondo questa caratteristica. Le operazioni svolte dai diversi oggetti sono
ancora legate dalle chiamate dirette di funzione delle varie procedure, il codice non è separato
come lo sono concettualmente gli oggetti. Lo stack unisce ancora le varie operazioni fatte dai
diversi oggetti. Quando, per esempio, il thread richiama le funzionalità del Supervisore,
quest’ultime vengono elaborate basandosi sullo stack del thread come in una normale
chiamata a funzione, inibendo la separazione che si vuole ottenere tra il codice del thread e
del Supervisore. Questo porta ad una struttura implementata abbastanza complessa che deve
essere semplificata se l’obiettivo e produrre un sistema più evoluto.
Una possibile soluzione potrebbe essere quella di utilizzare pesantemente le code di
messaggi che il Sistema Operativo Windows permette di implementare quando si definisce
una finestra. In pratica la separazione tra i vari oggetti utilizza le finestre come portale per la
comunicazione. Questo fa sì che anche a livello di codice le funzionalità racchiuse negli
102
oggetti vengano separate. Tuttavia questa non è altro che una congettura, una strada possibile
da percorrere, solo l’implementazione potrà verificare se è possibile intraprendere questo
cammino. Introducendo i messaggi e le rispettive code è importante testare se le operazioni
vengono ancora svolte in sequenza oppure si è obbligati ad introdurre una sorta di IPC per
regolare la comunicazione tra i vari oggetti.
Molteplici sono le soluzioni per superare questi problemi, tuttavia essi devono essere
superati per pensare di portare avanti il lavoro ed evolvere il sistema. La struttura che regola
ora il sistema è paragonabile alle solide fondamenta di una piccola casa, è impensabile
costruire su di esse un grattacielo.
7.3 Algoritmi Gli algoritmi, come è già stato detto, sono il cuore del sistema. Se supportati da una
struttura solida e ben regolamentata risulta possibile introdurre nuove potenzialità e, nello
stesso tempo, ottimizzare quelle già esistenti.
Ogni procedura descritta nei capitoli precedenti ha potenzialmente un ampio margine di
sviluppo. A questo stadio di maturazione si è cercato di trovare una implementazione che
permettesse di raggiungere gli obiettivi prefissati. Tuttavia non appena una soluzione per una
procedura si è rivelata valida, la ricerca si è spostata verso gli algoritmi successivi,
tralasciando lo sviluppo per conseguire le mete prestabilite. Il compito di migliorare
correttamente ogni algoritmo sarà affidato allo sviluppo futuro.
Per esempio, la prima procedura descritta nel capitolo dedicato all’implementazione può e
deve essere rivista per includere nuove potenzialità. Dallo studio attuato sul sistema fino ad
ora, è emerso che è restrittivo utilizzare una sola sfumatura per intercettare il riferimento fisso
sul volto dell’utente. In quanto, diverse angolazioni del volto rispetto alla fonte luminosa
enfatizzano diverse sfumature dello stesso colore. Il sistema dovrebbe valutare questa
caratteristica. Per far questo si dovrebbe fornire un’analisi più accurata sulla palette di colori
disponibile. Invece di studiare solo un’immagine si potrebbe stilare una casistica su più
catture ed indicare all’utente la scelta che si è rivelata più probante.
Anche la procedura che intercetta il movimento del riferimento non dovrebbe basare la sua
ricerca su una sola sfumatura ma attuare in parallelo l’intercettazione delle diverse sfumature
dello stesso colore. Verificando se durante la sessione di lavoro alcune di esse diventano più
efficaci di quella scelta all’inizio.
Addirittura la procedura che indica al sistema il punto fissato dall’utente sullo schermo del
Personal Computer potrebbe essere ottimizzata. Per ora la sua funzione si limita ad analizzare
103
i dati che ottiene dagli altri algoritmi ed indicare in coordinate assolute il punto. Tuttavia
nessuna compensazione viene attuata e l’elaborazione si basa su operazioni lineari. Questo
ostacola la libertà di scelta sulla posizione della camera nello spazio delineato intorno al
video. Le posizioni che introducono delle non linearità devono essere scartate. Inoltre
potrebbe essere possibile ottenere una sorta di previsione sul comportamento tenuto
dall’utente al fine di anticipare le sue scelte ed assecondare i suoi movimenti.
In conclusione i margini di sviluppo per ogni algoritmo sono enormi, tuttavia esiste una
regola importante che deve essere sempre rispettata. Le evoluzioni future del sistema devono
mantenere una coerenza nei messaggi che vengono inviati alle applicazioni indipendenti. È
impensabile che lo sviluppo più recente del sistema inibisca l’utilizzo delle applicazioni
scritte precedentemente per il solo fatto che i messaggi sono stati modificati. Se, in futuro, si
individueranno nuovi messaggi possibili, da inviare alle applicazioni, per accrescere le loro
potenzialità, il sistema dovrà comunque fornire un insieme di base che permette alle
applicazioni meno recenti di utilizzare ancora il sistema. Il pregio più importante che il
sistema possiede e che si sono divisi in maniera netta i compiti svolti dal sistema da quelli
svolti dalle applicazioni. Dal punto di vista dell’applicazione indipendente è ininfluente
l’elaborazione che sta dietro ad ogni messaggio, l’importante è che lo stesso messaggio venga
inviato da tutte le possibili versioni future del sistema.
7.4 Applicazioni Sullo sviluppo futuro delle applicazioni indipendenti poco si può prevedere, in quanto solo
l’estro e la fantasia degli sviluppatori è un sicuro limite alle loro prestazioni. Le applicazioni
non sono altro che programmi sviluppati per operare sotto i Sistemi Operativi della famiglia
Microsoft a cui vengono aggiunte delle potenzialità offerte dall’ausilio. Per questa ragione
sono imprevedibili le strade che possono essere percorse, in quanto qualsiasi problema può
essere risolto implementando una valida applicazione.
Pochi sono i miglioramenti che a questo punto dello sviluppo ha senso analizzare. Per
esempio, sfruttare le potenzialità che l’ausilio offre, non solo per le applicazioni ma anche per
il sistema stesso. La scelta dell’applicazione da attivare viene ora eseguita grazie al menu,
tuttavia questa operazione deve essere effettuata dall’assistente mentre potrebbe essere
positivo lasciare la scelta all’utente stesso. Per ottenere questo risultato, il sistema dovrebbe
visualizzare la finestra di un’applicazione, per così dire principale, dove vengono inserite
tutte le applicazioni, iscritte nel sistema, che l’utente può selezionare. Questo permetterebbe
104
all’utente di acquistare più autonomia e poter passare da una applicazione all’altra senza
l’aiuto dell’assistente.
Per attivare questa opzione è possibile intraprendere due strade differenti. Da un lato è
possibile implementare delle applicazioni indipendenti che comprendano la volontà
dell’utente di passare ad un’altra applicazione ed informino, di conseguenza, il sistema.
Dall’altro addossare al sistema stesso l’onere di intercettare le scelte dell’utente.
La prima soluzione non è percorribile perché infrange la regola base che stabilisce il
rapporto tra sistema e applicazioni indipendenti. Solo il sistema deve sapere quali sono le
applicazioni iscritte, mentre le applicazioni non devono e non possono comunicare con il
Supervisore ma solo elaborare i messaggi spediti da quest’ultimo.
La seconda soluzione, oltre ad essere l’unica percorribile, apre nuove strade nello sviluppo
non solo relativo alle applicazioni ma, come si è già detto, dell’intero sistema. Il sistema deve
elaborare parallelamente all’applicazione le scelte fatte dall’utente. Per ottenere questo è
possibile utilizzare una specie di banner sempre visibile, anche quando il sistema visualizza la
finestra dell’applicazione attiva. Invece di dimensionare la finestra dell’applicazione a pieno
schermo potrebbe essere utile sacrificare un po’ di spazio, per esempio in basso, per una
speciale finestra di controllo gestita direttamente dal sistema. Lo spazio offerto dallo schermo
del Personal Computer viene così condiviso sia dall’applicazione attiva, che ne occupa la
maggior parte, sia dal sistema stesso. L’algoritmo incaricato di decidere dove l’utente stia
fissando può filtrare l’informazione e inviare i messaggi all’applicazione solo se lo sguardo è
relativo allo spazio occupato dall’applicazione. Nell’altro caso è possibile elaborare
direttamente la posizione del riferimento per identificare i comandi che l’utente vuole
impartire al sistema stesso.
Il banner potrebbe implementare una serie di pulsanti di scelta rapida che permettono
all’utente di ritornare alla finestra principale dove è possibile riformulare la scelta
dell’applicazione che si vuole utilizzare. Oppure ancora, fornire la possibilità di modificare
direttamente i controlli descritti nel capitolo precedente senza l’aiuto dell’assistente. In questo
modo l’utente potrebbe gestire in prima persona tutte le potenzialità offerte dall’ausilio ed
essere in questo modo completamente indipendente per tutta la sessione di lavoro svolta con il
sistema.
Infine, lo sviluppo deve toccare anche la struttura che regola i rapporti tra sistema e
applicazioni indipendenti. Per ora sono molti i punti del sorgente che lo sviluppatore deve
manipolare per poter agganciare al sistema la sua creazione. Un miglioramento in questo
senso potrebbe ridurre al minimo queste zone di collegamento. Tuttavia ci si potrebbe
105
chiedere fino a che punto è possibile diminuire le zone del sorgente dove è possibile ancorare
le applicazioni indipendenti. La scelta del numero di punti di ancoraggio è un fattore talmente
importante che tutta la struttura può risentirne. Due sono le possibili soluzioni, da una parte si
continua a mantenere uno o più agganci, mentre dall’altra si eliminano completamente le zone
di ancoraggio.
La prima soluzione rispecchia un’evoluzione della struttura presente, in quanto devono
essere migliorate solamente le potenzialità offerte dal Supervisore. Rendere automatico
l’aggancio, agendo in una sola sezione del codice. Obbligare il Supervisore ad addossarsi tutte
le operazioni che devono essere fatte per permettere all’utente di poter scegliere ed utilizzare
l’applicazione che preferisce. La struttura in questo caso non subisce modifiche, le regole
definite nei capitoli precedenti restano invariate e continuano a valere.
Il sistema ha il pieno controllo delle applicazioni e in ogni istante conosce quale è attiva.
Tuttavia, solo una applicazione può essere attiva in un certo istante. Questo porta a
domandarsi se sia limitativo permettere all’utente di utilizzare solo un’applicazione alla volta.
La risposta può essere ricercata nella natura stessa dell’ausilio. Le applicazioni occupano, nel
loro funzionamento, tutto lo schermo del Personal Computer. Quindi è irrilevante se altre
applicazioni sono attive contemporaneamente in quanto solo una alla volta potrà occupare
tutto le spazio a disposizione. Se per alcune elaborazioni particolari, si dovranno utilizzare le
potenzialità di due distinte applicazioni è sempre possibile fonderle in una sola e utilizzare
due sottofinestre per simulare i diversi compiti. Sarà l’applicazione stessa ad elaborare ed
istradare i comandi alle due sottofinestre.
Dall’altra parte, eliminare totalmente l’iscrizione delle applicazioni indipendenti nel
sistema, permette di utilizzare più applicazioni attive nello stesso istante. I messaggi che
devono essere spediti alle applicazioni dovranno essere inoltrati al Sistema Operativo, il quale
avrà il compito di notificarli alle applicazioni indipendenti attive in quel momento. Questo
concetto rivoluziona totalmente la struttura del sistema. Si divide completamente il sistema
dalle applicazioni indipendenti, l’ausilio è composto solamente dalla parte che elabora le
immagini catturate dalla camera e inoltra i messaggi al Sistema Operativo. L’ausilio lavora,
per così dire, in background mentre le applicazioni lavorano in foreground o in primo piano
elaborando i messaggi che il Sistema Operativo gli inoltra. Questa tecnica tuttavia introduce
più svantaggi che vantaggi. Infatti diventa complessa la comunicazione tra Supervisore e
applicazioni in quanto devono essere utilizzate zone condivise di memoria. Anche i messaggi
non sono più leggeri da elaborare come nel caso precedente ma comportano una complessità
maggiore sia da parte del Supervisore sia da parte delle applicazioni. Inoltre anche il lavoro
106
degli sviluppatori di applicazioni diventa complesso in quanto non esiste modo di debaggare
in maniera semplice il sorgente scritto. Gli ambienti di sviluppo, per ora, non possono
analizzare più di un programma alla volta. Tuttavia separando in maniera completa il sistema
dalle applicazioni è possibile compilare in maniera indipendente il codice. Ogni volta che una
nuova applicazione viene prodotta non si è obbligati a ricompilare il codice relativo al sistema
ma la compilazione riguarda solamente il sorgente relativo all’applicazione.
7.5 Conclusioni Purtroppo nei libri che cercano di insegnare l’informatica e la programmazione si tralascia
un aspetto importante che deve caratterizzare il codice sorgente. Questo perché forse non è
semplice trattarlo oppure è troppo intuitivo e solo l’esperienza nella programmazione può
farlo intravedere. Sfortunatamente è difficile anche trovare un nome che possa definirlo,
quello che più si avvicina al concetto che si vuole analizzare forse è massa critica. Con il
termine massa critica, o meglio limite della massa critica, si intende il punto in cui non è più
possibile toccare il sorgente per poter aggiungere nuove potenzialità. Il sorgente è talmente
complesso (o disorganizzato) che ogni operazione fatta su di esso può compromettere il
funzionamento dell’intero sistema. Più il limite è basso, prima si raggiungerà la massa critica,
ciò significa che anche un programma descritto da poche decine di Kbyte di sorgente può
diventare impossibile da gestire e da sviluppare. Invece un sistema in cui il limite viene alzato
a dismisura ha ampi margini di sviluppo prima di raggiungere la massa critica.
Il concetto di massa critica è inoltre relativo al soggetto che si pone davanti al codice
sorgente. Per il creatore il livello sarà sempre più alto rispetto ad un estraneo che legge il
sorgente per la prima volta. Questo è dovuto al fatto che chi ha sviluppato dall’inizio il
sorgente ha ben preciso in mente quello che ha fatto, mentre l’estraneo non può immaginare
quello che è passato per la mente del creatore.
Il limite che più interessa è ha utilità a fini pratici può essere chiamato limite assoluto di
massa critica, questo limite si trova tra i due relativi descritti in precedenza. È più alto del
limite visto dall’estraneo in quanto quest’ultimo può studiare il sorgente e a mano a mano
capirlo sempre più in profondità. Mentre è più basso del limite visto dal creatore perché
quest’ultimo non tiene in considerazione la fase di apprendimento che l’estraneo deve fare sul
suo sorgente.
Più il sorgente è stato scritto con cura e con attenzione, più il limite assoluto si avvicinerà
al limite relativo del creatore, in quanto l’estraneo potrà capire, con lo studio, le tecniche usate
dal creatore. Più il codice sarà scritto male e disorganizzato più il limite assoluto si avvicinerà
107
a quello dell’estraneo, perché quest’ultimo non potrà capire, anche analizzandoli, i concetti
che ha utilizzato il creatore.
Il limite assoluto può essere modificato grazie alle operazioni svolte dallo sviluppatore.
Uno studio dettagliato sull’architettura, la scrittura del sorgente con tecniche e impostazioni
classiche, l’introduzione dei commenti e l’utilizzo di algoritmi semplici possono innalzare i
limiti relativi e di conseguenza l’assoluto, producendo un sorgente che allontana l’ombra della
massa critica. Al contrario, una superficialità diffusa nella creazione del sorgente, l’utilizzo di
numerose chiamate a funzione, obiettivi annebbiati e non ben delineati, una scrittura che non
segue le impostazioni classiche, una mancanza di commenti e una esasperazione
sull’ottimizzazione del sistema porta velocemente il sorgente alla massa critica, abbassando di
conseguenza tutti i limiti.
Questi concetti hanno accompagnato tutto il lavoro fin qui svolto, ora tocca ai futuri
sviluppatori tenere bene a mente queste caratteristiche se si vuole produrre un sistema che
abbia un futuro. Infatti il sistema, oltre ad offrire il suo supporto all’utente, deve promettere
ampi margini di miglioramento che solo un limite assoluto della massa critica estremamente
elevato possono assicurare.
Quindi l’unico consiglio che si può dare ai futuri sviluppatori è di evitare le fughe in
avanti. Il compito principale che deve essere sempre tenuto in considerazione consiste nel far
crescere in maniera omogenea l’intera struttura. Pensando nel contempo che il lavoro svolto
sarà la base di partenza per un altro sviluppatore, esclusi ovviamente coloro che sviluppano
nuove applicazioni indipendenti.
109
8 Conclusioni
Nulla è più possibile aggiungere. L’utilità dell’ausilio è in mano a coloro i quali avranno,
in futuro, il potere di svilupparlo e donarlo a chi ne ha più bisogno. Il lavoro fin qui fatto è
solo il primo piccolo passo verso un aiuto concreto alle persone disabili a cui questo sistema
si rivolge.
Gli obiettivi posti all’inizio dovrebbero essere stati raggiunti. L’ausilio cercava di dare un
aiuto alle persone con gravi problemi motori e per questo il sistema permette di selezionare
oggetti sul video del Personal Computer con il solo movimento del volto. L’ausilio doveva
utilizzare tecnologie economiche e per questo motivo il sistema utilizza solamente una
periferica di acquisizione video estremamente modesta come può esserlo una WebCam. Infine
si è cercato di rendere la sessione di lavoro il più confortevole possibile all’utente che deve
solo indossare un piccolo oggetto che permette al sistema di intercettare il movimento.
Ovviamente ulteriori miglioramenti saranno possibili anche grazie alla collaborazione
diretta di persone disabili che, con le loro sensazioni, potranno dirigere il lavoro futuro.
1
Appendice A Elementi principali
Main #include <windows.h>#include <process.h>#include "resource.h"
// Prima parte delle inclusioni private:
#include "Dichiarazioni.h"#include "Messaggi.h"#include "Supervisore.h"#include "Cattura.h"#include "Intercetta.h"#include "Palette.h"#include "Decisore.h"#include "Menu.h"#include "Errore.h"#include "Finestre.h"#include "MenuFinestre.h"#include "MenuCattura.h"#include "MenuIntercetta.h"#include "MenuPosiziona.h"#include "MenuDemo.h"#include "ProprietaDlg.h"
TCHAR szAppName[] = TEXT ("Eye"),szAppImmagineFrontale[] = TEXT ("ImmagineFrontale"),szAppImmagineLaterale[] = TEXT ("ImmagineLaterale"),szAppPaletteFrontale[] = TEXT ("PaletteFrontale"),szAppPaletteLaterale[] = TEXT ("PaletteLaterale"),szAppWebUno[] = TEXT ("WebUno"),szAppWebDue[] = TEXT ("WebDue"),szAppControlli[] = TEXT ("Controlli"),szAppPosiziona[] = TEXT ("Posiziona");
// Nome delle Applicazioni:
TCHAR szAppDemoUno[] = TEXT ("DemoUno"),szAppDemoDue[] = TEXT ("DemoDue"),szAppDemoTre[] = TEXT ("DemoTre");//...
Supervisore Angelo;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{// Handler per le Finestre di Eye:
HWND FinestraPrincipale,FinestraImmagineFrontale,FinestraImmagineLaterale,FinestraPaletteFrontale,FinestraPaletteLaterale,FinestraWebUno,FinestraWebDue,FinestraControlli,
// Handler per le finestre di Applicazione:HWND FinestraDemoUno,
FinestraDemoDue,FinestraDemoTre;// ...
MSG msg;WNDCLASS wndVirtuale;HMENU MenuPrincipale;Cattura CameraUno,
CameraDue;Intercetta MagoFrontale,MagoLaterale;Palette TavolozzaFrontale,TavolozzaLaterale;Decisore Oz;GestoreMenu Menu;GestoreErrore Errore;
2
GestoreFinestre Finestre;// Parametri standard per tutte le classi delle finestre
wndVirtuale.style = CS_HREDRAW | CS_VREDRAW;wndVirtuale.cbClsExtra = 0;wndVirtuale.cbWndExtra = 0;wndVirtuale.hInstance = hInstance;wndVirtuale.lpszMenuName = NULL;wndVirtuale.hCursor = LoadCursor (NULL, IDC_ARROW);wndVirtuale.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
// Parametri per definire la classe della Finestra Principale e registrazionewndVirtuale.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICONA_PRINCIPALE));wndVirtuale.lpfnWndProc = WndProc;wndVirtuale.lpszClassName = szAppName;if (!RegisterClass (&wndVirtuale)){
MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR);
return 0 ;}
// Parametri per definire le Finestre Secondarie:// Parametri per definire la classe della Finestra ImmagineFrontale e registrazione
wndVirtuale.hIcon = NULL; // diventa parametro standardwndVirtuale.lpfnWndProc = ImmagineFrontaleProc;wndVirtuale.lpszClassName = szAppImmagineFrontale;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra ImmagineLaterale e registrazionewndVirtuale.lpfnWndProc = ImmagineLateraleProc;wndVirtuale.lpszClassName = szAppImmagineLaterale;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra PaletteFrontale e registrazionewndVirtuale.lpfnWndProc = PaletteFrontaleProc;wndVirtuale.lpszClassName = szAppPaletteFrontale;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra PaletteLaterale e registrazionewndVirtuale.lpfnWndProc = PaletteLateraleProc;wndVirtuale.lpszClassName = szAppPaletteLaterale;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra Web Uno e registrazionewndVirtuale.lpfnWndProc = WebUnoProc;wndVirtuale.lpszClassName = szAppWebUno;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra Web Due e registrazionewndVirtuale.lpfnWndProc = WebDueProc;wndVirtuale.lpszClassName = szAppWebDue;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra Controlli e registrazionewndVirtuale.lpfnWndProc = ControlliProc;wndVirtuale.lpszClassName = szAppControlli;RegisterClass(&wndVirtuale);
// Parametri per definire la classe della Finestra Posiziona e registrazionewndVirtuale.lpfnWndProc = PosizionaProc;wndVirtuale.lpszClassName = szAppPosiziona;RegisterClass(&wndVirtuale);
// Parametri per definire le Finestre di Applicazione:// Demo Uno:
wndVirtuale.lpfnWndProc = DemoUnoProc;wndVirtuale.lpszClassName = szAppDemoUno;RegisterClass(&wndVirtuale);
// Demo Due:wndVirtuale.lpfnWndProc = DemoDueProc;wndVirtuale.lpszClassName = szAppDemoDue;RegisterClass(&wndVirtuale);
// Demo Tre:wndVirtuale.lpfnWndProc = DemoTreProc;wndVirtuale.lpszClassName = szAppDemoTre;RegisterClass(&wndVirtuale);
// Successive Demo...
// Creazione dell'aggancio al Menù PrincipaleMenuPrincipale = LoadMenu(hInstance,MAKEINTRESOURCE (IDR_MENUPRINCIPALE)) ;
// Creazione della Finestra PrincipaleFinestraPrincipale = CreateWindow (szAppName, // nome della classe
TEXT ("Eye_6_1"), // titoloWS_OVERLAPPEDWINDOW, // stile della finestra150, // posizione iniziale x50, // posizione iniziale y700, // larghezza650, // lunghezzaNULL, // finestra genitoreMenuPrincipale, // menuhInstance, // instanza del programmaNULL) ; // parametri di creazione
3
// Settaggio dell'Angelo e delle sottoFinestre attiveAngelo.Set(FinestraPrincipale, // Finestra Principale
MenuPrincipale, // Menu Principale(char*) szAppName, // Nome della Classe&CameraUno,&CameraDue, // Oggetti Cattura&MagoFrontale,&MagoLaterale, // Oggetti Intercetta&TavolozzaFrontale,&TavolozzaLaterale, // Oggetti Palette&Oz, // Oggetto Decisore&Menu, // Oggetto GestoreMenu&Errore, // Oggetto GestoreErrore&Finestre); // Oggetto GestoreFinestre
// Creazione delle Finestre Secondarie// Finestra Immagine Frontale:
FinestraImmagineFrontale = CreateWindow(szAppImmagineFrontale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE,FinestraImmagineFrontale);// Finestra Immagine Laterale:
FinestraImmagineLaterale = CreateWindow(szAppImmagineLaterale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE,FinestraImmagineLaterale);// Finestra Palette Frontale:
FinestraPaletteFrontale = CreateWindow(szAppPaletteFrontale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE,FinestraPaletteFrontale);// Finestra Palette Laterale:
FinestraPaletteLaterale = CreateWindow(szAppPaletteLaterale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_PALETTE_LATERALE,FinestraPaletteLaterale);// Finestra Web Uno:
FinestraWebUno = CreateWindow(szAppWebUno,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_WEB_UNO,FinestraWebUno);// Finestra Web Due:
FinestraWebDue = CreateWindow(szAppWebDue,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_WEB_DUE,FinestraWebDue);// Finestra Controlli:
FinestraControlli = CreateWindow(szAppControlli,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_CONTROLLI,FinestraControlli);// Finestra Posiziona:
FinestraPosiziona = CreateWindow(szAppPosiziona,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreSecondarie(FINESTRA_POSIZIONA,FinestraPosiziona);
// Iscrizione nell'Angelo delle Applicazioni:// Demo Uno:
FinestraDemoUno = CreateWindow(szAppDemoUno,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO,FinestraDemoUno,true);// Demo Due:
FinestraDemoDue = CreateWindow(szAppDemoDue,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE,FinestraDemoDue,false);// Demo Tre:
FinestraDemoTre = CreateWindow(szAppDemoTre,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);
Angelo.SetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE,FinestraDemoTre,false);// Successive Demo...
// Visualizzazione e creazione coda dei messaggi per la Finestra PrincipaleShowWindow(FinestraPrincipale,iCmdShow);UpdateWindow(FinestraPrincipale);
while (GetMessage(&msg,NULL,0,0)){
TranslateMessage(&msg);DispatchMessage(&msg);
}return msg.wParam ;
}
4
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
static HINSTANCE hInstance;HMENU hMenu;int cxClient,cyClient;
switch (message){case WM_CREATE:
hInstance = ((LPCREATESTRUCT) lParam)->hInstance;Angelo.SethInstance(hInstance);return 0;
case WM_SIZE:cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);
// Gestione delle dimensioni delle Finestre Secondarie:
if(Angelo.VisibileSecondarie(FINESTRA_POSIZIONA))MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_POSIZIONA),
0,0,cxClient,cyClient,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_POSIZIONA),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),0,50,cxClient/2,(cyClient/2)-50,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_IMMAGINE_LATERALE))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),cxClient/2,50,cxClient/2,(cyClient/2)-50,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_PALETTE_FRONTALE))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),0,0,cxClient/2,50,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_PALETTE_LATERALE))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),cxClient/2,0,cxClient/2,50,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_CONTROLLI))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_CONTROLLI),0,cyClient/2,cxClient,100,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_CONTROLLI),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_WEB_UNO))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_UNO),0,(cyClient/2)+100,cxClient/2,(cyClient/2)-100,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_UNO),
0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_WEB_DUE))
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_DUE),cxClient/2,(cyClient/2)+100,cxClient/2,(cyClient/2)-100,true);
elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_DUE),
0,0,0,0,true);
// Gestione delle dimensioi delle Finestre di Applicazione:
5
if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO))MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO),
0,0,cxClient,cyClient,true);
elseMoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO),
0,0,0,0,true);if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE))
MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE),0,0,cxClient,cyClient,true);
elseMoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE),
0,0,0,0,true);if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE))
MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE),0,0,cxClient,cyClient,true);
elseMoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE),
0,0,0,0,true);// ...return 0;
case WM_COMMAND:hMenu = GetMenu (hwnd) ;
switch (LOWORD (wParam)){case ID_FINESTRE_FINESTRAIMMAGINEFRONTALE:case ID_FINESTRE_FINESTRAIMMAGINELATERALE:case ID_FINESTRE_FINESTRAPALETTEFRONTALE:case ID_FINESTRE_FINESTRAPALETTELATERALE:case ID_FINESTRE_FINESTRACONTROLLI:case ID_FINESTRE_FINESTRAWEBUNO:case ID_FINESTRE_FINESTRAWEBDUE:case ID_FINESTRE_FINESTRAPOSIZIONA:
MenuFinestre(wParam,&Angelo);return 0;
case ID_CATTURA_AGGANCIO:case ID_CATTURA_FINESTRA_FORMATO:case ID_CATTURA_FINESTRA_SORGENTE:case ID_CATTURA_FOTO:case ID_CATTURA_RILASCIO:case ID_CATTURA_SETTAGGI_OVERLAY:case ID_CATTURA_SETTAGGI_INFORMAZIONI:case ID_CATTURA_ONOFF_2WEB:case ID_CATTURA_TARGET_FILE:case ID_CATTURA_PROPRIETA_PROPRIETAUNO:case ID_CATTURA_PROPRIETA_PROPRIETADUE:
MenuAggancio(wParam,&Angelo);return 0;
case ID_INTERCETTA_PALETTE_FRONTALE:case ID_INTERCETTA_PALETTE_LATERALE:case ID_INTERCETTA_VISUALIZZA_FRONTALE:case ID_INTERCETTA_START_FRONTALE:case ID_INTERCETTA_STOP_FRONTALE:case ID_INTERCETTA_VISUALIZZA_LATERALE:case ID_INTERCETTA_START_LATERALE:case ID_INTERCETTA_STOP_LATERALE:case ID_INTERCETTA_VISUALIZZA_FRONTALE_II:case ID_INTERCETTA_START_FRONTALE_II:case ID_INTERCETTA_STOP_FRONTALE_II:
MenuIntercetta(wParam,&Angelo);return 0 ;
case ID_POSIZIONA_AUTOMATICO:case ID_POSIZIONA_INTERROMPI:case ID_POSIZIONA_MANUALE:case ID_POSIZIONA_STOP:case ID_POSIZIONA_OK:case ID_POSIZIONA_RITORNA:
MenuPosiziona(wParam,&Angelo);return 0;
case ID_APPLICAZIONI_DEMOUNO:case ID_APPLICAZIONI_DEMODUE:case ID_APPLICAZIONI_DEMOTRE:// ...
MenuDemo(wParam,&Angelo);return 0;
}case WM_DESTROY:
6
PostQuitMessage (0) ;return 0 ;
}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// Seconda parte delle inclusioni private:#include "ThreadIntercettaFrontale.h"#include "ThreadIntercettaLaterale.h"#include "ThreadIntercettaII.h"#include "ThreadPosiziona.h"#include "FinestreCamereProc.h"#include "PaletteFrontaleProc.h"#include "PaletteLateraleProc.h"#include "ImmagineFrontaleProc.h"#include "ImmagineLateraleProc.h"#include "ControlliProc.h"#include "PosizionaProc.h"
// Inclusioni per le applicazioni:
#include "DemoUnoProc.h"#include "DemoDueProc.h"#include "DemoTreProc.h"//...
Dichiarazioni #include <vfw.h>#include "Vicdefs.h"
// Dichiarazione delle WinProc per Eye:
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK WebUnoProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK WebDueProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK PaletteFrontaleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK PaletteLateraleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK ImmagineFrontaleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK ImmagineLateraleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK ControlliProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK PosizionaProc (HWND,UINT,WPARAM,LPARAM);BOOL CALLBACK ProprietaDlgProc (HWND,UINT,WPARAM,LPARAM);
// Dichiarazioni delle WinProc per le Applicazioni:
LRESULT CALLBACK DemoUnoProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK DemoDueProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK DemoTreProc (HWND,UINT,WPARAM,LPARAM);//...
// Dichiarazione delle Classi
class Cattura;class Intercetta;class Supervisore;class GestoreMenu;class GestoreErrore;class GestoreFinestre;class Palette;class Decisore;
// Dichiarazioni delle Costanti
short SLEEP_THREAD_FRONTALE = 10;short SLEEP_THREAD_LATERALE = 10;short NUMERO_AQUISIZIONI = 10;short NUMERO_AQUISIZIONI_AREA_CLICK = 4;short BORDO_CLICK = 2;short BORDO_FOCUS = 20;
const short BORDO_DOMINIO = 10;const short FOCUS_EYE = 52; // deve essere un multiplo di 4const short MASSIMO_LESS = 5;const short LIMITE_BERSAGLI_MINIMO = 0;const short LUMINOSITA = 70;const short NUMERO_AQUISIZIONI_PROGRESSO = 6;
// Dichiarazioni delle Strutture
typedef struct CELLA_COLORE_LS{
7
BYTE Blu,Verde,Rosso;UINT TipoColore;struct CELLA_COLORE_LS *Precedente,*Successivo;
} CELLA_COLORE_LS;typedef struct RETTANGOLI{
RECT Zona;RECT Focus;RECT Dominio;RECT Lento;RECT FocusEye;
} RETTANGOLI;typedef struct CELLA_POSIZIONE{
RECT Posizione;UINT Stato;struct CELLA_POSIZIONE *Successivo,*Precedente;
} CELLA_POSIZIONE;typedef struct{
BYTE Blu,Verde,Rosso;} COLORE;typedef struct{
short BV,VR,RB;} SCOSTAMENTO;typedef struct{
UINT Camera;int ImageWidth,
ImageHeight;char* NomeDriver;bool LiveWindow,
OverlayWindow,Scale,UsingDefaultPalette,AudioHardware,CapFileExists,CapturingNow,HasOverlay,HasDlgVideoSource,HasDlgVideoFormat,HasDlgVideoDisplay,CaptureInitialized,DriverSuppliesPalettes;
} PROPRIETA;typedef struct{
UINT Tipo;HWND hwnd;Supervisore* Angelo;Intercetta* Mago;Decisore* Oz;imgdes* Immagine;imgdes* ImmagineEye;RETTANGOLI* Rettangoli;UINT Stato;bool Ucciso;
} PARAMS, *PPARAMS;typedef struct{
HWND hwnd;Supervisore* Angelo;UINT Stato;bool Ucciso;
} PARAMS_POSIZIONA, *PPARAMS_POSIZIONA;typedef struct{
bool DueWeb;bool Segnale;bool Aggancio;RECT Dimensioni;
} CUBE,*PCUBE;typedef struct{
float X,Y;} PUNTO;
// Classi
class Supervisore // Angelo{public:
8
inline Supervisore() {}void Set(HWND,
HMENU,char*,Cattura*,Cattura*,Intercetta*,Intercetta*,Palette*,Palette*,Decisore*,GestoreMenu*,GestoreErrore*,GestoreFinestre*);
void SethInstance(HINSTANCE);bool GetDueWeb();
// Setto l'handler delle Finestre Secondarie e d'Applicazione:
void SetFinestreSecondarie(UINT,HWND);void SetFinestreApplicazione(UINT,HWND,bool);
// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:
HWND GetFinestreSecondarie(UINT);HWND GetFinestreApplicazione(UINT);
// Controllo se sono visibili le Finestre Secondarie e d'Applicazione:
bool VisibileSecondarie(UINT);bool VisibileApplicazione(UINT);
// Funzione che definisce l'Applicazione prescelta:
void Prescelta(UINT);
// Funzioni che gestiscono i messaggi:
void Messaggio(UINT);void Messaggio(UINT,UINT);void Messaggio(UINT,UINT,UINT);
// Funzione che permette l'handshake tra i Thread:
void HandShake(UINT,UINT,UINT);
public:CUBE Cube;
private:void Scossa();
private:HWND FinestraDlgSettaggi;
HINSTANCE hInstance;Cattura *CameraUno,
*CameraDue;Intercetta *MagoFrontale,
*MagoLaterale;Palette *TavolozzaFrontale,
*TavolozzaLaterale;Decisore *Oz;GestoreMenu *Menu;GestoreErrore *Errore;GestoreFinestre *Finestre;static char NomeFileUno[20],
NomeFileDue[20];};
class GestoreMenu // Menu{public:
void Set(HMENU,PCUBE,GestoreFinestre*);void Messaggio(UINT);
private:HMENU MenuPrincipale;GestoreFinestre *Finestre;PCUBE Cube; // devono essere solo letti
};
class GestoreErrore // Errore{public:
void Set(HWND,char*);void Messaggio(UINT);
private:void SetMessageBox(char*);HWND FinestraPrincipale;
9
char* szAppName;};
class GestoreFinestre // Finestre{public:
// Settaggio dell'Oggetto:
void Set(HWND);
// Setto l'handler delle Finestre Secondarie e d'Applicazione:
void SetFinestreSecondarie(UINT,HWND);void SetFinestreApplicazione(UINT,HWND,bool);
// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:
HWND GetFinestreSecondarie(UINT);HWND GetFinestreApplicazione(UINT);HWND GetFinestreApplicazione();
// Verifico la visibilità delle Finestre Secondarie e d'Applicazione:
bool GetVisibileSecondarie(UINT);bool GetVisibileApplicazione(UINT);
// Setto la Visibilità delle Finestre Secondarie:
void SetVisibileSecondarie(UINT,bool);
// Setto l'Applicazione prescelta:
void SetPresceltaApplicazione(UINT);
// Verifico se l'Applicazione è prescelta:
bool GetPresceltaApplicazione(UINT);
// Gestore dei Messaggi:
void Messaggio(UINT);
private:typedef struct CELLA_FINESTRA{
UINT Tipo;HWND hwndFinestra;bool Visibile;bool Ripristino;struct CELLA_FINESTRA *Successivo;
};typedef struct CELLA_APPLICAZIONE{
UINT Tipo;HWND hwndApplicazione;bool Prescelta;bool Visibile;struct CELLA_APPLICAZIONE *Successivo;
};private:
HWND FinestraPrincipale;struct CELLA_FINESTRA *Testa,*Coda,*Indice,*Temp;struct CELLA_APPLICAZIONE *TestaLA,*CodaLA,*IndiceLA,*TempLA;
};class Cattura // CameraUno,CameraDue{public:
inline Cattura() {}bool Disconnetti();UINT Set(HWND,UINT,char*,int,int,int,int);UINT GetFormato();UINT GetSorgente();
// bool GetInformazioni();bool GetFoto();bool GetFile();void GetSettaggi(PROPRIETA*);HWND GetFinestraCattura();
// UINT OverLay();private:
HWND FinestraPadre,FinestraCattura;
CAPTUREPARMS ParametriCattura;
10
CAPDRIVERCAPS CapacitaDriver;CAPSTATUS StatoCattura;char cNomeDriver[80];int x,y,larghezza,lunghezza;UINT Indice;char Nome[80];char Versione[80];char* NomeFile;int Width;
};
class Intercetta // Mago{public:
inline Intercetta() {}UINT GetPosizioneI();void GetPosizioneEye();bool GetPosizioneII();imgdes* GetImmagine();imgdes* GetImmagineEye();bool GetAttivo();void SetAttivo(bool);UINT Set(Cattura*,char*);RETTANGOLI* SetColoreAttivo(UINT,RECT*);RETTANGOLI* GetRettangoli();CELLA_POSIZIONE* GetPosizioni();void Lock();void UnLock(UINT);UINT GetProgresso();void Free();
public:imgdes Immagine;imgdes ImmagineEye;RETTANGOLI Rettangoli;CELLA_POSIZIONE *Posizioni;
private:typedef struct LIMITI_COLORE{
bool ColoreAttivo;bool ColoreTrovato;UINT TipoColore;RECT Zona;RECT Dominio;RECT Focus;RECT Lento;RECT FocusEye;RECT ZonaEye;short RossoMin,RossoMax,
VerdeMin,VerdeMax,BluMin,BluMax;
};typedef struct HIT{
int Bersagli;RECT Zona;RECT ZonaEye;
};typedef struct PROGRESSO{
RECT Lento;};
private:bool TrovaColoreFocus();bool TrovaColoreDominio();void ConfrontaColore(COLORE,int,int);void Dominio();void Focus();UINT SetLento();
private:bool Attivo;bool DominioAttivo;Cattura* Camera;HWND FinestraSorgente;char* NomeFile;struct LIMITI_COLORE Colore;struct HIT Hit[100];struct PROGRESSO Progresso[NUMERO_AQUISIZIONI_PROGRESSO];short Indice;short IndiceProgresso;UINT StatoCattura;UINT StatoPosiziona;UINT StatoProgresso;CELLA_POSIZIONE *Testa,*Coda,*Temp;
11
int Width,Height,BitCount;};
class Palette // Tavolozza{public:
inline Palette() {}void Set();void SetAttivo(bool);bool GetAttivo();void Pulisci();void Colora(imgdes*);CELLA_COLORE_LS* GetListaColore();RECT* GetZonaColore(UINT);
public:struct CELLA_COLORE_LS *TestaLS,*CodaLS,*IndiceLS,*TempLS; // Lista Semplice
private:typedef struct CELLA_COLORE_LC{
COLORE Colore;SCOSTAMENTO Scostamento;UINT TipoColore;RECT Zona;int Bersagli;int Posizione;struct CELLA_COLORE_LC *Precedente,*Successivo;
};struct CELLA_COLORE_LC *TestaLC,*IndiceLC,*CodaLC,*TempLC; // Lista Complessaint Elementi;int X,Y;bool Attivo;
private:bool Trovato(CELLA_COLORE_LC*);void In(COLORE);UINT TipoColore(CELLA_COLORE_LC*);
};
class Decisore // Oz{public:
inline Decisore() {}void Set(RETTANGOLI*,CELLA_POSIZIONE*,int,int);void SetAttivo(bool);void SetDimensioniFinestra(int,int);bool GetAttivo();UINT GetDecisione();void Pulisci();PUNTO *GetPunto();
public:PUNTO Punto;
private:typedef struct SCOSTAMENTO{
short x,y;} SCOSTAMENTO;typedef struct CELLA_LINEA{
short NumeroLinea;SCOSTAMENTO *Linea;struct CELLA_LINEA *Precedente,*Successivo;
};typedef struct CLICK{
RECT Regione;short Attinenze;
};struct CELLA_LINEA *Testa,*Coda,*Indice,*Temp;struct CLICK Click;RETTANGOLI *Rettangoli;int NumeroDiLinee,NumeroDiColonne;short Contatore;float X,Y;SCOSTAMENTO *Scostamento;bool Attivo;
private:SCOSTAMENTO* GetScostamento(long,long);
};
Messaggi // Identificativi
12
#define AN_WEB_FRONTALE 1#define AN_WEB_LATERALE 2#define AN_THREAD_POSIZIONA 3#define AN_THREAD_INTERCETTA 4
#define STATO_CATTURA_INIZIO 11#define STATO_CATTURA_VERDE 12#define STATO_CATTURA_GIALLO 13#define STATO_CATTURA_ROSSO 14
#define STATO_ZERO 100#define STATO_UNO 101#define STATO_DUE 102#define STATO_TRE 103#define STATO_QUATTRO 104#define STATO_CINQUE 105#define STATO_SEI 106#define STATO_SETTE 107#define STATO_OTTO 108#define STATO_NOVE 109#define STATO_FINE 110#define STATO_MANUALE 111#define STATO_LAVORO 112
#define STATO_PROGRESSO_INIZIO 200#define STATO_PROGRESSO_UNO 201#define STATO_PROGRESSO_DUE 202#define STATO_PROGRESSO_TRE 203#define STATO_PROGRESSO_FINE 204
#define FINESTRE_ATTIVE 300#define FINESTRA_PRINCIPALE 301#define FINESTRA_IMMAGINE_FRONTALE 302#define FINESTRA_IMMAGINE_LATERALE 303#define FINESTRA_PALETTE_FRONTALE 304#define FINESTRA_PALETTE_LATERALE 305#define FINESTRA_WEB_UNO 306#define FINESTRA_WEB_DUE 307#define FINESTRA_CONTROLLI 308#define FINESTRA_POSIZIONA 309#define FINESTRA_APPLICAZIONE 310#define FINESTRE_SECONDARIE 311
#define FINESTRA_APPLICAZIONE_DEMO_UNO 400#define FINESTRA_APPLICAZIONE_DEMO_DUE 401#define FINESTRA_APPLICAZIONE_DEMO_TRE 402//...
// I messaggi successivi sono usati nelle WinProc e possono creare conflitto ,se// li definisco con una mia metrica privata con i messaggi di Windows ,usando// WM_USER sono sicuro che essi sono unici
// Messaggi per le WinProc
#define WM_START_THREAD_I (WM_USER + 1) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_STOP_THREAD_I (WM_USER + 2) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_SINGOLA_IMMAGINE_I (WM_USER + 3) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_START_THREAD_II (WM_USER + 4) // da Supervisore per ImmagineFrontaleProc#define WM_STOP_THREAD_II (WM_USER + 5) // da Supervisore per ImmagineFrontaleProc#define WM_SINGOLA_IMMAGINE_II(WM_USER + 6) // da Supervisore per ImmagineFrontaleProc#define WM_TAVOLOZZA_PRONTA (WM_USER + 7) // da Supervisore per PaletteProc#define WM_COLORE_PRONTO (WM_USER + 8) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_TAVOLOZZA_ATTIVA (WM_USER + 9) // da Supervisore per PaletteProc#define WM_STATO_CATTURA_MUTATO_FRONTALE (WM_USER + 10) // da Supervisore per SemaforiProc#define WM_STATO_CATTURA_MUTATO_LATERALE (WM_USER + 11) // da Supervisore per SemaforiProc#define WM_STATO_CATTURA_MUTATO (WM_USER + 12) // da Supervisore per ApplicazioneProc#define WM_START_POSIZIONA (WM_USER + 13) // da Supervisore per PosizionaProc#define WM_STATO_MUTATO (WM_USER + 14) // da Supervisore per ImmagineFrontaleProc#define WM_STOP_POSIZIONA (WM_USER + 15) // da Supervisore per PosizionaProc#define WM_STATO_PROGRESSO_MUTATO (WM_USER + 16) // da Supervisore per PosizionaProc#define WM_POSIZIONI_PRONTE (WM_USER + 17) // da Supervisore per ImmagineFrontale#define WM_CREATE_PUNTO (WM_USER + 18) // da Supervisore per ApplicazioneProc#define WM_CLICK (WM_USER + 19) // da Supervisore per ApplicazioneProc#define WM_NON_VALIDO (WM_USER + 20) // da Supervisore per ApplicazioneProc
// I messaggi successivi sono usati nelle mie procedure dunque non creano conflitto// e posso definirli con una mia metrica
// Messaggi di operazioni avvenute correttamente
#define AN_OK_AGGANCIO 1001 // da Supervisore per GestoreMenu#define AN_OK_RILASCIO 1002 // da Supervisore per GestoreMenu#define AN_OK_START_FRONTALE 1003 // da Supervisore per GestoreMenu
13
#define AN_OK_STOP_FRONTALE 1004 // da Supervisore per GestoreMenu#define AN_OK_START_LATERALE 1005 // da Supervisore per GestoreMenu#define AN_OK_STOP_LATERALE 1006 // da Supervisore per GestoreMenu#define AN_OK_START_FRONTALE_II 1007 // da Supervisore per GestoreMenu#define AN_OK_STOP_FRONTALE_II 1008 // da Supervisore per GestoreMenu#define AN_OK_DUE_WEB_NO 1009 // da Supervisore per GestoreMenu#define AN_OK_DUE_WEB_SI 1010 // da Supervisore per GestoreMenu#define AN_OK_VISUALIZZA_FRONTALE 1025 // da Supervisore per GestoreMenu#define AN_OK_VISUALIZZA_LATERALE 1026 // da Supervisore per GestoreMenu#define AN_OK_COLORE_SCELTO_FRONTALE 1027 // da Supervisore per GestoreMenu#define AN_OK_COLORE_SCELTO_LATERALE 1028 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO 1029 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI 1030 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO 1031 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI 1032 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO 1033 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI 1034 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO 1035 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI 1036 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO 1037 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI 1038 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO 1039 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI 1040 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO 1041 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI 1042 // da Supervisore per GestoreMenu#define AN_OK_START_POSIZIONA 1043 // da Supervisore per GestoreMenu#define AN_OK_STOP_POSIZIONA 1044 // da Supervisore per GestoreMenu#define AN_OK_VISUALIZZA_FRONTALE_II 1045 // da Supervisore per GestoreMenu#define AN_OK_START_MANUALE 1046 // da Supervisore per GestoreMenu#define AN_OK_STOP_MANUALE 1047 // da Supervisore per GestoreMenu#define AN_OK_OK 1048 // da supervisore per GestoreMenu#define AN_OK_RITORNA 1049 // da Supervisore per GestoreMenu#define AN_OK_APPLICAZIONE_PRESCELTA 1050 // da Supervisore per GestoreMenu
// Messaggi di operazioni fallite
#define AN_NO_AGGANCIO 2001 // da Supervisore per GestoreErrore#define AN_NO_DATI 2002 // da Supervisore per GestoreErrore#define AN_NO_FORMATO_ERRORE 2003 // da Supervisore per GestoreErrore#define AN_NO_FINESTRA_FORMATO 2004 // da Supervisore per GestoreErrore#define AN_NO_SORGENTE_ERRORE 2005 // da Supervisore per GestoreErrore#define AN_NO_FINESTRA_SORGENTE 2006 // da Supervisore per GestoreErrore#define AN_NO_FOTO 2007 // da Supervisore per GestoreErrore#define AN_NO_RILASCIO_WEB_UNO 2008 // da Supervisore per GestoreErrore#define AN_NO_FILE 2009 // da Supervisore per GestoreErrore#define AN_NO_ALLOCIMAGE 2010 // da Supervisore per GestoreErrore#define AN_NO_ELABORAZIONE 2011 // da Supervisore per GestoreErrore#define AN_NO_RILASCIO_WEB_DUE 2012 // da Supervisore per GestoreErrore
// Messaggi di OnOff
#define AN_ONOFF_DUE_WEB 3001 // da MenuCattura per Supervisore
// Messaggi di richiesta
#define AN_ASK_AGGANCIO 4001 // da MenuCattura per Supervisore#define AN_ASK_FINESTRA_FORMATO 4002 // da MenuCattura per Supervisore#define AN_ASK_FINESTRA_SORGENTE 4003 // da MenuCattura per Supervisore#define AN_ASK_FOTO 4004 // da MenuCattura per Supervisore#define AN_ASK_RILASCIO 4005 // da MenuCattura per Supervisore#define AN_ASK_DLG_PROPRIETA 4006 // da MenuCattura per Supervisore#define AN_ASK_FILE 4007 // da MenuCattura per Supervisore#define AN_ASK_VISUALIZZA 4008 // da MenuIntercetta per Supervisore#define AN_ASK_START 4009 // da MenuIntercetta per Supervisore#define AN_ASK_STOP 4010 // da MenuIntercetta per Supervisore#define AN_ASK_VISUALIZZA_II 4011 // da MenuIntercetta per Supervisore#define AN_ASK_START_II 4012 // da MenuIntercetta per Supervisore#define AN_ASK_STOP_II 4013 // da MenuIntercetta per Supervisore#define AN_ASK_PALETTE 4014 // da MenuIntercetta per Supervisore#define AN_ASK_INTERCETTA_COLORE 4016 // da PaletteProc per Supervisore;#define AN_ASK_STATO_CATTURA_MUTATO 4017 // da ThreadIntercettaFrontale/Laterale per Supervisore#define AN_ASK_START_POSIZIONA 4018 // da MenuPosiziona per Supervisore#define AN_ASK_FINESTRA_IMMAGINE_FRONTALE 4019 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_IMMAGINE_LATERALE 4020 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_PALETTE_FRONTALE 4021 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_PALETTE_LATERALE 4022 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_CONTROLLI 4023 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_WEB_UNO 4024 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_WEB_DUE 4025 // da MenuSet per Supervisore#define AN_ASK_INIZIO_STATO 4026 // da ThreadPosiziona per Supervisore#define AN_ASK_FINE_STATO 4027 // da ThreadIntercettaFrontale per Supervisore#define AN_ASK_FINE_SETTAGGIO 4028 // da ThreadPosiziona per Supervisore#define AN_ASK_STATO_COMPLETATO 4029 // da ThreadIntercettaFrontale per Supervisore
14
#define AN_ASK_STOP_POSIZIONA 4030 // da MenuPosiziona per Supervisore#define AN_ASK_STATO_PROGRESSO_MUTATO 4031 // da ThreadIntercettaFrontale per Supervisore#define AN_ASK_START_MANUALE 4032 // da MenuPosiziona per Supervisore#define AN_ASK_STOP_MANUALE 4033 // da MenuPosiziona per Supervisore#define AN_ASK_OK 4034 // da MenuPosiziona per Supervisore#define AN_ASK_RITORNA 4035 // da MenuPosiziona per Supervisore#define AN_ASK_DECISIONE_AVVENUTA 4036 // da ThreadIntercettaFrontale per Supervisore#define AN_ASK_CLICK_TEMPORALE_AVVENUTA 4037 // da ThreadIntercettaFrontale per Supervisore
// Codici di ritorno delle varie funzioni
#define AN_RET_OK 6001 // da Cattura per Supervisore#define AN_RET_AGGANCIO_NO 6002 // da Cattura per Supervisore#define AN_RET_DATI_NO 6003 // da Cattura per Supervisore#define AN_RET_FORMATO_ERRORE 6004 // da Cattura per Supervisore#define AN_RET_FORMATO_NO 6005 // da Cattura per Supervisore#define AN_RET_SORGENTE_ERRORE 6006 // da Cattura per Supervisore#define AN_RET_SORGENTE_NO 6007 // da Cattura per Supervisore#define AN_RET_OVERLAY_ERRORE 6008 // da Cattura per Supervisore#define AN_RET_OVERLAY_NO 6009 // da Cattura per Supervisore#define AN_RET_ALLOC_NO 6010 // da Intercetta per Supervisore#define AN_RET_NO_DECISIONE 6011 // da Decisore per ThreadIntercettaFrontale#define AN_RET_SI_DECISIONE 6012 // da Decisore per ThreadIntercettaFrontale#define AN_RET_CLICK_TEMPORALE 6013 // da Decisore per ThreadIntercettaFrontale
// Codici per i colori nella Tavolozza
#define GRIGIO_1 7001 // da TrovaGrigio (Palette)#define GRIGIO_2 7002 // da TrovaGrigio (Palette)#define GRIGIO_3 7003 // da TrovaGrigio (Palette)#define GRIGIO_4 7004 // da TrovaGrigio (Palette)#define GRIGIO_5 7005 // da TrovaGrigio (Palette)#define ROSSO_1 7006 // da TrovaColore (Palette)#define ROSSO_2 7007 // da TrovaColore (Palette)#define ROSSO_3 7008 // da TrovaColore (Palette)#define ROSSO_4 7009 // da TrovaColore (Palette)#define ROSSO_5 7010 // da TrovaColore (Palette)#define ROSSO_6 7011 // da TrovaColore (Palette)#define VERDE_1 7012 // da TrovaColore (Palette)#define VERDE_2 7013 // da TrovaColore (Palette)#define VERDE_3 7014 // da TrovaColore (Palette)#define VERDE_4 7015 // da TrovaColore (Palette)#define VERDE_5 7016 // da TrovaColore (Palette)#define VERDE_6 7017 // da TrovaColore (Palette)#define BLU_1 7018 // da TrovaColore (Palette)#define BLU_2 7019 // da TrovaColore (Palette)#define BLU_3 7020 // da TrovaColore (Palette)#define BLU_4 7021 // da TrovaColore (Palette)#define BLU_5 7022 // da TrovaColore (Palette)#define BLU_6 7023 // da TrovaColore (Palette)#define GIALLO_1 7024 // da TrovaColore (Palette)#define GIALLO_2 7025 // da TrovaColore (Palette)#define GIALLO_3 7026 // da TrovaColore (Palette)#define CYANO_1 7027 // da TrovaColore (Palette)#define CYANO_2 7028 // da TrovaColore (Palette)#define CYANO_3 7029 // da TrovaColore (Palette)#define VIOLETTO_1 7030 // da TrovaColore (Palette)#define VIOLETTO_2 7031 // da TrovaColore (Palette)#define VIOLETTO_3 7032 // da TrovaColore (Palette)#define INDEFINITO 7033 // da TrovaColore (Palette)
Supervisore // Definizioni membri statici:
char Supervisore::NomeFileUno[20] = "Z:\\FileUno.bmp";char Supervisore::NomeFileDue[20] = "Z:\\FileDue.bmp";
// Definizioni funzioni membro:void Supervisore::SethInstance(HINSTANCE hInstance) { this->hInstance=hInstance; }bool Supervisore::GetDueWeb() { return Cube.DueWeb; }
// Setto l'handler delle Finestre Secondarie e d'Applicazione:void Supervisore::SetFinestreSecondarie(UINT Tipo,HWND Finestra)
{ Finestre->SetFinestreSecondarie(Tipo,Finestra); }void Supervisore::SetFinestreApplicazione(UINT Tipo,HWND Finestra,bool Prescelta)
{ Finestre->SetFinestreApplicazione(Tipo,Finestra,Prescelta); }
// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:HWND Supervisore::GetFinestreSecondarie(UINT Tipo){ return Finestre->GetFinestreSecondarie(Tipo); }HWND Supervisore::GetFinestreApplicazione(UINT Tipo)
{ return Finestre->GetFinestreApplicazione(Tipo); }
15
// Controllo la visibilità delle Finestre Secondarie e d'Applicazione:bool Supervisore::VisibileSecondarie(UINT Tipo){ return Finestre->GetVisibileSecondarie(Tipo); }bool Supervisore::VisibileApplicazione(UINT Tipo){ return Finestre->GetVisibileApplicazione(Tipo); }
// Definisco l'Applicazione prescelta:void Supervisore::Prescelta(UINT Tipo){
Finestre->SetPresceltaApplicazione(Tipo);Menu->Messaggio(AN_OK_APPLICAZIONE_PRESCELTA);
}
// Settaggio dell'Angelo:void Supervisore::Set( HWND FinestraPrincipale,
HMENU MenuPrincipale,char* szAppName,Cattura* CameraUno,Cattura* CameraDue,Intercetta* MagoFrontale,Intercetta* MagoLaterale,Palette* TavolozzaFrontale,Palette* TavolozzaLaterale,Decisore* Oz,GestoreMenu* Menu,GestoreErrore* Errore,GestoreFinestre* Finestre)
{this->CameraUno = CameraUno;this->CameraDue = CameraDue;this->MagoFrontale = MagoFrontale;this->MagoLaterale = MagoLaterale;this->TavolozzaFrontale = TavolozzaFrontale;this->TavolozzaLaterale = TavolozzaLaterale;this->Oz = Oz;this->Menu = Menu;this->Errore = Errore;this->Finestre = Finestre;this->Menu->Set(MenuPrincipale,&Cube,Finestre);this->Errore->Set(FinestraPrincipale,szAppName);this->Finestre->Set(FinestraPrincipale);Cube.DueWeb = false;Cube.Aggancio = false;GetWindowRect(FinestraPrincipale,&Cube.Dimensioni);MagoFrontale->SetAttivo(false);MagoLaterale->SetAttivo(false);TavolozzaFrontale->SetAttivo(false);TavolozzaLaterale->SetAttivo(false);Oz->SetAttivo(false);
}void Supervisore::Scossa(){
GetWindowRect(Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),&Cube.Dimensioni);MoveWindow(Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),
Cube.Dimensioni.left,Cube.Dimensioni.top,Cube.Dimensioni.right-Cube.Dimensioni.left,Cube.Dimensioni.bottom-Cube.Dimensioni.top-1,true);
MoveWindow(Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),Cube.Dimensioni.left,Cube.Dimensioni.top,Cube.Dimensioni.right-Cube.Dimensioni.left,Cube.Dimensioni.bottom-Cube.Dimensioni.top,true);
}void Supervisore::Messaggio(UINT Messaggio){
switch (Messaggio){
// Gestione di OnOff:// Due Web:
case AN_ONOFF_DUE_WEB:if(Cube.DueWeb){
Cube.DueWeb = false;Menu->Messaggio(AN_OK_DUE_WEB_NO);
}else{
Cube.DueWeb = true;Menu->Messaggio(AN_OK_DUE_WEB_SI);
}InvalidateRect(Finestre->GetFinestreSecondarie(FINESTRA_CONTROLLI),NULL,true);break;
16
// Gestione delle finestre Visibili://Finestra Immagine Frontale:
case AN_ASK_FINESTRA_IMMAGINE_FRONTALE:if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE)){
Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE,false);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO);
}else{
Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE,true);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI);
}Scossa();break;
// Finestra Immagine Laterale:case AN_ASK_FINESTRA_IMMAGINE_LATERALE:
if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE)){
Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE,false);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO);
}else{
Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE,true);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI);
}Scossa();break;
// Finestra Palette Frontale:case AN_ASK_FINESTRA_PALETTE_FRONTALE:
if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE)){
Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE,false);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO);
}else{
Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE,true);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI);
}Scossa();break;
// Finestra Palette Laterale:case AN_ASK_FINESTRA_PALETTE_LATERALE:
if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_LATERALE)){
Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_LATERALE,false);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO);
}else{
Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_LATERALE,true);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI);
}Scossa();break;
// Finestra Semafori:case AN_ASK_FINESTRA_CONTROLLI:
if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI)){
Finestre->SetVisibileSecondarie(FINESTRA_CONTROLLI,false);Menu->Messaggio(AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO);
}else{
Finestre->SetVisibileSecondarie(FINESTRA_CONTROLLI,true);Menu->Messaggio(AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI);
}Scossa();break;
// Finestra Web Uno:case AN_ASK_FINESTRA_WEB_UNO:
if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_UNO)){
Finestre->SetVisibileSecondarie(FINESTRA_WEB_UNO,false);Menu->Messaggio(AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO);
}
17
else{
Finestre->SetVisibileSecondarie(FINESTRA_WEB_UNO,true);Menu->Messaggio(AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI);
}Scossa();break;
// Finestra Web Due:case AN_ASK_FINESTRA_WEB_DUE:
if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_DUE)){
Finestre->SetVisibileSecondarie(FINESTRA_WEB_DUE,false);Menu->Messaggio(AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO);
}else{
Finestre->SetVisibileSecondarie(FINESTRA_WEB_DUE,true);Menu->Messaggio(AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI);
}Scossa();break;
// Richiesta di posizionamento automatico:case AN_ASK_START_POSIZIONA:
Finestre->Messaggio(FINESTRA_POSIZIONA);Menu->Messaggio(AN_OK_START_POSIZIONA);Scossa();Cube.Segnale = false;SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),
WM_START_POSIZIONA,0,0);break;
case AN_ASK_STOP_POSIZIONA:SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_STATO_MUTATO,0,(long) STATO_ZERO);if(Cube.DueWeb == true)
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),WM_STATO_MUTATO,0,(long) STATO_ZERO);
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STOP_POSIZIONA,0,0);
Finestre->Messaggio(FINESTRE_ATTIVE);Menu->Messaggio(AN_OK_STOP_POSIZIONA);Scossa();break;
// Richiesta di posizionamento manuale:case AN_ASK_START_MANUALE:
Menu->Messaggio(AN_OK_START_MANUALE);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_STATO_MUTATO,0,(long) STATO_MANUALE);break;
case AN_ASK_STOP_MANUALE:RETTANGOLI *Rettangoli;
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) STATO_ZERO);
Menu->Messaggio(AN_OK_STOP_MANUALE);Rettangoli = MagoFrontale->GetRettangoli();if(Oz->GetAttivo())
Oz->Pulisci();Oz->Set(Rettangoli,NULL,
(Cube.Dimensioni.right - Cube.Dimensioni.left),(Cube.Dimensioni.bottom - Cube.Dimensioni.top));
break;
// Richiesta per il lancio dell'Applicazione:case AN_ASK_OK:PUNTO *Punto;
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) STATO_LAVORO);
Menu->Messaggio(AN_OK_OK);Finestre->Messaggio(FINESTRA_APPLICAZIONE);Punto = Oz->GetPunto();SendMessage(Finestre->GetFinestreApplicazione(),
WM_CREATE_PUNTO,0,(long) Punto);SendMessage(Finestre->GetFinestreApplicazione(),
WM_STATO_CATTURA_MUTATO,0,(long) STATO_CATTURA_VERDE);Scossa();break;
case AN_ASK_RITORNA:SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
18
WM_STATO_MUTATO,0,(long) STATO_ZERO);Menu->Messaggio(AN_OK_RITORNA);Finestre->Messaggio(FINESTRE_SECONDARIE);Scossa();break;
// Richiesta di decisione avvenuta:case AN_ASK_DECISIONE_AVVENUTA:
SendMessage(Finestre->GetFinestreApplicazione(),WM_NON_VALIDO,0,0);break;
// richiesta di click temporale:case AN_ASK_CLICK_TEMPORALE_AVVENUTA:
SendMessage(Finestre->GetFinestreApplicazione(),WM_CLICK,0,0);break;
}}void Supervisore::Messaggio(UINT Messaggio,UINT Generatore){
switch (Generatore){
// Gestione richieste da web:case AN_WEB_FRONTALE:
switch (Messaggio){
// Agganciocase AN_ASK_AGGANCIO:
switch (CameraUno->Set(Finestre->GetFinestreSecondarie(FINESTRA_WEB_UNO),AN_WEB_FRONTALE,(char*) &NomeFileUno,0,0,400,400))
{case AN_RET_AGGANCIO_NO:
Errore->Messaggio(AN_NO_AGGANCIO);break;
case AN_RET_DATI_NO:Errore->Messaggio(AN_NO_DATI);break;
case AN_RET_OK:Menu->Messaggio(AN_OK_AGGANCIO);Cube.Aggancio = true;break;
}break;
// Finestra Formatocase AN_ASK_FINESTRA_FORMATO:
switch (CameraUno->GetFormato()){case AN_RET_FORMATO_ERRORE:
Errore->Messaggio(AN_NO_FORMATO_ERRORE);break;
case AN_RET_FORMATO_NO:Errore->Messaggio(AN_NO_FINESTRA_FORMATO);break;
case AN_RET_OK:break;
}break;
// Finestra Sorgentecase AN_ASK_FINESTRA_SORGENTE:
switch (CameraUno->GetSorgente()){case AN_RET_SORGENTE_ERRORE:
Errore->Messaggio(AN_NO_SORGENTE_ERRORE);break;
case AN_RET_SORGENTE_NO:Errore->Messaggio(AN_NO_FINESTRA_SORGENTE);break;
case AN_RET_OK:break;
}break;
// Fotocase AN_ASK_FOTO:
if(!CameraUno->GetFoto())Errore->Messaggio(AN_NO_FOTO);
break;// Rilascio
case AN_ASK_RILASCIO:if(!CameraUno->Disconnetti())
Errore->Messaggio(AN_NO_RILASCIO_WEB_UNO);else
Menu->Messaggio(AN_OK_RILASCIO);Cube.Aggancio = false;
19
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_I,0,0);
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_II,0,0);
break;// File
case AN_ASK_FILE:if(!CameraUno->GetFile())
Errore->Messaggio(AN_NO_FILE);break;
// Palettecase AN_ASK_PALETTE:
if(TavolozzaFrontale->GetAttivo())TavolozzaFrontale->Pulisci();
TavolozzaFrontale->Set();TavolozzaFrontale->Colora(MagoFrontale->GetImmagine());SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),
WM_TAVOLOZZA_PRONTA,0,(long) TavolozzaFrontale->GetListaColore());
break;// Visualizza
case AN_ASK_VISUALIZZA:if(MagoFrontale->GetAttivo())
MagoFrontale->Free();switch (MagoFrontale->Set(CameraUno,(char*) &NomeFileUno)){case AN_RET_ALLOC_NO:
Errore->Messaggio(AN_NO_ALLOCIMAGE);break;
case AN_RET_OK:break;
}MagoFrontale->GetPosizioneI();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_SINGOLA_IMMAGINE_I,0,(long) MagoFrontale->GetImmagine());Menu->Messaggio(AN_OK_VISUALIZZA_FRONTALE);break;
// Startcase AN_ASK_START:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_START_THREAD_I,(long) Oz,(long) MagoFrontale);
Menu->Messaggio(AN_OK_START_FRONTALE);break;
// Stopcase AN_ASK_STOP:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_I,0,0);
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),WM_STOP_THREAD_I,0,0);
Menu->Messaggio(AN_OK_STOP_FRONTALE);break;
// Visualizza IIcase AN_ASK_VISUALIZZA_II:
if(MagoFrontale->GetAttivo())MagoFrontale->Free();
switch (MagoFrontale->Set(CameraUno,(char*) &NomeFileUno)){case AN_RET_ALLOC_NO:
Errore->Messaggio(AN_NO_ALLOCIMAGE);break;
case AN_RET_OK:break;
}if(!MagoFrontale->GetPosizioneII())
Errore->Messaggio(AN_NO_ELABORAZIONE);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_SINGOLA_IMMAGINE_II,0,(long) MagoFrontale->GetImmagine());Menu->Messaggio(AN_OK_VISUALIZZA_FRONTALE_II);break;
// Start IIcase AN_ASK_START_II:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_START_THREAD_II,0,(long) MagoFrontale);
Menu->Messaggio(AN_OK_START_FRONTALE_II);break;
// Stop IIcase AN_ASK_STOP_II:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_II,0,0);
Menu->Messaggio(AN_OK_STOP_FRONTALE_II);break;
20
// DLG Proprietàcase AN_ASK_DLG_PROPRIETA:
PROPRIETA SettaggiCamera = { 0,0,0,NULL, false,false,false,false,false,false,false,false,false,false,false,false,false };
SettaggiCamera.Camera=AN_WEB_FRONTALE;CameraUno->GetSettaggi(&SettaggiCamera);DialogBoxParam(hInstance,TEXT ("PROPRIETADLG"),
Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),ProprietaDlgProc,(long) &SettaggiCamera);
break;}break;
case AN_WEB_LATERALE:switch (Messaggio){
// Agganciocase AN_ASK_AGGANCIO:
switch (CameraDue->Set(Finestre->GetFinestreSecondarie(FINESTRA_WEB_DUE),AN_WEB_LATERALE,(char*) &NomeFileDue,0,0,400,400))
{case AN_RET_AGGANCIO_NO:
Errore->Messaggio(AN_NO_AGGANCIO);break;
case AN_RET_DATI_NO:Errore->Messaggio(AN_NO_DATI);break;
case AN_RET_OK:Menu->Messaggio(AN_OK_AGGANCIO);break;
}break;
// Finestra Formatocase AN_ASK_FINESTRA_FORMATO:
switch (CameraDue->GetFormato()){case AN_RET_FORMATO_ERRORE:
Errore->Messaggio(AN_NO_FORMATO_ERRORE);break;
case AN_RET_FORMATO_NO:Errore->Messaggio(AN_NO_FINESTRA_FORMATO);break;
case AN_RET_OK:break;
}break;
// Finestra Sorgentecase AN_ASK_FINESTRA_SORGENTE:
switch (CameraDue->GetSorgente()){case AN_RET_SORGENTE_ERRORE:
Errore->Messaggio(AN_NO_SORGENTE_ERRORE);break;
case AN_RET_SORGENTE_NO:Errore->Messaggio(AN_NO_FINESTRA_SORGENTE);break;
case AN_RET_OK:break;
}break;
// Fotocase AN_ASK_FOTO:
if(!CameraDue->GetFoto())Errore->Messaggio(AN_NO_FOTO);
break;// Rilascio
case AN_ASK_RILASCIO:if(!CameraDue->Disconnetti())
Errore->Messaggio(AN_NO_RILASCIO_WEB_DUE);else
Menu->Messaggio(AN_OK_RILASCIO);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
WM_STOP_THREAD_I,0,0);break;
// Filecase AN_ASK_FILE:
if(!CameraDue->GetFile())Errore->Messaggio(AN_NO_FILE);
break;// Palette
case AN_ASK_PALETTE:if(TavolozzaLaterale->GetAttivo())
TavolozzaLaterale->Pulisci();TavolozzaLaterale->Set();
21
TavolozzaLaterale->Colora(MagoLaterale->GetImmagine());SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),
WM_TAVOLOZZA_PRONTA,0,(long) TavolozzaLaterale->GetListaColore());break;
// Visualizzacase AN_ASK_VISUALIZZA:
if(MagoLaterale->GetAttivo())MagoLaterale->Free();
switch (MagoLaterale->Set(CameraDue,(char*) &NomeFileDue)){case AN_RET_ALLOC_NO:
Errore->Messaggio(AN_NO_ALLOCIMAGE);break;
case AN_RET_OK:break;
}MagoLaterale->GetPosizioneI();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
WM_SINGOLA_IMMAGINE_I,0,(long) MagoLaterale->GetImmagine());Menu->Messaggio(AN_OK_VISUALIZZA_LATERALE);break;
// Startcase AN_ASK_START:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),WM_START_THREAD_I,(long) Oz,(long) MagoLaterale);
Menu->Messaggio(AN_OK_START_LATERALE);break;
// Stopcase AN_ASK_STOP:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),WM_STOP_THREAD_I,0,0);
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),WM_STOP_THREAD_I,0,0);
Menu->Messaggio(AN_OK_STOP_LATERALE);break;
// DLG Proprietàcase AN_ASK_DLG_PROPRIETA:
PROPRIETA Settaggi = { 0,0,0,NULL,false,false,false,false,false,false,false,false,false,false,false,false,false };
Settaggi.Camera=AN_WEB_LATERALE;CameraDue->GetSettaggi(&Settaggi);DialogBoxParam(hInstance,TEXT ("PROPRIETADLG"),
Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),ProprietaDlgProc,(long) &Settaggi);
break;}break;
}}void Supervisore::Messaggio(UINT Messaggio,UINT Generatore,UINT Parametro){
switch (Generatore){case AN_WEB_FRONTALE:
switch (Messaggio){
// Intercetta Colorecase AN_ASK_INTERCETTA_COLORE:RECT Zona;RETTANGOLI* Rettangoli;
CopyRect(&Zona,TavolozzaFrontale->GetZonaColore(Parametro));Rettangoli = MagoFrontale->SetColoreAttivo(Parametro,&Zona);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_COLORE_PRONTO,0,(long) Rettangoli);Menu->Messaggio(AN_OK_COLORE_SCELTO_FRONTALE);break;
case AN_ASK_STATO_CATTURA_MUTATO:if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI)){
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_CONTROLLI),WM_STATO_CATTURA_MUTATO_FRONTALE,0,(long) Parametro);
break;}if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA)){
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STATO_CATTURA_MUTATO_FRONTALE,0,(long) Parametro);
break;}SendMessage(Finestre->GetFinestreApplicazione(),
WM_STATO_CATTURA_MUTATO,0,(long) Parametro);break;
case AN_ASK_STATO_PROGRESSO_MUTATO:
22
if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),
WM_STATO_PROGRESSO_MUTATO,0,(long) Parametro);break;
}break;
case AN_WEB_LATERALE:switch (Messaggio){case AN_ASK_INTERCETTA_COLORE:RECT Zona;RETTANGOLI* Rettangoli;
CopyRect(&Zona,TavolozzaLaterale->GetZonaColore(Parametro));Rettangoli = MagoLaterale->SetColoreAttivo(Parametro,&Zona);MagoLaterale->GetPosizioneEye();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
WM_COLORE_PRONTO,(long) MagoFrontale->GetImmagineEye(),(long) Rettangoli);
Menu->Messaggio(AN_OK_COLORE_SCELTO_LATERALE);break;
case AN_ASK_STATO_CATTURA_MUTATO:if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI))
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_CONTROLLI),WM_STATO_CATTURA_MUTATO_LATERALE,0,(long) Parametro);
if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),
WM_STATO_CATTURA_MUTATO_LATERALE,0,(long) Parametro);break;
/* case AN_ASK_STATO_PROGRESSO_MUTATO:if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STATO_PROGRESSO_MUTATO,0,(long) Parametro);
break;*/ }
break;}
}void Supervisore::HandShake(UINT Messaggio,UINT Generatore,UINT Parametro){
switch (Generatore){case AN_THREAD_POSIZIONA:
switch (Messaggio){case AN_ASK_INIZIO_STATO:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) Parametro);
if(Cube.DueWeb)SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
WM_STATO_MUTATO,0,(long) Parametro);break;
case AN_ASK_FINE_SETTAGGIO:CELLA_POSIZIONE *Posizioni;RETTANGOLI *Rettangoli;
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) STATO_ZERO);
Posizioni = MagoFrontale->GetPosizioni();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_POSIZIONI_PRONTE,0,(long) Posizioni);Rettangoli = MagoFrontale->GetRettangoli();if(Oz->GetAttivo())
Oz->Pulisci();Oz->Set(Rettangoli,Posizioni,
(Cube.Dimensioni.right - Cube.Dimensioni.left),(Cube.Dimensioni.bottom - Cube.Dimensioni.top));
if(Cube.DueWeb)SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
WM_STATO_MUTATO,0,(long) STATO_ZERO);Finestre->Messaggio(FINESTRE_ATTIVE);Menu->Messaggio(AN_OK_STOP_POSIZIONA);Scossa();break;
}break;
case AN_THREAD_INTERCETTA:switch (Messaggio){case AN_ASK_FINE_STATO:
if(!Cube.DueWeb)SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),
WM_STATO_MUTATO,0,(long) Parametro+1);else{
23
if(!Cube.Segnale)Cube.Segnale = true;
else{
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STATO_MUTATO,0,(long) Parametro+1);
Cube.Segnale = false;}
}break;
}break;
}}
25
Appendice B Oggetti principali
Cattura HWND Cattura::GetFinestraCattura() { return FinestraCattura; }
UINT Cattura::Set(HWND FinestraPadre,UINT Indice,char* NomeFile,int x,int y,int larghezza,int lunghezza)
{// Settaggi
this->FinestraPadre = FinestraPadre;this->Indice = Indice;this->NomeFile = NomeFile;this->x = x;this->y = y;this->larghezza = larghezza;this->lunghezza = lunghezza;
// Cerco di agganciare la Web alla Finestra di CatturaFinestraCattura=capCreateCaptureWindow(TEXT ("Finestra di cattura"),
WS_CHILD | WS_VISIBLE,x,y,larghezza,lunghezza,FinestraPadre,0);
if(!capDriverConnect(FinestraCattura,0))return AN_RET_AGGANCIO_NO;
if(!(capCaptureGetSetup(FinestraCattura,&ParametriCattura,sizeof ParametriCattura)) ||!(capDriverGetCaps(FinestraCattura,&CapacitaDriver,sizeof CapacitaDriver)) ||!(capDriverGetName(FinestraCattura,&cNomeDriver,sizeof cNomeDriver)) ||!(capGetStatus(FinestraCattura,&StatoCattura,sizeof StatoCattura)))
return AN_RET_DATI_NO;return AN_RET_OK;
}bool Cattura::Disconnetti(){
if(capDriverDisconnect(FinestraCattura))return true;
return false;}UINT Cattura::GetFormato(){
if(CapacitaDriver.fHasDlgVideoFormat == 1){
if(!capDlgVideoFormat(FinestraCattura))return AN_RET_FORMATO_ERRORE;
return AN_RET_OK;}else
return AN_RET_FORMATO_NO;}UINT Cattura::GetSorgente(){
if(CapacitaDriver.fHasDlgVideoSource == 1){
if(!capDlgVideoSource(FinestraCattura))return AN_RET_SORGENTE_ERRORE;
return AN_RET_OK;}else
return AN_RET_SORGENTE_NO;}bool Cattura::GetFoto(){
if(capGrabFrame(FinestraCattura)) return true;return false;
}bool Cattura::GetFile(){
if(capFileSaveDIB(FinestraCattura,NomeFile)) return true;return false;
}void Cattura::GetSettaggi(PROPRIETA *Settaggi){
Settaggi->ImageWidth = StatoCattura.uiImageWidth;Settaggi->ImageHeight = StatoCattura.uiImageHeight;
26
Settaggi->NomeDriver = (char*) &cNomeDriver;if(StatoCattura.fLiveWindow == 1) Settaggi->LiveWindow = true;if(StatoCattura.fOverlayWindow == 1) Settaggi->OverlayWindow = true;if(StatoCattura.fScale == 1) Settaggi->Scale = true;if(StatoCattura.fUsingDefaultPalette == 1) Settaggi->UsingDefaultPalette = true;if(StatoCattura.fAudioHardware == 1) Settaggi->AudioHardware = true;if(StatoCattura.fCapFileExists == 1) Settaggi->CapFileExists = true;if(StatoCattura.fCapturingNow == 1) Settaggi->CapturingNow = true;if(CapacitaDriver.fHasOverlay == 1) Settaggi->HasOverlay = true;if(CapacitaDriver.fHasDlgVideoSource == 1) Settaggi->HasDlgVideoSource = true;if(CapacitaDriver.fHasDlgVideoFormat == 1) Settaggi->HasDlgVideoFormat = true;if(CapacitaDriver.fHasDlgVideoDisplay == 1) Settaggi->HasDlgVideoDisplay = true;if(CapacitaDriver.fCaptureInitialized == 1) Settaggi->CaptureInitialized = true;if(CapacitaDriver.fDriverSuppliesPalettes == 1) Settaggi->DriverSuppliesPalettes = true;
}
Palette CELLA_COLORE_LS* Palette::GetListaColore() { return this->TestaLS; }void Palette::SetAttivo(bool Attivo) { this->Attivo = Attivo;}bool Palette::GetAttivo() { return this->Attivo; }
void Palette::Set(){
TestaLS = CodaLS = IndiceLS = NULL;TestaLC = CodaLC = IndiceLC = NULL;Elementi = 0;
// Creo la Cella Colore Lista Complessa che servirà da rifiuto per tutti i colori indefinitiTempLC = new(CELLA_COLORE_LC);TempLC->Colore.Blu = 0;TempLC->Colore.Verde = 0;TempLC->Colore.Rosso = 0;TempLC->Scostamento.BV = 0;TempLC->Scostamento.VR = 0;TempLC->Scostamento.RB = 0;TempLC->TipoColore = INDEFINITO;TempLC->Zona.left = 0;TempLC->Zona.top = 240;TempLC->Zona.right = 320;TempLC->Zona.bottom = 0;TempLC->Bersagli = 0;TempLC->Posizione = Elementi;
// Inserisco la Cella in testa (primo elemento)TempLC->Precedente = NULL;TempLC->Successivo = NULL;TestaLC = CodaLC = TempLC;
// Creo la Cella Colore Lista SempliceTempLS = new(CELLA_COLORE_LS);TempLS->Blu = 0;TempLS->Verde = 0;TempLS->Rosso = 0;TempLS->TipoColore = INDEFINITO;
// Inserisco la Cella in testa (primo elemento)TempLS->Precedente = NULL;TempLS->Successivo = NULL;TestaLS = CodaLS = TempLS;Elementi += 1;this->Attivo = true;
}void Palette::Pulisci(){
// Elimino gli elementi della lista ComplessaIndiceLC = CodaLC->Precedente;delete CodaLC;while(IndiceLC != NULL){
CodaLC = IndiceLC;IndiceLC = CodaLC->Precedente;delete CodaLC;
}TestaLC = CodaLC = IndiceLC = TempLC = NULL;// Elimino gli elementi della lista SempliceIndiceLS = CodaLS->Precedente;delete CodaLS;while(IndiceLS != NULL){
CodaLS = IndiceLS;IndiceLS = CodaLS->Precedente;delete CodaLS;
}TestaLS = CodaLS = IndiceLS = TempLS = NULL;// Setto il numero di elementi a 0
27
Elementi = 0;this->Attivo = false;
}RECT* Palette::GetZonaColore(UINT TipoColore){
IndiceLC = TestaLC;while(IndiceLC != NULL){
if(IndiceLC->TipoColore == TipoColore)return &IndiceLC->Zona;
IndiceLC = IndiceLC->Successivo;}return NULL;
}bool Palette::Trovato(CELLA_COLORE_LC *Cella){
IndiceLC = TestaLC;while(IndiceLC != NULL){
if(IndiceLC->TipoColore == Cella->TipoColore){
if(X < IndiceLC->Zona.left) IndiceLC->Zona.left = X;if(Y > IndiceLC->Zona.top) IndiceLC->Zona.top = Y;if(X > IndiceLC->Zona.right) IndiceLC->Zona.right = X;if(Y < IndiceLC->Zona.bottom) IndiceLC->Zona.bottom = Y;IndiceLC->Bersagli++;return true;
}IndiceLC = IndiceLC->Successivo;
}Cella->Zona.left = Cella->Zona.right = X;Cella->Zona.top = Cella->Zona.bottom = Y;Cella->Bersagli = 1;return false;
}void Palette::Colora(imgdes* Immagine){int i = 0; // Contatore di BYTEint j = 0; // Contatore di pixelCOLORE Colore;
X = Y = 0;while (i < ((int) Immagine->bmh->biSizeImage)){
Colore.Blu = Immagine->ibuff[i];Colore.Verde = Immagine->ibuff[i+1];Colore.Rosso = Immagine->ibuff[i+2];this->In(Colore);i = i + 3;j++;X = j % 320;Y = j / 320;
}}void Palette::In(COLORE Colore){// Creo la Cella Colore Lista Complessa
TempLC = new(CELLA_COLORE_LC);TempLC->Colore.Blu = Colore.Blu;TempLC->Colore.Verde = Colore.Verde;TempLC->Colore.Rosso = Colore.Rosso;TempLC->Scostamento.BV = Colore.Blu - Colore.Verde;TempLC->Scostamento.VR = Colore.Verde - Colore.Rosso;TempLC->Scostamento.RB = Colore.Rosso - Colore.Blu;TempLC->TipoColore = TipoColore(TempLC);TempLC->Posizione = Elementi;if (!Trovato(TempLC)){
// Inserisco la Cella in coda alla Lista ComplessaTempLC->Precedente = CodaLC;TempLC->Successivo = NULL;CodaLC->Successivo = TempLC;CodaLC = TempLC;
// Creo la Cella Colore Lista SempliceTempLS = new(CELLA_COLORE_LS);TempLS->Blu = Colore.Blu;TempLS->Verde = Colore.Verde;TempLS->Rosso = Colore.Rosso;TempLS->TipoColore = TipoColore(TempLC); // da rivedere
// Inserisco la Cella in coda alla Lista SempliceTempLS->Precedente = CodaLS;TempLS->Successivo = NULL;CodaLS->Successivo = TempLS;CodaLS = TempLS;
28
Elementi += 1;}else
delete TempLC;}UINT Palette::TipoColore(CELLA_COLORE_LC *Cella){// Controllo se il colore è un grigio
if((Cella->Scostamento.BV <= 10) && (Cella->Scostamento.BV >= -10) &&(Cella->Scostamento.VR <= 10) && (Cella->Scostamento.VR >= -10) &&(Cella->Scostamento.RB <= 10) && (Cella->Scostamento.RB >= -10))
{// Definisco il tipo di grigio da molto scuro (1) a bianco (5)
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 120)return GRIGIO_1;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 300)return GRIGIO_2;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 480)return GRIGIO_3;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 690)return GRIGIO_4;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 765)return GRIGIO_5;
}// Controllo se il colore è un rosso
if((Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_1;
if((Cella->Colore.Rosso <= 220) && (Cella->Colore.Rosso > 180) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_2;
if((Cella->Colore.Rosso <= 180) && (Cella->Colore.Rosso > 150) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_3;
if((Cella->Colore.Rosso <= 150) && (Cella->Colore.Rosso > 120) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_4;
if((Cella->Colore.Rosso <= 120) && (Cella->Colore.Rosso > 100) &&(Cella->Colore.Verde <= 90) && (Cella->Colore.Blu <= 90))return ROSSO_5;
if((Cella->Colore.Rosso <= 100) && (Cella->Colore.Rosso > 90) &&(Cella->Colore.Verde <= 80) && (Cella->Colore.Blu <= 80))return ROSSO_6;
// Controllo se il colore è un verdeif((Cella->Colore.Verde <= 255) && (Cella->Colore.Verde > 220) &&
(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_1;
if((Cella->Colore.Verde <= 220) && (Cella->Colore.Verde > 180) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_2;
if((Cella->Colore.Verde <= 180) && (Cella->Colore.Verde > 150) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_3;
if((Cella->Colore.Verde <= 150) && (Cella->Colore.Verde > 120) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_4;
if((Cella->Colore.Verde <= 120) && (Cella->Colore.Verde > 100) &&(Cella->Colore.Rosso <= 90) && (Cella->Colore.Blu <= 90))return VERDE_5;
if((Cella->Colore.Verde <= 100) && (Cella->Colore.Verde > 90) &&(Cella->Colore.Rosso <= 80) && (Cella->Colore.Blu <= 80))return VERDE_6;
// Controllo se il colore è un bluif((Cella->Colore.Blu <= 255) && (Cella->Colore.Blu > 220) &&
(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_1;
if((Cella->Colore.Blu <= 220) && (Cella->Colore.Blu > 180) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_2;
if((Cella->Colore.Blu <= 180) && (Cella->Colore.Blu > 150) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_3;
if((Cella->Colore.Blu <= 150) && (Cella->Colore.Blu > 120) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_4;
if((Cella->Colore.Blu <= 120) && (Cella->Colore.Blu > 100) &&(Cella->Colore.Rosso <= 90) && (Cella->Colore.Verde <= 90))return BLU_5;
if((Cella->Colore.Blu <= 100) && (Cella->Colore.Blu > 90) &&(Cella->Colore.Rosso <= 80) && (Cella->Colore.Verde <= 80))return BLU_6;
// Controllo se il colore è un gialloif((Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&
29
(Cella->Colore.Verde <= 255) && (Cella->Colore.Verde > 220) &&(Cella->Colore.Blu <= 50))return GIALLO_1;
if((Cella->Colore.Rosso <= 220) && (Cella->Colore.Rosso > 180) &&(Cella->Colore.Verde <= 220) && (Cella->Colore.Verde > 180) &&(Cella->Colore.Blu <= 50))return GIALLO_2;
if((Cella->Colore.Rosso <= 180) && (Cella->Colore.Rosso > 150) &&(Cella->Colore.Verde <= 180) && (Cella->Colore.Verde > 150) &&(Cella->Colore.Blu <= 50))return GIALLO_3;
// Controllo se il colore è un cyanoif((Cella->Colore.Blu <= 255) && (Cella->Colore.Blu > 220) &&
(Cella->Colore.Verde <= 255) && (Cella->Colore.Verde > 220) &&(Cella->Colore.Rosso <= 50))return CYANO_1;
if((Cella->Colore.Blu <= 220) && (Cella->Colore.Blu > 180) &&(Cella->Colore.Verde <= 220) && (Cella->Colore.Verde > 180) &&(Cella->Colore.Rosso <= 50))return CYANO_2;
if((Cella->Colore.Blu <= 180) && (Cella->Colore.Blu > 150) &&(Cella->Colore.Verde <= 180) && (Cella->Colore.Verde > 150) &&(Cella->Colore.Rosso <= 50))return CYANO_3;
// Controllo se il colore è un violettoif((Cella->Colore.Blu <= 255) && (Cella->Colore.Blu > 220) &&
(Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&(Cella->Colore.Verde <= 50))return VIOLETTO_1;
if((Cella->Colore.Blu <= 220) && (Cella->Colore.Blu > 180) &&(Cella->Colore.Rosso <= 220) && (Cella->Colore.Rosso > 180) &&(Cella->Colore.Verde <= 50))return VIOLETTO_2;
if((Cella->Colore.Blu <= 180) && (Cella->Colore.Blu > 150) &&(Cella->Colore.Rosso <= 180) && (Cella->Colore.Rosso > 150) &&(Cella->Colore.Verde <= 50))return VIOLETTO_3;
return INDEFINITO;}
Intercetta imgdes* Intercetta::GetImmagine() { return &Immagine; }imgdes* Intercetta::GetImmagineEye() { return &ImmagineEye; }bool Intercetta::GetAttivo() { return Attivo; }CELLA_POSIZIONE* Intercetta::GetPosizioni() { return Posizioni; }UINT Intercetta::GetProgresso() { return StatoProgresso; }void Intercetta::SetAttivo(bool Attivo) { this->Attivo = Attivo; }void Intercetta::Lock(){
this->DominioAttivo = false;this->StatoProgresso = STATO_PROGRESSO_INIZIO;
}void Intercetta::UnLock(UINT StatoPosiziona){
this->DominioAttivo = true;this->StatoPosiziona = StatoPosiziona;this->IndiceProgresso = 0;
}UINT Intercetta::Set(Cattura* Camera,char* NomeFile){int errore;BITMAPINFOHEADER Info;
this->Camera = Camera;this->NomeFile = NomeFile;this->FinestraSorgente = Camera->GetFinestraCattura();errore = bmpinfo(NomeFile,&Info);this->Width = Info.biWidth;this->Height = Info.biHeight;this->BitCount = Info.biBitCount;if(allocimage(&Immagine,Info.biWidth,Info.biHeight,Info.biBitCount) != NO_ERROR)
return AN_RET_ALLOC_NO;if(allocimage(&ImmagineEye,FOCUS_EYE,FOCUS_EYE,24) != NO_ERROR)
return AN_RET_ALLOC_NO;this->Attivo = true;this->Colore.ColoreAttivo = false;this->DominioAttivo = true;this->StatoCattura = STATO_CATTURA_INIZIO;this->StatoPosiziona = STATO_ZERO;this->StatoProgresso = STATO_PROGRESSO_INIZIO;
// Creo la lista delle posizioni:
30
Temp = new(CELLA_POSIZIONE);Temp->Precedente = Temp->Successivo = NULL;Testa = Coda = Posizioni = Temp;IndiceProgresso = 0;return AN_RET_OK;
}RETTANGOLI* Intercetta::GetRettangoli(){
CopyRect(&Rettangoli.Zona,&Colore.Zona);CopyRect(&Rettangoli.Focus,&Colore.Focus);CopyRect(&Rettangoli.Dominio,&Colore.Dominio);CopyRect(&Rettangoli.Lento,&Colore.Lento);CopyRect(&Rettangoli.FocusEye,&Colore.FocusEye);return &this->Rettangoli;
}void Intercetta::Free(){
freeimage(&Immagine);freeimage(&ImmagineEye);
// Cancello la lista delle posizioni:Temp = Coda->Precedente;delete Coda;while(Temp != NULL){
Coda = Temp;Temp = Coda->Precedente;delete Coda;
}Testa = Coda = Temp = NULL;IndiceProgresso = 0;this->Attivo = false;
}RETTANGOLI* Intercetta::SetColoreAttivo(UINT TipoColore,RECT* Zona){
Colore.TipoColore = TipoColore;// Posizionamento della Zona sull'Immagine
CopyRect(&Colore.Zona,Zona);// Posizionamento del Dominio sull'Immagine
CopyRect(&Colore.Dominio,&Colore.Zona);// Posizionamento del Lento sull'Immagine
CopyRect(&Colore.Lento,&Colore.Zona);// Posizionamento del Focus sull'Immagine
if(Colore.Zona.left > BORDO_FOCUS)Colore.Focus.left = (Colore.Zona.left - BORDO_FOCUS);
elseColore.Focus.left = 0;
if(Colore.Zona.top < (Height - BORDO_FOCUS))Colore.Focus.top = (Colore.Zona.top + BORDO_FOCUS);
elseColore.Focus.top = (Height - 1);
if(Colore.Zona.right < (Width - BORDO_FOCUS))Colore.Focus.right = (Colore.Zona.right + BORDO_FOCUS);
elseColore.Focus.right = (Width - 1);
if(Colore.Zona.bottom > 10)Colore.Focus.bottom = (Colore.Zona.bottom - BORDO_FOCUS);
elseColore.Focus.bottom = 0;
// Settaggio dei limiti del coloreswitch (TipoColore){case GRIGIO_1: // non entrerò mai in questi case li ho messi solocase GRIGIO_2: // per evitare errori.case GRIGIO_3:case GRIGIO_4:case GRIGIO_5:
Colore.RossoMin = Colore.VerdeMin = Colore.BluMin = 0;Colore.RossoMax = Colore.VerdeMax = Colore.BluMax = 0;break;
case ROSSO_1:Colore.RossoMin = 221;Colore.RossoMax = 255;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;
case ROSSO_2:Colore.RossoMin = 181;Colore.RossoMax = 220;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;
case ROSSO_3:Colore.RossoMin = 151;
31
Colore.RossoMax = 180;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;
case ROSSO_4:Colore.RossoMin = 121;Colore.RossoMax = 150;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;
case ROSSO_5:Colore.RossoMin = 101;Colore.RossoMax = 120;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 90;break;
case ROSSO_6:Colore.RossoMin = 91;Colore.RossoMax = 100;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 80;break;
case VERDE_1:Colore.VerdeMin = 221;Colore.VerdeMax = 255;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;
case VERDE_2:Colore.VerdeMin = 181;Colore.VerdeMax = 220;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;
case VERDE_3:Colore.VerdeMin = 151;Colore.VerdeMax = 180;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;
case VERDE_4:Colore.VerdeMin = 121;Colore.VerdeMax = 150;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;
case VERDE_5:Colore.VerdeMin = 101;Colore.VerdeMax = 120;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 90;break;
case VERDE_6:Colore.VerdeMin = 91;Colore.VerdeMax = 100;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 80;break;
case BLU_1:Colore.BluMin = 221;Colore.BluMax = 255;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;
case BLU_2:Colore.BluMin = 181;Colore.BluMax = 220;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;
case BLU_3:Colore.BluMin = 151;Colore.BluMax = 180;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;
case BLU_4:Colore.BluMin = 121;Colore.BluMax = 150;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;
case BLU_5:
32
Colore.BluMin = 101;Colore.BluMax = 120;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 90;break;
case BLU_6:Colore.BluMin = 91;Colore.BluMax = 100;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 80;break;
}// Settaggio iniziale per la struttura RETTANGOLI
CopyRect(&Rettangoli.Zona,&Colore.Zona);CopyRect(&Rettangoli.Focus,&Colore.Focus);CopyRect(&Rettangoli.Dominio,&Colore.Dominio);CopyRect(&Rettangoli.Lento,&Colore.Lento);CopyRect(&Rettangoli.FocusEye,&Colore.FocusEye);
// Settaggio iniziale per il vettore di strutture HITHit[0].Bersagli = 0;CopyRect(&Hit[0].Zona,&Colore.Zona);Indice = 1;
// Il colore è stato sceltoColore.ColoreAttivo = true;Colore.ColoreTrovato = true;return &Rettangoli;
}void Intercetta::GetPosizioneEye(){int i,j,k;short IndiceTemp;// Posizionamento del FocusEye sull'Immagine
if(Indice >= 1)IndiceTemp = (Indice - 1);
elseIndiceTemp = (NUMERO_AQUISIZIONI - 1);
if(Hit[IndiceTemp].Zona.left > FOCUS_EYE)Colore.FocusEye.left = (Hit[IndiceTemp].Zona.left - FOCUS_EYE);
elseColore.FocusEye.left = 0;
if(Hit[IndiceTemp].Zona.bottom > FOCUS_EYE)Colore.FocusEye.bottom = (Hit[IndiceTemp].Zona.bottom - FOCUS_EYE);
elseColore.FocusEye.bottom = 0;
Colore.FocusEye.right = Hit[IndiceTemp].Zona.left;Colore.FocusEye.top = Hit[IndiceTemp].Zona.bottom;k = 0;i = this->Colore.FocusEye.bottom * (320*3);while (i < (this->Colore.FocusEye.top * (320*3))){
j = (this->Colore.FocusEye.left * 3);while (j < (this->Colore.FocusEye.right * 3)){
if((Immagine.ibuff[(i+j)+0] > LUMINOSITA) &&(Immagine.ibuff[(i+j)+1] > LUMINOSITA) &&(Immagine.ibuff[(i+j)+2] > LUMINOSITA))
ImmagineEye.ibuff[k+0] = ImmagineEye.ibuff[k+1] =ImmagineEye.ibuff[k+2] = 255;
elseImmagineEye.ibuff[k+0] = ImmagineEye.ibuff[k+1] =
ImmagineEye.ibuff[k+2] = 0;j = j + 3;k = k + 3;
}i = i + (320*3);
}// Cerco il contorno dell'occhio e setto la ZonaEye
}UINT Intercetta::GetPosizioneI(){// Carica una nuova immagine
Camera->GetFoto();Camera->GetFile();loadbmp(NomeFile,&Immagine);if(Colore.ColoreAttivo){
// Setto la zona per accogliere le hitColore.Zona.left = 320;Colore.Zona.right = 0;Colore.Zona.bottom = 240;Colore.Zona.top = 0;Hit[Indice].Bersagli = 0;
33
// Setto il Dominio e il Focus per questa chiamataif (Colore.ColoreTrovato)
Colore.ColoreTrovato = TrovaColoreFocus();else
Colore.ColoreTrovato = TrovaColoreDominio();// Metto la zona trovata nel vettore delle catture
CopyRect(&Hit[Indice].Zona,&Colore.Zona);// Dopo NUMERO_CATTURE dò dei risultati più stabili
if(Indice >= (NUMERO_AQUISIZIONI - 1))return SetLento();
elseIndice++;
}return StatoCattura;
}UINT Intercetta::SetLento(){short i,Miss,Less;int Lunghezza,Larghezza;short Cont = 0;RECT Sum;
Miss = Less = 0;Indice = 0;Sum.left = Sum.right = Sum.top = Sum.bottom = 0;Larghezza = Colore.Lento.right - Colore.Lento.left;Lunghezza = Colore.Lento.top - Colore.Lento.bottom;for(i=0; i<NUMERO_AQUISIZIONI ; i++){
if(((Hit[i].Zona.right - Hit[i].Zona.left) <= (Larghezza + 10)) &&((Hit[i].Zona.top - Hit[i].Zona.bottom) <= (Lunghezza + 10)) &&(Hit[i].Zona.left < Hit[i].Zona.right))
{Sum.left = Sum.left + Hit[i].Zona.left;Sum.right = Sum.right + Hit[i].Zona.right;Sum.bottom = Sum.bottom + Hit[i].Zona.bottom;Sum.top = Sum.top + Hit[i].Zona.top;Cont++;
}if((Hit[i].Bersagli > 0) && (Hit[i].Bersagli < MASSIMO_LESS))
Less++;if(Hit[i].Bersagli == 0)
Miss++;}if(Cont > 0){
Colore.Lento.left = (Sum.left / Cont);Colore.Lento.right = (Sum.right / Cont);Colore.Lento.bottom = (Sum.bottom / Cont);Colore.Lento.top = (Sum.top / Cont);CopyRect(&Rettangoli.Lento,&Colore.Lento);
}if(Miss == NUMERO_AQUISIZIONI)
StatoCattura = STATO_CATTURA_ROSSO;if(((Miss > 2) && (Miss < NUMERO_AQUISIZIONI)) || (Less > 5))
StatoCattura = STATO_CATTURA_GIALLO;if((Miss <= 2) && (Less < 5))
StatoCattura = STATO_CATTURA_VERDE;return StatoCattura;
}bool Intercetta::TrovaColoreFocus(){int i,j;int X,Y;COLORE Colore;
Y = this->Colore.Focus.bottom;i = this->Colore.Focus.bottom * (320*3);while (i <= (this->Colore.Focus.top * (320*3))){
X = this->Colore.Focus.left;j = (this->Colore.Focus.left * 3);while (j <= (this->Colore.Focus.right * 3)){
Colore.Blu = Immagine.ibuff[(i+j)+0];Colore.Verde = Immagine.ibuff[(i+j)+1];Colore.Rosso = Immagine.ibuff[(i+j)+2];ConfrontaColore(Colore,X,Y);j = j + 3;X++;
}i = i + (320*3);Y++;
}if(Hit[Indice].Bersagli > LIMITE_BERSAGLI_MINIMO)
34
{Dominio();Focus();return true;
}return false;
}bool Intercetta::TrovaColoreDominio(){int i,j;int X,Y;COLORE Colore;
Y = this->Colore.Dominio.bottom;
i = this->Colore.Dominio.bottom * (320*3);while (i <= (this->Colore.Dominio.top * (320*3))){
X = this->Colore.Dominio.left;j = (this->Colore.Dominio.left * 3);while (j <= (this->Colore.Dominio.right * 3)){
Colore.Blu = Immagine.ibuff[(i+j)+0];Colore.Verde = Immagine.ibuff[(i+j)+1];Colore.Rosso = Immagine.ibuff[(i+j)+2];ConfrontaColore(Colore,X,Y);j = j + 3;X++;
}i = i + (320*3);Y++;
}if(Hit[Indice].Bersagli > LIMITE_BERSAGLI_MINIMO){
Focus();return true;
}return false;
}void Intercetta::ConfrontaColore(COLORE Colore,int X,int Y){
if((Colore.Verde >= this->Colore.VerdeMin) && (Colore.Verde <= this->Colore.VerdeMax) &&(Colore.Rosso >= this->Colore.RossoMin) && (Colore.Rosso <= this->Colore.RossoMax) &&(Colore.Blu >= this->Colore.BluMin) && (Colore.Blu <= this->Colore.BluMax))
{// Setto la Zona
if(X < this->Colore.Zona.left) this->Colore.Zona.left = X;if(Y > this->Colore.Zona.top) this->Colore.Zona.top = Y;if(X > this->Colore.Zona.right) this->Colore.Zona.right = X;if(Y < this->Colore.Zona.bottom) this->Colore.Zona.bottom = Y;
// Setto la HitHit[Indice].Bersagli++;
}}void Intercetta::Dominio(){short i;RECT Sum;
if(this->DominioAttivo){
// Setto il Dominioif(this->Colore.Lento.left < this->Colore.Dominio.left)
this->Colore.Dominio.left = this->Colore.Lento.left;if(this->Colore.Lento.top > this->Colore.Dominio.top)
this->Colore.Dominio.top = this->Colore.Lento.top;if(this->Colore.Lento.right > this->Colore.Dominio.right)
this->Colore.Dominio.right = this->Colore.Lento.right;if(this->Colore.Lento.bottom < this->Colore.Dominio.bottom)
this->Colore.Dominio.bottom = this->Colore.Lento.bottom;// Setto il progresso
if((Indice == 0) && (StatoCattura == STATO_CATTURA_VERDE)){
CopyRect(&Progresso[IndiceProgresso].Lento,&Colore.Lento);IndiceProgresso++;
}if(IndiceProgresso < 2)
this->StatoProgresso = STATO_PROGRESSO_INIZIO;if((IndiceProgresso >= 2) && (IndiceProgresso < 4))
this->StatoProgresso = STATO_PROGRESSO_UNO;if((IndiceProgresso >= 4) && (IndiceProgresso < 5))
this->StatoProgresso = STATO_PROGRESSO_DUE;if((IndiceProgresso >= 5) && (IndiceProgresso < NUMERO_AQUISIZIONI_PROGRESSO))
this->StatoProgresso = STATO_PROGRESSO_TRE;if(IndiceProgresso == NUMERO_AQUISIZIONI_PROGRESSO)
35
{Sum.left = Sum.right = Sum.top = Sum.bottom = 0;for(i=2; i<NUMERO_AQUISIZIONI_PROGRESSO ; i++){
Sum.left = Sum.left + Progresso[i].Lento.left;Sum.right = Sum.right + Progresso[i].Lento.right;Sum.bottom = Sum.bottom + Progresso[i].Lento.bottom;Sum.top = Sum.top + Progresso[i].Lento.top;
}Sum.left = Sum.left / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);Sum.right = Sum.right / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);Sum.bottom = Sum.bottom / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);Sum.top = Sum.top / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);
// setto una nuova cella della lista delle posizioniTemp = new(CELLA_POSIZIONE);CopyRect(&Temp->Posizione,&Sum);Temp->Stato = this->StatoPosiziona;Temp->Successivo = NULL;Temp->Precedente = Coda;Coda->Successivo = Temp;Coda = Temp;IndiceProgresso = 0;this->StatoProgresso = STATO_PROGRESSO_FINE;
}}
}
void Intercetta::Focus(){// Setto il Focus
if(this->Colore.Zona.left > BORDO_FOCUS)this->Colore.Focus.left = (this->Colore.Zona.left - BORDO_FOCUS);
elsethis->Colore.Focus.left = 0;
if(this->Colore.Zona.top < (this->Height - BORDO_FOCUS))this->Colore.Focus.top = (this->Colore.Zona.top + BORDO_FOCUS);
elsethis->Colore.Focus.top = (this->Height - 1);
if(this->Colore.Zona.right < (this->Width - BORDO_FOCUS))this->Colore.Focus.right = (this->Colore.Zona.right + BORDO_FOCUS);
elsethis->Colore.Focus.right = (this->Width - 1);
if(this->Colore.Zona.bottom > BORDO_FOCUS)this->Colore.Focus.bottom = (this->Colore.Zona.bottom - BORDO_FOCUS);
elsethis->Colore.Focus.bottom = 0;
}bool Intercetta::GetPosizioneII(){int errore;imgdes Temp;
Camera->GetFoto();errore = clienttoimage(FinestraSorgente,&Temp);errore = copyimage(&Temp,&Immagine);freeimage(&Temp);return true;
}
Decisore void Decisore::SetAttivo(bool Attivo) { this->Attivo = Attivo; }bool Decisore::GetAttivo() { return this->Attivo; }PUNTO *Decisore::GetPunto() { return &this->Punto; }
void Decisore::Set(RETTANGOLI *Rettangoli,CELLA_POSIZIONE *Posizione,int X,int Y){int i;
this->Rettangoli = Rettangoli;this->Contatore = 0;this->NumeroDiLinee = Rettangoli->Dominio.top - Rettangoli->Dominio.bottom;this->NumeroDiColonne = Rettangoli->Dominio.right - Rettangoli->Dominio.left;this->Click.Attinenze = 0;this->Punto.X = this->Punto.Y = 1;SetRect(&this->Click.Regione,0,0,0,0);Temp = new(CELLA_LINEA);Temp->NumeroLinea = NumeroDiLinee;;Temp->Linea = new SCOSTAMENTO[NumeroDiColonne];Temp->Precedente = NULL;Temp->Successivo = NULL;Testa = Coda = Temp;i = NumeroDiLinee;while(i > 0)
36
{Temp = new(CELLA_LINEA);Temp->NumeroLinea = i - 1;Temp->Linea = new SCOSTAMENTO[NumeroDiColonne];Temp->Precedente = Coda;Temp->Successivo = NULL;Coda->Successivo = Temp;Coda = Temp;i--;
}this->Attivo = true;
}void Decisore::Pulisci(){// Elimino gli elementi della lista e con essi gli array associati:
Indice = Coda->Precedente;delete[] Coda->Linea;delete Coda;while(Indice != NULL){
Coda = Indice;Indice = Coda->Precedente;delete[] Coda->Linea;delete Coda;
}Testa = Coda = Indice = Temp = NULL;
}UINT Decisore::GetDecisione(){
Contatore++;if(Contatore >= (NUMERO_AQUISIZIONI - 1)){
Contatore = 0;// Trovo il punto mediano di Lento:
X = (float)((Rettangoli->Lento.right + Rettangoli->Lento.left) / 2);Y = (float)((Rettangoli->Lento.top + Rettangoli->Lento.bottom) / 2);
// Controllo se il Punto mediano di Lento è nelle "vicinanze" del Dominio:if((X > (Rettangoli->Dominio.left - BORDO_DOMINIO)) &&
(X < Rettangoli->Dominio.left))X = (float)Rettangoli->Dominio.left;
if((X < (Rettangoli->Dominio.right + BORDO_DOMINIO)) &&(X > Rettangoli->Dominio.right))
X = (float)Rettangoli->Dominio.right;if((Y > (Rettangoli->Dominio.bottom - BORDO_DOMINIO)) &&
(Y < Rettangoli->Dominio.bottom))Y = (float)Rettangoli->Dominio.bottom;
if((Y < (Rettangoli->Dominio.top + BORDO_DOMINIO)) &&(Y > Rettangoli->Dominio.top))
Y = (float)Rettangoli->Dominio.top;// Se il Punto mediano di Lento è contenuto nel Dominio:
if((X >= Rettangoli->Dominio.left) &&(X <= Rettangoli->Dominio.right) &&(Y >= Rettangoli->Dominio.bottom) &&(Y <= Rettangoli->Dominio.top))
{// Lo setto nel SdR del Dominio:
X = X - Rettangoli->Dominio.left;Y = Y - Rettangoli->Dominio.bottom;
// Gli aggiungo lo Scostamento relativo:/* if((Scostamento = GetScostamento(X,Y)) != NULL)
{X = X + Scostamento->x;Y = Y + Scostamento->y;
}*/// Lo traslo sulla Finestra Applicazione e lo metto nell'area publica:
Punto.X = (1 - (X / NumeroDiColonne));Punto.Y = (1 - (Y / NumeroDiLinee));
// Controllo se il punto è attinente al Click:if((X >= Click.Regione.left) &&
(X <= Click.Regione.right) &&(Y >= Click.Regione.bottom) &&(Y <= Click.Regione.top))
{Click.Attinenze++;if(Click.Attinenze > NUMERO_AQUISIZIONI_AREA_CLICK){
Click.Attinenze = 0;SetRect(&this->Click.Regione,(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,
(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);return AN_RET_CLICK_TEMPORALE;
}}else
37
{Click.Attinenze = 0;SetRect(&this->Click.Regione,(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,
(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);}return AN_RET_SI_DECISIONE;
}}return AN_RET_NO_DECISIONE;
}Decisore::SCOSTAMENTO*Decisore::GetScostamento(long X,long Y){
Indice = Testa;while(Indice != NULL){
if(Temp->NumeroLinea == Y) return &Temp->Linea[X];Temp = Temp->Successivo;
}return NULL;
}
Thread principale void ThreadIntercettaFrontale(PVOID pvoid){UINT StatoCattura;UINT StatoProgresso;UINT TemporaneoCattura;UINT TemporaneoStato;UINT TemporaneoStatoProgresso;PPARAMS pparams = (PPARAMS) pvoid;
while (!pparams->Ucciso){
StatoCattura = STATO_CATTURA_INIZIO;if(pparams->Stato == STATO_ZERO){
pparams->Mago->Lock();while ((!pparams->Ucciso) && (pparams->Stato == STATO_ZERO)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();
InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);
}}if((pparams->Stato >= STATO_UNO) && (pparams->Stato <= STATO_NOVE)){
pparams->Mago->UnLock(pparams->Stato);TemporaneoStato = pparams->Stato;StatoProgresso = STATO_PROGRESSO_INIZIO;while ((!pparams->Ucciso) && (pparams->Stato == TemporaneoStato)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}TemporaneoStatoProgresso = pparams->Mago->GetProgresso();if(StatoProgresso != TemporaneoStatoProgresso){
StatoProgresso = TemporaneoStatoProgresso;if(StatoProgresso == STATO_PROGRESSO_FINE){
pparams->Angelo->HandShake(AN_ASK_FINE_STATO,AN_THREAD_INTERCETTA,pparams->Stato);
pparams->Mago->Lock();StatoProgresso = STATO_PROGRESSO_INIZIO;
}pparams->Angelo->Messaggio(AN_ASK_STATO_PROGRESSO_MUTATO,
pparams->Tipo,StatoProgresso);}Sleep(SLEEP_THREAD_FRONTALE);
}
38
}if(pparams->Stato == STATO_MANUALE){
pparams->Mago->UnLock(pparams->Stato);while ((!pparams->Ucciso) && (pparams->Stato == STATO_MANUALE)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);
}}if(pparams->Stato == STATO_LAVORO){
pparams->Mago->Lock();while ((!pparams->Ucciso) && (pparams->Stato == STATO_LAVORO)){
TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){
StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,
pparams->Tipo,StatoCattura);}switch (pparams->Oz->GetDecisione()){case AN_RET_NO_DECISIONE:
break;case AN_RET_SI_DECISIONE:
pparams->Angelo->Messaggio(AN_ASK_DECISIONE_AVVENUTA);break;
case AN_RET_CLICK_TEMPORALE:pparams->Angelo->Messaggio(AN_ASK_CLICK_TEMPORALE_AVVENUTA);break;
}Sleep(SLEEP_THREAD_FRONTALE);
}}
}pparams->Mago->Free();_endthread();
}
Thread di calibrazione void ThreadPosiziona(PVOID pvoid){UINT TemporaneoStato;PPARAMS_POSIZIONA pparams = (PPARAMS_POSIZIONA) pvoid;
Sleep(1000);while(!pparams->Ucciso){
InvalidateRect(pparams->hwnd,NULL,true);if(pparams->Stato <= STATO_NOVE){
TemporaneoStato = pparams->Stato;pparams->Angelo->HandShake(AN_ASK_INIZIO_STATO,AN_THREAD_POSIZIONA,
pparams->Stato);while((!pparams->Ucciso) && (pparams->Stato == TemporaneoStato))
Sleep(200);}else{
Sleep(2000);pparams->Angelo->HandShake(AN_ASK_FINE_SETTAGGIO,AN_THREAD_POSIZIONA,0);pparams->Ucciso = true;
}}pparams->Stato = STATO_ZERO;_endthread();
}
39
Appendice C Windows procedure delle sottofinestre
Tavolozza LRESULT CALLBACK PaletteFrontaleProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){HDC hdc;PAINTSTRUCT ps;static HPEN PennaContorno;static COLORE ColoreScelto;static CELLA_COLORE_LS *Testa,*Indice;static int Elementi;static bool TavolozzaAttiva;int i;
switch (message){
case WM_CREATE:PennaContorno = CreatePen(PS_SOLID,1,RGB(0,0,0));ColoreScelto.Blu = ColoreScelto.Verde = ColoreScelto.Rosso = 255;return 0;
case WM_PAINT:hdc = BeginPaint(hwnd,&ps);
if(TavolozzaAttiva){
SelectObject(hdc,PennaContorno);SelectObject(hdc,CreateSolidBrush
(RGB(ColoreScelto.Rosso,ColoreScelto.Verde,ColoreScelto.Blu)));
Rectangle(hdc,10,10,40,40);i = 0;Elementi = 0;for(Indice = Testa; Indice != NULL; Indice = Indice->Successivo){
SelectObject(hdc,CreateSolidBrush(RGB(Indice->Rosso,Indice->Verde,Indice->Blu)));
Rectangle(hdc,50+i,10,61+i,40);i = i + 10;Elementi++;DeleteObject(SelectObject(hdc,GetStockObject(WHITE_BRUSH)));
}}DeleteObject(SelectObject(hdc,GetStockObject(WHITE_BRUSH)));EndPaint(hwnd,&ps);return 0;
case WM_STOP_THREAD_I:TavolozzaAttiva = false;InvalidateRect(hwnd,NULL,true);return 0;
case WM_TAVOLOZZA_PRONTA:Testa = (CELLA_COLORE_LS*) lParam;TavolozzaAttiva = true;InvalidateRect(hwnd,NULL,true);return 0;
case WM_LBUTTONDOWN:int x,y ;x = LOWORD(lParam);y = HIWORD(lParam);if(TavolozzaAttiva){
Indice = Testa;if((y >= 10) && (y < 40) && (x >= 50) && (x < ((Elementi*10)+50)))
{for(i = 0; i < ((x-50)/10); i++)
Indice = Indice->Successivo;ColoreScelto.Blu = Indice->Blu;ColoreScelto.Verde = Indice->Verde;ColoreScelto.Rosso = Indice->Rosso;
Angelo.Messaggio(AN_ASK_INTERCETTA_COLORE,AN_WEB_FRONTALE,Indice->TipoColore);InvalidateRect(hwnd,NULL,true);
}}return 0;
case WM_DESTROY:
40
DeleteObject(PennaContorno);return 0;
}return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Immagine LRESULT CALLBACK ImmagineFrontaleProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){
HDC hdc;PAINTSTRUCT ps;
static PARAMS params;static CELLA_POSIZIONE *Testa,*Temp;static bool ImmagineAttiva,ColoreAttivo,PosizioniAttive;static HPEN PennaZona,PennaFocus,PennaDominio,PennaLento,PennaPosizioni;
switch (message){
case WM_CREATE:PennaZona = CreatePen(PS_SOLID,1,RGB(0,255,0));PennaDominio = CreatePen(PS_SOLID,1,RGB(255,0,0));PennaFocus = CreatePen(PS_SOLID,1,RGB(0,0,255));PennaLento = CreatePen(PS_SOLID,1,RGB(255,255,0));PennaPosizioni = CreatePen(PS_SOLID,1,RGB(255,0,255));ImmagineAttiva = false;ColoreAttivo = false;PosizioniAttive = false;params.Stato = STATO_ZERO;params.Tipo = AN_WEB_FRONTALE;params.hwnd = hwnd;params.Angelo = &Angelo;return 0;
case WM_PAINT:hdc = BeginPaint (hwnd, &ps);
if(ImmagineAttiva){imgdes* Immagine;
Immagine = params.Immagine;SetDIBitsToDevice(hdc,0,0, // X,Y Posizioni del DeviceContext(unsigned)Immagine->bmh->biWidth, // Larghezza Immagine(unsigned)Immagine->bmh->biHeight, // Altezza Immagine0,0, // Posizione di partenza nell'immagine0, // linea di partenza(unsigned)Immagine->bmh->biHeight, // Numero di linee da visualizzareImmagine->ibuff, // Indirizzo del Ibuffer(BITMAPINFO far *)Immagine->bmh, // Indirizzo del BITMAPINFODIB_RGB_COLORS); // Tipo di colore usato
}if(ColoreAttivo){SelectObject(hdc,PennaZona);MoveToEx(hdc,params.Rettangoli->Zona.left,(240-params.Rettangoli->Zona.top),NULL);LineTo(hdc,params.Rettangoli->Zona.left,(240-params.Rettangoli->Zona.bottom));LineTo(hdc,params.Rettangoli->Zona.right,(240-params.Rettangoli->Zona.bottom));LineTo(hdc,params.Rettangoli->Zona.right,(240-params.Rettangoli->Zona.top));LineTo(hdc,params.Rettangoli->Zona.left,(240-params.Rettangoli->Zona.top));SelectObject(hdc,PennaDominio);MoveToEx(hdc,params.Rettangoli->Dominio.left,
(240-params.Rettangoli->Dominio.top),NULL);LineTo(hdc,params.Rettangoli->Dominio.left,
(240-params.Rettangoli->Dominio.bottom));LineTo(hdc,params.Rettangoli->Dominio.right,
(240-params.Rettangoli->Dominio.bottom));LineTo(hdc,params.Rettangoli->Dominio.right,(240-params.Rettangoli->Dominio.top));LineTo(hdc,params.Rettangoli->Dominio.left,(240-params.Rettangoli->Dominio.top));SelectObject(hdc,PennaFocus);MoveToEx(hdc,params.Rettangoli->Focus.left,(240-params.Rettangoli->Focus.top),NULL);LineTo(hdc,params.Rettangoli->Focus.left,(240-params.Rettangoli->Focus.bottom));LineTo(hdc,params.Rettangoli->Focus.right,(240-params.Rettangoli->Focus.bottom));LineTo(hdc,params.Rettangoli->Focus.right,(240-params.Rettangoli->Focus.top));LineTo(hdc,params.Rettangoli->Focus.left,(240-params.Rettangoli->Focus.top));SelectObject(hdc,PennaLento);MoveToEx(hdc,params.Rettangoli->Lento.left,(240-params.Rettangoli->Lento.top),NULL);LineTo(hdc,params.Rettangoli->Lento.left,(240-params.Rettangoli->Lento.bottom));LineTo(hdc,params.Rettangoli->Lento.right,(240-params.Rettangoli->Lento.bottom));LineTo(hdc,params.Rettangoli->Lento.right,(240-params.Rettangoli->Lento.top));LineTo(hdc,params.Rettangoli->Lento.left,(240-params.Rettangoli->Lento.top));}if(PosizioniAttive){
SelectObject(hdc,PennaPosizioni);Temp = Testa;
41
while(Temp != NULL){
MoveToEx(hdc,Temp->Posizione.left,(240-Temp->Posizione.top),NULL);LineTo(hdc,Temp->Posizione.left,(240-Temp->Posizione.bottom));LineTo(hdc,Temp->Posizione.right,(240-Temp->Posizione.bottom));LineTo(hdc,Temp->Posizione.right,(240-Temp->Posizione.top));LineTo(hdc,Temp->Posizione.left,(240-Temp->Posizione.top));Temp = Temp->Successivo;
}}
EndPaint (hwnd, &ps);return 0 ;case WM_SINGOLA_IMMAGINE_I:
params.Immagine = (imgdes*) lParam;ImmagineAttiva = true;InvalidateRect(hwnd,NULL,true);return 0;
case WM_COLORE_PRONTO:params.Rettangoli = (RETTANGOLI*) lParam;ColoreAttivo = true;InvalidateRect(hwnd,NULL,true);return 0;
case WM_POSIZIONI_PRONTE:Testa = ((CELLA_POSIZIONE*) lParam)->Successivo;PosizioniAttive = true;return 0;
case WM_START_THREAD_I:params.Oz = (Decisore*) wParam;params.Mago = (Intercetta*) lParam;params.Ucciso = false;ColoreAttivo = true;ImmagineAttiva = true;_beginthread(ThreadIntercettaFrontale,0,¶ms);return 0;
case WM_STATO_MUTATO:params.Stato = (UINT) lParam;break;
case WM_STOP_THREAD_I:params.Ucciso = true;ImmagineAttiva = false;ColoreAttivo = false;PosizioniAttive = false;InvalidateRect(hwnd,NULL,true);return 0;
case WM_SINGOLA_IMMAGINE_II:params.Immagine = (imgdes*) lParam;ImmagineAttiva = true;InvalidateRect(hwnd,NULL,true);return 0;
case WM_START_THREAD_II:params.Mago = (Intercetta*) lParam;params.Ucciso = false;ImmagineAttiva = true;_beginthread(ThreadIntercettaII,0,¶ms);return 0;
case WM_STOP_THREAD_II:params.Ucciso = true;ImmagineAttiva = false;return 0;
case WM_DESTROY:DeleteObject(PennaZona);DeleteObject(PennaFocus);DeleteObject(PennaDominio);DeleteObject(PennaLento);DeleteObject(PennaPosizioni);params.Ucciso = true;return 0;
}return DefWindowProc(hwnd,message,wParam,lParam);
}
Controlli LRESULT CALLBACK ControlliProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){const short VELOCITA_THREAD_FRONTALE = 1;const short VELOCITA_THREAD_LATERALE = 4;const short AQUISIZIONI = 7;const short CLICK_TEMPORALE = 10;const short BORDO_CLICH_TEMPORALE = 13;const short FOCUS = 16;HDC hdc;
42
PAINTSTRUCT ps;HINSTANCE hInstance;TCHAR Buffer[4];static RECT Invalido;static UINT StatoFrontale,StatoLaterale;static HPEN PennaContorno;static HBRUSH PennelloSfondo,PennelloNero,PennelloStatoCatturaRosso,
PennelloStatoCatturaGiallo,PennelloStatoCatturaVerde,PennelloStatoCatturaInizio,PennelloRossoSpento,PennelloGialloSpento,PennelloVerdeSpento;
static HWND SleepThreadFrontale,LabelSleepThreadFrontale,ValueSleepThreadFrontale,SleepThreadLaterale,LabelSleepThreadLaterale,ValueSleepThreadLaterale,NumeroAquisizioni,LabelNumeroAquisizioni,ValueNumeroAquisizioni,ClickTemporale,LabelClickTemporale,ValueClickTemporale,BordoClickTemporale,LabelBordoClickTemporale,ValueBordoClickTemporale,Focus,LabelFocus,ValueFocus;
static bool Hit;static int cxClient,cyClient;
switch (message){
case WM_CREATE:hInstance = (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE);PennaContorno = CreatePen(PS_SOLID,1,RGB(0,0,0));PennelloSfondo = CreateSolidBrush(RGB(170,170,170));PennelloNero = CreateSolidBrush(RGB(100,100,100));PennelloStatoCatturaRosso = CreateSolidBrush(RGB(255,0,0));PennelloStatoCatturaGiallo = CreateSolidBrush(RGB(255,255,0));PennelloStatoCatturaVerde = CreateSolidBrush(RGB(0,255,0));PennelloStatoCatturaInizio = CreateSolidBrush(RGB(255,255,255));PennelloRossoSpento = CreateSolidBrush(RGB(100,0,0));PennelloGialloSpento = CreateSolidBrush(RGB(100,100,0));PennelloVerdeSpento = CreateSolidBrush(RGB(0,100,0));SleepThreadFrontale = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 1,hInstance,NULL);
LabelSleepThreadFrontale = CreateWindow(TEXT ("static"),TEXT ("Velocità thread Frontale"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 2,hInstance,NULL);
ValueSleepThreadFrontale = CreateWindow(TEXT ("static"),TEXT ("10"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 3,hInstance,NULL);
SetScrollRange(SleepThreadFrontale,SB_CTL,1,100,false);SetScrollPos(SleepThreadFrontale,SB_CTL,10,false);SleepThreadLaterale = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 4,hInstance,NULL);
LabelSleepThreadLaterale = CreateWindow(TEXT ("static"),TEXT ("Velocità thread Laterale"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 5,hInstance,NULL);
ValueSleepThreadLaterale = CreateWindow(TEXT ("static"),TEXT ("10"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 6,hInstance,NULL);
SetScrollRange(SleepThreadLaterale,SB_CTL,1,100,false);SetScrollPos(SleepThreadLaterale,SB_CTL,10,false);NumeroAquisizioni = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 7,hInstance,NULL);
LabelNumeroAquisizioni = CreateWindow(TEXT ("static"),TEXT ("Aquisizioni Lento"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 8,hInstance,NULL);
ValueNumeroAquisizioni = CreateWindow(TEXT ("static"),TEXT ("10"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 9,hInstance,NULL);
SetScrollRange(NumeroAquisizioni,SB_CTL,1,30,false);SetScrollPos(NumeroAquisizioni,SB_CTL,10,false);ClickTemporale = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 10,hInstance,NULL);
LabelClickTemporale = CreateWindow(TEXT ("static"),TEXT ("Aquisizioni Click"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 11,hInstance,NULL);
ValueClickTemporale = CreateWindow(TEXT ("static"),TEXT ("4"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 12,hInstance,NULL);
SetScrollRange(ClickTemporale,SB_CTL,1,20,false);SetScrollPos(ClickTemporale,SB_CTL,4,false);BordoClickTemporale = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 13,hInstance,NULL);
43
LabelBordoClickTemporale = CreateWindow(TEXT ("static"),TEXT ("Dimensione Click"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 14,hInstance,NULL);
ValueBordoClickTemporale = CreateWindow(TEXT ("static"),TEXT ("2"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 15,hInstance,NULL);
SetScrollRange(BordoClickTemporale,SB_CTL,1,10,false);SetScrollPos(BordoClickTemporale,SB_CTL,2,false);Focus = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 16,hInstance,NULL);
LabelFocus = CreateWindow(TEXT ("static"),TEXT ("Dimensione Focus"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 17,hInstance,NULL);
ValueFocus = CreateWindow(TEXT ("static"),TEXT ("20"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 18,hInstance,NULL);
SetScrollRange(Focus,SB_CTL,1,30,false);SetScrollPos(Focus,SB_CTL,20,false);StatoFrontale = STATO_CATTURA_INIZIO;StatoLaterale = STATO_CATTURA_INIZIO;return 0;
case WM_SIZE:cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);return 0;
case WM_PAINT:MoveWindow(SleepThreadFrontale,(cxClient*3/17),
(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelSleepThreadFrontale,(cxClient*3/17),
(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueSleepThreadFrontale,(cxClient*5/17),
(cyClient - 70),(cxClient*1/17),20,true);if(Angelo.GetDueWeb()){
MoveWindow(SleepThreadLaterale,(cxClient*3/17),(cyClient - 30),(cxClient*2/17),20,true);
MoveWindow(LabelSleepThreadLaterale,(cxClient*3/17),(cyClient - 50),(cxClient*3/17),20,true);
MoveWindow(ValueSleepThreadLaterale,(cxClient*5/17),(cyClient - 30),(cxClient*1/17),20,true);
}else{
MoveWindow(SleepThreadLaterale,0,0,0,0,true);MoveWindow(LabelSleepThreadLaterale,0,0,0,0,true);MoveWindow(ValueSleepThreadLaterale,0,0,0,0,true);
}MoveWindow(NumeroAquisizioni,(cxClient*6/17),
(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelNumeroAquisizioni,(cxClient*6/17),
(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueNumeroAquisizioni,(cxClient*8/17),
(cyClient - 70),(cxClient*1/17),20,true);MoveWindow(ClickTemporale,(cxClient*9/17),(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelClickTemporale,(cxClient*9/17),
(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueClickTemporale,(cxClient*11/17),
(cyClient - 70),(cxClient*1/17),20,true);MoveWindow(BordoClickTemporale,(cxClient*9/17),
(cyClient - 30),(cxClient*2/17),20,true);MoveWindow(LabelBordoClickTemporale,(cxClient*9/17),
(cyClient - 50),(cxClient*3/17),20,true);MoveWindow(ValueBordoClickTemporale,(cxClient*11/17),
(cyClient - 30),(cxClient*1/17),20,true);MoveWindow(Focus,(cxClient*12/17),(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelFocus,(cxClient*12/17),(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueFocus,(cxClient*14/17),(cyClient - 70),(cxClient*1/17),20,true);hdc = BeginPaint(hwnd,&ps);SelectObject(hdc,PennelloSfondo);Rectangle(hdc,0,0,cxClient,100);SelectObject(hdc,PennaContorno);SelectObject(hdc,PennelloNero);RoundRect(hdc,(cxClient*1/17),10,(cxClient*1/17)+20,70,5,5);SelectObject(hdc,PennelloRossoSpento);RoundRect(hdc,(cxClient*1/17),10,(cxClient*1/17)+20,30,20,20);SelectObject(hdc,PennelloGialloSpento);RoundRect(hdc,(cxClient*1/17),30,(cxClient*1/17)+20,50,20,20);SelectObject(hdc,PennelloVerdeSpento);RoundRect(hdc,(cxClient*1/17),50,(cxClient*1/17)+20,70,20,20);if(Angelo.GetDueWeb())
44
{SelectObject(hdc,PennelloNero);RoundRect(hdc,(cxClient*2/17),10,(cxClient*2/17)+20,70,5,5);SelectObject(hdc,PennelloRossoSpento);RoundRect(hdc,(cxClient*2/17),10,(cxClient*2/17)+20,30,20,20);SelectObject(hdc,PennelloGialloSpento);RoundRect(hdc,(cxClient*2/17),30,(cxClient*2/17)+20,50,20,20);SelectObject(hdc,PennelloVerdeSpento);RoundRect(hdc,(cxClient*2/17),50,(cxClient*2/17)+20,70,20,20);
}switch (StatoFrontale){case STATO_CATTURA_INIZIO:
break;case STATO_CATTURA_VERDE:
SelectObject(hdc,PennelloStatoCatturaVerde);RoundRect(hdc,(cxClient*1/17),50,(cxClient*1/17)+20,70,20,20);break;
case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);RoundRect(hdc,(cxClient*1/17),30,(cxClient*1/17)+20,50,20,20);break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);RoundRect(hdc,(cxClient*1/17),10,(cxClient*1/17)+20,30,20,20);break;
}if(Angelo.GetDueWeb()){
switch (StatoLaterale){case STATO_CATTURA_INIZIO:
break;case STATO_CATTURA_VERDE:
SelectObject(hdc,PennelloStatoCatturaVerde);RoundRect(hdc,(cxClient*2/17),50,(cxClient*2/17)+20,70,20,20);break;
case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);RoundRect(hdc,(cxClient*2/17),30,(cxClient*2/17)+20,50,20,20);break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);RoundRect(hdc,(cxClient*2/17),10,(cxClient*2/17)+20,30,20,20);break;
}}
EndPaint (hwnd, &ps);return 0;
case WM_STATO_CATTURA_MUTATO_FRONTALE:StatoFrontale = (UINT) lParam;SetRect(&Invalido,(cxClient*1/17),10,(cxClient*1/17)+20,70);InvalidateRect(hwnd,&Invalido,true);return 0;
case WM_STATO_CATTURA_MUTATO_LATERALE:StatoLaterale = (UINT) lParam;SetRect(&Invalido,(cxClient*2/17),10,(cxClient*2/17)+20,70);InvalidateRect(hwnd,&Invalido,true);return 0;
case WM_HSCROLL:Hit = false;switch(GetWindowLong((HWND) lParam,GWL_ID)){case VELOCITA_THREAD_FRONTALE:
switch(LOWORD(wParam)){case SB_LINEUP:
SLEEP_THREAD_FRONTALE = max(1,SLEEP_THREAD_FRONTALE - 1);Hit = true;break;
case SB_LINEDOWN:SLEEP_THREAD_FRONTALE = min(100,SLEEP_THREAD_FRONTALE + 1);Hit = true;break;
case SB_THUMBTRACK:SLEEP_THREAD_FRONTALE = HIWORD(wParam);Hit = true;break;
case SB_PAGEDOWN:SLEEP_THREAD_FRONTALE = min(100,SLEEP_THREAD_FRONTALE + 10);Hit = true;break;
45
case SB_PAGEUP:SLEEP_THREAD_FRONTALE = max(1,SLEEP_THREAD_FRONTALE - 10);Hit = true;break;
}if(Hit){
SetScrollPos(SleepThreadFrontale,SB_CTL,SLEEP_THREAD_FRONTALE,true);wsprintf(Buffer,TEXT("%i"),SLEEP_THREAD_FRONTALE);SetWindowText(ValueSleepThreadFrontale,Buffer);SetRect(&Invalido,(cxClient*5/17),(cyClient - 70),
(cxClient*6/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);
}break;
case VELOCITA_THREAD_LATERALE:switch(LOWORD(wParam)){case SB_LINEUP:
SLEEP_THREAD_LATERALE = max(1,SLEEP_THREAD_LATERALE - 1);Hit = true;break;
case SB_LINEDOWN:SLEEP_THREAD_LATERALE = min(100,SLEEP_THREAD_LATERALE + 1);Hit = true;break;
case SB_THUMBTRACK:SLEEP_THREAD_LATERALE = HIWORD(wParam);Hit = true;break;
case SB_PAGEDOWN:SLEEP_THREAD_LATERALE = min(100,SLEEP_THREAD_LATERALE + 10);Hit = true;break;
case SB_PAGEUP:SLEEP_THREAD_LATERALE = max(1,SLEEP_THREAD_LATERALE - 10);Hit = true;break;
}if(Hit){
SetScrollPos(SleepThreadLaterale,SB_CTL,SLEEP_THREAD_LATERALE,true);wsprintf(Buffer,TEXT("%i"),SLEEP_THREAD_LATERALE);SetWindowText(ValueSleepThreadLaterale,Buffer);SetRect(&Invalido,(cxClient*5/17),
(cyClient - 30),(cxClient*6/17),(cyClient - 10));InvalidateRect(hwnd,&Invalido,true);
}break;
case AQUISIZIONI:switch(LOWORD(wParam)){case SB_LINEUP:
NUMERO_AQUISIZIONI = max(1,NUMERO_AQUISIZIONI - 1);Hit = true;break;
case SB_LINEDOWN:NUMERO_AQUISIZIONI = min(30,NUMERO_AQUISIZIONI + 1);Hit = true;break;
case SB_THUMBTRACK:NUMERO_AQUISIZIONI = HIWORD(wParam);Hit = true;break;
case SB_PAGEDOWN:NUMERO_AQUISIZIONI = min(30,NUMERO_AQUISIZIONI + 10);Hit = true;break;
case SB_PAGEUP:NUMERO_AQUISIZIONI = max(1,NUMERO_AQUISIZIONI - 10);Hit = true;break;
}if(Hit){
SetScrollPos(NumeroAquisizioni,SB_CTL,NUMERO_AQUISIZIONI,true);wsprintf(Buffer,TEXT("%i"),NUMERO_AQUISIZIONI);SetWindowText(ValueNumeroAquisizioni,Buffer);SetRect(&Invalido,(cxClient*8/17),(cyClient - 70),
(cxClient*9/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);
}break;
46
case CLICK_TEMPORALE:switch(LOWORD(wParam)){case SB_LINEUP:NUMERO_AQUISIZIONI_AREA_CLICK = max(1,NUMERO_AQUISIZIONI_AREA_CLICK - 1);
Hit = true;break;
case SB_LINEDOWN:NUMERO_AQUISIZIONI_AREA_CLICK = min(20,NUMERO_AQUISIZIONI_AREA_CLICK + 1);
Hit = true;break;
case SB_THUMBTRACK:NUMERO_AQUISIZIONI_AREA_CLICK = HIWORD(wParam);Hit = true;break;
case SB_PAGEDOWN:NUMERO_AQUISIZIONI_AREA_CLICK = min(20,NUMERO_AQUISIZIONI_AREA_CLICK + 10);
Hit = true;break;
case SB_PAGEUP:NUMERO_AQUISIZIONI_AREA_CLICK = max(1,NUMERO_AQUISIZIONI_AREA_CLICK - 10);
Hit = true;break;
}if(Hit){
SetScrollPos(ClickTemporale,SB_CTL,NUMERO_AQUISIZIONI_AREA_CLICK,true);wsprintf(Buffer,TEXT("%i"),NUMERO_AQUISIZIONI_AREA_CLICK);SetWindowText(ValueClickTemporale,Buffer);SetRect(&Invalido,(cxClient*11/17),(cyClient - 70),
(cxClient*12/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);
}break;
case BORDO_CLICH_TEMPORALE:switch(LOWORD(wParam)){case SB_LINEUP:
BORDO_CLICK = max(1,BORDO_CLICK - 1);Hit = true;break;
case SB_LINEDOWN:BORDO_CLICK = min(10,BORDO_CLICK + 1);Hit = true;break;
case SB_THUMBTRACK:BORDO_CLICK = HIWORD(wParam);Hit = true;break;
case SB_PAGEDOWN:BORDO_CLICK = min(10,BORDO_CLICK + 2);Hit = true;break;
case SB_PAGEUP:BORDO_CLICK = max(1,BORDO_CLICK - 2);Hit = true;break;
}if(Hit){
SetScrollPos(BordoClickTemporale,SB_CTL,BORDO_CLICK,true);wsprintf(Buffer,TEXT("%i"),BORDO_CLICK);SetWindowText(ValueBordoClickTemporale,Buffer);SetRect(&Invalido,(cxClient*11/17),(cyClient - 30),
(cxClient*12/17),(cyClient - 10));InvalidateRect(hwnd,&Invalido,true);
}break;
case FOCUS:switch(LOWORD(wParam)){case SB_LINEUP:
BORDO_FOCUS = max(1,BORDO_FOCUS - 1);Hit = true;break;
case SB_LINEDOWN:BORDO_FOCUS = min(30,BORDO_FOCUS + 1);Hit = true;break;
case SB_THUMBTRACK:BORDO_FOCUS = HIWORD(wParam);Hit = true;break;
47
case SB_PAGEDOWN:BORDO_FOCUS = min(30,BORDO_FOCUS + 5);Hit = true;break;
case SB_PAGEUP:BORDO_FOCUS = max(1,BORDO_FOCUS - 5);Hit = true;break;
}if(Hit){
SetScrollPos(Focus,SB_CTL,BORDO_FOCUS,true);wsprintf(Buffer,TEXT("%i"),BORDO_FOCUS);SetWindowText(ValueFocus,Buffer);SetRect(&Invalido,(cxClient*14/17),(cyClient - 70),
(cxClient*15/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);
}break;
}return 0;
case WM_DESTROY:DeleteObject(PennaContorno);DeleteObject(PennelloSfondo);DeleteObject(PennelloNero);DeleteObject(PennelloStatoCatturaRosso);DeleteObject(PennelloStatoCatturaGiallo);DeleteObject(PennelloStatoCatturaVerde);DeleteObject(PennelloStatoCatturaInizio);DeleteObject(PennelloRossoSpento);DeleteObject(PennelloGialloSpento);DeleteObject(PennelloVerdeSpento);return 0;
}return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Posiziona LRESULT CALLBACK PosizionaProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){
HDC hdc;PAINTSTRUCT ps;
static PARAMS_POSIZIONA params;static HPEN PennaPuntoRosso,PennaPuntoGiallo,PennaPuntoVerde,PennaPuntoInizio,
PennaBackRosso,PennaBackGiallo,PennaBackVerde,PennaBackInizio;RECT Rect;
static UINT StatoCatturaFrontale,StatoCatturaLaterale,StatoProgresso;switch (message){case WM_CREATE:
PennaPuntoRosso = CreatePen(PS_SOLID,18,RGB(255,0,0));PennaPuntoGiallo = CreatePen(PS_SOLID,18,RGB(255,255,0));PennaPuntoVerde = CreatePen(PS_SOLID,18,RGB(0,255,0));PennaPuntoInizio = CreatePen(PS_SOLID,18,RGB(0,255,0));PennaBackRosso = CreatePen(PS_SOLID,20,RGB(255,0,0));PennaBackGiallo = CreatePen(PS_SOLID,20,RGB(255,255,0));PennaBackVerde = CreatePen(PS_SOLID,20,RGB(0,255,0));PennaBackInizio = CreatePen(PS_SOLID,20,RGB(255,255,255));params.hwnd = hwnd;params.Angelo = &Angelo;params.Stato = STATO_ZERO;StatoCatturaFrontale = STATO_CATTURA_INIZIO;StatoCatturaLaterale = STATO_CATTURA_INIZIO;StatoProgresso = STATO_PROGRESSO_INIZIO;return 0;
case WM_PAINT:hdc = BeginPaint(hwnd,&ps);
switch (StatoProgresso){case STATO_PROGRESSO_INIZIO:
SelectObject(hdc,PennaBackInizio);break;
case STATO_PROGRESSO_UNO:SelectObject(hdc,PennaBackRosso);break;
case STATO_PROGRESSO_DUE:SelectObject(hdc,PennaBackGiallo);break;
case STATO_PROGRESSO_TRE:SelectObject(hdc,PennaBackVerde);break;
48
}GetClientRect(hwnd,&Rect);switch (params.Stato){case STATO_UNO:
DrawText(hdc,TEXT ("Segui il pallino !"),-1,&Rect,DT_SINGLELINE | DT_CENTER );
MoveToEx(hdc,Rect.right/2,Rect.bottom/2,NULL);LineTo(hdc,Rect.right/2,Rect.bottom/2);break;
case STATO_DUE:MoveToEx(hdc,10,Rect.bottom/2,NULL);LineTo(hdc,10,Rect.bottom/2);break;
case STATO_TRE:MoveToEx(hdc,10,Rect.bottom-10,NULL);LineTo(hdc,10,Rect.bottom-10);break;
case STATO_QUATTRO:MoveToEx(hdc,Rect.right/2,Rect.bottom-10,NULL);LineTo(hdc,Rect.right/2,Rect.bottom-10);break;
case STATO_CINQUE:MoveToEx(hdc,Rect.right-10,Rect.bottom-10,NULL);LineTo(hdc,Rect.right-10,Rect.bottom-10);break;
case STATO_SEI:MoveToEx(hdc,Rect.right-10,Rect.bottom/2,NULL);LineTo(hdc,Rect.right-10,Rect.bottom/2);break;
case STATO_SETTE:MoveToEx(hdc,Rect.right-10,10,NULL);LineTo(hdc,Rect.right-10,10);break;
case STATO_OTTO:MoveToEx(hdc,Rect.right/2,10,NULL);LineTo(hdc,Rect.right/2,10);break;
case STATO_NOVE:MoveToEx(hdc,10,10,NULL);LineTo(hdc,10,10);break;
case STATO_FINE:DrawText(hdc,TEXT ("Settaggio concluso !"),-1,
&Rect,DT_SINGLELINE | DT_CENTER );break;
}switch (StatoCatturaFrontale){case STATO_CATTURA_INIZIO:case STATO_CATTURA_VERDE:
switch (StatoCatturaLaterale){case STATO_CATTURA_INIZIO:case STATO_CATTURA_VERDE:
SelectObject(hdc,PennaPuntoVerde);break;
case STATO_CATTURA_GIALLO:SelectObject(hdc,PennaPuntoGiallo);break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennaPuntoRosso);break;
}break;
case STATO_CATTURA_GIALLO:switch (StatoCatturaLaterale){case STATO_CATTURA_INIZIO:case STATO_CATTURA_VERDE:case STATO_CATTURA_GIALLO:
SelectObject(hdc,PennaPuntoGiallo);break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennaPuntoRosso);break;
}break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennaPuntoRosso);break;
}switch (params.Stato)
49
{case STATO_UNO:
MoveToEx(hdc,Rect.right/2,Rect.bottom/2,NULL);LineTo(hdc,Rect.right/2,Rect.bottom/2);break;
case STATO_DUE:MoveToEx(hdc,10,Rect.bottom/2,NULL);LineTo(hdc,10,Rect.bottom/2);break;
case STATO_TRE:MoveToEx(hdc,10,Rect.bottom-10,NULL);LineTo(hdc,10,Rect.bottom-10);break;
case STATO_QUATTRO:MoveToEx(hdc,Rect.right/2,Rect.bottom-10,NULL);LineTo(hdc,Rect.right/2,Rect.bottom-10);break;
case STATO_CINQUE:MoveToEx(hdc,Rect.right-10,Rect.bottom-10,NULL);LineTo(hdc,Rect.right-10,Rect.bottom-10);break;
case STATO_SEI:MoveToEx(hdc,Rect.right-10,Rect.bottom/2,NULL);LineTo(hdc,Rect.right-10,Rect.bottom/2);break;
case STATO_SETTE:MoveToEx(hdc,Rect.right-10,10,NULL);LineTo(hdc,Rect.right-10,10);break;
case STATO_OTTO:MoveToEx(hdc,Rect.right/2,10,NULL);LineTo(hdc,Rect.right/2,10);break;
case STATO_NOVE:MoveToEx(hdc,10,10,NULL);LineTo(hdc,10,10);break;
case STATO_FINE:DrawText(hdc,TEXT ("Settaggio concluso !"),-1,&Rect,
DT_SINGLELINE | DT_CENTER );break;
}EndPaint(hwnd,&ps);return 0 ;case WM_STATO_CATTURA_MUTATO_FRONTALE:
StatoCatturaFrontale = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;
case WM_STATO_CATTURA_MUTATO_LATERALE:StatoCatturaLaterale = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;
case WM_STATO_PROGRESSO_MUTATO:StatoProgresso = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;
case WM_STATO_MUTATO:params.Stato = (UINT) lParam;return 0;
case WM_START_POSIZIONA:params.Stato = STATO_UNO;StatoCatturaFrontale = STATO_CATTURA_INIZIO;StatoCatturaLaterale = STATO_CATTURA_INIZIO;params.Ucciso= false;_beginthread(ThreadPosiziona,0,¶ms);return 0;
case WM_STOP_POSIZIONA:params.Ucciso = true;return 0;
case WM_DESTROY:params.Ucciso = true;DeleteObject(PennaPuntoRosso);DeleteObject(PennaPuntoGiallo);DeleteObject(PennaPuntoVerde);DeleteObject(PennaPuntoInizio);DeleteObject(PennaBackRosso);DeleteObject(PennaBackGiallo);DeleteObject(PennaBackVerde);DeleteObject(PennaBackInizio);return 0;
}return DefWindowProc(hwnd,message,wParam,lParam) ;
}
51
Appendice D Windows procedure delle applicazioni indipendenti
Applicazione Uno: addestramento LRESULT CALLBACK DemoUnoProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){const short DIMENSIONE_BERSAGLIO = 40;const short BORDO_INVALIDO = 5;
HDC hdc;PAINTSTRUCT ps;
static PUNTO *Punto,Precedente;static bool Click,PuntoAttivo;static UINT StatoCattura;static HPEN PennaPunto,PennaBersaglio,PennaContorno;static HBRUSH PennelloStatoCatturaRosso,PennelloStatoCatturaGiallo,
PennelloStatoCatturaVerde,PennelloBersaglio;static RECT Bersaglio,Invalido;static int cxClient,cyClient;
switch (message){case WM_CREATE:
PennaPunto = CreatePen(PS_SOLID,10,RGB(0,0,255));PennaBersaglio = CreatePen(PS_SOLID,2,RGB(255,0,0));PennaContorno = CreatePen(PS_SOLID,1,RGB(0,0,0));PennelloStatoCatturaRosso = CreateSolidBrush(RGB(255,0,0));PennelloStatoCatturaGiallo = CreateSolidBrush(RGB(255,255,0));PennelloStatoCatturaVerde = CreateSolidBrush(RGB(0,255,0));PennelloBersaglio = CreateSolidBrush(RGB(255,255,255));PuntoAttivo = false;Click = false;StatoCattura = STATO_CATTURA_INIZIO;return 0;
case WM_SIZE:cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);Bersaglio.left = (rand() % (cxClient - DIMENSIONE_BERSAGLIO));Bersaglio.top = (rand() % (cyClient - DIMENSIONE_BERSAGLIO));Bersaglio.right = Bersaglio.left + DIMENSIONE_BERSAGLIO;Bersaglio.bottom = Bersaglio.top + DIMENSIONE_BERSAGLIO;return 0;
case WM_CREATE_PUNTO:Punto = (PUNTO*) lParam;Precedente.X = Punto->X;Precedente.Y = Punto->Y;PuntoAttivo = true;return 0;
case WM_NON_VALIDO:if(Precedente.X >= Punto->X){
Invalido.left = (int)(Punto->X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Precedente.X * cxClient) + BORDO_INVALIDO;
}else{
Invalido.left = (int)(Precedente.X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Punto->X * cxClient) + BORDO_INVALIDO;
}if(Precedente.Y >= Punto->Y)
{Invalido.top = (int)(Punto->Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Precedente.Y * cyClient) + BORDO_INVALIDO;
}else{
Invalido.top = (int)(Precedente.Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Punto->Y * cyClient) + BORDO_INVALIDO;
}InvalidateRect(hwnd,&Invalido,true);return 0;
case WM_CLICK:Click = true;InvalidateRect(hwnd,NULL,true);return 0;
52
case WM_STATO_CATTURA_MUTATO:StatoCattura = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;
case WM_PAINT:hdc = BeginPaint(hwnd,&ps);switch (StatoCattura){case STATO_CATTURA_VERDE:
SelectObject(hdc,PennelloStatoCatturaVerde);break;
case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);break;
}SelectObject(hdc,PennaContorno);RoundRect(hdc,10,10,30,50,10,10);SelectObject(hdc,PennaBersaglio);SelectObject(hdc,PennelloBersaglio);Rectangle(hdc,Bersaglio.left,Bersaglio.top,Bersaglio.right,Bersaglio.bottom);if(PuntoAttivo){
if(Click){
Click = false;if(((int)(Punto->X * cxClient) >= Bersaglio.left) &&
((int)(Punto->X * cxClient) <= Bersaglio.right) &&((int)(Punto->Y * cyClient) >= Bersaglio.top) &&((int)(Punto->Y * cyClient) <= Bersaglio.bottom))
{Bersaglio.left = (rand() % (cxClient - DIMENSIONE_BERSAGLIO));Bersaglio.top = (rand() % (cyClient - DIMENSIONE_BERSAGLIO));Bersaglio.right = Bersaglio.left + DIMENSIONE_BERSAGLIO;Bersaglio.bottom = Bersaglio.top + DIMENSIONE_BERSAGLIO;InvalidateRect(hwnd,NULL,true);
}}SelectObject(hdc,PennaPunto);MoveToEx(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient),NULL);LineTo(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient));Precedente.X = Punto->X;Precedente.Y = Punto->Y;
}EndPaint(hwnd,&ps);return 0;
case WM_DESTROY:DeleteObject(PennaPunto);DeleteObject(PennaBersaglio);DeleteObject(PennaContorno);DeleteObject(PennelloStatoCatturaRosso);DeleteObject(PennelloStatoCatturaGiallo);DeleteObject(PennelloStatoCatturaVerde);DeleteObject(PennelloBersaglio);return 0;
}return DefWindowProc (hwnd,message,wParam,lParam) ;
}
Applicazione Due: tastiera #include "Spaziatore.h"
LRESULT CALLBACK DemoDueProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){const short BORDO_INVALIDO = 5;
HDC hdc;PAINTSTRUCT ps;TCHAR Buffer[3];
static PUNTO *Punto,Precedente;static bool Click,PuntoAttivo;static RECT Invalido;static HPEN PennaPunto,PennaCella,PennaFrase,PennaClick;static HBRUSH PennelloStatoCatturaRosso,PennelloStatoCatturaGiallo,
PennelloStatoCatturaVerde;static UINT StatoCattura;static HWND Scroll,Label,Value,Frase;static Spaziatore Griglia;HINSTANCE hInstance;static int Definizione,cxClient,cyClient;
53
bool Hit;switch (message){case WM_CREATE:
hInstance = (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE);PennaPunto = CreatePen(PS_SOLID,10,RGB(255,0,0));PennaClick = CreatePen(PS_SOLID,10,RGB(0,255,0));PennaCella = CreatePen(PS_SOLID,2,RGB(40,190,230));PennaFrase = CreatePen(PS_SOLID,1,RGB(0,0,0));PennelloStatoCatturaRosso = CreateSolidBrush(RGB(255,0,0));PennelloStatoCatturaGiallo = CreateSolidBrush(RGB(255,255,0));PennelloStatoCatturaVerde = CreateSolidBrush(RGB(0,255,0));StatoCattura = STATO_CATTURA_VERDE;PuntoAttivo = false;Click = false;Definizione = 1;Scroll = CreateWindow(TEXT ("scrollbar"),NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 1,hInstance,NULL) ;
SetScrollRange(Scroll,SB_CTL,1,5,FALSE);SetScrollPos(Scroll,SB_CTL,1,FALSE);Label = CreateWindow(TEXT ("static"),TEXT ("Dimensione Tasti"),
WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 2,hInstance,NULL);
Value = CreateWindow(TEXT ("static"),TEXT ("1"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 3,hInstance,NULL);
return 0;case WM_SIZE:
cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);Griglia.Set(cxClient,(cyClient - 80),Definizione);MoveWindow(Scroll,(cxClient-140),(cyClient - 40),100,20,true);MoveWindow(Label,(cxClient-200),(cyClient - 60),160,20,true);MoveWindow(Value,(cxClient-200),(cyClient - 40),60,20,true);return 0;
case WM_CREATE_PUNTO:Punto = (PUNTO*) lParam;Precedente.X = Punto->X;Precedente.Y = Punto->Y;PuntoAttivo = true;return 0;
case WM_NON_VALIDO:if(Precedente.X >= Punto->X){
Invalido.left = (int)(Punto->X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Precedente.X * cxClient) + BORDO_INVALIDO;
}else{
Invalido.left = (int)(Precedente.X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Punto->X * cxClient) + BORDO_INVALIDO;
}if(Precedente.Y >= Punto->Y){
Invalido.top = (int)(Punto->Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Precedente.Y * cyClient) + BORDO_INVALIDO;
}else{
Invalido.top = (int)(Precedente.Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Punto->Y * cyClient) + BORDO_INVALIDO;
}InvalidateRect(hwnd,&Invalido,true);return 0;
case WM_CLICK:Griglia.GetFrase((int)(Punto->X * cxClient),(int)(Punto->Y * cyClient));Click = true;if(Precedente.X >= Punto->X){
Invalido.left = (int)(Punto->X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Precedente.X * cxClient) + BORDO_INVALIDO;
}else{
Invalido.left = (int)(Precedente.X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Punto->X * cxClient) + BORDO_INVALIDO;
}if(Precedente.Y >= Punto->Y){
Invalido.top = (int)(Punto->Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Precedente.Y * cyClient) + BORDO_INVALIDO;
}
54
else{
Invalido.top = (int)(Precedente.Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Punto->Y * cyClient) + BORDO_INVALIDO;
}InvalidateRect(hwnd,&Invalido,true);SetRect(&Invalido,10,(cyClient - 60),(cxClient - 200),(cyClient - 20));InvalidateRect(hwnd,&Invalido,true);return 0;
case WM_STATO_CATTURA_MUTATO:StatoCattura = (UINT) lParam;SetRect(&Invalido,(cxClient-30),(cyClient-60),(cxClient-10),(cyClient-20));InvalidateRect(hwnd,NULL,true);return 0;
case WM_PAINT:hdc = BeginPaint (hwnd, &ps);SelectObject(hdc,PennaCella);MoveToEx(hdc,0,0,NULL);while(Griglia.Get()){
MoveToEx(hdc,Griglia.Cella.left,Griglia.Cella.top,NULL);LineTo(hdc,Griglia.Cella.left,Griglia.Cella.bottom);LineTo(hdc,Griglia.Cella.right,Griglia.Cella.bottom);LineTo(hdc,Griglia.Cella.right,Griglia.Cella.top);LineTo(hdc,Griglia.Cella.left,Griglia.Cella.top);TextOut(hdc,(Griglia.Cella.left + 5),(Griglia.Cella.top +5),
&Griglia.Lettera,1);}TextOut(hdc,5,5,TEXT ("Canc"),4);SelectObject(hdc,PennaFrase);SelectObject(hdc,WHITE_BRUSH);RoundRect(hdc,10,(cyClient - 60),(cxClient - 220),(cyClient - 20),10,10);TextOut(hdc,15,(cyClient - 50),(char*)&Griglia.Frase,Griglia.ContatoreFrase);switch (StatoCattura){case STATO_CATTURA_VERDE:
SelectObject(hdc,PennelloStatoCatturaVerde);break;
case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);break;
case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);break;
}RoundRect(hdc,(cxClient - 30),(cyClient - 60),(cxClient - 10),(cyClient - 20),10,10);if(PuntoAttivo){
if(Click){
SelectObject(hdc,PennaClick);Click = false;
}else
SelectObject(hdc,PennaPunto);MoveToEx(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient),NULL);LineTo(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient));Precedente.X = Punto->X;Precedente.Y = Punto->Y;
}EndPaint (hwnd, &ps);return 0;
case WM_HSCROLL:Hit = false;switch(LOWORD(wParam)){case SB_LINEUP:
Definizione = max(1,Definizione - 1);Hit = true;break;
case SB_LINEDOWN:Definizione = min(5,Definizione + 1);Hit = true;break;
case SB_THUMBTRACK:Definizione = HIWORD(wParam);Hit = true;break;
case SB_PAGEDOWN:Definizione = min(5,Definizione + 2);Hit = true;break;
case SB_PAGEUP:
55
Definizione = max(1,Definizione - 2);Hit = true;break;
}if(Hit){
Griglia.Set(cxClient,(cyClient - 80),Definizione);SetScrollPos(Scroll,SB_CTL,Definizione,true);wsprintf(Buffer,TEXT("%i"),Definizione);SetWindowText(Value,Buffer);InvalidateRect(hwnd,NULL,true);
}return 0;
case WM_DESTROY:DeleteObject(PennaPunto);DeleteObject(PennaCella);DeleteObject(PennaFrase);DeleteObject(PennelloStatoCatturaRosso);DeleteObject(PennelloStatoCatturaGiallo);DeleteObject(PennelloStatoCatturaVerde);return 0;
}return DefWindowProc (hwnd,message,wParam,lParam) ;
}
Spaziatore #include <math.h>
class Spaziatore{public:
RECT Cella;char Lettera;char Frase[100];short ContatoreFrase;
public:inline Spaziatore() {}void Set(int,int,short);bool Get();void GetFrase(int,int);
private:int A,B,X,Y;short Definizione;short NumeroCelle,NumeroCelleX,NumeroCelleY,
ContatoreX,ContatoreY,ContatoreLettere;static char Celle1[25],Celle2[31],Celle3[41],Celle4[51],Celle5[81];char* Lettere;
};
char Spaziatore::Celle1[25] = " abcdefghilmnopqrstuvz'";char Spaziatore::Celle2[31] = " abcdefghijklmnopqrstuvwxyz'\"";char Spaziatore::Celle3[41] = " abcdefghijklmnopqrstuvwxyz'\"0123456789";char Spaziatore::Celle4[51] = " abcdefghijklmnopqrstuvwxyz'\"0123456789,.;:*+-<>_";char Spaziatore::Celle5[81] = " abcdefghijklmnopqrstuvwxyz'\"0123456789àèéìòù^°ç§\\|£$%&()[],.-
;:_<>@#!+*/ ";
void Spaziatore::Set(int A,int B,short Definizione){
this->A = A;this->B = B;switch (Definizione){case 1:
NumeroCelle = 24;NumeroCelleX = 6;NumeroCelleY = 4;Lettere = (char*)&Celle1;break;
case 2:NumeroCelle = 30;NumeroCelleX = 6;NumeroCelleY = 5;Lettere = (char*)&Celle2;break;
case 3:NumeroCelle = 40;NumeroCelleX = 8;NumeroCelleY = 5;Lettere = (char*)&Celle3;break;
case 4:
56
NumeroCelle = 50;NumeroCelleX = 10;NumeroCelleY = 5;Lettere = (char*)&Celle4;break;
case 5:NumeroCelle = 80;NumeroCelleX = 10;NumeroCelleY = 8;Lettere = (char*)&Celle5;break;
}if((A > 0) && (B > 0)){
X = A / NumeroCelleX;Y = B / NumeroCelleY;
}for(ContatoreFrase = 0; ContatoreFrase < 100; ContatoreFrase++)
Frase[ContatoreFrase] = ' ';ContatoreFrase = ContatoreLettere = ContatoreX = ContatoreY = 0;
}bool Spaziatore::Get(){
if(ContatoreY < NumeroCelleY){
SetRect(&Cella,ContatoreX * X,ContatoreY * Y,(ContatoreX + 1) * X,(ContatoreY + 1) * Y);
Lettera = Lettere[ContatoreLettere];if(ContatoreX < (NumeroCelleX - 1))
ContatoreX++;else{
ContatoreX = 0;ContatoreY++;
}ContatoreLettere++;return true;
}ContatoreLettere = ContatoreY = 0;return false;
}void Spaziatore::GetFrase(int x,int y){short PosizioneX,PosizioneY;
if((x <= (NumeroCelleX * X)) && (y <= (NumeroCelleY * Y))){
PosizioneX = x / X;PosizioneY = y / Y;if(ContatoreFrase == 100){
for(ContatoreFrase = 0; ContatoreFrase < 100; ContatoreFrase++)Frase[ContatoreFrase] = ' ';
ContatoreFrase = 0;}if((PosizioneX == 0) && (PosizioneY == 0)){
if(ContatoreFrase > 0){
ContatoreFrase--;Frase[ContatoreFrase] = ' ';
}}else{
Frase[ContatoreFrase] = Lettere[(NumeroCelleX * PosizioneY) + PosizioneX];ContatoreFrase++;
}}
}
Applicazione Tre LRESULT CALLBACK DemoTreProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){
HDC hdc;PAINTSTRUCT ps;
static PUNTO *Punto;static HPEN PennaPunto;static bool PuntoAttivo;static RECT Rect;
switch (message){
57
case WM_CREATE:PennaPunto = CreatePen(PS_SOLID,10,RGB(0,255,0));PuntoAttivo = false;return 0;
case WM_SIZE:GetClientRect(hwnd,&Rect);return 0;
case WM_CREATE_PUNTO:Punto = (PUNTO*) lParam;PuntoAttivo = true;return 0;
case WM_NON_VALIDO:InvalidateRect(hwnd,NULL,true);return 0;
case WM_PAINT:hdc = BeginPaint (hwnd, &ps);SelectObject(hdc,PennaPunto);if (PuntoAttivo){
MoveToEx(hdc,(int)(Punto->X * Rect.right),(int)(Punto->Y * Rect.bottom),NULL);
LineTo(hdc,(int)(Punto->X * Rect.right),(int)(Punto->Y * Rect.bottom));}EndPaint (hwnd, &ps);return 0;
case WM_DESTROY:DeleteObject(PennaPunto);return 0;
}return DefWindowProc (hwnd,message,wParam,lParam) ;
}
59
Appendice E Messaggi definiti da Windows
Resource #define IDR_MENUPRINCIPALE 101#define IDI_ICONA_PRINCIPALE 102#define IDD_OLE_PROPPAGE_LARGE 105#define IDC_OK 1034#define IDC_STATIC_VAR_NOME 1036#define IDC_VAR_IMAGEWIDTH 1036#define IDC_STATIC_NOME 1037#define IDC_VAR_IMAGEHEIGHT 1039#define IDC_VAR_LIVEWINDOW 1041#define IDC_VAR_OVERLAYWINDOW 1043#define IDC_VAR_SCALE 1045#define IDC_VAR_USINGDEFAULTPALETTE 1047#define IDC_VAR_AUDIOHARDWARE 1049#define IDC_VAR_CAPFILEEXISTS 1051#define IDC_VAR_CAPTURINGNOW 1053#define IDC_STATIC_STATO 1054#define IDC_STATIC_CAPACITA 1055#define IDC_VAR_HASOVERLAY 1057#define IDC_VAR_HASDLGVIDEOSOURCE 1059#define IDC_VAR_HASDLGVIDEOFORMAT 1061#define IDC_VAR_HASDLGVIDEODISPLAY 1063#define IDC_VAR_NOMEDRIVER 1066#define IDC_OK_PROPRIETA 1069#define ID_CATTURA_AGGANCIO 40001#define ID_CATTURA_DATI 40002#define ID_CATTURA_RILASCIO 40003#define ID_CATTURA_FOTO 40004#define ID_CATTURA_FINESTRA_FORMATO 40005#define ID_CATTURA_FINESTRA_SORGENTE 40006#define ID_CATTURA_SETTAGGI_OVERLAY 40008#define ID_CATTURA_SETTAGGI_INFORMAZIONI 40009#define ID_CATTURA_ONOFF_2WEB 40012#define ID_CATTURA_TARGET_FILE 40015#define ID_CATTURA_SETTAGGI_SETTAGGIUNO 40016#define ID_CATTURA_PROPRIETA_PROPRIETAUNO 40016#define ID_CATTURA_SETTAGGI_SETTAGGIDUE 40017#define ID_CATTURA_PROPRIETA_PROPRIETADUE 40017#define ID_INTERCETTA_VISUALIZZA_FRONTALE 40018#define ID_INTERCETTA_START_FRONTALE 40020#define ID_INTERCETTA_STOP_FRONTALE 40021#define ID_INTERCETTA_VISUALIZZA_FRONTALE_II 40022#define ID_INTERCETTA_START_FRONTALE_II 40023#define ID_INTERCETTA_STOP_FRONTALE_II 40024#define ID_INTERCETTA_PALETTE_FRONTALE 40025#define ID_INTERCETTA_VISUALIZZA_LATERALE 40031#define ID_INTERCETTA_START_LATERALE 40032#define ID_INTERCETTA_STOP_LATERALE 40033#define ID_INTERCETTA_PALETTE_LATERALE 40034#define ID_POSIZIONA_VAI 40052#define ID_POSIZIONA_AUTOMATICO 40052#define ID_FINESTRE_FINESTRAIMMAGINEFRONTALE 40053#define ID_FINESTRE_FINESTRAIMMAGINELATERALE 40054#define ID_FINESTRE_FINESTRAPALETTEFRONTALE 40055#define ID_FINESTRE_FINESTRAPALETTELATERALE 40056#define ID_FINESTRE_FINESTRACONTROLLI 40057#define ID_FINESTRE_FINESTRAWEBUNO 40058#define ID_FINESTRE_FINESTRAWEBDUE 40059#define ID_FINESTRE_FINESTRAPOSIZIONA 40060#define ID_POSIZIONA_INTERROMPI 40061#define ID_POSIZIONA_MANUALE 40062#define ID_POSIZIONA_STOP 40063#define ID_APPLICAZIONI_DEMOUNO 40065#define ID_POSIZIONA_OK 40066#define ID_APPLICAZIONI_DEMODUE 40067#define ID_APPLICAZIONI_DEMOTRE 40068#define ID_POSIZIONA_RITORNA 40069
// Next default values for new objects//#ifdef APSTUDIO_INVOKED
60
#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE 103#define _APS_NEXT_COMMAND_VALUE 40070#define _APS_NEXT_CONTROL_VALUE 1070#define _APS_NEXT_SYMED_VALUE 101#endif#endif
Oggetti relativi alla gestione
Menu void GestoreMenu::Set(HMENU MenuPrincipale,PCUBE Cube,GestoreFinestre *Finestre){
this->MenuPrincipale = MenuPrincipale;this->Finestre = Finestre;this->Cube = Cube;
}void GestoreMenu::Messaggio(UINT Messaggio){
switch (Messaggio){
// Gestione dei comandi di Cattura:case AN_OK_AGGANCIO:
EnableMenuItem(MenuPrincipale,ID_CATTURA_AGGANCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_FORMATO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_SORGENTE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FOTO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_OVERLAY,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_INFORMAZIONI,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_TARGET_FILE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIUNO,MF_ENABLED);if(Cube->DueWeb){
EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIDUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_ENABLED);
}EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_ENABLED);break;
case AN_OK_RILASCIO:EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_FORMATO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_SORGENTE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FOTO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_OVERLAY,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_INFORMAZIONI,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_TARGET_FILE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIDUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II ,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_AGGANCIO,MF_ENABLED);break;
// Gestione dei comandi di posiziona:
case AN_OK_START_POSIZIONA:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_CHECKED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_GRAYED);
61
EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_INTERROMPI,MF_ENABLED);break;
case AN_OK_STOP_POSIZIONA:if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_LATERALE))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_UNO))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_DUE))
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_CHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_UNCHECKED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_INTERROMPI,MF_GRAYED);break;
case AN_OK_START_MANUALE:EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_STOP,MF_ENABLED);break;
case AN_OK_STOP_MANUALE:EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_STOP,MF_GRAYED);break;
case AN_OK_OK:EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_GRAYED);//...EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE ,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE ,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_RITORNA,MF_ENABLED);break;
case AN_OK_RITORNA:EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_ENABLED);//...EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE ,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE ,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_ENABLED);
62
EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_RITORNA,MF_GRAYED);break;
// Gestione sottomenu Applicazioni:
case AN_OK_APPLICAZIONE_PRESCELTA:if(Finestre->GetPresceltaApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO)){
CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_CHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_UNCHECKED);//...
}if(Finestre->GetPresceltaApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE)){
CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_CHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_UNCHECKED);//...
}if(Finestre->GetPresceltaApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE)){
CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_CHECKED);//...
}//...break;
// Gestione dei comandi per l'Intercettazione manuale:
case AN_OK_VISUALIZZA_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);break;
case AN_OK_VISUALIZZA_FRONTALE_II:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);break;
case AN_OK_VISUALIZZA_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_LATERALE,MF_ENABLED);break;
case AN_OK_COLORE_SCELTO_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_ENABLED);break;
case AN_OK_COLORE_SCELTO_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_LATERALE,MF_ENABLED);break;
// Thread Frontale:
case AN_OK_START_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);break;
case AN_OK_STOP_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);break;
// Thread Laterale:
63
case AN_OK_START_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_ENABLED);break;
case AN_OK_STOP_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);break;
// Thread Frontale II:
case AN_OK_START_FRONTALE_II:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE_II,MF_ENABLED);break;
case AN_OK_STOP_FRONTALE_II:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);break;
// Gestione Due Web:
case AN_OK_DUE_WEB_NO:CheckMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_UNCHECKED);break;
case AN_OK_DUE_WEB_SI:CheckMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_CHECKED);break;
// Gestione Finestra Immagine Frontale:
case AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_UNCHECKED);break;
case AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_CHECKED);break;
// Gestione Finestra Immagine Laterale:
case AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_UNCHECKED);break;
case AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_CHECKED);break;
// Gestione Finestra Palette Frontale:
case AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_UNCHECKED);break;
case AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_CHECKED);break;
// Gestione Finestra Palette Laterale:
case AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_UNCHECKED);break;
case AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_CHECKED);break;
// Gestione Finestra Semafori:
case AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_UNCHECKED);break;
case AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI:
64
CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_CHECKED);break;
// Gestione Finestra Web Uno:
case AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_UNCHECKED);break;
case AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_CHECKED);break;
// Gestione Finestra Web Due:
case AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_UNCHECKED);break;
case AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_CHECKED);break;
}}
Errore void GestoreErrore::SetMessageBox(char* Testo){ MessageBox(FinestraPrincipale,TEXT (Testo),szAppName,MB_ICONINFORMATION | MB_OK);}void GestoreErrore::Set(HWND FinestraPrincipale,char* szAppName){
this->FinestraPrincipale = FinestraPrincipale;this->szAppName = szAppName;
}void GestoreErrore::Messaggio(UINT Messaggio){
switch (Messaggio){case AN_NO_AGGANCIO:
SetMessageBox("Non riesco ad agganciarmi alla finestra, Aggancio fallito!");break;
case AN_NO_DATI:SetMessageBox("Non riesco ad agganciare i dati della cattura, Aggancio fallito!");break;
case AN_NO_FINESTRA_FORMATO:SetMessageBox("Non esiste finestra di dialogo per il formato!");break;
case AN_NO_FORMATO_ERRORE:SetMessageBox("Errore nel visualizzare la finestra di dialogo per il formato!");break;
case AN_NO_FINESTRA_SORGENTE:SetMessageBox("Non posso aprire finestra del sorgente!");break;
case AN_NO_SORGENTE_ERRORE:SetMessageBox("Errore nel visualizzare la finestra di dialogo per il sorgente!");break;
case AN_NO_FOTO:SetMessageBox("Non riesco a fare una cattura!");
break;case AN_NO_FILE:
SetMessageBox("Problemi a salvare su file");break;
case AN_NO_ELABORAZIONE:SetMessageBox("Problemi a elaborare l'immagine");break;
case AN_NO_RILASCIO_WEB_UNO:SetMessageBox("Non riesco a disconnettere Camera1!");break;
case AN_NO_RILASCIO_WEB_DUE:SetMessageBox("Non riesco a disconnettere Camera2!");break;
case AN_NO_ALLOCIMAGE:SetMessageBox("Non Riesco a riservare memoria per l'immagine!");break;
}}
Finestre void GestoreFinestre::Set(HWND FinestraPrincipale){short i;
this->FinestraPrincipale = FinestraPrincipale;
65
// Creazione della lista delle Finestre Secondarie:
Testa = Coda = Indice = NULL;// Cella della Finestra Principale:
Temp = new(CELLA_FINESTRA);Temp->hwndFinestra = FinestraPrincipale;Temp->Tipo = FINESTRA_PRINCIPALE;Temp->Visibile = true; // IninfluenteTemp->Successivo = NULL;Testa = Coda = Temp;
for(i=FINESTRA_IMMAGINE_FRONTALE; i <= FINESTRA_CONTROLLI; i++){
Temp = new(CELLA_FINESTRA);Temp->hwndFinestra = NULL;Temp->Tipo = i;Temp->Visibile = true;Temp->Successivo = NULL;Coda->Successivo = Temp;Coda = Temp;
}// Cella della Finestra Posiziona:
Temp = new(CELLA_FINESTRA);Temp->hwndFinestra = NULL;Temp->Tipo = FINESTRA_POSIZIONA;Temp->Visibile = false;Temp->Successivo = NULL;Coda->Successivo = Temp;Coda = Temp;
// Creazione della lista delle Finestre di Applicazione:
TestaLA = CodaLA = IndiceLA = NULL;}
// Setto l'handler delle Finestre Secondarie e d'Applicazione:
void GestoreFinestre::SetFinestreSecondarie(UINT Tipo,HWND hwndFinestra){
Temp = Testa;while(Temp != NULL){
if(Temp->Tipo == Tipo)Temp->hwndFinestra = hwndFinestra;
Temp = Temp->Successivo;}
}void GestoreFinestre::SetFinestreApplicazione(UINT Tipo,HWND Finestra,bool Prescelta){
if(TestaLA == NULL){
TempLA = new (CELLA_APPLICAZIONE);TempLA->Tipo = Tipo;TempLA->hwndApplicazione = Finestra;TempLA->Visibile = false;TempLA->Prescelta = Prescelta;TempLA->Successivo = NULL;TestaLA = CodaLA = TempLA;
}else{
TempLA = new (CELLA_APPLICAZIONE);TempLA->Tipo = Tipo;TempLA->hwndApplicazione = Finestra;TempLA->Visibile = false;TempLA->Prescelta = Prescelta;TempLA->Successivo = NULL;CodaLA->Successivo = TempLA;CodaLA = TempLA;
}}
// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:
HWND GestoreFinestre::GetFinestreSecondarie(UINT Tipo){
Temp = Testa;while(Temp != NULL){
if(Temp->Tipo == Tipo)return Temp->hwndFinestra;
Temp = Temp->Successivo;
66
}return NULL;
}HWND GestoreFinestre::GetFinestreApplicazione(UINT Tipo){
TempLA = TestaLA;while(TempLA != NULL){
if(TempLA->Tipo == Tipo)return TempLA->hwndApplicazione;
TempLA = TempLA->Successivo;}return NULL;
}HWND GestoreFinestre::GetFinestreApplicazione(){
TempLA = TestaLA;while(TempLA != NULL){
if(TempLA->Prescelta)return TempLA->hwndApplicazione;
TempLA = TempLA->Successivo;}return NULL;
}// Controllo la visibilità delle Finestre Secondarie e d'Applicazione:
bool GestoreFinestre::GetVisibileSecondarie(UINT Tipo){
Temp = Testa;while(Temp != NULL){
if(Temp->Tipo == Tipo)return Temp->Visibile;
Temp = Temp->Successivo;}return false;
}bool GestoreFinestre::GetVisibileApplicazione(UINT Tipo){
TempLA = TestaLA;while(TempLA != NULL){
if(TempLA->Tipo == Tipo)return TempLA->Visibile;
TempLA = TempLA->Successivo;}return false;
}
// Setto la visibilità delle Finestre Secondarie:
void GestoreFinestre::SetVisibileSecondarie(UINT Tipo,bool Visibile){
Temp = Testa;while(Temp != NULL){
if(Temp->Tipo == Tipo)Temp->Visibile = Visibile;
Temp = Temp->Successivo;}
}
// Setto l'Applicazione prescelta:
void GestoreFinestre::SetPresceltaApplicazione(UINT Tipo){
TempLA = TestaLA;while(TempLA != NULL){
if(TempLA->Tipo == Tipo)TempLA->Prescelta = true;
elseTempLA->Prescelta = false;
TempLA = TempLA->Successivo;}
}
// Verifico se l'Applicazione è prescelta:
bool GestoreFinestre::GetPresceltaApplicazione(UINT Tipo){
TempLA = TestaLA;
67
while(TempLA != NULL){
if(TempLA->Tipo == Tipo)return TempLA->Prescelta;
TempLA = TempLA->Successivo;}return false;
}
// Gestore dei Messaggi:
void GestoreFinestre::Messaggio(UINT Messaggio){
switch (Messaggio){case FINESTRA_POSIZIONA:
Temp = Testa;while(Temp != NULL){
if(Temp->Tipo != FINESTRA_POSIZIONA){
Temp->Ripristino = Temp->Visibile;Temp->Visibile = false;
}else
Temp->Visibile = true;Temp = Temp->Successivo;
}break;
case FINESTRE_ATTIVE:Temp = Testa;while(Temp != NULL){
if(Temp->Tipo != FINESTRA_POSIZIONA)Temp->Visibile = Temp->Ripristino;
elseTemp->Visibile = false;
Temp = Temp->Successivo;}break;
case FINESTRA_APPLICAZIONE:Temp = Testa;while(Temp != NULL){
Temp->Ripristino = Temp->Visibile;Temp->Visibile = false;Temp = Temp->Successivo;
}TempLA = TestaLA;while(TempLA != NULL){
if(TempLA->Prescelta){
TempLA->Visibile = true;break;
}TempLA = TempLA->Successivo;
}break;
case FINESTRE_SECONDARIE:Temp = Testa;while(Temp != NULL){
Temp->Visibile = Temp->Ripristino;Temp = Temp->Successivo;
}TempLA = TestaLA;while(TempLA != NULL){
if(TempLA->Prescelta){
TempLA->Visibile = false;break;
}TempLA = TempLA->Successivo;
}break;
}}
68
Procedure di selezione comandi
Menu Cattura void MenuAggancio(WPARAM wParam,Supervisore* Angelo){
switch (LOWORD (wParam)){case ID_CATTURA_AGGANCIO:
Angelo->Messaggio(AN_ASK_AGGANCIO,AN_WEB_FRONTALE);break;
case ID_CATTURA_FINESTRA_FORMATO:Angelo->Messaggio(AN_ASK_FINESTRA_FORMATO,AN_WEB_FRONTALE);break;
case ID_CATTURA_FINESTRA_SORGENTE:Angelo->Messaggio(AN_ASK_FINESTRA_SORGENTE,AN_WEB_FRONTALE);break;
case ID_CATTURA_FOTO:Angelo->Messaggio(AN_ASK_FOTO,AN_WEB_FRONTALE);break;
case ID_CATTURA_RILASCIO:Angelo->Messaggio(AN_ASK_RILASCIO,AN_WEB_FRONTALE);break;
/* case ID_CATTURA_SETTAGGI_OVERLAY:if(!CameraUno->OverLay())
Angelo->SetMessageBox("Problemi sull'overlay");break;
*/ case ID_CATTURA_SETTAGGI_SETTAGGIUNO:Angelo->Messaggio(AN_ASK_DLG_PROPRIETA,AN_WEB_FRONTALE);break;
case ID_CATTURA_ONOFF_2WEB:Angelo->Messaggio(AN_ONOFF_DUE_WEB);break;
case ID_CATTURA_TARGET_FILE:Angelo->Messaggio(AN_ASK_FILE,AN_WEB_FRONTALE);break;
}if (Angelo->GetDueWeb()){
switch (LOWORD (wParam)){case ID_CATTURA_AGGANCIO:
Angelo->Messaggio(AN_ASK_AGGANCIO,AN_WEB_LATERALE);break;
case ID_CATTURA_FINESTRA_FORMATO:Angelo->Messaggio(AN_ASK_FINESTRA_FORMATO,AN_WEB_LATERALE);break;
case ID_CATTURA_FINESTRA_SORGENTE:Angelo->Messaggio(AN_ASK_FINESTRA_SORGENTE,AN_WEB_LATERALE);break;
case ID_CATTURA_FOTO:Angelo->Messaggio(AN_ASK_FOTO,AN_WEB_LATERALE);break;
case ID_CATTURA_RILASCIO:Angelo->Messaggio(AN_ASK_RILASCIO,AN_WEB_LATERALE);break;
case ID_CATTURA_SETTAGGI_SETTAGGIDUE:Angelo->Messaggio(AN_ASK_DLG_PROPRIETA,AN_WEB_LATERALE);break;
case ID_CATTURA_TARGET_FILE:Angelo->Messaggio(AN_ASK_FILE,AN_WEB_LATERALE);break;}
}}
Menu Intercetta void MenuIntercetta(WPARAM wParam,Supervisore* Angelo){
switch (LOWORD (wParam)){case ID_INTERCETTA_PALETTE_FRONTALE:
Angelo->Messaggio(AN_ASK_PALETTE,AN_WEB_FRONTALE);break;
case ID_INTERCETTA_PALETTE_LATERALE:Angelo->Messaggio(AN_ASK_PALETTE,AN_WEB_LATERALE);break;
case ID_INTERCETTA_VISUALIZZA_FRONTALE:Angelo->Messaggio(AN_ASK_VISUALIZZA,AN_WEB_FRONTALE);
69
break;case ID_INTERCETTA_START_FRONTALE:
Angelo->Messaggio(AN_ASK_START,AN_WEB_FRONTALE);break;
case ID_INTERCETTA_STOP_FRONTALE:Angelo->Messaggio(AN_ASK_STOP,AN_WEB_FRONTALE);break;
case ID_INTERCETTA_VISUALIZZA_LATERALE:Angelo->Messaggio(AN_ASK_VISUALIZZA,AN_WEB_LATERALE);break;
case ID_INTERCETTA_START_LATERALE:Angelo->Messaggio(AN_ASK_START,AN_WEB_LATERALE);break;
case ID_INTERCETTA_STOP_LATERALE:Angelo->Messaggio(AN_ASK_STOP,AN_WEB_LATERALE);break;
case ID_INTERCETTA_VISUALIZZA_FRONTALE_II:Angelo->Messaggio(AN_ASK_VISUALIZZA_II,AN_WEB_FRONTALE);break;
case ID_INTERCETTA_START_FRONTALE_II:Angelo->Messaggio(AN_ASK_START_II,AN_WEB_FRONTALE);break;
case ID_INTERCETTA_STOP_FRONTALE_II:Angelo->Messaggio(AN_ASK_STOP_II,AN_WEB_FRONTALE);break;
}}
Menu Posiziona void MenuPosiziona(WPARAM wParam,Supervisore* Angelo){
switch (LOWORD (wParam)){case ID_POSIZIONA_AUTOMATICO:
Angelo->Messaggio(AN_ASK_START_POSIZIONA);break;
case ID_POSIZIONA_INTERROMPI:Angelo->Messaggio(AN_ASK_STOP_POSIZIONA);break;
case ID_POSIZIONA_MANUALE:Angelo->Messaggio(AN_ASK_START_MANUALE);break;
case ID_POSIZIONA_STOP:Angelo->Messaggio(AN_ASK_STOP_MANUALE);break;
case ID_POSIZIONA_OK:Angelo->Messaggio(AN_ASK_OK);break;
case ID_POSIZIONA_RITORNA:Angelo->Messaggio(AN_ASK_RITORNA);break;
}}
Menu Applicazioni void MenuDemo(WPARAM wParam,Supervisore* Angelo){
switch (LOWORD (wParam)){case ID_APPLICAZIONI_DEMOUNO:
Angelo->Prescelta(FINESTRA_APPLICAZIONE_DEMO_UNO);break;
case ID_APPLICAZIONI_DEMODUE:Angelo->Prescelta(FINESTRA_APPLICAZIONE_DEMO_DUE);break;
case ID_APPLICAZIONI_DEMOTRE:Angelo->Prescelta(FINESTRA_APPLICAZIONE_DEMO_TRE);break;
//...}
}
Menu finestre void MenuFinestre(WPARAM wParam,Supervisore* Angelo){
switch (LOWORD (wParam))
70
{case ID_FINESTRE_FINESTRAIMMAGINEFRONTALE:
Angelo->Messaggio(AN_ASK_FINESTRA_IMMAGINE_FRONTALE);break;
case ID_FINESTRE_FINESTRAIMMAGINELATERALE:Angelo->Messaggio(AN_ASK_FINESTRA_IMMAGINE_LATERALE);break;
case ID_FINESTRE_FINESTRAPALETTEFRONTALE:Angelo->Messaggio(AN_ASK_FINESTRA_PALETTE_FRONTALE);break;
case ID_FINESTRE_FINESTRAPALETTELATERALE:Angelo->Messaggio(AN_ASK_FINESTRA_PALETTE_LATERALE);break;
case ID_FINESTRE_FINESTRACONTROLLI:Angelo->Messaggio(AN_ASK_FINESTRA_CONTROLLI);break;
case ID_FINESTRE_FINESTRAWEBUNO:Angelo->Messaggio(AN_ASK_FINESTRA_WEB_UNO);break;
case ID_FINESTRE_FINESTRAWEBDUE:Angelo->Messaggio(AN_ASK_FINESTRA_WEB_DUE);break;
case ID_FINESTRE_FINESTRAPOSIZIONA:break;
}}