Copyright Derek O'Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.
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:
[0, 0, 0, 0, 2, -1, 0, -1, 0]
[1, 2, 1, 2, 4, 2, 1, 2, 1]
[ 0, -2, 0, -2, 11, -2, 0, -2, 0]
[1, 1, 1, 1, -7, 1, 1, 1, 1]
What is the matrix for the convolution that does not change the original image?
<!DOCTYPE html> <html> <head> <title>Course notes example code</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> img, canvas { width:500px; height:500px; border:thin solid black; } #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 convolutionMatrix = embossConvolutionMatrix /* select which convolution to use */ let canvas = null let ctx = null let offscreenCanvas = null let offscreenCanvasCtx = null let originalImage = null let imageData = null let data = null let originalImageData = null let originalData = null window.onload = onAllAssetsLoaded document.write("<div id='loadingMessage'>Loading...</div>") function onAllAssetsLoaded() { // hide the webpage loading message document.getElementById('loadingMessage').style.visibility = "hidden" originalImage = document.getElementById('originalImage') canvas = document.getElementById('canvas') ctx = canvas.getContext('2d') offscreenCanvas = document.createElement('canvas') offscreenCanvasCtx = offscreenCanvas.getContext('2d') canvas.width = canvas.clientWidth canvas.height = canvas.clientHeight offscreenCanvas.width = canvas.width offscreenCanvas.height = canvas.height renderCanvas() } function renderCanvas() { // draw the original image into the double buffer offscreenCanvasCtx.drawImage(originalImage, 0, 0, canvas.width, canvas.height) // do the convolution let totalConvolutionSum = 0 for (let j = 0; j < 9; j++) { totalConvolutionSum += convolutionMatrix[j] } // get the image data (i.e. the pixels) from the double buffer imageData = offscreenCanvasCtx.getImageData(0, 0, canvas.width, canvas.height) data = imageData.data // keep a copy of the original data, as we need to look at the pixels around the current pixel when doing the convolution originalImageData = offscreenCanvasCtx.getImageData(0, 0, canvas.width, canvas.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 - canvas.width * 4 - 4], originalData[i + rgbOffset - canvas.width * 4], originalData[i + rgbOffset - canvas.width * 4 + 4], originalData[i + rgbOffset - 4], originalData[i + rgbOffset], originalData[i + rgbOffset + 4], originalData[i + rgbOffset + canvas.width * 4 - 4], originalData[i + rgbOffset + canvas.width * 4], originalData[i + rgbOffset + canvas.width * 4 + 4]] // do the convolution let 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 /= totalConvolutionSum data[i + rgbOffset] = convolvedPixel } } } offscreenCanvasCtx.putImageData(imageData, 0, 0) ctx.drawImage(offscreenCanvas, 0, 0, canvas.width, canvas.height) } </script> </head> <body> <img id = 'originalImage' 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.
Copyright Derek O' Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.