Copyright Derek O'Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.
A filter changes the colour of a pixel based on a manipulation of the original colour of the pixel. Each pixel is processed independently of all other pixels. Therefore, the new colour of any pixel is only dependent on the original colour of that pixel. Common filters include:
To make an image brighter, we need to increase each of the red, green and blue values. We do this by multiplying by a brightness factor, where:
Example of a brightness filter (Run Example)
<!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> let canvas = null let ctx = null let width = null let height = null let originalImage = 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') width = originalImage.clientWidth height = originalImage.clientHeight canvas.width = width canvas.height = height renderCanvas() } let imageData = null let data = null let brightnessFactor = 2 /* A higher brightnessFactor value will make the image brighter */ function renderCanvas() { ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height) imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) data = imageData.data // Loop through the pixels, turning them grayscale for (let i = 0; i < data.length; i += 4) { data[i] *= brightnessFactor data[i + 1] *= brightnessFactor data[i + 2] *= brightnessFactor } ctx.putImageData(imageData, 0, 0) } </script> </head> <body> <img id = 'originalImage' src = 'images/dancing.png'> <canvas id = 'canvas'></canvas> </body> </html>
Modify the code above so that the user can use a slider to adjust the brightness of an image.
Modify the code above so that we can independently adjust the brightness of the red, green and blue colour.
By setting the red, green and blue of a pixel to be the same value, we produce a greyscale pixel. A good value to set as the greyscale is the average of the red, green and blue values, as shown in the example below.
Example of a greyscale filter (Run Example).
<!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> let canvas = null let ctx = null let width = null let height = null let originalImage = null let imageData = null let data = 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') width = originalImage.clientWidth height = originalImage.clientHeight canvas.width = width canvas.height = height renderCanvas() } function renderCanvas() { ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height) imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) data = imageData.data // convert to greyscale let brightnessFactor = 1 // we can add a brightness factor to the greyscale // Loop through the pixels, turning them into grayscale for (let i = 0; i < data.length; i += 4) { // get the average value grayScale = ((data[i] * brightnessFactor) + (data[i + 1] * brightnessFactor) + (data[i + 2] * brightnessFactor)) / 3 // assign the same value to red, green and blue to create grayScale data[i] = grayScale data[i + 1] = grayScale data[i + 2] = grayScale } ctx.putImageData(imageData, 0, 0) } </script> </head> <body> <img id = 'originalImage' src = 'images/dancing.png'> <canvas id = 'canvas'></canvas> </body> </html>
Modify the code above so that the image is in a blue-scale, as shown at this link.
The code above uses the average of the red, green and blue values. What other values could be used to produce the greyscale? What would be the effect of using these other values?
Sepia is a reddish-brown colour. It is commonly used to make images look older.
Example of a sepia filter (Run example)
<!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> let canvas = null let ctx = null let width = null let height = null let originalImage = null let imageData = null let data = 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') width = originalImage.clientWidth height = originalImage.clientHeight canvas.width = width canvas.height = height renderCanvas() } function renderCanvas() { ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height) imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) data = imageData.data // Loop through the pixels, turning them into sepia for (let i = 0; i < data.length; i += 4) { red = data[i] green = data[i + 1] blue = data[i + 2] data[i] = (red * 0.393) + (green * 0.769) + (blue * 0.189) data[i + 1] = (red * 0.349) + (green * 0.686) + (blue * 0.168) data[i + 2] = (red * 0.272) + (green * 0.534) + (blue * 0.131) } ctx.putImageData(imageData, 0, 0) } </script> </head> <body> <img id = 'originalImage' src = 'images/dancing.png'> <canvas id = 'canvas'></canvas> </body> </html>
Adjust the scale values in the code above to create other filters.
Example of an invert filter (Run Example)
<!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>
let canvas = null
let ctx = null
let width = null
let height = null
let originalImage = null
let imageData = null
let data = 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')
width = originalImage.clientWidth
height = originalImage.clientHeight
canvas.width = width
canvas.height = height
renderCanvas()
}
function renderCanvas()
{
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
data = imageData.data
// do the invert
for (let i = 0; i < data.length; i += 4)
{
data[i + 0] = 255 - data[i + 0]
data[i + 1] = 255 - data[i + 1]
data[i + 2] = 255 - data[i + 2]
data[i + 3] = 255
}
ctx.putImageData(imageData, 0, 0)
}
</script>
</head>
<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>
Modify the code above to invert only on red, as shown at this link.
Modify the code above to invert only on yellow, as shown at this link.
A posterised image only uses some of the available colour set. The example below only uses red, green and blue values have a step size of 64.
Example of a posterise filter (Run Example)
<!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>
let canvas = null
let ctx = null
let width = null
let height = null
let originalImage = null
let imageData = null
let data = 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')
width = originalImage.clientWidth
height = originalImage.clientHeight
canvas.width = width
canvas.height = height
renderCanvas()
}
function renderCanvas()
{
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
data = imageData.data
// do the invert
for (let i = 0; i < data.length; i += 4)
{
data[i + 0] = data[i + 0] - data[i + 0] % 64
data[i + 1] = data[i + 1] - data[i + 1] % 64
data[i + 2] = data[i + 2] - data[i + 2] % 64
data[i + 3] = 255
}
ctx.putImageData(imageData, 0, 0)
}
</script>
</head>
<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>
Modify the code above so that the posterised colours have a step size of 128.
A threshold filter maps each red, green and blue value to be either have zero or full saturation (ie values of 0 or 255).
Example of a threshold lookup filter (Run Example)
<!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>
let canvas = null
let ctx = null
let width = null
let height = null
let originalImage = null
let imageData = null
let data = 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')
width = originalImage.clientWidth
height = originalImage.clientHeight
canvas.width = width
canvas.height = height
renderCanvas()
}
function renderCanvas()
{
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
data = imageData.data
// do the threshold
for (let i = 0; i < data.length; i += 4)
{
for (let rgb = 0; rgb < 3; rgb++)
{
if (data[i + rgb] < 128)
{
data[i + rgb] = 0
}
else
{
data[i + rgb] = 255
}
}
data[i + 3] = 255
}
ctx.putImageData(imageData, 0, 0)
}
</script>
</head>
<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>
Modify the above code to only have a threshold in blue, as shown at this link.
Copyright Derek O' Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.