Script: Histogram Redistribution Methodology

Upcoming mother of all image editors

Script: Histogram Redistribution Methodology

Postby Oscar » Tue Oct 07, 2014 8:49 pm

This has been asked in the forum, so I took the challenge.
Based on Fred Weinhaus ImageMagick script "redist".
The original description is:
http://www.imagemagick.org/Usage/color_ ... ian_redist

The reactor script is heavily different, I only took the basic idea, then implemented it into the reactor script. I was a bit curious if the script is flexible enough to handle this cos we rely on lot of arrays and surprise-surprise it does it with flying colors.

Set
SHOWGRAPH = 1;
if you want to display the graphs over the image (used for debugging)

Code: Select all
//##NAME:Redistribute
//##DESCRIPTION:Gaussian redistribution of histogram
//##INPUTS:1
//##VAR1:60
//##VAR1_NAME:Sigma (gauss width)
//##VAR2:50
//##VAR2_NAME:Mean (shift)

// Please retain this intro-comment
// This software is FREE!!  Open Source software, do what you want with it, open to the community, etc.....
// GNU General Public License
// see http://www.gnu.org/licenses/gpl-2.0.html

// Inspired by: Fred Weinhaus has developed a script, called "redist" for imagemagick
// It redistributes the histogram of an image into a uniform distribution, while appling the same change to all color channels equally.

// modified and adapted for Photo-Reactor Script-block by Oscar

int SHOWGRAPH = 0;

void HistogramRedist(image &img)
{

   int width = img.width;
   int height = img.height;

   ////////////////// MAX
   float maximum = 1;
   float maximumgauss = 1;
   
   ////////////////// MIN
   int minimum = 65535;
   float minimumgauss =65535;

   //////HISTOGRAM
   int[] histogram(256);

   for (int z=0; z< 256; z++)
      histogram[z] = 0;


   for (int y=0; y<height; y++)
   {
      for (int x=0; x<width; x++)
      {

         // one way to get color from pixel
         pixel color = img.Pixel(x,y);

         // Average
         int gray = (color.r+color.g+color.b)/3;
         // luminosity
         // int gray = (0.21*color.r+0.72*color.g+0.07*color.b);
         if (gray>255) gray =255;

         histogram[gray] +=1;

         if (histogram[gray]>maximum) maximum = histogram[gray];

      }
   
   }

   float fx = width/256.0;

// display gray histogram
// this is for testing purposes


if (SHOWGRAPH>0)
   for (int x=0; x<255; x++)
   {


      float nhisto = (histogram[x]/maximum);

      int ypos = nhisto*height;
      if (ypos>height-1) ypos = height-1;

      
      for (int y=0; y<ypos; y++)
      {
         pixel color = img.Pixel(x*fx, height-y-1);

         img.SetRGB(x*fx, height-y-1, (255+color.r)/2, (255+color.g)/2, (255+color.b)/2);
         img.SetRGB(x*fx+1, height-y-1, (255+color.r)/2, (255+color.g)/2, (255+color.b)/2);
      }

      img.SetRGB(x*fx, height-ypos-1, 0, 0, 0);

   }

   //make cummulative
   
   int cumulative = 0;

   for (int x=0; x<256; x++)
   {

      cumulative += histogram[x];
      histogram[x] = cumulative;

   }

   maximum = histogram[255];
   minimum = 0;

   //enerate gaussian distribution graph
   int[] gaussian(256);

   for (int z=0; z< 256; z++)
      gaussian[z] = 0;


   double temp;

   int KSize = (VAR2*2.55); // mean
   float sigma  = VAR1;

   for (int j= 0; j< 256; j++)
   {
       temp = (j-KSize)/sigma;
       gaussian[j] = 256* (pow(2.71828,-temp * temp / 2.0));

       if (gaussian[j]>maximumgauss) maximumgauss = gaussian[j];

      //traceint(gaussian[j]);

   }
 

// display gaussian
// this is for testing purposes

if (SHOWGRAPH>0)
   for (int x=0; x<255; x++)
   {


      float nhisto = (gaussian[x]/maximumgauss);

      int ypos = nhisto*height;
      if (ypos>height-1) ypos = height-1;

      
      for (int y=0; y<ypos; y++)
      {
         pixel color = img.Pixel(x*fx, height-y-1);

         img.SetRGB(x*fx, height-y-1, color.r/2, color.g/2, (255+color.b)/2);
      }

      img.SetRGB(x*fx, height-ypos-1, 0, 0, 0);

   }



   cumulative = 0;

   for (int x=0; x<256; x++)
   {

      cumulative += gaussian[x];
      gaussian[x] = cumulative;

   }

   maximumgauss = gaussian[255];
   minimumgauss = 0;



   // move the normal histogram to the gaussian historgram


   int[] clut(256);

    int k=0; 
    for (int j=0; j<256; j++ )
    {
        while ( k<255 && (gaussian[k]/maximumgauss <= histogram[j]/maximum ))
        {
            k++;
        }

          clut[j] = k;
        //traceint (clut[j]);
     }

   // now we have lut
   // apply it to the image

   int r,g,b;

   for (int y=0; y<height; y++)
   {
      for (int x=0; x<width; x++)
      {

         // one way to get color from pixel
         pixel color = img.Pixel(x,y);

         r = clut[color.r];
         g = clut[color.g];
         b = clut[color.b];
         img.SetRGB(x, y, r, g, b);

      }
   
   }


// display curve
// this is for testing purposes

if (SHOWGRAPH>0)
   for (int x=0; x<255; x++)
   {


      float nhisto = clut[x]/255.0;

      int ypos = nhisto*height;
      if (ypos>height-1) ypos = height-1;

      
      img.SetRGB(x*fx, height-ypos-1,255, 0, 0);
      img.SetRGB(x*fx+1, height-ypos-1,255, 0, 0);

   }

}



void main()
{
   // get the image from input socket
   image img(INPUT);
   
   
   // sample function call
   HistogramRedist(img);
   
   
   // send the image to output
   img.SetOutput();
   
}
Oscar
Site Admin
 
Posts: 863
Images: 2
Joined: Fri Oct 22, 2010 9:54 am

Re: Script: Histogram Redistribution Methodology

Postby Oscar » Tue Oct 07, 2014 8:58 pm

So what it does is distribute histogram using Gaussian curve as an alternative to a function that would just equalize histogram.

This does unnecessary things to an image that has already well distributed histogram (it is well exposed). However if the image is underexposed or overexposed it will then correct that part by hammering the ugly histogram back into shape using a gaussian distribution as a template (where the most pixels are in the middle tonal range).

Sigma value will make the gaussian wider or more narrower. Narrower gaussian will make the image grayish, wider gaussian will make it contrasty by upping up levels that are suppressed in original histogram. A value of 60 is a good stretch across the dynamic range.

Mean value will move the gaussian left or right (should be set to 50 for center gaussian)

This is mostly for education as I am not sure about the general usability due to the fact that we will easily produce banding when messing with histogram and not having enough captured tone range. I just liked the challenge. I hope the script will be useful for learning how to mess with histograms.

Good Example (subject in shadow, but we have enough tones):
redist3.jpg
redist3.jpg (109.67 KiB) Viewed 3332 times

Ditto:
redist5.jpg
redist5.jpg (153.14 KiB) Viewed 3329 times

Image from wikimedia under underexposure
Note: when you start seeing gaps in histogram, that means the image has not enough dynamic range at that zone.

Bad example (we don't have enough tones in darks to pull it up (stretch the histo over dynamic range). We are left with a gap in the darks.
redist4.jpg
redist4.jpg (127.47 KiB) Viewed 3332 times


As a side note, I had hard time to find a bad photo to use as example - todays digital cameras are too good for exposure.
Oscar
Site Admin
 
Posts: 863
Images: 2
Joined: Fri Oct 22, 2010 9:54 am

Re: Script: Histogram Redistribution Methodology

Postby JEL » Fri Oct 10, 2014 2:23 am

Thank you Oscar!! :)

I just downloaded it and will return in a few days when I've taken some time with it.

I wish we could bottle your skills and sell them to the rest of us :)
DAP (AOPs): http://jelstudio.dk/DAP/
PhotoReactor (flows, effects and scripts): http://jelstudio.dk/PhotoReactor/
JEL
 
Posts: 294
Joined: Fri Jan 06, 2012 9:35 pm

Re: Script: Histogram Redistribution Methodology

Postby JEL » Fri Oct 10, 2014 9:41 pm

I've been trying out various ways to increase image-brightness, including your new gaussian redistribution.

Here's a flow with 5 different methods.

#1: main image (the source)

#2: the standard histogram-equalize node

#3: a brightness-boost flow I've made

#4: Oscar's script as posted at the top of this thread

#5: the same brightness-boost flow as #3, except using Oscar's script as the booster.

Each method has various strengths and drawbacks.
Attachments
GaussHistogramStretch,byOscar_snapshot.jpg
GaussHistogramStretch,byOscar_snapshot.jpg (169.92 KiB) Viewed 3290 times
Brightness.zip
(159.29 KiB) Downloaded 169 times
DAP (AOPs): http://jelstudio.dk/DAP/
PhotoReactor (flows, effects and scripts): http://jelstudio.dk/PhotoReactor/
JEL
 
Posts: 294
Joined: Fri Jan 06, 2012 9:35 pm


Return to Photo Reactor

Who is online

Users browsing this forum: No registered users and 4 guests