Adjusting The Contrast Of An Image.

Image contrast is a function of scale. This is to say that the relative differences between one pixel and the next are increased with greater contrast and decreased with smaller contrast. The easiest way to perform this adjustment with GDI+ is using the ColorMatrix class to scale the pixels.

The ColorMatrix is used in conjunction with the ImageAttributes class to process the pixels of an image as it is drawn so, to get an image with different contrast we just need a method of setting the scale of the red, green and blue elements to the same relative values. This is accomplished with a ColorMatrix that looks like the one shown below.

C 0 0 0 0

0 C 0 0 0

0 0 C 0 0

0 0 0 1 0

0 0 0 0 1

 

In this case the "C" is the contrast scale component. To leave the contrast as it is in the original, the contrast scaling value may be set to 1.0. This creates an identity matrix which will process the image without change. To halve the contrast you can use 0.5 and to double it you would use 2. The setting in matrix position [3,3] is to maintain the alpha component unchanged.

 

Adjusting the contrast in this manner also changes the brightness of an image. Reducing contrast makes the image darker because reducing the scale of the colours moves the RGB values towards zero. Conversely, increasing the scale makes the image brighter because the values are driven towards the maximum of 255. Images that are adjusted for color also often need to be adjusted for brightness.

 

A Bug or a Feature?

 

There is one small problem with this theory and that is the ColorMatrix has a nasty habit of causing arithmetic overflows in the images colour values such that simply scaling will cause an undesirable colourization of the image. Any red, green or blue colour elements having a zero value can overflow to some large value which causes the pixel to take on a totally different colour. To fix this, and it is a blatant fudge, we simply need to shift the colours minutely in one direction or another and the problem goes away. The fudge in the code below offsets the image colours by a measly 0.001, just a tenth of one percent. This means that our final matrix looks like this...

 

C     0     0     0 0

0     C     0     0 0

0     0     C     0 0

0     0     0     1 0

0.001 0.001 0.001 0 1

 

The colour purists may complain but it's better than the alternative.

 

The following listings show an application that enables you to adjust the contrast of an image.

 

Show me code in C#

 

using System;

using System.IO;

using System.Drawing;

using System.Drawing.Imaging;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

 

namespace ImageContrast

{

  /// <summary>

  /// Summary description for Form1.

  /// </summary>

  public class Form1 : System.Windows.Forms.Form

  {

 

    Bitmap _image;

 

    float _contrastFactor=1f;

 

 

    private System.Windows.Forms.Button button1;

    private System.Windows.Forms.Button button2;

    private System.Windows.Forms.Button button3;

    private System.Windows.Forms.TrackBar trackBar1;

    private System.Windows.Forms.PictureBox pictureBox1;

    private System.Windows.Forms.Label label1;

    /// <summary>

    /// Required designer variable.

    /// </summary>

    private System.ComponentModel.Container components = null;

 

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      //

      // TODO: Add any constructor code after InitializeComponent call

      //

    }

 

    /// <summary>

    /// Clean up any resources being used.

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      if( disposing )

      {

        if (components != null)

        {

          components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

 

    #region Windows Form Designer generated code

    /// <summary>

    /// Required method for Designer support - do not modify

    /// the contents of this method with the code editor.

    /// </summary>

    private void InitializeComponent()

    {

      this.button1 = new System.Windows.Forms.Button();

      this.button2 = new System.Windows.Forms.Button();

      this.button3 = new System.Windows.Forms.Button();

      this.trackBar1 = new System.Windows.Forms.TrackBar();

      this.pictureBox1 = new System.Windows.Forms.PictureBox();

      this.label1 = new System.Windows.Forms.Label();

      ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();

      this.SuspendLayout();

      //

      // button1

      //

      this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));

      this.button1.Location = new System.Drawing.Point(448, 280);

      this.button1.Name = "button1";

      this.button1.TabIndex = 0;

      this.button1.Text = "Open";

      this.button1.Click += new System.EventHandler(this.button1_Click);

      //

      // button2

      //

      this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));

      this.button2.Location = new System.Drawing.Point(448, 312);

      this.button2.Name = "button2";

      this.button2.TabIndex = 0;

      this.button2.Text = "Save";

      this.button2.Click += new System.EventHandler(this.button2_Click);

      //

      // button3

      //

      this.button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));

      this.button3.Location = new System.Drawing.Point(448, 344);

      this.button3.Name = "button3";

      this.button3.TabIndex = 0;

      this.button3.Text = "Done";

      this.button3.Click += new System.EventHandler(this.button3_Click);

      //

      // trackBar1

      //

      this.trackBar1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));

      this.trackBar1.Location = new System.Drawing.Point(464, 88);

      this.trackBar1.Maximum = 100;

      this.trackBar1.Name = "trackBar1";

      this.trackBar1.Orientation = System.Windows.Forms.Orientation.Vertical;

      this.trackBar1.Size = new System.Drawing.Size(45, 176);

      this.trackBar1.TabIndex = 1;

      this.trackBar1.TickFrequency = 10;

      this.trackBar1.Value = 25;

      this.trackBar1.Scroll += new System.EventHandler(this.trackBar1_Scroll);

      //

      // pictureBox1

      //

      this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)

        | System.Windows.Forms.AnchorStyles.Left)

        | System.Windows.Forms.AnchorStyles.Right)));

      this.pictureBox1.Location = new System.Drawing.Point(8, 8);

      this.pictureBox1.Name = "pictureBox1";

      this.pictureBox1.Size = new System.Drawing.Size(432, 360);

      this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;

      this.pictureBox1.TabIndex = 2;

      this.pictureBox1.TabStop = false;

      //

      // label1

      //

      this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));

      this.label1.Location = new System.Drawing.Point(456, 40);

      this.label1.Name = "label1";

      this.label1.Size = new System.Drawing.Size(56, 23);

      this.label1.TabIndex = 3;

      this.label1.Text = "1";

      this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;

      //

      // Form1

      //

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

      this.ClientSize = new System.Drawing.Size(536, 389);

      this.Controls.Add(this.label1);

      this.Controls.Add(this.pictureBox1);

      this.Controls.Add(this.trackBar1);

      this.Controls.Add(this.button1);

      this.Controls.Add(this.button2);

      this.Controls.Add(this.button3);

      this.Name = "Form1";

      this.Text = "Form1";

      ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();

      this.ResumeLayout(false);

 

    }

    #endregion

 

    /// <summary>

    /// The main entry point for the application.

    /// </summary>

    [STAThread]

    static void Main()

    {

      Application.Run(new Form1());

    }

 

    private void button1_Click(object sender, System.EventArgs e)

    {

      OpenFileDialog dlg=new OpenFileDialog();

      dlg.Filter="Image files|*.bmp; *.jpg; *.gif; *.tif; *.png";

      if(dlg.ShowDialog()==DialogResult.OK)

      {

        _image=(Bitmap)Image.FromFile(dlg.FileName,true);

        this.Text=dlg.FileName;

      }

      this.trackBar1.Value=25; //reset the contrast and update the image

      UpdateImage();

    }

 

    private void button3_Click(object sender, System.EventArgs e)

    {

      Application.Exit();

    }

 

    private void trackBar1_Scroll(object sender, System.EventArgs e)

    {

      _contrastFactor=0.04f*this.trackBar1.Value;

 

      this.label1.Text=this._contrastFactor.ToString("0.00");

      UpdateImage();

    }

 

    /// <summary>

    /// Adjusts the contrast of the image by modifying the scale of the original colours using a ColorMatrix

    /// </summary>

    void UpdateImage()

    {

      if(_image==null)

        return;

      Image oldImage=this.pictureBox1.Image; // save the old image so we can dispose of it nicely

 

      Bitmap bm=new Bitmap(this._image.Width, this._image.Height); //create a new image

      Graphics g=Graphics.FromImage(bm); //ready to draw on it

      ImageAttributes ia=new ImageAttributes();

      //create the scaling matrix

      ColorMatrix cm=new ColorMatrix(new float[][]{  new float[]{_contrastFactor,0f,0f,0f,0f},

                              new float[]{0f,_contrastFactor,0f,0f,0f},

                              new float[]{0f,0f,_contrastFactor,0f,0f},

                              new float[]{0f,0f,0f,1f,0f},

                              //including the BLATANT FUDGE

                              new float[]{0.001f,0.001f,0.001f,0f,1f}});

      //use it in the image attributes

      ia.SetColorMatrix(cm);

      //draw the original to the temporary using the matrix

      g.DrawImage(_image,new Rectangle(0,0,_image.Width,_image.Height),0,0,_image.Width,_image.Height,GraphicsUnit.Pixel,ia);

      g.Dispose(); //Don't need this anymore;

      ia.Dispose(); // or this

      this.pictureBox1.Image=bm; //replace the picture;

      if(oldImage!=null)

        oldImage.Dispose(); // get rid of the old one if it exists.

    }

 

    private void button2_Click(object sender, System.EventArgs e)

    {

      //Saves the image in the chosen format

      if(_image==null)

        return;

      SaveFileDialog dlg=new SaveFileDialog();

      dlg.Filter="Image files|*.bmp; *.jpg; *.gif; *.tif; *.png ";

      dlg.FileName=this.Text;

      if(dlg.ShowDialog()==DialogResult.OK)

      {

        this.Text=dlg.FileName;

        ImageFormat fmt=ImageFormat.Jpeg;

        switch(Path.GetExtension(dlg.FileName).ToLower())

        {

          case ".jpg":

          case ".jpeg":

            fmt=ImageFormat.Jpeg;

            break;

          case ".bmp":

            fmt=ImageFormat.Bmp;

            break;

          case ".png":

            fmt=ImageFormat.Png;

            break;

          case ".tif":

          case ".tiff":

            fmt=ImageFormat.Tiff;

            break;

          case ".gif":

            fmt=ImageFormat.Gif;

            break;

        }

        //make the image in the picturebox the working image

        this._image.Dispose();

        this.pictureBox1.Image.Save(dlg.FileName,fmt);

        this._image=(Bitmap)this.pictureBox1.Image;

      }

    }

 

  }

}

 

Show me code in VB

 

Imports System

Imports System.IO

Imports System.Drawing

Imports System.Drawing.Imaging

Imports System.Collections

Imports System.ComponentModel

Imports System.Windows.Forms

Imports System.Data

 

 

Namespace ImageContrast

   '/ <summary>

   '/ Summary description for Form1.

   '/ </summary>

  

   Public Class Form1

    Inherits System.Windows.Forms.Form

    

    Private _image As Bitmap

    

    Private _contrastFactor As Single = 1F

    

    

    Private WithEvents button1 As System.Windows.Forms.Button

    Private WithEvents button2 As System.Windows.Forms.Button

    Private WithEvents button3 As System.Windows.Forms.Button

    Private WithEvents trackBar1 As System.Windows.Forms.TrackBar

    Private pictureBox1 As System.Windows.Forms.PictureBox

    Private label1 As System.Windows.Forms.Label

    '/ <summary>

    '/ Required designer variable.

    '/ </summary>

    Private components As System.ComponentModel.Container = Nothing

    

    

    Public Sub New()

     '

     ' Required for Windows Form Designer support

     '

     InitializeComponent()

    End Sub 'New

     

    '

    ' TODO: Add any constructor code after InitializeComponent call

    '

    

    '/ <summary>

    '/ Clean up any resources being used.

    '/ </summary>

    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

      If disposing Then

        If Not (components Is Nothing) Then

          components.Dispose()

        End If

      End If

      MyBase.Dispose(disposing)

    End Sub 'Dispose

 

#Region "Windows Form Designer generated code"

 

    '/ <summary>

    '/ Required method for Designer support - do not modify

    '/ the contents of this method with the code editor.

    '/ </summary>

    Private Sub InitializeComponent()

      Me.button1 = New System.Windows.Forms.Button

      Me.button2 = New System.Windows.Forms.Button

      Me.button3 = New System.Windows.Forms.Button

      Me.trackBar1 = New System.Windows.Forms.TrackBar

      Me.pictureBox1 = New System.Windows.Forms.PictureBox

      Me.label1 = New System.Windows.Forms.Label

      CType(Me.trackBar1, System.ComponentModel.ISupportInitialize).BeginInit()

      Me.SuspendLayout()

      '

      ' button1

      '

      Me.button1.Anchor = CType(System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles)

      Me.button1.Location = New System.Drawing.Point(448, 280)

      Me.button1.Name = "button1"

      Me.button1.TabIndex = 0

      Me.button1.Text = "Open"

      '

      ' button2

      '

      Me.button2.Anchor = CType(System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles)

      Me.button2.Location = New System.Drawing.Point(448, 312)

      Me.button2.Name = "button2"

      Me.button2.TabIndex = 0

      Me.button2.Text = "Save"

      '

      ' button3

      '

      Me.button3.Anchor = CType(System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles)

      Me.button3.Location = New System.Drawing.Point(448, 344)

      Me.button3.Name = "button3"

      Me.button3.TabIndex = 0

      Me.button3.Text = "Done"

      '

      ' trackBar1

      '

      Me.trackBar1.Anchor = CType(System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles)

      Me.trackBar1.Location = New System.Drawing.Point(464, 88)

      Me.trackBar1.Maximum = 100

      Me.trackBar1.Name = "trackBar1"

      Me.trackBar1.Orientation = System.Windows.Forms.Orientation.Vertical

      Me.trackBar1.Size = New System.Drawing.Size(45, 176)

      Me.trackBar1.TabIndex = 1

      Me.trackBar1.TickFrequency = 10

      Me.trackBar1.Value = 25

      '

      ' pictureBox1

      '

      Me.pictureBox1.Anchor = CType(System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles)

      Me.pictureBox1.Location = New System.Drawing.Point(8, 8)

      Me.pictureBox1.Name = "pictureBox1"

      Me.pictureBox1.Size = New System.Drawing.Size(432, 360)

      Me.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage

      Me.pictureBox1.TabIndex = 2

      Me.pictureBox1.TabStop = False

      '

      ' label1

      '

      Me.label1.Anchor = CType(System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles)

      Me.label1.Location = New System.Drawing.Point(456, 40)

      Me.label1.Name = "label1"

      Me.label1.Size = New System.Drawing.Size(56, 23)

      Me.label1.TabIndex = 3

      Me.label1.Text = "1"

      Me.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter

      '

      ' Form1

      '

      Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

      Me.ClientSize = New System.Drawing.Size(536, 389)

      Me.Controls.Add(label1)

      Me.Controls.Add(pictureBox1)

      Me.Controls.Add(trackBar1)

      Me.Controls.Add(button1)

      Me.Controls.Add(button2)

      Me.Controls.Add(button3)

      Me.Name = "Form1"

      Me.Text = "Form1"

      CType(Me.trackBar1, System.ComponentModel.ISupportInitialize).EndInit()

      Me.ResumeLayout(False)

    End Sub 'InitializeComponent

#End Region

 

 

    '/ <summary>

    '/ The main entry point for the application.

    '/ </summary>

    <STAThread()> _

    Shared Sub Main()

      Application.Run(New Form1)

    End Sub 'Main

 

 

    Private Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button1.Click

      Dim dlg As New OpenFileDialog

      dlg.Filter = "Image files|*.bmp; *.jpg; *.gif; *.tif; *.png"

      If dlg.ShowDialog() = DialogResult.OK Then

        _image = CType(Image.FromFile(dlg.FileName, True), Bitmap)

        Me.Text = dlg.FileName

      End If

      Me.trackBar1.Value = 25 'reset the contrast and update the image

      UpdateImage()

    End Sub 'button1_Click

 

 

    Private Sub button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button3.Click

      Application.Exit()

    End Sub 'button3_Click

 

 

    Private Sub trackBar1_Scroll(ByVal sender As Object, ByVal e As System.EventArgs) Handles trackBar1.Scroll