November 10, 2020

Coding4 - Pixel Manipulation and Image Processing

We will discuss the storage and representation of pixel information, and how to create simple shapes and texture effects through mathematical expressions.


1.Create Image

We can use the createImageData() method to draw the image. In this process, we traverse each pixel on the canvas, and we can also use a simple expression to determine what color each pixel should be.

2.Write Output Pixel

We can set the RGBA value of the pixel:

1
2
3
4
5
position=(x+y*imageData.width)*4;
imageData.data[position] = c%255;
imageData.data[position+1] = c%255;
imageData.data[position+2] = c%255
imageData.data[position+3] = 255;

In JavaScript, the pixels are kept in the imageData buffer, and we can also write the value directly into the imageData; it is actually a one-dimensional array, so we don’t need to treat it as a rectangular image.

For more effective practice, we can use nested for loops to help us access data in the form of rows and columns, so that each pixel has a coordinate (i, j), and the four colors of RGBA are needed in the array aisle.

Each value is 8-bit, so each pixel occupies 32 bits of information. Simple calculations, if we process the imageData array, we need to get (50, 10) pixels on a 100*100 pixel image, we need to find ((50 * 100) + 10) * in the array 4, the next 4 numbers are RGBA information.

After summarizing, we can get a formula:

1
2
3
4
((y * imageWidth) + x) * colourChannels

// what we actually did in JavaScript
imageData.data[((50 * 100) + 10) * 4];

It should be noted that different platforms may use different color description methods, but in JavaScript it is usually RGBA.

We also need to use nested for loops to help simplify the access of pixel data, treating i as a column variable (y) and j as a row variable (x). We multiply i by the width of the image and add j to the current pixel.

Because the actual size of the array is 4 times that of the image, we also need to multiply by 4.

1
2
3
4
5
imageData.data[((imageWidth * i) + j) * 4 + 0] = 255;
// we set the RED pixel at i to its maximum value of 255

imageData.data[((imageWidth * i) + j) * 4 +2] = 255
// we set the BLUE pixel at i to its maxium value of 255

3.Basic Image Processing

We can also load the image through getImageData() and perform simple processing.

For example, to increase the brightness of the picture:

Increase contrast:

4.Rotating an Image by Hand

You can check the example below, how to rotate pixels to new positions.
The code has two important parts: calculate where the pixel needs to be moved, and then 2D rotate it to move it.

1
2
3
// This is a 2D matrix rotation!!! It's very similar to the code we used to rotate points around a circle.
var x = Math.floor((Math.cos(theta) * j) - (Math.sin(theta) * i));
var y = Math.floor((Math.sin(theta) * j) + (Math.cos(theta) * i));
1
imageData2.data[((imageWidth * i) + j) * 4] = data[((imageWidth * y) + x) * 4];

5.Image Processing using Convolution

6.Performing an Edge Detection / Gradient analysis

1
imageData2.data[((imageWidth * i) + j) * 4] = (-1*(data[((imageWidth * i) + j-1) * 4])) + (data[((imageWidth * i) + j+1) * 4]);

Below is a simple kernel I made to achieve the effect of Gaussian blur, you can also try to adjust the value in main.js.

About this Post

This post is written by Siqi Shu, licensed under CC BY-NC 4.0.