How to separately get the RGB channel of an image with HTML5 and the Canvas API?

01-10-2023 - Andrés Cruz

En español
How to separately get the RGB channel of an image with HTML5 and the Canvas API?

Digital image processing is a task that we have to do in our applications every day, from changing the size of images to optimize them for our applications, such as cropping and even altering the color, brightness, saturation, and even separately obtaining the RGB channel of an image using the Canvas as we will see in this entry.

In this article we will see a tutorial on how to obtain the three RGB channels of an image and operate them separately, that is, we will have the RGB channels individually as we see in the promotional image of this entry; Like those in the past articles, we will use HTML5 and native JavaScript to achieve the goal; the body of the document does not differ much from the other exercises we have already done:

Defining the HTML

The HTML is really simple, just as we have used in previous posts, it consists of a Canvas tag which we will use to draw the base image, that is, the image with its three channels, which is as it is by default:

<canvas id="canvas">
        <p>Tu navegador no soporta Canvas.</p>
</canvas>

And three images where the three RGB channels will be placed using JavaScript that we will show in the next section of this tutorial:

<img id="r"/>
<img id="g"/>
<img id="b"/>

Defining the JavaScript to obtain the RGB channels of the image

The first thing we do is define the usual global variables to obtain the canvas and its context, then we obtain access to the images that by default are empty (the src attribute that defines the image has nothing) and we will fill it a little later with the processing we will do using the Canvas API to obtain the RGB channel:

var canvas = document.getElementById('canvas');// canvas
var ctx = canvas.getContext('2d'); // contexto

var imgR = document.getElementById('r');// imagen que representa el canal R
var imgG = document.getElementById('g');// imagen que representa el canal G
var imgB = document.getElementById('b');// imagen que representa el canal B

var srcImg = "image.png";// imagen fuente

We create an object of type Image and assign it a source image, which is the one we specified previously:

    img = new Image();
    img.src = srcImg;

Once the image was uploaded, we will resize the Canvas to the original size of the image; we draw the image on the Canvas and place the RGB channels separately in the image by invoking the getRGB() method; These are the 3 fundamental steps that you can detail in the following code:

// load the image
img.onload = function() {

    // rescale the canvas to the image dimensions
    canvas.width = img.width;
    canvas.height = img.height;

    // we draw the image on the Canvas
    ctx.drawImage(this, 0, 0);
    getRGB();

};

The following function allows us to separate the three RGB channels of an image (which in our case is the one we loaded previously) and save it in individual variables. As we see, we obtain the imageData, which is a string or a very long text with the content of the image. image (similar to what happens when you open an image with a notepad or other simple word processor).

The next step we do is to dump the content of the imageData of each of the channels that we already have separated from the previous step in the source of each of our HTML images that we previously defined in our HTML and that we previously referenced as well:

// paint one image per channel
function getRGB() {

    // we get the ImageData
    var imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);
    // we get the ImageData for R
    var imgdR = ctx.getImageData(0, 0, canvas.width, canvas.height);
    // we get the ImageData for G
    var imgdG = ctx.getImageData(0, 0, canvas.width, canvas.height);
    // we get the ImageData for B
    var imgdB = ctx.getImageData(0, 0, canvas.width, canvas.height);

    // each of these array will have a single channel
    var pixR = imgdR.data;
    var pixG = imgdG.data;
    var pixB = imgdB.data;
// it will have the 3 channels; we will use it 
//to restore the original colors on the canvas
    var pix = imgd.data;

    // we change the contrast
    for (var i = 0, n = pixR.length; i < n; i += 4) {
        //we change the contrast R
        pixR[i + 1] = 0;//G
        pixR[i + 2] = 0;//B

        //we change the contrast G
        pixG[i] = 0;//R
        pixG[i + 2] = 0;//B

        //we change the contrast B
        pixB[i] = 0;//R
        pixB[i + 1] = 0;//B
    }

    // we return the modified data to the Canvas; R channel
    ctx.putImageData(imgdR, 0, 0);
    dataURL = canvas.toDataURL();
    imgR.src = dataURL;
    // we return the modified data to the Canvas; G channel
    ctx.putImageData(imgdG, 0, 0);
    dataURL = canvas.toDataURL();
    imgG.src = dataURL;
    // we return the modified data to the Canvas; B channel
    ctx.putImageData(imgdB, 0, 0);
    dataURL = canvas.toDataURL();
    imgB.src = dataURL;
    // we return the original data to the Canvas; RGB channel 
    ctx.putImageData(imgd, 0, 0);
}

To place each of the RGB channels in individual images we have to process the image data that we obtained in an array where only one of the channels prevails, the other two are set with the value of zero.

Analyzing the previous function to process the RGB channels of the images

We obtain the ImageData or data about the entire image drawn on the Canvas, which will allow us to operate the data that makes up the image at the pixel level in its RGB scale; you can see more about ImageData in the following link:

// we get the ImageData
var imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);
// we get the ImageData for R
var imgdR = ctx.getImageData(0, 0, canvas.width, canvas.height);
// we get the ImageData for G
var imgdG = ctx.getImageData(0, 0, canvas.width, canvas.height);
// we get the ImageData for B
var imgdB = ctx.getImageData(0, 0, canvas.width, canvas.height);

The next step is to obtain all the pixels that make up the image; This information is stored in an Array:

// each of these array will have a single channel
var pixR = imgdR.data;
var pixG = imgdG.data;
var pixB = imgdB.data;
// It will have 3 channels; we will use it
// to restore the original colors on the canvas
var pix = imgd.data;

This section of code represents the heart of the exercise; you get one channel at a time, setting the rest to zero:

// we change the contrast
for (var i = 0, n = pixR.length; i < n; i += 4) {
    //I maintain the channel R
    pixR[i + 1] = 0;//G
    pixR[i + 2] = 0;//B

    //I maintain the channel G
    pixG[i] = 0;//R
    pixG[i + 2] = 0;//B

    //I maintain the channel B
    pixB[i] = 0;//R
    pixB[i + 1] = 0;//B
}

After we have divided the channels; the next step is to save the three RGB channels in separate images:

// we return the modified data to the Canvas; R channel
ctx.putImageData(imgdR, 0, 0);
dataURL = canvas.toDataURL();
imgR.src = dataURL;
// we return the modified data to the Canvas; G channel
ctx.putImageData(imgdG, 0, 0);
dataURL = canvas.toDataURL();
imgG.src = dataURL;
// we return the modified data to the Canvas; B channel
ctx.putImageData(imgdB, 0, 0);
dataURL = canvas.toDataURL();
imgB.src = dataURL;
// rwe return the modified data to the Canvas; RGB channel
ctx.putImageData(imgd, 0, 0);

Final score

Finally you can see the result of our experiment in the following section and download the source code:

Demo Download

As we can see, with HTML we can do practically everything: games, word processors, images where imagination is the limit.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.