In this post I explain how to create a heat map using Processing language. Processing is mostly used to generate creative visual designs through programming. Underlying language of Processing is Java. But in Processing it uses simplified syntax for graphical programming unlike Java. For more information you can refer home page of Processing.
To design our heat map I am going to use Bi-linear Interpolation algorithm. Bi-linear interpolation is an extension of linear interpolation for interpolating functions of two variables (e.g., x and y) on a regular 2D grid (Wikipedia).
Algorithm
Goal - Find the unknown value f(p) at a point p =(x,y)using the,
Points with Known values -
Linear interpolation in y direction,
Combining both results, f(p) = f(x,y)
Here is the Processing sketch of the above algorithm for an 2X2 array. The interpolated values are represented by colors, which will eventually generate the desired heat map.
int r = 2; // number of rows in input array
int c = 2; // number of columns in input array
int t = 300; // parameter (array resize factor)
int rows = (r-1)*t; // height of the heat map
int cols = (c-1)*t; // width of the heat map
float[][] array = {{3,1},{2,5}}; // input array
float[][] interp_array = new float[rows][cols]; // interpolated array
void setup() {
size(cols, rows);
noStroke();
}
void draw() {
bilinearInterpolation();
applyColor();
}
void bilinearInterpolation() { // Bi-linear Interpolation algorithm
for (int i=0; i<r; i++) {
for (int j=0; j<c; j++) {
int x = j*t - 1;
int y = i*t - 1;
if (x<0)
x=0;
if (y<0)
y=0;
interp_array[y][x] = array[i][j];
}
}
for (int y=0; y<rows; y++) {
int dy1 = floor(y/(t*1.0));
int dy2 = ceil(y/(t*1.0));
int y1 = dy1*t - 1;
int y2 = dy2*t - 1;
if (y1<0)
y1 = 0;
if (y2<0)
y2 = 0;
for (int x=0; x<cols; x++) {
int dx1 = floor(x/(t*1.0));
int dx2 = ceil(x/(t*1.0));
int x1 = dx1*t - 1;
int x2 = dx2*t - 1;
if (x1<0)
x1 = 0;
if (x2<0)
x2 = 0;
float q11 = array[dy1][dx1];
float q12 = array[dy2][dx1];
float q21 = array[dy1][dx2];
float q22 = array[dy2][dx2];
int count = 0;
if (q11>0)
count++;
if (q12>0)
count++;
if (q21>0)
count++;
if (q22>0)
count++;
if (count>2) {
if (!(y1==y2 && x1==x2)) {
float t1 = (x-x1);
float t2 = (x2-x);
float t3 = (y-y1);
float t4 = (y2-y);
float t5 = (x2-x1);
float t6 = (y2-y1);
if (y1==y2) {
interp_array[y][x] = q11*t2/t5 + q21*t1/t5;
} else if (x1==x2) {
interp_array[y][x] = q11*t4/t6 + q12*t3/t6;
} else {
float diff = t5*t6;
interp_array[y][x] = (q11*t2*t4 + q21*t1*t4 + q12*t2*t3 + q22*t1*t3)/diff;
}
} else {
interp_array[y][x] = q11;
}
} else {
interp_array[y][x] = 0;
}
}
}
}
void applyColor() { // Generate the heat map
color c1 = color(0, 0, 255); // Blue color
color c2 = color(0, 255, 0); // Green color
color c3 = color(255, 0, 0); // Red color
color c4 = color(255,255,0); // Yellow color
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
float value = interp_array[i][j];
color c;
float fraction;
if (value>=1 && value<2) {
fraction = (value-1)/1.0;
c = lerpColor(c1, c2, fraction);
} else if (value>=2 && value<3) {
fraction = (value-2)/1.0;
c = lerpColor(c2, c3, fraction);
} else if (value>=3 && value<5) {
fraction = (value-3)/2.0;
c = lerpColor(c3, c4, fraction);
} else
c = c4;
stroke(c);
point(j, i);
}
}
}
If you are not familiar with Processing syntax, you can refer the Processing reference page.
Here is the generated heat map.
To design our heat map I am going to use Bi-linear Interpolation algorithm. Bi-linear interpolation is an extension of linear interpolation for interpolating functions of two variables (e.g., x and y) on a regular 2D grid (Wikipedia).
Algorithm
Source - Wikipedia |
Points with Known values -
Linear interpolation in x direction,
- Q11 = (x1,y1)
- Q12 = (x1,y2)
- Q21 = (x2,y1)
- Q22 = (x2,y2)
Source - Wikipedia |
Source - Wikipedia |
Source - Wikipedia |
Here is the Processing sketch of the above algorithm for an 2X2 array. The interpolated values are represented by colors, which will eventually generate the desired heat map.
int r = 2; // number of rows in input array
int c = 2; // number of columns in input array
int t = 300; // parameter (array resize factor)
int rows = (r-1)*t; // height of the heat map
int cols = (c-1)*t; // width of the heat map
float[][] array = {{3,1},{2,5}}; // input array
float[][] interp_array = new float[rows][cols]; // interpolated array
void setup() {
size(cols, rows);
noStroke();
}
void draw() {
bilinearInterpolation();
applyColor();
}
void bilinearInterpolation() { // Bi-linear Interpolation algorithm
for (int i=0; i<r; i++) {
for (int j=0; j<c; j++) {
int x = j*t - 1;
int y = i*t - 1;
if (x<0)
x=0;
if (y<0)
y=0;
interp_array[y][x] = array[i][j];
}
}
for (int y=0; y<rows; y++) {
int dy1 = floor(y/(t*1.0));
int dy2 = ceil(y/(t*1.0));
int y1 = dy1*t - 1;
int y2 = dy2*t - 1;
if (y1<0)
y1 = 0;
if (y2<0)
y2 = 0;
for (int x=0; x<cols; x++) {
int dx1 = floor(x/(t*1.0));
int dx2 = ceil(x/(t*1.0));
int x1 = dx1*t - 1;
int x2 = dx2*t - 1;
if (x1<0)
x1 = 0;
if (x2<0)
x2 = 0;
float q11 = array[dy1][dx1];
float q12 = array[dy2][dx1];
float q21 = array[dy1][dx2];
float q22 = array[dy2][dx2];
int count = 0;
if (q11>0)
count++;
if (q12>0)
count++;
if (q21>0)
count++;
if (q22>0)
count++;
if (count>2) {
if (!(y1==y2 && x1==x2)) {
float t1 = (x-x1);
float t2 = (x2-x);
float t3 = (y-y1);
float t4 = (y2-y);
float t5 = (x2-x1);
float t6 = (y2-y1);
if (y1==y2) {
interp_array[y][x] = q11*t2/t5 + q21*t1/t5;
} else if (x1==x2) {
interp_array[y][x] = q11*t4/t6 + q12*t3/t6;
} else {
float diff = t5*t6;
interp_array[y][x] = (q11*t2*t4 + q21*t1*t4 + q12*t2*t3 + q22*t1*t3)/diff;
}
} else {
interp_array[y][x] = q11;
}
} else {
interp_array[y][x] = 0;
}
}
}
}
void applyColor() { // Generate the heat map
color c1 = color(0, 0, 255); // Blue color
color c2 = color(0, 255, 0); // Green color
color c3 = color(255, 0, 0); // Red color
color c4 = color(255,255,0); // Yellow color
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
float value = interp_array[i][j];
color c;
float fraction;
if (value>=1 && value<2) {
fraction = (value-1)/1.0;
c = lerpColor(c1, c2, fraction);
} else if (value>=2 && value<3) {
fraction = (value-2)/1.0;
c = lerpColor(c2, c3, fraction);
} else if (value>=3 && value<5) {
fraction = (value-3)/2.0;
c = lerpColor(c3, c4, fraction);
} else
c = c4;
stroke(c);
point(j, i);
}
}
}
If you are not familiar with Processing syntax, you can refer the Processing reference page.
Here is the generated heat map.
Hello - Thank you for this awesome work on heat maps display. I have been looking for some time for canned software such as this for a project I am working on. I am not a professional programmer and struggle a bit with all but the simplest code. I would would like to incorporate your code in my prototype. Can you assist me in some modifications? I have inputs from an 8x8 array and would like to display using a heat map for the values. This is in real time as much as 10 Hz.
ReplyDeleteLooking forward to your reply.
Regards,
Ken
Hi,
DeleteThanks for incorporating my post into your project.
Please do the following changes in code to use it with 8X8 input array.
int r = 8; // number of rows in input array
int c = 8; // number of columns in input array
float[][] array = new float[r][c];
In a seperate function pass the array inputs as an argument to the function and assign those values to the variable array.
You can refer Processing home page how to handle functions & array assignments.
Thanks
Hello, I keep getting "this size of sketch could not be determined from your code. Use only numbers (not variables) for the size() command. Thoughts?
ReplyDeletevoid settings() {
Deletesize(cols, rows);
}
^^^^^^^^^^^ remove "size()" from void setup and add the above!
Not sure if I'll even get a response but here it goes...
ReplyDeleteWhat do you have as the maximum and minimum values in the variable array? I'm trying to normalize my data down to the range that you use. Thanks!
For anyone wondering this in the future. I did some testing and the values range from 1.0-5.0 (inclusive)
Delete