Dans ce TP on va manipuler des images mais aussi des vidéos.
L'incrustation, ou chroma key compositing est la technique qui permet de combiner deux images (ou vidéos) enregistrées séparément.
À partir d'une image qui a une couleur de fond facilement reconnaissable (souvent vert ou bleu), on rend les pixels du fond invisibles pour insérer un fond différent.
La difficulté de cette technique est donc d'identifier quels pixels il faut supprimer.
Dans cet exercice vous allez programmer un algorithme qui combine deux images pour mettre un lapin dans l'espace.
Votre code de base est le suivant :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>M3201-TP3</title>
<style> body {padding: 0; margin: 0;} </style>
<script src="https://cdn.jsdelivr.net/npm/p5@0.10.2/lib/p5.js"></script>
</head>
<body>
<script>
let img_vert, img_fond;
function preload() {
img_vert = loadImage('https://i.postimg.cc/Y24Zp1Qz/conejo.png');
img_fond = loadImage('https://i.postimg.cc/bJrMSnW8/outer-space.jpg');
}
function setup() {
let c = createCanvas(img_vert.width, img_vert.height); // créer le canvas avec les dimensions de l'image
image(img_fond, 0, 0); // afficher l'image de fond
img_vert.loadPixels(); // obligatoire avant la modification des pixels
const n = 4 * (pixelDensity() * width) * (pixelDensity() * height); // nombre d'éléments dans le tableau
for (let i = 0; i < n; i += 4) {
const couleur = [img_vert.pixels[i], img_vert.pixels[i+1], img_vert.pixels[i+2]];
if (fond(couleur)) {
img_vert.pixels[i+3] = 0;
}
}
img_vert.updatePixels(); // obligatoire après la modification des pixels
image(img_vert, 0, 0); // afficher l'image
// saveCanvas(c, 'exo1', 'png'); // sauvegarder l'image comme exo1.png
}
function fond(couleur) {
/*
* remplir ceci
*/
}
</script>
</body>
</html>
Il vous faut remplir la fonction fond()
, qui rend un booléen (vrai/faux) pour dire si ce pixel appartient au fond à partir de sa couleur. Pour cela, vous devez comparer sa couleur avec celle du fond, et dire si elle est assez similaire.
Il est difficile d'avoir un résultat parfait, mais faites votre mieux.
Vous avez dû remarquer les difficultés de l'incrustation : il faut éviter les ombres sur le fond vert, et il doit être illuminé de façon homogène. Pour plus de détails, vous avez cette vidéo.
Si vous voulez aller plus loin, essayez de mettre Ewan McGregor à Marseille :
function preload() {
img_vert = loadImage('https://i.postimg.cc/KzZ6mznS/ewan.jpg');
img_fond = loadImage('https://i.postimg.cc/xjk28Xwn/marseille.jpg');
}
Dans cette partie on va manipuler les pixels d'une vidéo, qui n'est qu'une séquence d'images.
Dans cet exercice on va créer un filtre moyen temporel pour une vidéo. Pour chaque pixel et pour chaque canal, on prend les valeurs des trois (ou plus) dernières images et on calcule la moyenne.
Le code suivant récupère des images de votre webcam en temps réel et permet de modifier la couleur de chaque pixel en fonction des valeur de 3 dernières images.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>M3201-TP3</title>
<style> body {padding: 0; margin: 0;} </style>
<script src="https://cdn.jsdelivr.net/npm/p5@0.10.2/lib/p5.js"></script>
</head>
<body>
<script>
const W = 640, H = 480; // dimensions du canvas
let capture;
let max_frames = 3;
let frames = new Array(); // array d'images
let count = 0;
function setup() {
let c = createCanvas(W, H);
capture = createCapture(VIDEO); // accéder à la webcam
capture.hide(); // sinon, on voit la vidéo
}
function draw() {
capture.loadPixels();
// sauvegarder la nouvelle image
if (frames.length >= max_frames) {
frames.shift();
}
frames.push(capture.pixels);
let img = createImage(capture.width, capture.height); // extraire un frame de la webcam
img.loadPixels();
const n = 4 * (pixelDensity() * img.width) * (pixelDensity() * img.height); // nombre d'éléments dans le tableau
for (let i = 0; i < n; i+=4) {
// mettre les valeurs de ce pixel dans 3 tableaux
let arr_r = new Array(frames.length);
let arr_g = new Array(frames.length);
let arr_b = new Array(frames.length);
for (let j = 0; j < frames.length; j++) {
arr_r[j] = frames[j][i+0];
arr_g[j] = frames[j][i+1];
arr_b[j] = frames[j][i+2];
}
// calculer les moyennes pour chaque canal (fonction à écrire)
const r = moyenne(arr_r);
const g = moyenne(arr_g);
const b = moyenne(arr_b);
img.pixels[i+0] = r;
img.pixels[i+1] = g;
img.pixels[i+2] = b;
img.pixels[i+3] = 255;
}
img.updatePixels();
image(img, 0, 0); // afficher le frame modifié
text(count, 20, 30); // afficher le numéro de frame
count++;
}
// calculer la moyenne des nombres dans le tableau arr
function moyenne(arr) {
/*
* remplir ceci
*/
}
</script>
</body>
</html>
Soyez patient avec ce programme, vous faites un traitement de vidéo en temps réel avec un langage peu performant (JavaScript).
Savez-vous à quoi sert ce filtre ? Quel est l'effet d'utiliser un plus grand nombre d'images dans la moyenne ?
Dans l'exercice suivant on va utiliser un filtre très similaire, mais qui a un effet différent.
Dans cet exercice, on affecte à chaque pixel la couleur médiane des 10 (ou plus) dernières images de la vidéo.
Vous pouvez réutiliser le code de l'exercice 2. Pour calculer la médiane vous avez besoin de trier les valeurs d'un tableau, ce qui est fait en JavaScript comme ça :
arr.sort((a, b) => a - b);
Quel est l'effet de ce filtre quand on fixe la caméra ?
Si votre webcam a une bonne résolution, vos images ont beaucoup de pixels et donc vos programmes peuvent être très lents. Vous pouvez utiliser ce code pour utiliser seulement une partie de votre webcam :
if (frames.length >= max_frames) {
frames.shift();
}
let subimg = capture.get((640-W)/2, (480-H)/2, W, H);
subimg.loadPixels();
frames.push(subimg.pixels);