Jump to content

FFT / IFFT Effect


ArgusMagnus

Recommended Posts

Update: Ready to be tested

 

So here is what I have done so far: https://onedrive.liv...ithint=file,zip

 

The dll contains 4 effects:

 

Transform->Fast Fourier Transform: Transforms either intensity image or one of the channels (R,G,B ) to and from the frequency domain. The transformed image contains the magnitude information in the R and G values and the phase information in the B value (which basically means: don't change the B value of any pixel too much or at all if you want to get a meaningful result when transforming back).

 

Signal Processing->Low Pass Filter: The same as using the transform effect, removing (setting to black or transparent) all pixel outside a rectangle around the center and transforming back only this is faster, more accuratly (less information is lost) and easier to control

 

Signal Processing->High Pass Filter: The same as using the transform effect, removing all pixels inside a rectangle around the center and transforming back (again, faster, more accuratly, easier controlled)

 

Color->Reverse Blend: Has nothing to do with the above, is simply in the same assembly.

 

 

Be aware that these 3 Effects won't work on non-rectangular selections. For the Transform->Fast Fourier Transform this cannot be changed. For the Signal Processing Effects, this is a bug and will be fixed in the future.

 

 

The plugin actually uses the FFTW library (fftw.org), I have not written the transformation code myself (mainly for performance reasons)

 

I made these playing with the High Pass Filter:

JKzh9N.png

09sGip.png

 

 

 

Original Post:

 

I wrote an effect which calculates the FFT or the IFFT from an image. This works fine. The idea is to get the FFT from an image, modify it in frequency domain and convert it back using IFFT to get some cool effects.

But here is my problem: The result of an FFT are complex numbers (magnitude and phase information) and I'm not sure how I should encode this information into the 32bits per pixel paint.net offers me.

At first set the destination pixel in HSV space: Saturation = 100, Value = magnitude (capped at 100) and hue = phase. This gave some intersting looking results when converting back (IFFT), kinda like the pencil effect. But having only 101 (0-100) discret values to describe the magnitude results in data loss: either I cap the values at 100 or I scale the whole spectrum to [0,100] which results in loss of information (typically, there are many low values (~1-100) and a few very high ~70'000)

So my next approach was to simply store the magnitude in 3 bytes (a, r and g component, scaling magnitude range to [0,0xFFFFFF]) and the phase in one byte (b component, scaled to [0,255]) which "works", transforming forth and back gives the original image (without noticeable data loss), BUT working with the FFT in this representation proved to be less than ideal (not intuitive).

 

Has anyone a good Idea how I could represent the complex number as Color? Ideal would be a reprensentation where one could modify the magnitude without changing the phase. (This is the problem with the second option)

 

Example Image 1 (HSV, capped at 100, top: Original, middle: FFT, bottom: IFFT)

ulJjp8.png

 

Example Image 2 (bgra method, top: Original, middle: FFT, bottom: IFFT)

EEJAYJ.png

Edited by ArgusMagnus
  • Upvote 1

My batch Image Processor: https://imagenator.codeplex.com

Link to comment
Share on other sites

But you do not expect that here in the forum are so many developers or user with knowledge about FFT, or? ;-)

Nevermind I'm excited what you may provide us.

midoras signature.gif

Link to comment
Share on other sites

I'm not sure what your solution is, but if you do the HSV conversions yourself, instead of relying on PDN's built-in version, which returns integers, you can get the results in whatever range you want, such as 0-255. Actually, I guess the best idea I can see is to just use double (or float) versions, which would get converted to and from RGB.

 

In case you want some code to do conversions (with all HSV values between 0.0 and 1.0):

Hidden Content:
public ColorBgra HSVtoRGBA(double H, double S, double V, double A)
{
    return HSVtoRGBA(H, S, V, (byte)(255 * A + 0.5));
}

public ColorBgra HSVtoRGBA(double H, double S, double V, byte A)
{
    byte r, g, b;
    HSVtoRGB(H, S, V, out r, out g, out ;
    return ColorBgra.FromBgra(b, g, r, (byte)A);
}

public void HSVtoRGB(double H, double S, double V, out byte bR, out byte bG, out byte bB)
{
    // Parameters must satisfy the following ranges:
    // 0.0 <= H < 1.0
    // 0.0 <= S <= 1.0
    // 0.0 <= V <= 1.0

    // Handle special case of gray (so no Hue) first
    if ((S == 0.0) || (V == 0.0))
    {
        byte x = (byte)(int)(V * 255.0);
        bR = x;
        bG = x;
        bB = x;
        return;
    }

    H = HueConstrain(H);

    double R = V, G = V, B = V;
    double Hi = Math.Floor(6.0 * H);
    double f = 6.0 * H - Hi;
    double p = V * (1.0 - S);
    double q = V * (1.0 - f * S);
    double t = V * (1.0 - (1.0 - f) * S);
    if (Hi == 0.0)
    {
        R = V;
        G = t;
        B = p;
    }
    else if (Hi == 1.0)
    {
        R = q;
        G = V;
        B = p;
    }
    else if (Hi == 2.0)
    {
        R = p;
        G = V;
        B = t;
    }
    else if (Hi == 3.0)
    {
        R = p;
        G = q;
        B = V;
    }
    else if (Hi == 4.0)
    {
        R = t;
        G = p;
        B = V;
    }
    else // if (Hi == 5.0)
    {
        R = V;
        G = p;
        B = q;
    }

    int iR = (int)(R * 255.0 + 0.5);
    int iG = (int)(G * 255.0 + 0.5);
    int iB = (int)(B * 255.0 + 0.5);
    bR = (byte)iR;
    bG = (byte)iG;
    bB = (byte)iB;
}

public void RGBtoHSV(ColorBgra color, out double H, out double S, out double V)
{
    RGBtoHSV(color.R, color.G, color.B, out H, out S, out V);
}

public void RGBtoHSV(int R, int G, int B, out double outH, out double outS, out double outV)
{
    const double H_UNDEFINED = 0.0;    // Arbitrarily set undefined hue to 0
    const double recip6 = 1.0 / 6.0;
    
    // R, G, and B must range from 0 to 255
    // Ouput value ranges:
    // outH - 0.0 to 1.0
    // outS - 0.0 to 1.0
    // outV - 0.0 to 1.0

    double dR = (double)R / 255.0;
    double dG = (double)G / 255.0;
    double dB = (double)B / 255.0;
    double dmaxRGB = Max3(dR, dG, dB);
    double dminRGB = Min3(dR, dG, dB);
    double delta = dmaxRGB - dminRGB;

    // Set value
    outV = dmaxRGB;

    // Handle special case of V = 0 (black)
    if (dmaxRGB == 0)
    {
        outH = H_UNDEFINED;
        outS = 0.0;
        return;
    }

    // Handle specai case of S = 0 (gray)
    outS = delta / dmaxRGB;
    if (dmaxRGB == dminRGB)
    {
        outH = H_UNDEFINED;
        return;
    }

    // Finally, compute hue
    if (dR == dmaxRGB)
    {
        outH = (dG - dB) / delta;
    }
    else if (dG == dmaxRGB)
    {
        outH = 2.0 + (dB - dR) / delta;
    }
    else //if (dB == dmaxRGB)
    {
        outH = 4.0 + (dR - dG) / delta;
    }

    outH *= recip6;
    outH = HueConstrain(outH);
}

public double Max3(double x, double y, double z)
{
    return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z);
}

public double Min3(double x, double y, double z)
{
    return (x < y) ? ((x < z) ? x : z) : ((y < z) ? y : z);
}

public double HueConstrain(double MyHue)
{
    // Makes sure that 0.0 <= MyHue < 1.0
    // Wraps around the value if its outside this range
    while (MyHue >= 1.0)
    {
        MyHue -= 1.0;
    }
    while (MyHue < 0.0)
    {
        MyHue += 1.0;
    }
    return MyHue;
}

 

Also, if you want to access the colors as integers, you can use ColorBgra.Bgra (I think that's the name), which returns an unsigned int with alpha as the high byte and blue as the low byte. It's very efficient, since it actually loads all four bytes simultaneously. The ColorBgra colors are stored in what would be called a union in C.

Edited by MJW
Link to comment
Share on other sites

Thank you all for your responses!

I think I need to clarify a bit: I actually face two problems:

1. I need to store a complex number (2 floating points) in 32 bits because in the end, the destination surface will only hold 32 bits per pixel, passed as a ColorBgra structure.

2. The resulting image should still be meaningful to the user. Because of fhis, using the alpha channel is not a good idea and Im basecally left with 24 bits in Problem 1.

For problem 2, HSV would be ideal. Representing the magnitude as brightness and the angle/phase as hue would make perfectly sense. However for Problem 1, HSV is less than ideal. You are right, if I do the HSV conversation myself, I can end up with a wider range of values than when using HSVColor.FromColor, but at some point I will have to convert it to an ColorBgra structure to write it to the destination surface. At that point, the Value channel must be squeezed into one byte. This is better (256 instead of 101 discret values) but I don't think it's enough. (I will test it however)

What I currently am doing is saving the phase to the blue byte and the magnitude to the r and g bytes (16bits -> 2^16 discret values). The result is not as good as HSV to the eye, but it still has the tendency that pixels with higher magnitude are brighter, which makes sense.

I confused the byte ordering in the first try, that's why it didn't work then.

I will post the dll/code to test shortly :)

My batch Image Processor: https://imagenator.codeplex.com

Link to comment
Share on other sites

So here is what I have done so far: https://onedrive.live.com/redir?resid=429F16CCEF18E484!336393&authkey=!AOpTQOahVbphCCI&ithint=file%2czip

 

The dll contains 4 effects:

 

Transform->Fast Fourier Transform: Transforms either intensity image or one of the channels (R,G,B ) to and from the frequency domain. The transformed image contains the magnitude information in the R and G values and the phase information in the B value (which basically means: don't change the B value of any pixel too much are at all if you want to get a meaningful result when transforming back).

 

Signal Processing->Low Pass Filter: The same as using the transform effect, removing (setting to black or transparent) all pixel outside a rectangle around the center and transforming back only this is faster, more accuratly (less information is lost) and easier to control

 

Signal Processing->High Pass Filter: The same as using the transform effect, removing all pixels inside a rectangle around the center and transforming back (again, faster, more accuratly, easier controlled)

 

Color->Reverse Blend: Has nothing to do with the above, is simply in the same assembly.

 

 

Be aware that these 3 Effects won't work on non-rectangular selections. For the Transform->Fast Fourier Transform this cannot be changed. For the Signal Processing Effects, this is a bug and will be fixed in the future.

 

 

The plugin actually uses the FFTW library (fftw.org), I have not written the transformation code myself (mainly for performance reasons)

Edited by ArgusMagnus

My batch Image Processor: https://imagenator.codeplex.com

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...