Come applicare la tecnica del Chroma Key in Adobe Flash

La tecnica del Chroma Key, cioè la sostituzione di un determinato colore con un altro (o una diversa sorgente), è riesplosa nell’ultimo decennio con notevole prepotenza, grazie alla sempre più diffusa tecnologia presente nel cinema e nel trattamento di immagini digitali. Se in passato il Chroma Key era utilizzato spesso con scarsi risultati, oggi è una tecnica applicata continuamente, anche in contesti e situazioni non sempre immaginabili o riconoscibili, come dimostra il video qui sotto:

Vi mostreò adesso come applicare in modo abbastanza semplice la tecnica del Chroma Key in ambiente Adobe Flash. Nell’esempio che ho realizzato mi sono avvalso di un oggetto video agganciato alla vostra Camera (o WebCam che sia).

Nota bene: negli esempi di codici qui sotto, e nel filmato Flash dimostrativo, ho usato come colore di chroma il nero! Nella realtà, come forse saprete, vengono usati pannelli o teli di colore blu (da qui il nome di Blue Screen) o verdi (Pantone 354)! Inoltre noterete che i valori uint del colore sono tutti nella forma 0xAARRGGBB; dove è AA è il canale alpha di trasparenza.

Per elaborare l’immagine ho utilizzato il metodo threshold() dell’oggetto BitmapData. threshold() è un metodo molto potente e al tempo stesso molto semplice da utilizzare. Esso permette di eseguire un controllo pixel per pixel su una Bitmap, verificando che un colore (mascherato) sia uguale, diverso, maggiore o minore di un altro e sostituiendolo con un valore dato. Il suo prototipo è il seguente:

1
2
3
4
5
6
7
8
public function threshold(sourceBitmapData:BitmapData,
    sourceRect:Rectangle,
    destPoint:Point,
    operation:String,
    threshold:uint,
    color:uint = 0,
    mask:uint = 0xFFFFFFFF,
    copySource:Boolean = false):uint
  • sourceBitmapData sono i dati Bitmap su cui operare. Tale sorgente può essere diversa o uguale alla BitmapData su cui si sta operando
  • sourceRect è un puntatore ad un oggetto di tipo Rectangle che definisce l’area su cui eseguire l’operazione
  • destPoint è un puntatore ad un oggetto di tipo Point che definisce le coordinate x ed y di destinazione: quasi sempre 0,0
  • operation questa è una stringa che definisce l’operazione da eseguire sui pixel (vedi sotto per dettagli) e può assumere i seguenti valori: "<", "<=", ">", ">=", "==", "!="
  • threshold è un unsigned int, un intero senza segno, indicante il colore (o valore soglia) da confrontare con i dati pixel della bitmap, ad esempio 0x00FF0000
  • color è un unsigned int, un intero senza segno, indicante il colore che dovrà essere sostituito se l’operazione di confronto ha successo
  • mask è un unsigned int, un intero senza segno, indicante la maschera da applicare al parametro di threshold e ai pixel della bitmap sorgente
  • copySource è un valore booleano che se impostato a true copierà il valore corrente quando l’operazione di confronto fallisce

Il metodo restituisce un uint che rappresenta il numero di pixel modificati. L’operazione che il metodo svolge può essere schematizzata in questo modo:

  • Partendo dalle coordinate definite in sourceRect
  • Viene letto il valore del pixel nella sourceBitmap e gli viene applicata la maschera mask, cioè viene eseguita l’operazione di AND logico tra i due valori: (pixelLetto & mask)
  • Il valore ottenuto viene confrontato con (threshold & mask), anch’esso mascherato, applicando l’operazione di confronto di operation
  • Se il confronto restituisce true, allora partendo da destPoint viene impostato quel pixel su color
  • Se il confronto restituisce false, non viene eseguita nessuna operazione, a meno che non abbiamo impostato il parametro copySource a true: in questo caso viene copiato il valore così com’è

Nell’esempio che ho realizzato noterete quattro box con quattro immagini della camera:

chromakey
Ricordarsi di consentire l’accesso alla camera: nel caso possedete più Camere collegate
selezionate quella giusta con il tasto destro su impostazioni

Il primo box in alto a sinistra, Video Originale, è il video così come viene visualizzato quando usiamo l’oggetto Camera agganciato all’oggetto Video:

1
2
var cam:Camera = Camera.getCamera();
vid_video.attachCamera( cam );

Subito sotto troviamo il box Threshold, questo è generato in questo modo:

1
2
3
4
// Threshold
var simpleBitmapData:BitmapData = new BitmapData( vid_video.width, vid_video.height, true);
var simpleBitmap:Bitmap = new Bitmap(simpleBitmapData);
addChild(simpleBitmap);

In pratica viene creata prima un’istanza di un oggetto BitmapData delle dimensioni del video. Questa viene agganciata ad un oggetto Bitmap, il quale può essere aggiunto allo stage. A questo punto possiamo applicare il metodo threshold, inserendo la nostra procedura all’interno dell’evento ENTER_FRAME (impostate almeno un valore di 30 fotogrammi al secondo nel vostro progetto):

1
2
3
4
5
6
7
8
9
10
11
12
13
addEventListener( Event.ENTER_FRAME,
    function (e:Event):void {
        simpleBitmap.bitmapData.draw( vid_video );
        simpleBitmap.bitmapData.threshold(simpleBitmap.bitmapData,
             new Rectangle(0, 0, simpleBitmap.bitmapData.width, simpleBitmap.bitmapData.height),
             new Point(0,0),
             "==",
             0x00000000,
             0x00000000,
             0x00FFFFFF,
             true );
    }
);

La riga 3 copia (ad ogni ENTER_FRAME) il contenuto video nella nostra BitmapData, visualizzata nello stage tramite l’oggetto Bitmap simpleBitmap. La riga 4 applica (sempre ad ogni ENTER_FRAME) sulla nostra BitmapData il metodo threshold(), nelle modalità descritte in precedenza. Nell’esempio proposto i parametri che ho usato applicano threshold sull’intera area della Bitmap eseguendo l’operazione di uguaglianza "==". Se il pixel letto è uguale al nero 0x00000000 (o 0xFF000000 in dipendenza della maschera) viene sostituito con il vuoto 0x00000000. Alla fine, dunque, otteniamo un chroma key sul nero.

Uso di un range

Questa tecnica va bene quando il colore da sostituire è preciso. Nel nostro precedente esempio, infatti, un valore di 0x00000001 non viene preso in considerazione. Per superare questo ostacolo bisogna eseguire un’operazione diversa da "==". Inoltre, in un filmato reale, ci sono da considerare ombre e riflessi che, in pratica, spostano il nostro colore di chroma da un livello minimo ad un livello massimo. L’ideale, quindi, sarebbe quello di creare una serie di filtri threshold che verificano sia un livello scuro che un livello chiaro del colore che abbiamo scelto per il chroma.
Per realizzare questo non è sufficiente scrivere:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
addEventListener( Event.ENTER_FRAME,
    function (e:Event):void {
        simpleBitmap.bitmapData.draw( vid_video );

        simpleBitmap.bitmapData.threshold(simpleBitmap.bitmapData,
             new Rectangle(0, 0, simpleBitmap.bitmapData.width, simpleBitmap.bitmapData.height),
             new Point(0,0),
             "<=",
             0x00222222,
             0x00000000,
             0x00FFFFFF,
             true );
    }
);

Il risultato, per quanto apprezzabile, non è corretto, per un semplice motivo: utilizzando l’operatore "<=", che concettualmente è corretto, stiamo confrontanto tutti i livelli inferiori o uguali a 0x00222222. Ne segue che il valore 0x00002200 – che è un verde scuro – verrà considerato come chroma, il che non era nelle nostre intenzioni, volendo filtrare solo il nero e qualche gradazione un po’ più chiara ma sempre del “nero”. La tecnica più precisa è quella di sfruttare il parametro mask e analizzare tramite il threshold le tre componenti RGB separatamente. Quello che andremo a realizzare adesso è un filtro inverso, cioè andremo a creare un maschera che conterrà solo il nostro range. Prima di tutto creiamo una funzione in grado di operare un controllo su un canale RGB specifico:

1
2
3
4
5
6
7
8
9
10
11
function applyThresholdRGB( myBitmapData:BitmapData, channel:String, op:String, threshold:uint ):void {
    var maskColor:uint = (channel == "red") ? 0x00FF0000 : ( (channel == "green") ? 0x0000FF00 : 0x000000FF );
    myBitmapData.threshold(myBitmapData,
        new Rectangle(0, 0, myBitmapData.width, myBitmapData.height),
        new Point(0,0),
        op,
        threshold,
        0x00000000,
        maskColor,
        false );
}

La funzione applyThresholdRGB() permette di eseguire un determinato confronto (vedi parametro op) su un canale RGB specificato nel parametro channel. Creiamo adesso un funzione che genera una maschera:

1
2
3
4
5
6
7
8
9
10
11
12
13
function createBitmapMask(myBitmapData:BitmapData, colorLow:uint, colorHi:uint):void {
    //red
    applyThresholdRGB( myBitmapData, "red", "<", colorLow );
    applyThresholdRGB( myBitmapData, "red", ">", colorHi );

    //green
    applyThresholdRGB( myBitmapData, "green", "<", colorLow );
    applyThresholdRGB( myBitmapData, "green", ">", colorHi );

    //blue
    applyThresholdRGB( myBitmapData, "blue", "<", colorLow );
    applyThresholdRGB( myBitmapData, "blue", ">", colorHi );
}

Ora che abbiamo la maschera (nell’esempio è il riquadro in alto a destra Bitmap Mask) possiamo sfruttarla per sovrapporla all’immagine originale, eliminando le parti che non servono. Ricapitolando abbiamo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
addEventListener( Event.ENTER_FRAME,
    function (e:Event):void {
        // Immagine clone del video
        simpleBitmap.bitmapData.draw( vid_video );
       
        // Creo un'oggetto Bitmap con i dati BitmapData uguali all'originale
        var bitmapMask:Bitmap = new Bitmap(simpleBitmap.bitmapData.clone());
       
        // Creo la maschera
        createBitmapMask(bitmapMask.bitmapData,  0x00000000, 0x00222222);
 
        // Applico la maschera all'originale in BlendMode.ERASE
        simpleBitmap.bitmapData.draw(bitmapMask, null,  null, BlendMode.ERASE, null, false);
    }
);

Il risultato finale è visibile nel box in basso a destra Bitmap Merge. In questo modo è possibile applicare il Chroma Key non ad un colore specifico ma ad un range.

3 commenti a: “Come applicare la tecnica del Chroma Key in Adobe Flash”

  1. 17 set, 2011 ale:

    potresti caricare il file .fla che hai creato??

Lascia un commento

TAG XHTML PERMESSI: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> INSERIMENTO CODICE:
<pre></pre> // blocco generico
					<code></code> // blocco generico
					[cc_actionscript][/cc_actionscript] // Actionscript
					[cc_actionscript3][/cc_actionscript3] // Actionscript 3
					[cc_css][/cc_css] // CSS Style Sheet
					[cc_html][/cc_html] // HTML
					[cc_js][/cc_js] // Javascript
					[cc_objc][/cc_objc] // Objective-C
					[cc_php][/cc_objc] // PHP
					[cc_sql][/cc_sql] // SQL


Stop SOPA