Canvas Image Convolutions

A convolution changes the colour of a pixel based on a manipulation of the original colour of the pixel and the original colour of the sourrounding pixels. Therefore, a convolution is dependent on the values of the sourrounding pixels. A 3x3 convolution replaces the value of a pixel with a value that is a weighted average of the original pixel plus the eight sourrounding pixels. A 3x3 convolution matrix (known as a kernal) contains the contribution amounts for the pixel and the eight sourrounding pixels. Four common 3x3 convolution matrices are shown below:

Emboss
[0,  0,  0, 
 0,  2, -1,
 0, -1,  0]
Blur
[1, 2, 1,
 2, 4, 2,
 1, 2, 1]
Sharpen
[ 0, -2,  0,
 -2, 11, -2,
  0, -2,  0]
Edge Detection
[1,  1, 1,
 1, -7, 1,
 1,  1, 1]

What is the matrix for the convolution that does not change the original image?

Example of an emboss convolution (Run Example)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>

// set up the convolution matrix
let embossConvolutionMatrix = [0, 0, 0,
    0, 2, -1,
    0, -1, 0];

let blurConvolutionMatrix = [1, 2, 1,
    2, 4, 2,
    1, 2, 1];

let sharpenConvolutionMatrix = [0, -2, 0,
    -2, 11, -2,
    0, -2, 0];

let edgeDetectionConvolutionMatrix = [1, 1, 1,
    1, -7, 1,
    1, 1, 1];

let noConvolutionMatrix = [0, 0, 0,
    0, 1, 0,
    0, 0, 0];

let canvas = null;
let ctx = null;
let doubleBuffer = null;
let doubleBufferG = null;
let width = null;
let height = null;
let imageToConvolve = null;
let imageData = null;
let data = null;
let originalImageData = null;
let originalData = null;
let convolvedPixel = null;
let convolutionMatrix = null;

window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    imageToConvolve = document.getElementById('imageToConvolve');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    doubleBuffer = document.createElement('canvas');
    doubleBufferG = doubleBuffer.getContext('2d');
    width = imageToConvolve.clientWidth;
    height = imageToConvolve.clientHeight;
    canvas.width = width;
    canvas.height = height;
    doubleBuffer.width = width;
    doubleBuffer.height = height;

    convolutionMatrix = embossConvolutionMatrix; /* select which convolution to use */

    renderCanvas();
}


function renderCanvas()
{
    // draw the original image into the double buffer
    doubleBufferG.drawImage(imageToConvolve, 0, 0, width, height);

    // get the image data (i.e. the pixels) from the double buffer
    imageData = doubleBufferG.getImageData(0, 0, width, height);
    data = imageData.data;

    convolutionAmount = 0;
    for (let j = 0; j < 9; j++)
    {
        convolutionAmount += convolutionMatrix[j];
    }

    originalImageData = doubleBufferG.getImageData(0, 0, width, height);
    originalData = originalImageData.data;


    for (let i = 0; i < data.length; i += 4)
    {
        data[ i + 3] = 255; // alpha

        // apply the convolution for each of red, green and blue
        for (let rgbOffset = 0; rgbOffset < 3; rgbOffset++)
        {
            // get the pixel and its eight sourrounding pixel values from the original image 
            let convolutionPixels = [originalData[i + rgbOffset - width * 4 - 4],
                originalData[i + rgbOffset - width * 4],
                originalData[i + rgbOffset - width * 4 + 4],
                originalData[i + rgbOffset - 4],
                originalData[i + rgbOffset],
                originalData[i + rgbOffset + 4],
                originalData[i + rgbOffset + width * 4 - 4],
                originalData[i + rgbOffset + width * 4],
                originalData[i + rgbOffset + width * 4 + 4]];

            // do the convolution
            convolvedPixel = 0;
            for (let j = 0; j < 9; j++)
            {
                convolvedPixel += convolutionPixels[j] * convolutionMatrix[j];
            }

            // place the convolved pixel in the double buffer		 
            if (convolutionMatrix === embossConvolutionMatrix) // embossed is treated differently
            {
                data[i + rgbOffset] = convolvedPixel + 127;
            }
            else
            {
                convolvedPixel /= convolutionAmount;
                data[i + rgbOffset] = convolvedPixel;
            }
        }
    }

    // Draw the imageData onto the canvas
    ctx.putImageData(imageData, 0, 0);
}



</script>
</head>

<body>
<img id = 'imageToConvolve' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html> 

Modify the code above to produce a blurred image.

Modify your code to make the image more blurred.

Modify the code above to produce a sharpened image.

Modify the code above to produce an edge detection image.

Find another 3x3 convolution matrix on the www and modify the code above to implement this.

The emboss in the example above is in the direction of the bottom left corner. Modify the code to show four convolves, each one directed toward a different corner.

Modify the code above to create a convolution that produces a brighter image. Hint: Increasing the value of the convolution matrix centre value increases the weighting of the original pixel on the final pixel's colour.

By changing the various values of the convolution matrix, you can produce other interesting convolutions. Modify the code above to allow a user to modify each of the nine convolution matrix values. Note that it is common, but not necessary,for the sum of the nine matrix values to equal 1. The image should update in real time to reflect any changes in the matrix values.

Convolution matricies can be 5x5 or bigger. What are the advantages of having a bigger convolution matrix? What are the disadvantages of having a bigger convolution matrix?

Modify the code above so that it can take any sized matrix as it input. Using your amended code, implement a 5x5 blur matrix.

 
<div align="center"><a href="../../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>