Click here to Skip to main content
15,881,719 members
Articles / Web Development / ASP.NET
Article

Applying a Color Wash to Images

Rate me:
Please Sign up or sign in to vote.
4.65/5 (18 votes)
15 Dec 2005CPOL7 min read 107.4K   1.1K   54   14
This article demonstrates how to apply a color wash to images within .NET, complimenting what's possible with CSS.

Introduction

Ever had a web site that needs branding for individual customers but found the presence of image elements complicates the process as you can't adjust their color from a simple style sheet? Wouldn't it be nice to use a base set of grayscale images and automatically apply a color wash to them to bring them in line with the customer's color scheme without fiddling with a graphics package?

Background

There's a web application I'm involved in the ongoing development of which makes use of a style sheet in order to allow the look and feel of the application to be customized to match each individual customer's corporate look and feel. This works fine for most elements of the site; however we use a number of duotone brand images in the application, and to make sure these match the color scheme, can be a bit more tedious than the simple editing of the CSS file. As the .NET Framework puts a lot more power in the hands of the developer, I decided to automate the process of applying color washes to these images to streamline the customer customization process.

Image 1

Fig. 1 - Color wash in action.

Though I've used this functionality in a web application and on grayscale images, the library is not limited to web development or grayscale images.

The method

Not coming from a graphics based background, my method may not be the best solution, but it seems to work just fine.

Theory

To apply a color wash, a base RGB color value is decided upon as a reference point and an adjustment value calculated for the color wash's RGB value to be applied. This adjustment is calculated for each of the red, green and blue components. Once this is done, each of the image pixel's red, green and blue components are adjusted by the calculated amount to produce the color wash.

Each of the RGB components of a color can be represented by values from 0 to 255, so taking the mid point of 127 as the base reference point and taking the desired RGB component values of the color wash to apply, we can calculate the adjustments to be made to each pixel.

Adjusting red, green and blue component values can sometimes result in a value outside the allowed range of 0 to 255; in these cases, we compromise and simply use the minimum or maximum value allowed as appropriate.

The table below illustrates how values are adjusted:

Hex valueRedGreenBlueColor
Base Color7F7F7F127127127
Image 2
Color wash to apply1280AA18128170
Image 3
Adjustment value to use18 - 127 = -109128 - 127 = 1170 - 127 = 43
Example Pixel 1 original colorA5A5A5165165165
Image 4
Example Pixel 1 washed color38A6D0165 - 109 = 56165 + 1 = 166165 + 43 = 208
Image 5
Example Pixel 2 original color545454848484
Image 6
Example Pixel 2 washed color00557F84 - 109 = -25 = 0 *84 + 1 = 8584 + 43 = 127
Image 7
Example Pixel 3 original color2E2E2E464646
Image 8
Example Pixel 3 washed color002F5946 - 109 = -63 = 0 *46 + 1 = 4746 + 43 = 89
Image 9

* The value is less than the minimum allowed so we use the minimum instead.

Implementation

The implementation of this technique is in fact very simple:

  1. Load the image to be color washed.
  2. Clone the image using a format that won't result in an indexed palette being present (to prevent errors occurring when we start manipulating pixel colors).
  3. Calculate the adjustments that need to be made to each pixel's red, green and blue components.

    Note that Red, Green and Blue are from the color wash that we are applying:

    VB
    'Approx non-fraction mid point between 0 and 255
    Const cintAdjustAgainstBase As Integer = 127
    
    ...
    
    'Calculate base adjustment values
    Dim intAdjustR As Integer = Red - cintAdjustAgainstBase
    Dim intAdjustG As Integer = Green - cintAdjustAgainstBase
    Dim intAdjustB As Integer = Blue - cintAdjustAgainstBase
  4. Loop through every pixel within the image adjusting red, green and blue components to produce the desired color wash.
    VB
    'Adjust RGB values for every pixel 
    Dim iX As Integer
    Dim iY As Integer
    iX = 0
    Do While iX <= bmp.Width - 1
        iY = 0
        Do While iY <= bmp.Height - 1
            Dim c As System.Drawing.Color = bmp.GetPixel(iX, iY)
            bmp.SetPixel(iX, iY, c.FromArgb(c.A,_
                 AdjustRGBValue(c.R, intAdjustR),_
                 AdjustRGBValue(c.G, intAdjustG),_
                 AdjustRGBValue(c.B, intAdjustB)))
            iY += 1
        Loop
        iX += 1
    Loop

    Below is the function used to calculate the new red, green and blue components to use and ensure that the values stay within the acceptable limits:

    VB
    Private Shared Function AdjustRGBValue(ByVal Value As Integer, _
                ByVal Adjust As Integer) As Integer
        Dim intReturn As Integer = Value + Adjust
        If intReturn > 255 Then intReturn = 255
        If intReturn < 0 Then intReturn = 0
        Return intReturn
    End Function
  5. Save the resulting color washed image.

Using the code

To use this functionality in your projects, simply download the source files accompanying this article, drop GavDev.Image.dll into the bin directory of your project, then make a reference to the class library GavDev.Image.dll. There are two interfaces provided for your convenience:

The first interface accepts individual red, green and blue values:

VB
ColorWashImage(ByVal FilePathOriginal As String,_
    ByVal FilePathNew As String,_
    ByVal Red As Integer,_
    ByVal Green As Integer,_
    ByVal Blue As Integer,_
    Optional ByVal Quality As Integer = 100,_
    Optional ByVal Encoding As String = "image/jpeg")

The second interface allows you to pass a single combined 6 digit hexadecimal RGB value in as the color wash value:

VB
ColorWashImage(ByVal FilePathOriginal As String,_
    ByVal FilePathNew As String,_
    ByVal HexRGBValue As String,_
    Optional ByVal Quality As Integer = 100,_
    Optional ByVal Encoding As String = "image/jpeg")

Both interfaces expect parameters FilePathOriginal, the physical location of the original grayscale image, and FilePathNew, the physical location to save the resulting color washed image. Optional parameters are also present to dictate the quality and encoding of the output images.

Below is an example of the class being used as it is in the accompanying demo project:

VB
GavDev.Image.Manipulate.ColorWashImage(_
    Server.MapPath("Images/Gav.jpg"),_
    Server.MapPath("Images/Gav." & txtColorWash.Text & ".jpg"),_
    txtColorWash.Text)

Image 10

Fig. 2 - Demo project screenshot.

A better application of this functionality in your projects maybe to set the RGB value to use for a customer in Web.config and apply the color wash if the color washed files have not previously been generated (e.g. it's the first time the customer has run the web application). Obviously it's better to only create color washed images once rather than creating them every single time the web application is started.

Here's what the Web.config entry might look like:

XML
<!--
Style sheet for customer’s look and feel.
-->
<add key="StyleSheet" value="GavDev.css"/>

<!--
The hex RGB colour value to apply to brand images.
-->
<add key="BrandImageRGB" value="447777"/>

And the code in Global.asax:

VB
Sub Application_Start(ByVal sender As Object, _
                                ByVal e As EventArgs)
    Dim strImages As String() = _
      {"HelpDesk", "DeskBooking", "FacilityBooking",_
      "TopLeftCorner", "TopRightCorner", "BottomLeftCorner", _
      "BottomRightCorner"}
    Dim strHexRGB As String = _
        ConfigurationSettings.AppSettings("BrandImageRGB")
    Dim blnImageMissing As Boolean

    'Check all color washed images are present
    For Each str As String In strImages
        If Not System.IO.File.Exists(Server.MapPath("~") & _
               "\Images\" & str & "." & strHexRGB & ".jpg") Then
             blnImageMissing = True
             Exit For
        End If
    Next

    'Generate color washed images if they aren't present
    If blnImageMissing Then
        For Each str As String In strImageIdentifiers
          GavDev.Image.Manipulate.ColorWashImage(Server.MapPath("~") & _
             "\Images\" & str & ".jpg", Server.MapPath("~") & "\Images\" _
             & str & "." & strHexRGB & ".jpg", strHexRGB)
        Next
    End If

End Sub

Alternate usage scenario

Though this article and the demo code focuses on applying color washes to photo type images, another useful application is the coloration of minor images that are often used to provide presentation effects, e.g. applying rounded corners to boxes containing content on web sites. In such a scenario, you would make the dominant color of such minor images match the color we use as a base value when calculating the red, green and blue adjustment values. As mentioned previously, this is 127 for red, green and blue components (#7F7F7F in hexadecimal).

Observation

Though the method used is very effective, it would be more efficient to adjust the image's palette values rather than every individual pixel within the image. However I found no obvious method to achieve this.

Possible enhancement

Applying a color wash using the method described above doesn't always result in a satisfactory color washed image, sometimes the shade will be too light or two dark requiring some experimentation with the color wash value applied to achieve best results.

Instead of taking a mid point in the color range (#7F7F7F) to act as the reference point to apply the color wash to, I feel a better result would be achieved consistently if the whole source image was parsed at the beginning to determine what specific color is the most predominant in the image. The predominant color would then be used as the reference point to apply the unmodified color wash to and variations worked out from this image dependent reference point rather than the global midpoint (#7F7F7F).

Though I have not tried the theory out I do believe it would yield far more satisfactory results. When I have more time on my hands I may revisit my code and experiment to see if better results are achievable.

Bonus library functionality

Also present within the GavDev.Image.Manipulate class library, you'll find the ImposeUpperLimitOnDimensions function which is very useful for creating thumbnail images, though I won't cover it in detail here as there are already multiple articles out there detailing such functionality.

History

  • 04th Mar, 2005 - Original article and code.
  • 11th Mar, 2005 - Support added for images with indexed pixel formats.
  • 13th Mar, 2005 - Article updated to include highlights of code.
  • 13th Dec, 2005 - Possible enhancement suggested.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
New Zealand New Zealand
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionTransparent PNG Pin
remitrinita30-Mar-12 4:27
remitrinita30-Mar-12 4:27 
GeneralGreat code! Here's a tip for non-JPeg images: [modified] Pin
Mcb_monica23-Dec-09 9:21
Mcb_monica23-Dec-09 9:21 
GeneralPure CSS solution Pin
LimeyRedneck20-Nov-06 13:26
professionalLimeyRedneck20-Nov-06 13:26 
In my frequent wanderings through CSS sites I came across this technique ...

http://www.alistapart.com/articles/supereasyblendys

As far as I can see, as long as you are using greyscale images, the concept should work quite nicely for this.

Not knocking you efforts, but time and experience has neatly provided a pure css solution that reduces the amount of testing/maintenance you;ll need.

BTW add A list apart to your bookmarks, it's always got something nice to consider.

Nothing is impossible, we just don't know the way of it yet.

GeneralRe: Pure CSS solution Pin
Gavin Harriss25-Nov-06 20:53
Gavin Harriss25-Nov-06 20:53 
QuestionWhy not use ColorMatrix? Pin
jmw16-Mar-05 13:19
jmw16-Mar-05 13:19 
AnswerRe: Why not use ColorMatrix? Pin
Gavin Harriss16-Mar-05 21:24
Gavin Harriss16-Mar-05 21:24 
GeneralRe: Why not use ColorMatrix? Pin
John Harald Apeland12-Jun-05 12:18
sussJohn Harald Apeland12-Jun-05 12:18 
GeneralEnter key not working Pin
worldspawn14-Mar-05 15:36
worldspawn14-Mar-05 15:36 
GeneralRe: Enter key not working Pin
Gavin Harriss14-Mar-05 21:42
Gavin Harriss14-Mar-05 21:42 
GeneralRe: Enter key not working Pin
worldspawn15-Mar-05 14:14
worldspawn15-Mar-05 14:14 
GeneralRe: Enter key not working Pin
Gavin Harriss16-Mar-05 0:17
Gavin Harriss16-Mar-05 0:17 
GeneralNeat Idea Pin
Anonymous4-Mar-05 21:34
Anonymous4-Mar-05 21:34 
GeneralRe: Neat Idea Pin
Gavin Harriss5-Mar-05 8:26
Gavin Harriss5-Mar-05 8:26 
GeneralRe: Neat Idea Pin
Gavin Harriss13-Mar-05 6:56
Gavin Harriss13-Mar-05 6:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.