Image Processing with JavaScript
Tags: javascript, webdev, programming, tutorial
Published: 2023-03-01 15:18:16

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 

```javascript
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);
}
```
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**
```javascript
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);
}
```


**Contrast**

```javascript
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);
}
```

**Saturation**

```javascript
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);
}

```
**Box Blur**
```javascript
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);   
}
```
**Gaussian Blur**
```javascript
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); 
}
```
**Sharpness**
```javascript
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);  
    }   
}
```
**Greyscale**
```javascript
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);
}
```

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

```Html
<!-- 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>
```
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
//JavaScript
var Filter;
const RadioButtons= document.querySelectorAll('input[name="Filters"]');

function handleClick(){
    for (RB of RadioButtons) {
        if (RB.checked) {
            Filter = RB.value;
            break;
        }
    }
}
```
## Slider
You will need to create a slider to change the effect value of the filter that is applied to the image.
```html
<!-- HTML -->
<div class="slidecontainer">
   <input type="range" min="-50" max="50" value="0" class="slider" id="Range">
   <output class="bubble" id="InputValue"></output>
</div> 
```
Then add some javscript.
```javascript
//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);
    }
} 
```

## 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
<!-- 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>
```
Then add some javascript.
```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);
}
```

## Result
{% embed https://youtu.be/PjZtlg8n74g %}
CodePen: [Image Processing (Filters)](https://codepen.io/MohamedSoliman21/pen/MWqJBog)

**Also Read**
[3D Product Card With ReactJS](https://dev.to/mohamedsoliman21/3d-product-card-with-reactjs-1f4e)

**Kindly follow me on**
[LinkedIn](https://www.linkedin.com/in/mohamed-soliman21/)
[GitHub](https://github.com/MohamedSoliman21)
[CodePen](https://codepen.io/MohamedSoliman21)