This is a test page for checking out p5.js
animation. This is the 5th javascript export version of processing
programming language. Here, the fourier transformation of the boundary points of the eight note is taken and using the discrete fourier transformation of the \(x\) and \(y\) coordinates, a set of epicycles is used to recreate the figure.
This is the image
import cv2
import numpy as np
# Load the image
= cv2.imread('eight_note.jpg')
image
# make it grayscale
= cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray
# Find Canny edges
= cv2.Canny(gray, 30, 200)
edged
# Finding Contours
= cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours, hierarchy
# Remove extra array axes
= np.squeeze(contours)
new_contours
# Extract the points
= []
points for point in new_contours:
= {'x': point[0], 'y': point[1]}
temp
points.append(temp)
# Print them for viewing
# print(points)
# Write the points as a javascript file.
= "logo.js" ## your path variable
file_path with open(file_path, 'w') as f:
"%s\n" % 'let drawing = [')
f.write(for item in points:
"%s,\n" % item)
f.write("%s\n" % '];') f.write(
The logo.js
file will look like this:
let drawing = [
'x': 197, 'y': 21},
{'x': 196, 'y': 22},
{'x': 195, 'y': 22},
{'x': 195, 'y': 23},
{'x': 195, 'y': 24},
{'x': 195, 'y': 25},
{.....
'x': 200, 'y': 21},
{'x': 199, 'y': 21},
{'x': 198, 'y': 21},
{; ]
p5.js
The Discrete Fourier Transform (DFT) of \(N\) complex numbers \(\{x_n\} := x_0, x_1, \dots, x_{n-1}\) is another sequence of \(N\) complex numbers \(\{X_n\} := X_0, X_1, \dots, X_{n-1}\) given by
\[ \begin{align*} X_k &= \sum_{n=0}^{N-1}x_n.e^{-\frac{i2\pi}{N}kn}\\ &= \sum_{n=0}^{N-1}x_n.[cos(2\pi kn/N) - i.sin(2\pi kn/N)] \end{align*} \]
The equivalent source code is written in the file fourier.js
.
function dft(x)
{
let X = [];
const N = x.length;
for (let k = 0; k < N; k++)
{let re = 0;
let im = 0;
for (let n = 0; n < N; n++)
{const phi = (TWO_PI * k * n) / N;
+= x[n] * cos(phi);
re -= x[n] * sin(phi);
im
}
= re / N;
re = im / N;
im
let freq = k;
let amp = sqrt(re * re + im * im);
let phase = atan2(im, re);
= {re, im, freq, amp, phase};
X[k]
}return X;
}
let x = [];
let y = [];
let fourierX = [];
let fourierY = [];
let time = 0;
let path = [];
function setup() {
createCanvas(800, 800);
const skip = 1;
for(let i = 0; i < drawing.length; i+= skip){
= drawing[i].x;
x[i] = drawing[i].y;
y[i]
}
// for(let i = 0; i < 100; i+= skip){
// x[i] = i;
// y[i] = i;
// }
= dft(x);
fourierX = dft(y);
fourierY
.sort((a, b) => b.amp - a.amp);
fourierX.sort((a, b) => b.amp - a.amp);
fourierY
}
function epiCycle(x, y, rotation, fourier)
{for (let i = 0; i < fourierY.length; i++)
{let prev_x = x;
let prev_y = y;
let freq = fourier[i].freq;
let radius = fourier[i].amp;
let angle = rotation + freq * time + fourier[i].phase;
+= radius * cos(angle);
x += radius * sin(angle);
y
stroke(255, 100);
noFill();
ellipse(prev_x, prev_y, radius * 2);
stroke(255);
line(prev_x, prev_y, x, y);
}return createVector(x, y);
}
function draw() {
background(0);
let vx = epiCycle(300, 50, 0, fourierX);
let vy = epiCycle(50, 200, HALF_PI, fourierY);
let v = createVector(vx.x, vy.y);
.unshift(v);
path
// translate(200, 0);
line(vx.x, vx.y, v.x, v.y);
line(vy.x, vy.y, v.x, v.y);
beginShape();
noFill();
for (let i = 0; i < path.length; i++) {
vertex(path[i].x, path[i].y);
}endShape();
const dt = TWO_PI / fourierY.length;
+= dt;
time
if (time > TWO_PI)
{= 0;
time .pop();
path
} }
It works pretty slowly! The code is quite slow as it calculates more than 1700 components for \(x\) coordinates and another 1700+ components for \(y\) coordinates respectively.
Special thanks to Daniel Shiffman for his phenomenal enthusiasm in getting me interested in p5.js
. This code is a slightly tweaked version of the same code Daniel used to teach the tutorial in his YouTube Channel - The Coding Train and in his website - The Coding Train