Modifier une image pixel par pixel

Formation

À Paris

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Lieu

    Paris

Nous vous proposons des cours ouverts pour se former autrement, pour devenir acteur de votre vie. Nous vous aidons à prendre votre envol, mais ça ne s'arrête pas là. Notre volonté est de vous accompagner tout au long de votre vie, dans votre parcours professionnel.Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Les sites et dates disponibles

Lieu

Date de début

Paris ((75) Paris)
Voir plan
7 Cité Paradis, 75010

Date de début

Consulter

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Les matières

  • Image

Le programme

Introduction du cours

Bonjour !! Soyez les bienvenus dans ce mini-tuto qui va vous apprendre à modifier une image pixel par pixel en SDL.

Qu'est-ce que la SDL ?

:o ... Hélas, ceci est un mini-tuto et je vais me concentrer sur l'essentiel. :) Donc si vous ne savez pas ce que c'est que le C, la SDL, pas la peine de s'embêter à lire ce tutoriel, il ne vous sera pas d'une grande aide. Je vous conseille plutôt d'aller voir le très bon tutoriel de M@teo21 ici.

Les pré requis :

Et bien il faut connaître à fond le tutoriel de M@teo21 sur le C/C++ :)
Vous devez disposer d'un IDE C++, avec la SDL installée.

Et puis... c'est tout :D

Et que saura t'on faire à la fin ?

Modifier une image pixel par pixel. Plus précisément, je vais dans un premier temps m'attacher à l'exemple de la réalisation d'une balance des couleurs, puis nous verrons d'autres fonctions particulièrement intéressantes à réaliser.

Un peu de théorie

Posons le problème

Pour ce tutoriel, je vais partir du principe que vous n'êtes plus tout à fait des zéros ;) .

Voici notre problème principal :

Lorsque j'utilise la SDL, comment faire pour modifier mes images en direct, les assombrir, enlever la moitié des pixels, dessiner un éléphant rose ?

Bon pour l'éléphant rose, je cherche encore :p .

Comment faire ?

Le principe est extrêmement simple :

Vous savez naturellement ce qu'est une surface en SDL :D . (Sinon, allez hop au galop on va relire le cours ici)

Une surface est un ensemble de pixels. Elle est composée de n pixels, avec n = son nombre de pixels en largeur multiplié par son nombre de pixels en hauteur.

Qu'est-ce qu'un pixel ? C'est un point sur l'écran :p . Dans une surface, il contient 4 informations : une valeur en rouge, une valeur en vert, une valeur en bleu, et une valeur en alpha.
Je suppose que RVB, vous voyez ce que c'est ;) .

Mais Alpha, c'est quoi ce machin la ?

Et bien cela représente le degré de transparence (entre 0 et 255) du pixel. Avec 255, le pixel est opaque, avec 0 il est transparent.

M@teo21 en a déjà causé ici.

Le souci c'est qu'il parle d'une transparence alpha s'appliquant de manière globale, à une image entière.
Il est possible de mettre une valeur alpha différente pour chaque pixel, c'est d?ailleurs le principe des masques. Je ne vais pas m'appesantir sur le sujet pour le moment, sachez seulement que ça existe, voici d?ailleurs une image expliquant assez clairement comment fonctionne un masque :

Ca va jusque là ;) ? J'espère... :-°

Qu'allons nous donc faire ?

Très simple :

1)Je prends ma surface.
2)Je parcours ma surface suivant l'axe des X et l'axe des Y, et je modifie chaque pixel comme je l'entends.
3)Je libère ma surface.

Cependant, il y a quand même quelques complications .

En effet, pour modifier un pixel, il faut récupérer l'adresse de ce pixel dans la surface, puis récupérer la valeur représentant la couleur du pixel, la décomposer suivant ses quatre composantes, modifier ces composantes, les réunir en une seule valeur, et renvoyer cette information à l?adresse du pixel.

Pfiou... ça m'a pas l'air si simple que ça finalement

Ne vous inquiétez pas, on va réussir à simplifier grandement le problème. En effet, vous n'êtes pas les premiers à vouloir modifier une image pixel par pixel. Dans la documentation de la SDL, les exemples de codes pour modifier un pixel sont fournis. Et une fois qu'on les a, tout se simplifie !!

Donc merci la doc SDL et merci Internet, ça prémache le boulot :D :D

Bon, et bien on a tout vu d'un point de vue théorique. Si c'est pas clair... :o Je pense que ça le deviendra avec l'application !! C'est partiii !! :p

Les bases techniques pour que ça fonctionne

Dans cette seconde partie, nous allons étudier toutes les petites briques à assembler pour créer un superbe lego.

Attention, cette fois on rentre dans le code, et tout de suite c'est un peu moins marrant.

Lesquelles ? Haha !! Grande question ;) .
En fait, il nous faut une fonction pour connaître la couleur d'un pixel, et une fonction pour modifier sa couleur.

Et c'est la partie la plus difficile de ce tutoriel. En effet, ce sont des notions que vous n'avez pas forcement vus avec M@teo21, et qui ne sont pas évidentes à comprendre.

Je vous propose donc d'utiliser ces fonctions comme des boites noires . On se fiche de savoir comment elles fonctionnent, du moment qu'elles marchent autant s'en servir. Ce n'est pas très déontologique, mais ça va nous permettre d'avancer plus vite.

J'ai décidé de quand même commenter le code, si vous maitrisez les pointeurs et les zones de mémoires, le fonctionnement devrait vous paraitre plus clair.

D'abord, récupérer la couleur d'un pixel :

/* ********************************************************************* */ /*obtenirPixel : permet de récupérer la couleur d'un pixel Paramètres d'entrée/sortie : SDL_Surface *surface : la surface sur laquelle on va récupérer la couleur d'un pixel int x : la coordonnée en x du pixel à récupérer int y : la coordonnée en y du pixel à récupérer Uint32 resultat : la fonction renvoie le pixel aux coordonnées (x,y) dans la surface */ Uint32 obtenirPixel(SDL_Surface *surface, int x, int y) { /*nbOctetsParPixel représente le nombre d'octets utilisés pour stocker un pixel. En multipliant ce nombre d'octets par 8 (un octet = 8 bits), on obtient la profondeur de couleur de l'image : 8, 16, 24 ou 32 bits.*/ int nbOctetsParPixel = surface->format->BytesPerPixel; /* Ici p est l'adresse du pixel que l'on veut connaitre */ /*surface->pixels contient l'adresse du premier pixel de l'image*/ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * nbOctetsParPixel; /*Gestion différente suivant le nombre d'octets par pixel de l'image*/ switch(nbOctetsParPixel) { case 1: return *p; case 2: return *(Uint16 *)p; case 3: /*Suivant l'architecture de la machine*/ if(SDL_BYTEORDER == SDL_BIG_ENDIAN) return p[0] << 16 | p[1] << 8 | p[2]; else return p[0] | p[1] << 8 | p[2] << 16; case 4: return *(Uint32 *)p; /*Ne devrait pas arriver, mais évite les erreurs*/ default: return 0; } }

Que doit t'on retenir ?

- Cette fonction reçoit une surface en paramètre, ainsi que la position en X et en Y du pixel sur l'image (rappel : le point de coordonnées 0,0 est en haut à gauche de l'image).
- Elle retourne un entier qui décrit la couleur du pixel (rouge, vert, bleu et alpha).

Donc, sur le même principe, voici comment modifier un pixel :

/* ********************************************************************* */ /*definirPixel : permet de modifier la couleur d'un pixel Paramètres d'entrée/sortie : SDL_Surface *surface : la surface sur laquelle on va modifier la couleur d'un pixel int x : la coordonnée en x du pixel à modifier int y : la coordonnée en y du pixel à modifier Uint32 pixel : le pixel à insérer */ void definirPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) { /*nbOctetsParPixel représente le nombre d'octets utilisés pour stocker un pixel. En multipliant ce nombre d'octets par 8 (un octet = 8 bits), on obtient la profondeur de couleur de l'image : 8, 16, 24 ou 32 bits.*/ int nbOctetsParPixel = surface->format->BytesPerPixel; /*Ici p est l'adresse du pixel que l'on veut modifier*/ /*surface->pixels contient l'adresse du premier pixel de l'image*/ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * nbOctetsParPixel; /*Gestion différente suivant le nombre d'octets par pixel de l'image*/ switch(nbOctetsParPixel) { case 1: *p = pixel; break; case 2: *(Uint16 *)p = pixel; break; case 3: /*Suivant l'architecture de la machine*/ if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (pixel >> 16) & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = pixel & 0xff; } else { p[0] = pixel & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = (pixel >> 16) & 0xff; } break; case 4: *(Uint32 *)p = pixel; break; } }

De même :
Cette fonction reçoit en paramètre une surface, les coordonnées x et y du pixel, et la couleur que l'on veut y mettre.

L'intérêt principal de ces fonctions ?

Elles sont faites pour fonctionner avec n'importe quel format d'image, vous n'aurez jamais de couleurs faussées avec ces fonctions.

Mais... Qu'est-ce qu'un Uint32 ???

C'est un entier d'un type un peu particulier (non signé et enregistré sur 32 bits), créé par la bibliothèque SDL (il existe des Uint8, des Uint16, etc.).

Un entier non signé ne peut contenir que des valeurs positives. 32 bits indiques le nombre de valeurs qu'il peut prendre.
Un Uint32 peut prendre des valeurs entre 0 et (2^32) -1.
Un Unit8 peut prendre des valeurs entre 0 et (2^8) -1, c'est à dire entre 0 et 255.

Vous devez donc récupérer la valeur d'un pixel avec ce genre de code :

Uint32 pixel; /*Pour récupérer le code couleur d'un pixel*/ pixel=obtenirPixel(surface,x,y); /*Et pour changer la valeur d'un pixel*/ definirPixel(surface,x,y,pixel);

Maintenant bien sur, ce qui nous intéresse, c'est modifier la valeur de la variable pixel.

Donc dans un premier temps, il faut récupérer les composantes rouge, vert, bleu et alpha du pixel, puis les modifier, et enfin les réinjecter.

En SDL, chaque composante couleur est stockée sur 8 bits. Ce sont donc des Uint8.

On va utiliser les fonctions SDL_GetRGBA et SDL_MapRGBA.

Il faut lui passer les 4 variables de composante par référence.
Voici un code relativement clair, du moins je l'espère :

/*Composantes pouvant prendre des valeurs entre 0 et 255*/ Uint8 r,g,b,a; /* On extrait de pixel la valeur de chaque composante*/ SDL_GetRGBA(pixel, surface->format, &r, &v, &b, &a); /* On modifiera ici les variables r, v, b et a.*/ /*Et une fois qu'on les a modifiées, on réinjecte dans pixel.*/ pixel=SDL_MapRGBA(surface->format, r, v, b, a);

Petite précision : 'surface' est le nom de votre surface :p , et format permet à la fonction de connaître la façon dont sont enregistrés vos pixels (nous... on s'en fout :p ). Il faut juste retenir que le premier paramètre est sous la forme 'nomDeLaSurface->format'.

Heu.. Serait-il possible de résumer ?

Bien sur, bien sur :

Uint32 pixel; Uint8 r,g,b,a; int x,y; x=3; y=5; SDL_LockSurface(surface); /*On bloque la surface*/ pixel=obtenirPixel(surface,x,y); SDL_GetRGBA(pixel, surface->format, &r, &v, &b, &a); /*Ici, on mettra du code pour modifier les composantes du pixel.*/ /*Et une fois qu'on les a modifiés :*/ pixel=SDL_MapRGBA(surface->format, r, v, b, a); /*Et pour changer la valeur d'un pixel :*/ definirPixel(surface,x,y,pixel); SDL_UnlockSurface(surface); /*On libère la surface, elle peut être utilisée pour une autre tâche*/

C'est à peu près clair :) ? Bon bah on va pouvoir continuer !! C'est bientôt fini, ne vous inquiétez pas !! :D

Donc désormais, nous avons récupéré r, v, b, et a qui sont les composantes de notre pixel.

Ces composantes sont des Uint8. Ils sont stockés sur 8 bits. Donc... ?
Et bien ils peuvent prendre chacun 256 valeurs différentes. C'est à dire une valeur entre 0 et 255.

Comment ça je l'ai déjà dit ? :-° C'est pour que ça rentre mieux :D :D .

Le 0 indique l'absence de la composante, (et la transparence totale pour la composante alpha), et le 255 indique le maximum de cette couleur (et opaque pour la composante alpha).

Quelques exemples :

r,g,b,a :

0,0,0,255 : Aucune couleur --> noir. Et opaque.
255,255,255,255 : Toutes les couleurs --> de la lumière blanche !! Et opaque aussi bien sur.
255,0,0,128 : Seule la composante rouge est présente pour les couleurs, c'est donc un rouge. Cependant, ce pixel est à moitié transparent (128=255/2) donc il laisse filtrer une partie de la couleur du pixel sur lequel il sera collé.
255,255,255,0 : Un pixel de couleur blanche... Et totalement transparent !! Donc... Il ne sera pas visible !! :)

C'est compris ? ;)

A savoir : les opérations sur des Uint8 ne doivent jamais sortir de l'intervalle [0,255]

Si par exemple vous prenez un pixel qui a une composante rouge de 150, et que vous lui additionnez 150, vous n'obtiendrez ni 300 (puisque le maximum est 255), ni 255, mais plutôt... N'importe quoi :D .

La solution ? Convertir les Uint8 en int lors des calculs.

Concrètement, voici comment faire :

int tampon; /*Variable entière qui sert de tampon*/ int ajout; Uint8 composante,resultat; composante=155; ajout=120; tampon=(int)composante + ajout; /*On fait en sorte que tampon reste dans l'intervalle [0,255] */ if (tampon>255) {tampon=255;} else if (tampon<0) {tampon=0;} /*Avec l'opérateur cast (Uint8) on tranforme tampon en un entier de type Uint8*/ resultat=(Uint8)tampon;

Bon et bien vous avez tous les outils en main il me semble.

Comment modifier l'intégralité de l'image ?

Comme dans les cours de M@teo21, avec une boucle for.

Par exemple :

int x,y; SDL_LockSurface(surface); /*On bloque la surface*/ for (y=0;y<surface->h;y++) { for (x=0;x<surface->w;x++) { /*Ici on modifie chaque pixel*/ } } SDL_UnlockSurface(surface); /*On libère la surface, elle peut être utilisée*/

Dernier point, mais non des moindres : une fois une image chargée en mémoire, il peut être intéressant d'en réaliser une copie, dans une autre image, afin de modifier cette deuxième image et garder l'original intact.

Voici donc le principe de fonctionnement :
1) Charger l'image originale
2) Créer une surface vide dans la nouvelle image, possédant les caractéristiques de l'image originale
3) Copier l'image originale dans la nouvelle image.

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Modifier une image pixel par pixel

Prix sur demande