Décomposition RGB avec Pixel Bender
Le 18 octobre 2011
Il est possible très simplement avec Pixel Bender de décomposer une image RGB en ses 3 composantes rouge, verte et bleue. Il est tout aussi facile de les manipuler et de les filtrer pour obtenir divers effets applicables à un flux vidéo provenant d’une webcam par exemple, comme dans cette animation.
Pixélisation d’une image avec Pixel Bender
Pour pixéliser une image à l’aide de Pixel Bender, chaque pixel de l’image source va être représenté par un carré de coté c sur l’image de sortie (l’image de sortie sera donc c fois plus grande que l’image source). Dans l’image de sortie chaque carré prendra la couleur d’un seul pixel de l’image source. Il faut donc récupérer, pour chaque carré, c fois la couleur de chaque pixel de l’image source en largeur et c fois la couleurs de chaque pixel de l’image source en hauteur. On détermine donc la couleur de chaque pixel de coordonnées (x,y) de l’image de sortie en récupérant la partie entière de la division de x et y par la dimension c du carré.
Ainsi, pour les coordonnées x et y de l’image de sortie allant de 0 à c-1, les parties entières ex et ey des divisions x/c et y/c sont égales à 0. Pour les coordonnées x et y de l’image de sortie allant de c à 2c-1, les parties entières ex et ey des divisions x/c et y/c sont égales à 1 et ainsi de suite. Il suffit de lire la couleur du pixel de coordonnées (ex,ey) de l’image source pour déterminer la couleur de chaque carré correspondant de l’image de sortie.
<languageVersion : 1.0;>
kernel SquareDecomposition
< namespace : "brainswitch";
vendor : "Brainswitch";
version : 1;
>
{
input image4 src;
output pixel4 dst;
void evaluatePixel()
{
const float squareWidth = 10.0;
float outX = outCoord().x;
float outY = outCoord().y;
float positionX = floor( outX/squareWidth );
float positionY = floor( outY/squareWidth );
float3 offset = float3( positionX, positionY, 0.0 );
float4 outColor = sampleNearest( src, offset.xy );
dst.r = outColor.r;
dst.g = outColor.g;
dst.b = outColor.b;
dst.a = 1.0;
}
}
L’image suivante a été obtenue à partir de ce shader.

Pixélisation et décomposition RGB d’une image avec Pixel Bender
Dans chaque carré de l’image de sortie on peut maintenant décomposer la couleur du pixel d’entrée correspondant en bandes rouge, verte et bleue. Si on veut dessiner dans chaque carré, 3 bandes de largeur b et de longueur c, on aura c = 3b. On peut également séparer les carrés par un espace d’un pixel, dans ce cas c vaudra 3b+1.
En largeur, la couleur de chaque pixel de la première bande, sera égale à la composante rouge du pixel correspondant de l’image source, idem pour les deuxième et troisième bandes avec les composantes verte et bleue. On aura donc en x, sur l’image de sortie, une trait rouge, une trait vert, une trait bleu, puis un point noir puis de nouveau une trait rouge et ainsi de suite. On va se servir de la fonction mod (modulo) de Pixel Bender pour réaliser cette décomposition. Si l’on choisit b=3, c sera égal à 3*3+1 soit 10, et on aura pour x (la coordonnée x du pixel de l’image de sortie) variant de 0 à 19:
0 mod 10 = 0, 1 mod 10 = 1, 2 mod 10 = 2, pour le rouge
3 mod 10 = 3, 4 mod 10 = 4, 5 mod 10 = 5, pour le vert
6 mod 10 = 6, 7 mod 10 = 7, 8 mod 10 = 8, pour le bleu
9 mod 10 = 9, pour le noir
10 mod 10 = 0, 11 mod 10 = 1, 12 mod 10 = 2, pour le rouge
13 mod 10 = 3, 14 mod 10 = 4, 15 mod 10 = 5, pour le vert
16 mod 10 = 6, 17 mod 10 = 7, 18 mod 10 = 8, pour le bleu
19 mod 10 = 9, pour le noir
Ainsi, pour les coordonnées x de chaque pixel de l’image de sortie, x mod 10 retournera toujours des valeurs de 0 à 9 nous permettant de déterminer sa couleur. En hauteur (coordonnées y de chaque pixel de l’image de sortie), le principe est le même mais on se soucie uniquement de la valeur 9 pour tracer un trait noir de séparation entre les carrées.
<languageVersion : 1.0;>
kernel RVBSquareDecomposition
< namespace : "brainswitch";
vendor : "Brainswitch";
version : 1;
>
{
input image4 src;
output pixel4 dst;
void evaluatePixel()
{
const float squareWidth = 10.0;
float outX = outCoord().x;
float outY = outCoord().y;
float positionX = floor( outX/squareWidth );
float positionY = floor( outY/squareWidth );
float3 offset = float3( positionX, positionY, 0.0 );
float4 outColor = sampleNearest( src, offset.xy );
float stepX = mod( floor( outX/1.0 ), squareWidth );
float stepY = mod( floor( outY/1.0 ), squareWidth );
if ( stepX == 0.0 || stepX == 1.0 || stepX == 2.0 ) {
dst.r = outColor.r;
dst.g = 0.0;
dst.b = 0.0;
} else if ( stepX == 3.0 || stepX == 4.0 || stepX == 5.0 ) {
dst.r = 0.0;
dst.g = outColor.g;
dst.b = 0.0;
} else if ( stepX == 6.0 || stepX == 7.0 || stepX == 8.0 ) {
dst.r = 0.0;
dst.g = 0.0;
dst.b = outColor.b;
} else if ( stepX == 9.0 ) {
dst.r = 0.0;
dst.g = 0.0;
dst.b = 0.0;
}
if ( stepY == 9.0 ) {
dst.r = 0.0;
dst.g = 0.0;
dst.b = 0.0;
}
dst.a = 1.0;
}
}
L’image suivante a été obtenue à partir de ce shader.

Exécution du shader à l’aide d’un objet ShaderJob
Un objet ShaderJob permet d’exécuter un shader en mode autonome de manière synchrone ou asynchrone. Le morceau de code suivant charge une image et le shader RVBSquareDecomposition.pbj, crée et éxécute un ShaderJob avec la fonction imgToRGBSquare puis affiche le résultat à l’écran.
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shader;
import flash.display.ShaderJob;
// classes issues du framework Flex: indispensables pour utiliser l'embed dans Flash
import mx.core.*;
[Embed ( source="assets/image.jpg" )]
var InputImg:Class;
[Embed ( source="assets/RVBSquareDecomposition.pbj", mimeType = "application/octet-stream" )]
var RgbSquare:Class;
var inputImg:Bitmap = new InputImg();
var inputBitmapData:BitmapData = inputImg.bitmapData;
var outputBitmapData:BitmapData = new BitmapData( 1100, 650, false, 0x000000 );
var squaresBitmapData:BitmapData = new BitmapData( 1100, 650, false, 0x000000 );
var screenBitmap:Bitmap = new Bitmap ( outputBitmapData );
addChild( screenBitmap );
squaresBitmapData = imgToRGBSquare( inputBitmapData, 10, true );
outputBitmapData.copyPixels( squaresBitmapData, new Rectangle( 0, 0, 1100, 650 ), new Point( 0, 0 ) );
function imgToRGBSquare( input:BitmapData, squareSize:int, cleanup:Boolean = true ):BitmapData
{
// Création et configuration d'un ShadeJob
var shader:Shader = new Shader();
shader.byteCode = new RgbSquare(); // Instanciation d'un objet byteCode
shader.data.src.input = input; // L'image source envoyée au shader
var outputWidth:int = input.width;
var outputHeight:int = input.height;
// Création d'une Bitmap pour récupérer les données de sortie du shader
var output:BitmapData = new BitmapData( outputWidth * squareSize, outputHeight * squareSize );
// Création du shaderJob
var job:ShaderJob = new ShaderJob();
job.target = output; // ShaderJob utilise l'objet output comme sortie
job.shader = shader; // Le shader assigné au ShaderJob
job.start( true ); // Exécution synchrone, false pour une éxécution asynchrone
return output;
}
Il est possible de faire évoluer ce shader en ajoutant des conditions ou en effectuant un ajustement de couleurs pour obtenir des images totalement différentes. Il est également possible de convertir les pixels en traits ou en toute autre formes plutôt qu’en carrés. Les images ci-dessous sont issue de l’animation du haut de la page, les composantes RGB des pixels de l’image source sont filtrées en fonction de leur intensité.


Posté dans Code source, Flash, Pixel-bender | 3 commentaires
Commentaires
3 commentaires sur “Décomposition RGB avec Pixel Bender”
Laisser une réaction
awesome blog, do you have twitter or facebook? i will bookmark this page thanks.
Great information, thanks for the share!
@Santiago
Thanks! No facebook or twitter page at this time.