Le but de cet exemple est de vous montrer comment on modifie le contraste d'une image en couleur en procédant à une modification de ses pixels. L'implémentation est réalisée en langage C# avec WPF au sein d'un projet Microsoft Visual Studio 2013.
Configuration de l'exemple
Un contrôle
Image x_img charge une image couleur codée sur 32 bits, qui est placée en ressource dans l'assembly. Une glissière
x_slider de type
Slider permet d'augmenter le contraste de l'image en déplaçant le curseur vers la droite ou bien de diminuer le contraste en déplaçant le curseur vers la gauche. La position centrale du curseur sur la glissière correspond à l'image originale sans modification du contraste.
Principe de la modification du contraste
Modifier le contraste d'une image consiste à multiplier tous les pixels de l'image par un facteur. Un coefficient supérieur à 1 augmentera le contraste et un coefficient inférieur à 1 diminuera le contraste. Nous touchons donc là à l'histogramme de l'image. La glissière a sa position centrale qui correspond à la valeur 1 et ses extrémités sont les valeurs 0 (à gauche) et 2 (à droite). Dans la modification du contraste d'une image, les pixels sombres deviennent de plus en plus sombres, et les pixels clairs deviennent de plus en plus clairs. Cette réflexion laisse entrevoir l'utilisation d'un seuil qui définira grosso modo ce qu'est un pixel sombre et un pixel clair (dans notre exemple, le seuil est la valeur médiane 128). Ce seuil permet donc de ramener la moitié de l'histogramme des valeurs dites sombres sous 0 (valeurs négatives) et les valeurs dites lumineuses au-dessus de 0 (valeurs positives). Par conséquent, lorsque nous appliquerons par la suite le coefficient multiplicateur, les valeurs négatives le seront d'autant plus et les valeurs positives également.
Implémentation
On définit un objet
Uri qui référence la ressource image utilisée et qui est embarquée en ressource dans l?assembly. Dans la zone de texte
TextBlock x_tbl_infos, on affiche les caratéristiques de l'image fournie par la méthode
AfficherInfosImage. Ces caractéristiques (propriétés de l'objet
BitmapImage) sont les dimensions de l'image (propriétés
PixelWidth et
PixelHeight), le format de codage (propriété
Format), et la résolution (propriétés
DpiX et
DpiY).
private void Window_Loaded(object sender, RoutedEventArgs e) {
v_fen_charge = true;
v_uri_couleur = new Uri("pack://application:,,,/VS2013_ContrasteImage;
component/contenu/oscar_32bit_800x1072_96dpi.jpg", UriKind.Absolute);
x_tbl_infos.Text = AfficherInfosImage(v_uri_couleur);
x_slider.Value = 1;
v_contraste = x_slider.Value;
}
//afficher les caracteristiques de l'image
private string AfficherInfosImage(Uri uri_image) {
string aff = "";
BitmapImage bti = new BitmapImage();
bti.BeginInit();
bti.UriSource = uri_image;
bti.EndInit();
aff += "dimensions de " + bti.PixelWidth.ToString() + " par " + bti.PixelHeight.ToString() + " pixels, ";
aff += "avec un format " + bti.Format.ToString() + " , ";
aff += "un codage de " + bti.Format.BitsPerPixel.ToString() + " bits par couches, ";
aff += "une résolution de " + bti.DpiX.ToString() + " dpi ";
return aff;
}
La modification du contraste de l'image couleur est effectuée par la méthode
ModifierContrasteCouleur pour une image codée en 32 bits (16 millions de couleurs).
On commence par stocker les pixels de l'image dans un tableau
tab_pixel unidimensionnel de type
byte[]. La longueur de ce tableau correspond au produit de la largeur de numérisation par la hauteur de l?image en pixel. La largeur de numérisation correspond au nombre de bytes utilisés pour coder une ligne entière de pixels pour une largeur de l'image. La propriété
BitsPerPixel de l'objet
Format retourne le nombre de bits utilisés pour le codage, et la propriété
Format de l'objet
BitmapImage retourne l'objet
Format utilisé. Donc la largeur de numérisation pour l'objet
BitmapImage bti sera égale au produit de
bti.Format.BitsPerPixel/8 par la largeur de l'image (
bti.PixelWidth). Le rapport
bti.Format.BitsPerPixel/8 retourne 4 bytes pour une image codée sur 32 bits (16 millions de couleurs).
Ensuite on instancie un tableau
tab_pixel_modif unidimensionnel de type
byte[] et de même taille que
tab_pixel. Ce tableau recevra
les pixels de l'image après modification du contraste. Par une boucle for, on effectue un parcours du tableau
tab_pixel, et on relève quatre composantes à la
suite pour la couleur. Comme on choisit le seuil de 128 pour séparer les pixels sombres des pixels clairs, en fonction du facteur relevé par modification du curseur de la
glissière et stocké dans la variable
v_contraste, on calcule les quatre nouvelles composantes pour l?image en couleur qui seront la composante B avec
comp_b_contraste de type
double (avec
comp_b_contraste = ((double)tab_pixel[xx] - 128.0) * v_contraste + 128.0),
la composante G avec
comp_g_contraste de type
double (avec
comp_g_contraste = ((double)tab_pixel[xx+1] - 128.0) * v_contraste + 128.0),
la composante R avec
comp_r_contraste de type
double (avec
comp_r_contraste = ((double)tab_pixel[xx+2] - 128.0) * v_contraste + 128.0),
et la composante de transparence qui reste inchangée (avec
tab_pixel[xx+3]).
A noter que le tableau de pixels obtenu par la méthode
CopyPixels de l'objet
BitmapImage est une suite de bytes représentant les composantes
couleurs selon le mode BGRA (c?est pourquoi le format de l'image de l'objet
BitmapImage est le format Bgr32).
Pour procéder à la mise à jour des pixels de l'objet
WriteableBitmap, avec le tableau
tab_pixel_modif, avant sa visualisation, il faut en
premier bloquer l'affichage de la mémoire tampon d'arrière-plan par l'appel de la méthode d'instance
Lock. En seconde étape, on écrit dans la mémoire
tampon d'arrière-plan avec la méthode d?instance
WritePixels. Et en dernière étape, on débloque l'affichage de la mémoire tampon d'arrière-plan avec
la méthode d'instance
Unlock.
La méthode d'instance
WritePixels reçoit quatre paramètres. Le premier paramètre est un objet de type
Int32Rect qui spécifie la zone à deux
dimensions en pixels à modifier. Un objet
Int32Rect reçoit en paramètre la position du coin haut gauche, une largeur et une hauteur. Ici, pour spécifier la
surface de toute l'image, on passera
new Int32Rect(0,0,wb.PixelWidth,wb.PixelHeight).
Le deuxième paramètre est le tableau unidimensionnel contenant les pixels. Le troisième paramètre est la largeur de numérisation. Et le quatrième paramètre est l'indice du
point de départ de lecture des données dans le tableau des pixels. A partir de là, on peut visualiser l?objet
WriteableBitmap modifié dans un contrôle
Image.
//methode pour modifier le contraste d'une image en couleurs
private void ModifierConstrasteCouleur() {
BitmapImage bti = new BitmapImage();
bti.BeginInit();
bti.UriSource = v_uri_couleur;
bti.EndInit();
WriteableBitmap wb = new WriteableBitmap(bti);
int largeur_numerisation = (bti.Format.BitsPerPixel / 8) * bti.PixelWidth;
byte[] tab_pixel = new byte[largeur_numerisation * bti.PixelHeight];
wb.CopyPixels(tab_pixel, largeur_numerisation, 0);
byte[] tab_pixel_modif = new byte[largeur_numerisation * bti.PixelHeight];
for (int xx = 0; xx < tab_pixel.Length; xx += 4) {
//codage avec une suite BGRA
double comp_b_contraste = ((double)tab_pixel[xx] - 128.0) * v_contraste + 128.0;
if (comp_b_contraste > 255) {
comp_b_contraste = 255d;
}
if (comp_b_contraste < 0) {
comp_b_contraste = 0d;
}
tab_pixel_modif[xx] = (byte)comp_b_contraste;
double comp_g_contraste = ((double)tab_pixel[xx + 1] - 128.0) * v_contraste + 128.0;
if (comp_g_contraste > 255) {
comp_g_contraste = 255d;
}
if (comp_g_contraste < 0) {
comp_g_contraste = 0d;
}
tab_pixel_modif[xx + 1] = (byte)comp_g_contraste;
double comp_r_contraste = ((double)tab_pixel[xx + 2] - 128.0) * v_contraste + 128.0;
if (comp_r_contraste > 255) {
comp_r_contraste = 255d;
}
if (comp_r_contraste < 0) {
comp_r_contraste = 0d;
}
tab_pixel_modif[xx + 2] = (byte)comp_r_contraste;
tab_pixel_modif[xx + 3] = tab_pixel[xx + 3];
}
wb.Lock();
wb.WritePixels(new Int32Rect(0, 0, wb.PixelWidth, wb.PixelHeight), tab_pixel_modif, largeur_numerisation, 0);
wb.Unlock();
x_img.Stretch = Stretch.None;
x_img.Width = wb.PixelWidth;
x_img.Height = wb.PixelHeight;
x_img.Source = wb;
}
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.