Jump to content
How to Install Plugins ×

Simple whitepoint correction


Lord Crc

Recommended Posts

This is a simple "whitepoint" correction plugin. It's ideal for images which have a slight color tint, and which contains some pixels which should be neutral grey.

I wrote this plugin as my mobile camera usually ends up taking images with poor color balance, and I wanted a simple tool to correct it. I'm publishing it in case anyone else finds it useful.

Usage:

  1. Select the Color Picker tool.
  2. Use the Color Picker to select a Primary Color which should be neutral grey.
  3. Select the Adjustments menu and select the Whitepoint plugin.
  4. If not satisfied, undo, try another color and run it again.

Here's an illustration image.

Example:

Before image

After image

How it works:

The plugin will apply a photo filter-like effect on the image using opposite color of the selected Primary Color, while preserving the brightness of the source color. For example, if the Primary Color is blue-green (cyan) it will apply a red photo filter.

Download:

http://partialgeek.n...tCorrection.zip

Notes:

  • Distributed under the LGPL license, the .cs file is for use with the CodeLab plugin, ignore it if you don't want to look at or modify the code.
  • Note that picking a color from a uniform area works best, the brightness of the color is much less important. For the example picture below I picked a color from the shadowed area on the napkin, as can be seen in the illustration image above.
  • For images with a lot of color noise it may be beneficial to downsize the image before picking a color to get a better basis color for the filtering. Undo the resize after you've picked the color.
  • As always "garbage in = garbage out". This filter cannot undo destructive processes, so for example washed out highlights may not look good. It works best for images with a slight tint.

Hope you enjoy :)

 

Source Code
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published 
//  by the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Author: Asbjørn Heid <lordcrc@gmail.com>

#region UICode
#endregion

class MathUtil
{
    private MathUtil()
    {
    }
    
    public static float Lerp(float t, float v_min, float v_max)
    {
        return (1.0f - t) * v_min + t * v_max;
    }    
}

class Gamma
{
    public Gamma(double g)
    {
        toLinear = new float[256];
        for (int i = 0; i < 256; i++)
        {
            toLinear[i] = (float)Math.Pow(i / 255.0, g);
        }
        fromLinear = new byte[65536];
        for (int i = 0; i < 65536; i++)
        {
            fromLinear[i] = (byte)(255 * Math.Pow(i / 65535.0, 1.0 / g));
        }
    }
    
    public byte FromLinear(float v)
    {
        int x = Math.Max(0, Math.Min((int)(v * 65535), 65535));
        return fromLinear[x];
    }
    
    public float ToLinear(byte v)
    {
        return toLinear[v];
    }
        
    private float[] toLinear;
    private byte[] fromLinear;
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor;

    Gamma g = new Gamma(2.2);
    
    float wpR = g.ToLinear(PrimaryColor.R);
    float wpG = g.ToLinear(PrimaryColor.G);
    float wpB = g.ToLinear(PrimaryColor.B);
    
    if (wpR <= 0 && wpG <= 0 && wpB <= 0)
    {
        wpR = 1;
        wpG = 1;
        wpB = 1;
    }
    
    float wpY = (wpR + wpG) / 2;
    float wpC = (wpG + wpB) / 2;
    float wpM = (wpR + wpB) / 2;
    
    // filter weights
    float wC = wpR / (wpR + wpG + wpB);
    float wM = wpG / (wpR + wpG + wpB);
    float wY = wpB / (wpR + wpG + wpB);
    float wR = wpC / (wpC + wpM + wpY);
    float wG = wpM / (wpC + wpM + wpY);
    float wB = wpY / (wpC + wpM + wpY);

    // filter coefficients
    float fR = wM + wY;
    float fG = wY + wC;
    float fB = wC + wM;
    float fC = wG + wB;
    float fM = wB + wR;
    float fY = wR + wG;
    
    // rgb vs cmy weight
    float v;
    if (wpR < wpG && wpR < wpB)
    {
        // cyan dominance
        v = 1.0f - (Math.Max(wpG, wpB) - wpC) / wpC;
    } else if (wpG < wpB) 
    {
        // magenta dominance
        v = 1.0f - (Math.Max(wpR, wpB) - wpM) / wpM;
    } else 
    {
        // yellow dominance
        v = 1.0f - (Math.Max(wpR, wpG) - wpY) / wpY;
    }
    
    ColorBgra c;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        for (int x = rect.Left; x < rect.Right; x++)
        {
            c = src[x,y];
            
            float cR = g.ToLinear(c.R);
            float cG = g.ToLinear(c.G);
            float cB = g.ToLinear(c.B);
            float cY = (cR + cG) * 0.5f;
            float cC = (cG + cB) * 0.5f;
            float cM = (cR + cB) * 0.5f;
            
            float cL = 0.299f * cR + 0.587f * cG + 0.114f * cB;
            
            if (cL <= 0.0f)
                continue;
            
            // filter
            cR *= fR;
            cG *= fG;
            cB *= fB;
            cY *= fY;
            cC *= fC;
            cM *= fM;
           
            cR = MathUtil.Lerp(v, cR, (cY - cC + cM));
            cG = MathUtil.Lerp(v, cG, (cC - cM + cY));
            cB = MathUtil.Lerp(v, cB, (cM - cY + cC));                      
                        
            float cFL = 0.299f * cR + 0.587f * cG + 0.114f * cB;

            // recover brightness 
            float Lfactor = cL / cFL;
            
            cR *= Lfactor;
            cG *= Lfactor;
            cB *= Lfactor;
            
            //cR = cG = cB = v;
            
            dst[x,y] = ColorBgra.FromBgra(g.FromLinear(cB), g.FromLinear(cG), g.FromLinear(cR), 255);
        }
    }
}

 

Whitepoint.zip

Link to comment
Share on other sites

Very nice! This will actually be useful to me in the near future. :)

No, Paint.NET is not spyware...but, installing it is an IQ test. ~BoltBait

Blend modes are like the filling in your sandwich. It's the filling that can change your experience of the sandwich. ~Ego Eram Reputo

Link to comment
Share on other sites

Well in cursory testing it can provide some alternatives as well for a color tint wash like YM indicated. Done mainly on primitives and the negation aspect is different than White Balance or most auto leveling passes. Definitely GIGO as well based on user selection as well as image density.

Thanks for sharing up your efforts on this.

***

Gallery at PDN-Fans

Link to comment
Share on other sites

Sorry forgot to mention that the CS file is for use with the CodeLab plugin

If you don't want to look at or modify the code, feel free to ignore it :)

Edited by Lord Crc
Link to comment
Share on other sites

Well in cursory testing it can provide some alternatives as well for a color tint wash like YM indicated. Done mainly on primitives and the negation aspect is different than White Balance or most auto leveling passes. Definitely GIGO as well based on user selection as well as image density.

Thanks for sharing up your efforts on this.

Thanks! I must admit I haven't explored the more creative uses of this plugin, fun :)

Link to comment
Share on other sites

  • 3 years later...

Pretty cool Lord Crc. I wonder if your plugin will let me accomplish what I am looking for.

Moderartor suggested I should try your plugin for what I want to accomplish. What I want to accomplish is commercial photography by applying proper touch up for photos I made with my old digital camera. My photos even when shooting on sunlight do not have enough lighting plus it feels as camera also leaves green tint on photos. The process is removing background, putting object on white background and adjusting it by raising light levels so although artificially, it looks like it was shot with enough lighting. Applying color adjustment later would be bonus and not as required as lighting. To give you example for what I am looking for, here's a link to "Before - After" photo:

http://imgur.com/a/0vlqM

Can you view them?

Another problem is that I am not savvy in knowing terms and adjusting parameters. Would that be obstacle?

Link to comment
Share on other sites

  • 7 years later...

Necro bump.  I used this a long time ago and it was great. But now I am on a new machine and the original link seems to be nogo.  Is there another version of this or an update somewhere?

Link to comment
Share on other sites

5 hours ago, papercut said:

Is there another version of this or an update somewhere?

 

As you noted, this is a very old post and the author has not visited since 2016, so it looks like the 'host site' of the Plugin is now dead.

The only hope is, if anyone still has this plugin, to post it here.

 

(Pipped by @BDP :) )

30b8T8B.gif

How I made Jennifer & Halle in Paint.net

My Gallery | My Deviant Art

"Rescuing one animal may not change the world, but for that animal their world is changed forever!" anon.

 
Link to comment
Share on other sites

I've dropped @BDP's recompiled version into the first post, along with the source code provided by @user.by. Thanks to these two for making these files available :star:

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...