Tinting and Gray Scaling Pictures with Visual Studio 2012

Introduction

I love photos. I love taking photos, even though I am by no means a professional or even an amateur. I love taking pictures spontaneously, especially about my wife and daughter. Funny thing is: I hate being in a photograph!

I also love PhotoShop with all its features to improve the quality of pictures.  I love the effect of a black and white photo! This got me thinking, wouldn't it be nice to make a small tool for my personal use, to make my pictures tinted or gray scaled. This is what we'll do today. It is not too complicated, so we can take it easy ( for a change ).

Design

Our design is quite simple. All you will need on your form is the following objects:

  • 6 Buttons
    1. Red
    2. Green
    3. Blue
    4. Choose A Color
    5. Gray
    6. Save
  • 2 PictureBoxes
    1. Source PictureBox
    2. Destination PictureBox
  • 1 SaveFileDialog
  • 1 ColorDialog

You could name all the objects anything you like.

Coding

In all honesty, I was pleasantly surprised about the small amount of code that was needed in this project. I think it comes down to using the right tools for the job. In this case: the ColorMatrix object

As usual, let us start with the Namespaces:

VB.NET

Imports System.Drawing.Imaging ' Advanced Image Functionalities
Imports System.IO 'File Operations

C#

using System.Drawing.Imaging; //Advanced Image Functionalities
using System.IO; //File Operations

Add our Tint function:

VB.NET

    Private Function Tint(ByVal bmpSource As Bitmap, ByVal clrScaleColor As Color, ByVal sngScaleDepth As Single) As Bitmap

        Dim bmpTemp As New Bitmap(bmpSource.Width, bmpSource.Height) 'Create Temporary Bitmap To Work With

        Dim iaImageProps As New ImageAttributes 'Contains information about how bitmap and metafile colors are manipulated during rendering. 

        Dim cmNewColors As ColorMatrix 'Defines a 5 x 5 matrix that contains the coordinates for the RGBAW space

        cmNewColors = New ColorMatrix(New Single()() _
            {New Single() {1, 0, 0, 0, 0}, _
             New Single() {0, 1, 0, 0, 0}, _
             New Single() {0, 0, 1, 0, 0}, _
             New Single() {0, 0, 0, 1, 0}, _
             New Single() {clrScaleColor.R / 255 * sngScaleDepth, clrScaleColor.G / 255 * sngScaleDepth, clrScaleColor.B / 255 * sngScaleDepth, 0, 1}})

        iaImageProps.SetColorMatrix(cmNewColors) 'Apply Matrix

        Dim grpGraphics As Graphics = Graphics.FromImage(bmpTemp) 'Create Graphics Object and Draw Bitmap Onto Graphics Object

        grpGraphics.DrawImage(bmpSource, New Rectangle(0, 0, bmpSource.Width, bmpSource.Height), 0, 0, bmpSource.Width, bmpSource.Height, GraphicsUnit.Pixel, iaImageProps)

        Return bmpTemp

    End Function

C#

    private Bitmap Tint(Bitmap bmpSource, Color clrScaleColor, float sngScaleDepth)
    {

        Bitmap bmpTemp = new Bitmap(bmpSource.Width, bmpSource.Height); //Create Temporary Bitmap To Work With

        ImageAttributes iaImageProps = new ImageAttributes(); //Contains information about how bitmap and metafile colors are manipulated during rendering. 

        ColorMatrix cmNewColors = default(ColorMatrix); //Defines a 5 x 5 matrix that contains the coordinates for the RGBAW space
	    cmNewColors = new ColorMatrix(new float[][] {
		    new float[] {
			    1,
			    0,
			    0,
			    0,
			    0
		    },
		    new float[] {
			    0,
			    1,
			    0,
			    0,
			    0
		    },
		    new float[] {
			    0,
			    0,
			    1,
			    0,
			    0
		    },
		    new float[] {
			    0,
			    0,
			    0,
		    	1,
		    	0
		    },
		    new float[] {
			    clrScaleColor.R / 255 * sngScaleDepth,
			    clrScaleColor.G / 255 * sngScaleDepth,
			    clrScaleColor.B / 255 * sngScaleDepth,
			    0,
			    1
		    }
	    });

	    iaImageProps.SetColorMatrix(cmNewColors); //Apply Matrix

        Graphics grpGraphics = Graphics.FromImage(bmpTemp); //Create Graphics Object and Draw Bitmap Onto Graphics Object

	    grpGraphics.DrawImage(bmpSource, new Rectangle(0, 0, bmpSource.Width, bmpSource.Height), 0, 0, bmpSource.Width, bmpSource.Height, GraphicsUnit.Pixel, iaImageProps);
	    
        return bmpTemp;

    }

This function makes use of the ColorMatrix class to calculate how the colors in the picture should change. We get all the associated RGB ( Red, Green, Blue ) values from the picture, divide it by 255 ( there are only 256 shades of red or green or blue in the RGB Color model. Then, we multiply it with the depth that we want. Any value between .25 and .75 will give you the best results.

Create a Bitmap Variable and add the following line into your Form_Load event:

VB.NET

    Dim bmpNewPic As Bitmap 'Source Bitmap

    Private Sub frmTint_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        bmpNewPic = New Bitmap(picSource.Image) ' Source Picture To Work With

    End Sub

C#

    Bitmap bmpNewPic; //Source Bitmap

        private void frmTint_Load(object sender, EventArgs e)
        {

             bmpNewPic = new Bitmap(picSource.Image); //Source Picture To Work With

        }

This creates a bitmap object to apply the tint to.

Add the following for the Red, Green and Blue buttons:

VB.NET

    Private Sub btnRed_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRed.Click

        picDest.Image = Tint(bmpNewPic, Color.Red, 0.3) 'Red

    End Sub

    Private Sub btnGreen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGreen.Click

        picDest.Image = Tint(bmpNewPic, Color.Green, 0.3) 'Green

    End Sub

    Private Sub btnBlue_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBlue.Click

        picDest.Image = Tint(bmpNewPic, Color.Blue, 0.3) 'Blue

    End Sub

C#

    private void btnRed_Click(System.Object sender, System.EventArgs e)
    {

        picDest.Image = Tint(bmpNewPic, Color.Red, 0.3f); //Red

    }

    private void btnGreen_Click(System.Object sender, System.EventArgs e)
    {

    	picDest.Image = Tint(bmpNewPic, Color.Green, 0.3f); //Green

    }

    private void btnBlue_Click(System.Object sender, System.EventArgs e)
    {

	    picDest.Image = Tint(bmpNewPic, Color.Blue, 0.3f); //Blue

    }

We call the Tint function for each of the associated colors.

Add the following code for the Choose Color button:

VB.NET

    Private Sub btnChoose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChoose.Click

        If cdTint.ShowDialog = DialogResult.OK Then 'Show Color Dialog

            Dim clrSel As Color
            clrSel = cdTint.Color 'Store Selected Color

            picDest.Image = Tint(bmpNewPic, clrSel, 0.3) 'Apply Tint

        End If

    End Sub

C#

    private void btnChoose_Click(object sender, EventArgs e)
    {
        DialogResult drColors = cdTint.ShowDialog(); //Show Color Dialog
        
        if (drColors == DialogResult.OK)
        {
        
            Color clrSel = cdTint.Color; //Store Selected Color
        
            picDest.Image = Tint(bmpNewPic, clrSel, 0.3f); //Apply Tint

        }
    }

Same logic, we just allow the user to select a tint color.

Add the following code for the Gray Scale button:

VB.NET

    Private Sub btnGray_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGray.Click

        Dim intX As Integer 'X Counter
        Dim intY As Integer 'Y Counter

        Dim clrOld As Integer 'Previous Color

        For intX = 0 To bmpNewPic.Width - 1 'Loop Horizontally

            For intY = 0 To bmpNewPic.Height - 1 'Loop Vertically

                'Get Existing Color At Each Pixel Location and Divide By Scale Depth
                'Any Range Between .25 and .75 Will Give optimal Results
                clrOld = (CInt(bmpNewPic.GetPixel(intX, intY).R) + _
                       bmpNewPic.GetPixel(intX, intY).G + _
                       bmpNewPic.GetPixel(intX, intY).B) \ 3

                bmpNewPic.SetPixel(intX, intY, Color.FromArgb(clrOld, clrOld, clrOld)) 'Apply Tint

            Next intY

        Next intX

        picDest.Image = bmpNewPic 'Set New Picture

    End Sub

C#

    private void btnGray_Click(System.Object sender, System.EventArgs e)
    {

	    int intX = 0; //X Counter
	    int intY = 0; //Y Counter

	    int clrOld = 0; //Previous Color

        for (intX = 0; intX <= bmpNewPic.Width - 1; intX++) //Loop Horizontally
        {
            for (intY = 0; intY <= bmpNewPic.Height - 1; intY++) //Loop Vertically
            {
                 //Get Existing Color At Each Pixel Location and Divide By Scale Depth
                //Any Range Between .25 and .75 Will Give optimal Results
                clrOld = (Convert.ToInt32(bmpNewPic.GetPixel(intX, intY).R) + bmpNewPic.GetPixel(intX, intY).G + bmpNewPic.GetPixel(intX, intY).B) / 3;

                bmpNewPic.SetPixel(intX, intY, Color.FromArgb(clrOld, clrOld, clrOld)); //Apply Tint
		    }
	     }

        picDest.Image = bmpNewPic; //Set New Picture
      
    }

A more complicated way of tinting. We get each pixel's color and replace it. This is more time consuming and processor intensive.

All that is left is to save our tinted picture:

VB.NET

    Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click

        If sfdTint.ShowDialog = DialogResult.OK Then

            picDest.Image.Save(sfdTint.FileName) 'Save

        End If

    End Sub

C#

        private void btnSave_Click(object sender, EventArgs e)
        {
            DialogResult drSave = sfdTint.ShowDialog();

           if(drSave == DialogResult.OK)
           {

               picDest.Image.Save(sfdTint.FileName); //Save

           }
        
        }

Conclusion

This wasn't so complicated now, was it? I am attaching the working projects with this article. Thanks for reading! Until next time, cheers!



Related Articles

Downloads