Writing a simple browser based color corrector

I was studying JavaScript lately. Frankly, I never took the language seriously, but that was due to my ignorance in web technologies.  After my amateur experiments with the language, JS turned out to be pretty cool for fast interactive browser apps. As you might know already, JS is the exclusive language of the web front-end and a popular choice for web back-end solutions, but as a technical artist I saw an alternative use of the language for image color processing. Follow the link to check out a simple interactive color corrector ColorPage

JS has all the necessary tools out of the box, it has a native support for HTML and almost unlimited capabilities for image manipulation. In my case, I used HTML and CSS for GUI creation and some very basic JS for image processing. Although, Python is the de-facto language for VFX and animation, it would require much more effort to create something similar in Python.

Actually, it turned out to be so easy that I decided to write a post about it and provide a little breakdown of the fundamental color processing operations. A quick disclaimer, my JS skills are very poor, and all the code is written from the C++ or Python like code perspective. I know this is bad… but the point of the post is not the JS coding but the work with images and color.

I started my little journey from GUI  design for the color corrector. Essentially, the GUI is an HTML page heavily modified with CSS. To cut the GUI development time I started with the dummy HTML template from www.script-tutorials.com The template is based on Bootstrap CSS framework which provides easy means for layout and design.

gui

Once I finished designing the look of the GUI, I started injecting some JS code for interactive image manipulation. The JS magic starts with the use of the HTML element called Canvas. Canvas is an area where we can draw images and graphics using JS. Once the image is loaded into the canvas, we can access the image’s pixel data using a 1 dimensional array.

var pixelArray = ctx.getImageData(0, 0,  canvasX, canvasY);

for (var i = 0;  i < pixelArray.data.length;  i  +=  4) {
    pixelArray.data[i]  *=  gainR*gain;
    pixelArray.data[i + 1] *= gainG*gain;
    pixelArray.data[i + 2]  *= gainB*gain;
}

To see the entire JS code click here.

Using the simple for loop I could adjust the gain of each pixel in the image. As you might have noticed the first pixel is at index zero and every other pixel starts at the array index which is a multiple of a number 4. A picture worth a thousand words, so here is a little diagram I drew for you guys.

image array

Now, when we know where all the color channels are stored we can perform fundamental color operations like gain, gamma, offset and if you read my post on Logarithmic color space you would be able to perform advanced linear and logarithmic color space transformations inside your browser!

In the code snippet above I perform a simple color gain of each individual channel. The simple gain operation can be described as a product of the value at an index and a user defined parameter.

newColorValue = oldColoValue * userGainParameter

The gamma operation is little bit trickier but still very easy to do.

pixelArray.data[i] = 255 * Math.pow(pixelArray.data[i] / 255, 1 / gamma);
pixelArray.data[i + 1] = 255 * Math.pow(pixelArray.data[i + 1] / 255, 1 / gamma);
pixelArray.data[i + 2] = 255* Math.pow(pixelArray.data[i + 2] / 255, 1 / gamma);

At the core the gamma operation is nothing more than a power function. First, we take the value at index and divide it by 255, this is called normalization. Normalization brings arbitrary value to the value which lies between 0 and 1. Then, we use power function to raise the normalized value to the power of the user specified parameter gamma. Notice that the gamma parameter is inversed (1 / gamma). Once the value is raised to some power, it is multiplied by 255, by doing so we bring the value back in the range from 0 to 255. It may look a little bit confusing at first, but once you break it down into smaller operations it becomes more comprehendible.
There are number of ways to do the offset, but I go with the simplest one.

pixelArray.data[i] = 255 * (pixelArray.data[i]/255 + offset);
pixelArray.data[i + 1] = 255 * (pixelArray.data[i + 1] / 255 + offset);
pixelArray.data[i + 2] = 255 * (pixelArray.data[i + 2] / 255 + offset);

This one is pretty self-explanatory. First we normalize the value at index, then we add the user specified parameter offset and finally we bring back the value into the range 0-255.

We could take this concept further and add mattes or write a simple blur function or even write a Screen Space Ambient Occlusion Shader which you can check out in my show reel. Even such an advanced thing as SSAO builds on top of the same concepts listed in this post. Even though the color corrector features only a handful number of operations it is enough  to obtain decent grading results.

gif-presets

JS is fun and it has a big future. Even though I know little about the language semantics and structure philosophy I was able to quickly prototype the tool and post it online. In order to see the full source code of the JS program, simply save the html content of the color corrector page and open up the image.js file.

Leave a Reply

The reply will be processed within a day