Modifier le contraste d'une image couleur

Description

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; 
} 

Codes Sources

A voir également

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.