Anyway, I might give a simple draw-on-canvas website a go, give me a few hours.
[0] https://www.nayuki.io/page/free-small-fft-in-multiple-langua...
[1] https://www.nayuki.io/res/free-small-fft-in-multiple-languag...
typedef struct {
double r;
double i;
} complex;
void fourier(complex * x, complex * y, int np)
{
int n, k;
for (k = 0; k < np; ++k)
{
y[k].r = y[k].i = 0;
for (n = 0; n < np; ++n)
{
double c = cos(2*M_PI*k*n/np);
double s = sin(2*M_PI*k*n/np);
y[k].r += x[n].r * c + x[n].i * s;
y[k].i += -x[n].r * s + x[n].i * c;
}
}
}
20 years ago, on a middle of the road desktop, that was fast enough that I was able to do an almost real time guitar tuner with it.I'm always struck by this thought when I see code like this. It's not a lot of code, but if I had to actually go through and internalize it, it would certainly take me a while.
Oh yeah, I definitely didn't mean to imply that it was easy code. It has that elegance that you really only see in the kind of concise code that can only be written by people who really grok the underlying system (both programming and FFT).
Maybe a DCT would be more amenable to hand-editing because it's real and has no symmetry constraints.
Or I should have a 2d image, and I draw dark/light. It reflects appropriately to get the other half, and multiplies the image (without rotating). Right now, this image looks like a donut.
I'm drawing the filter, you see. It applies the filter, and shows me:
* The source image FFT(what I drewFFT(image))
The point spread function / convolution kernel FFT(what I drew)
Notice that the direct and inverse transforms are essentially the same transformation. So you can draw on the image side and swap both images on your mind.