DEV Community

Cover image for Image Processing with JavaScript
Mohamed Soliman
Mohamed Soliman

Posted on

Image Processing with JavaScript

In this article you will learn how to create image filters and apply them on any kind of image.

Canvas

First, you will need to create two canvas. one for the original image and the other for modified image. Also, you will need to create image class

const Img = new Image();
Img.src = "Images/Img.jpg";

const ImgCanvas = document.getElementById('Img');
const ImgContext = ImgCanvas.getContext('2d');

const MainCanvas = document.getElementById('MainCanvas');
const MainContext = MainCanvas.getContext('2d');

window.onload = () => {
    ImgCanvas.width = Img.width; 
    ImgCanvas.height = Img.height; 

    MainCanvas.width = Img.width; 
    MainCanvas.height = Img.height; 

    ImgContext.drawImage(Img, 0, 0, ImgCanvas.width, ImgCanvas.height)
    MainContext.drawImage(Img, 0, 0, MainCanvas.width, MainCanvas.height);
}
Enter fullscreen mode Exit fullscreen mode

You can do it with one canvas and it will process faster, but doing it with 2 canvas helps you to visualize the difference. drawImage function is used inside window.onload function to draw
the image to the canvas after the image is fully loaded.

Filters

Then you will need to create some filters to test on our image. I will make few filters and you don't need to do all of them. you can just choose a couple.

Brightness

function SaturationFilter(imageData, Value){
    const data = imageData.data;

    for (let i = 0; i < data.length; i += 4) {
      const red = data[i];
      const green = data[i + 1];
      const blue = data[i + 2];

      // Calculate the grayscale value
      const gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue;

      // Adjust the saturation based on the given percentage
      const r = (1 - Value / 100) * red + Value / 100 * gray;
      const g = (1 - Value / 100) * green + Value / 100 * gray;
      const b = (1 - Value / 100) * blue + Value / 100 * gray;

      data[i] = r;
      data[i + 1] = g;
      data[i + 2] = b;
    }

    MainContext.putImageData(imageData, 0, 0);
}
Enter fullscreen mode Exit fullscreen mode

Contrast

function ContrastFilter(imageData, Value){
    const data = imageData.data;

    const factor = (259 * (Value + 255)) / (255 * (259 - Value));

    for (let i = 0; i < data.length; i += 4) {
    const red = data[i];
    const green = data[i + 1];
    const blue = data[i + 2];

    // Adjust the contrast
    data[i] = factor * (red - 128) + 128;
    data[i + 1] = factor * (green - 128) + 128;
    data[i + 2] = factor * (blue - 128) + 128;
    }

    MainContext.putImageData(imageData, 0, 0);
}
Enter fullscreen mode Exit fullscreen mode

Saturation

function SaturationFilter(imageData, Value){
    const data = imageData.data;

    for (let i = 0; i < data.length; i += 4) {
      const red = data[i];
      const green = data[i + 1];
      const blue = data[i + 2];

      // Calculate the grayscale value
      const gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue;

      // Adjust the saturation based on the given percentage
      const r = (1 - Value / 100) * red + Value / 100 * gray;
      const g = (1 - Value / 100) * green + Value / 100 * gray;
      const b = (1 - Value / 100) * blue + Value / 100 * gray;

      data[i] = r;
      data[i + 1] = g;
      data[i + 2] = b;
    }

    MainContext.putImageData(imageData, 0, 0);
}

Enter fullscreen mode Exit fullscreen mode

Box Blur

function BoxBlurFilter(imageData, radius) {
    var data = imageData.data;

    // Define the box blur kernel
    var kernelSize = radius * 2;
    var kernel = new Array(kernelSize * kernelSize);
    for (var i = 0; i < kernel.length; i++) {
      kernel[i] = 1 / (kernel.length);
    }

    // Loop through each pixel and apply the box blur
    for (var y = 0; y < imageData.height; y++) {
      for (var x = 0; x < imageData.width; x++) {
        var r = 0;
        var g = 0;
        var b = 0;

        // Loop through the kernel and apply the blur
        for (var ky = 0; ky < kernelSize; ky++) {
          for (var kx = 0; kx < kernelSize; kx++) {
            var cx = x + kx - radius;
            var cy = y + ky - radius;
            var idx = (cy * imageData.width + cx) * 4;

            if (cx >= 0 && cx < imageData.width && cy >= 0 && cy < imageData.height) {
              r += data[idx] * kernel[ky * kernelSize + kx];
              g += data[idx + 1] * kernel[ky * kernelSize + kx];
              b += data[idx + 2] * kernel[ky * kernelSize + kx];
            }
          }
        }

        // Set the new pixel value
        var idx = (y * imageData.width + x) * 4;
        data[idx] = r;
        data[idx + 1] = g;
        data[idx + 2] = b;
      }
    }

    MainContext.putImageData(imageData, 0, 0);   
}
Enter fullscreen mode Exit fullscreen mode

Gaussian Blur

function GaussianBlur(imageData, radius) {
    var data = imageData.data;
    var side = radius * 2 + 1;
    var matrix = [];
    var sigma = radius / 3;
    var sigma22 = 2 * sigma * sigma;
    var sqrtPiSigma22 = Math.sqrt(Math.PI * sigma22);

    for (var y = -radius; y <= radius; y++) {
        for (var x = -radius; x <= radius; x++) {
            var weight = Math.exp(-(x * x + y * y) / sigma22) / sqrtPiSigma22;
            matrix.push(weight);
        }
    }

    var weights = new Float32Array(matrix);

    for (var i = 0; i < data.length; i += 4) {
      var r = 0, g = 0, b = 0, a = 0;

        for (var wy = -radius; wy <= radius; wy++) {
            for (var wx = -radius; wx <= radius; wx++) {
                var y = i / (imageData.width * 4) + wy;
                var x = i / 4 - y * imageData.width + wx;
                if (x >= 0 && x < imageData.width && y >= 0 && y < imageData.height) {
                    var index = (y * imageData.width + x) * 4;
                    var weight = weights[(wy + radius) * side + wx + radius];
                    r += data[index] * weight;
                    g += data[index + 1] * weight;
                    b += data[index + 2] * weight;
                    a += data[index + 3] * weight;
                }
            }
        }

        data[i] = r;
        data[i + 1] = g;
        data[i + 2] = b;
        data[i + 3] = a;
    }

    MainContext.putImageData(imageData, 0, 0); 
}
Enter fullscreen mode Exit fullscreen mode

Sharpness

function SharpnessFilter(imageData, Value){
    const data = imageData.data;

    if(Value >= 1){
    var matrix = [0, -1, 0, -1, Value + 4, -1, 0, -1, 0];
    var divisor = Value + 4;
    var bias = -Value / 4;

        for (var y = 0; y < imageData.height; y++) {
            for (var x = 0; x < imageData.width; x++) {
                var pixelIndex = (y * imageData.width + x) * 4;
                var r = 0, g = 0, b = 0;

                for (var row = -1; row <= 1; row++) {
                    for (var col = -1; col <= 1; col++) {
                        var rowIndex = y + row;
                        var colIndex = x + col;

                        if (rowIndex >= 0 && colIndex >= 0 && rowIndex < imageData.height && colIndex < imageData.width) {
                            var index = (rowIndex * imageData.width + colIndex) * 4;
                            var weight = matrix[(row + 1) * 3 + col + 1];

                            r += data[index] * weight;
                            g += data[index + 1] * weight;
                            b += data[index + 2] * weight;
                        }
                    }
                }

            data[pixelIndex] = Math.min(255, Math.max(0, (r / divisor) + bias));
            data[pixelIndex + 1] = Math.min(255, Math.max(0, (g / divisor) + bias));
            data[pixelIndex + 2] = Math.min(255, Math.max(0, (b / divisor) + bias));
            }
        }    

        MainContext.putImageData(imageData, 0, 0);  
    }   
}
Enter fullscreen mode Exit fullscreen mode

Greyscale

function GreyScaleFilter(imageData, value){
    const data = imageData.data;

    for(let i = 0; i < data.length; i += 4){
        const Total = data[i] + data[i+1] + data[i+2];
        const Average = Total/2;
        data[i] = Average + value; 
        data[i+1] = Average+ value; 
        data[i+2] = Average+ value; 
    }

    MainContext.putImageData(imageData , 0, 0);
}
Enter fullscreen mode Exit fullscreen mode

Filter Group

Now we will need to create some radio buttons to handle the change of the filter.

<!-- HTML -->
<input type="radio" name="Filters" id="saturation" value="Saturation">
   <label class="icon_img icon-saturation" for="saturation">
      <img src="Saturation.png">
      <span class="tooltip">Saturation</span>
   </label>
Enter fullscreen mode Exit fullscreen mode

You will have to create radio button for each filter you have selected. Also, you will need to change the values of input id, input value and for value in label according to the selected filter.

Then add some javascript.

//JavaScript
var Filter;
const RadioButtons= document.querySelectorAll('input[name="Filters"]');

function handleClick(){
    for (RB of RadioButtons) {
        if (RB.checked) {
            Filter = RB.value;
            break;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Slider

You will need to create a slider to change the effect value of the filter that is applied to the image.

<!-- HTML -->
<div class="slidecontainer">
   <input type="range" min="-50" max="50" value="0" class="slider" id="Range">
   <output class="bubble" id="InputValue"></output>
</div> 
Enter fullscreen mode Exit fullscreen mode

Then add some javscript.

//JavaScript
const Slider = document.getElementById("Range");
const InputValue = document.getElementById("InputValue");

InputValue.innerHTML = Slider.value;

Slider.oninput = function()  {
    InputValue.innerHTML = this.value;
}

Slider.onchange = () => {
    const imageData = ImgContext.getImageData(0, 0, ImgCanvas.width, ImgCanvas.height);

    handleClick();
    if(Filter === 'Brightness'){
        MainContext.putImageData(BrightnessFilter(imageData, Number(Slider.value)), 0, 0);
    }
    else if(Filter === 'Saturation'){
        MainContext.putImageData(SaturationFilter(imageData, Number(Slider.value)), 0, 0);
    }
    else if(Filter === 'BoxBlur'){
        MainContext.putImageData(BoxBlurFilter(imageData, Number(Slider.value)), 0, 0);
    }
    else if(Filter === 'GaussianBlur'){
        MainContext.putImageData(GaussianBlur(imageData, Number(Slider.value)), 0, 0);
    }
    else if(Filter === 'Sharp'){
        MainContext.putImageData(SharpnessFilter(imageData, Number(Slider.value)), 0, 0);
    }
    else if(Filter === 'Contrast'){
        MainContext.putImageData(ContrastFilter(imageData, Number(Slider.value)), 0, 0);
    }
    else if(Filter === 'Greyscale'){
        MainContext.putImageData(GreyScaleFilter(imageData, Number(Slider.value)), 0, 0);
    }
} 
Enter fullscreen mode Exit fullscreen mode

Buttons

Lastly, you will need to create two buttons. One for applying the filters on the image, and the other to download the image with filters applied on it.

<!-- HTML -->
<a>
   <Button id="Btn" class="btn btn__secondary"><p>Apply</p></Button>
</a>
<a id="download" download="Image.png">
   <Button id="DownloadBtn" class="btn btn__primary"><p>Download</p></Button>
</a>
Enter fullscreen mode Exit fullscreen mode

Then add some javascript.

//JavaScript
const ApplyBtn = document.getElementById('ApplyBtn');
const DownloadBtn = document.getElementById('DownloadBtn');

ApplyBtn.onclick = () => {
    const imageData = MainContext.getImageData(0, 0, MainCanvas.width, MainCanvas.height);
    ImgContext.putImageData(imageData, 0, 0);
}

DownloadBtn.onclick = () => {
    var download = document.getElementById("download");
    var image = ImgCanvas.toDataURL("image/png");
    download.setAttribute("href", image);
}
Enter fullscreen mode Exit fullscreen mode

Result


CodePen: Image Processing (Filters)

Also Read
3D Product Card With ReactJS

Kindly follow me on
LinkedIn
GitHub
CodePen

Top comments (0)