1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- """Fourier 2D shape reconstruction with epicycles representation"""
- # Original version from D. Shiffman (2019), adapted for vedo by M. Musy (2022)
- # https://thecodingtrain.com/CodingChallenges/130.2-fourier-transform-drawing.html
- import numpy as np
- import vedo
- order = 20 # restrict to this nr of fourier coefficients in reconstruction
- def DFT(x):
- X = []
- N = len(x)
- for freq in range(N):
- re, im = [0, 0]
- for n in range(N):
- phi = (2 * np.pi * freq * n) / N
- re += x[n] * np.cos(phi)
- im -= x[n] * np.sin(phi)
- re, im = [re/N, im/N]
- amp = np.sqrt(re*re + im*im)
- phase = np.arctan2(im, re)
- X.append([re, im, freq, amp, phase])
- return vedo.utils.sort_by_column(X, 3, invert=True)
- def epicycles(time, rotation, fourier, order):
- global objs
- plt.remove(objs)
- x, y = [0, 0]
- objs, path = [], []
- for i in range(len(fourier[:order])):
- re, im, freq, amp, phase = fourier[i]
- if amp > 0.2:
- c = vedo.Circle([x,y], amp).wireframe().lw(1)
- objs.append(c)
- x += amp * np.cos(freq * time + phase + rotation)
- y += amp * np.sin(freq * time + phase + rotation)
- path.append([x,y])
- if len(points)>0:
- hline = vedo.Line([x,y], points[-1]).c('red5').lw(1)
- pline = vedo.Line(path).c('green5').lw(2)
- oline = vedo.Line(points).c('red4').lw(5)
- objs += [hline, pline, oline]
- plt.add(objs).render()
- return [x, y]
- # Load some 2D shape and make it symmetric
- shaper = vedo.Assembly(vedo.dataurl+'timecourse1d.npy')[55]
- # shaper = shape.clone().mirror('x').reverse()
- shapel = vedo.Line(np.flip(shaper.clone().mirror('x').coordinates, axis=0))
- shape = vedo.merge(shaper, shapel)
- x, y, _ = shape.points.T
- # Compute Fourier Discrete Transform in x and y separately:
- fourierX = DFT(x)
- fourierY = DFT(y)
- vedo.settings.default_font = 'Glasgo'
- plt = vedo.Plotter(size=(1500,750), bg='black', axes=1, interactive=False)
- txt = vedo.Text2D(f"{__doc__} (order={order})", c='red9', bg='white', pos='bottom-center')
- plt.show(shape, txt, mode='image', zoom=1.9)
- objs, points = [], []
- times = np.linspace(0, 2*np.pi, len(fourierX), endpoint=False)
- for time in times:
- x, _ = epicycles(time, 0, fourierX, order)
- _, y = epicycles(time, np.pi/2, fourierY, order)
- points.append([x, y])
- plt.interactive().close()
|