Jump to content

Reconstruction filter tool box


Recommended Posts

Hi,

 

I deliver the first version of the WaveToolBox plugin. The goal is to provide advanced settings for processing image using Sinc filters.

This is work in progress but this first version already offers the following settings:

 - Kernel shape (square, disc and diamond) (diamond is not a shape really common)

 - Kernel radius, literally the size of the box filter in pixels

 - Border handling (uniform color, extend, mirror)

 - Global contrast

 

The processing may be heavy in case of large radius; the plugin is practical but lack of optimization.

 

I place the plugin under Effects -> Render.

 

Here an illustration of the tool:

- Inverse dithering

- Anti-aliasing

 

waveToolBox_demo.gif

 

And the source code:

// Name: WaveToolBox
// Submenu: Render
// Author: Pascal Ollive
// Title: Wave tool box
// Version: 1.0
// Desc: Reconstruction filter tool box
// Keywords: convolution|kernel|filter
// URL:
// Help:
#region UICode
RadioButtonControl kernelShape = 0; // Shape|Square|Disc|Diamond
IntSliderControl kernelRadius = 8; // [8,100] Radius
RadioButtonControl borderHandling = 2; // Border handling|Uniform|Extend|Mirror
IntSliderControl contrastSliderIndex = 5; // [0,10] Contrast
#endregion

private const double SQRT_2 = 1.41421356237309505;

private double[] kernelMatrix = null;
private double kernelNorm = 1.0;

private static byte to8bit(double x) {
    if (x < 0) {
        return 0;
    }
    if (x > 255) {
        return 255;
    }
    return (byte) Math.Round(x);
}

private static double lanczos8(double x) {
    if (x == 0.0) {
        return 1.0;
    }
    double xMultiplyPI = Math.PI * x;
    return 8.0 * Math.Sin(xMultiplyPI) * Math.Sin(xMultiplyPI / 8.0) / (xMultiplyPI * xMultiplyPI);
}

private static double computeLanczosCoef(int i, int j, int radius, byte shape) {
    // center shift
    double x = (i - radius + 1);
    double y = (j - radius + 1);

    if (shape == 0) {
        // Square
        // normalize into lanczos8 window
        return lanczos8(8.0 * x / radius) * lanczos8(8.0 * y / radius);
    }

    if (shape == 1) {
        // Disc
        double d = Math.Sqrt(x * x + y * y);
        if (d < radius) {
            // normalize into lanczos8 window
            return lanczos8(8.0 * d / radius);
        }
    }

    if (shape == 2) {
        // Diamond (45 degrees rotated square)
        double p = (x + y) * SQRT_2 / 2;
        if (p < radius) {
            // normalize into lanczos8 window
            return lanczos8(8.0 * p / radius) * lanczos8(8.0 * (p - SQRT_2 * y) / radius);
        }
    }    

    return 0.0;
}

private static double[] createLanczosKernel(int radius, byte shape) {
    int dimension = 2 * radius - 1;
    double[] matrix = new double[dimension * dimension];

    for (int j = 0; j < dimension; j++) {
        for (int i = 0; i < dimension; i++) {
            matrix[dimension * j + i] = computeLanczosCoef(i, j, radius, shape);
        }
    }

    return matrix;
}

private static double getCoefficientSum(double[] matrix) {
    double result = matrix[0];
    for (int i = 1; i < matrix.Length; i++) {
        result = result + matrix[i];
    }

    return result;
}

private static int surfaceExtend(int x, int maxValue) {
     if (x < 0) {
         return 0;
     }
     if (x > maxValue) {
         return maxValue;
     }
     return x;
}

private static int surfaceMirror(int x, int maxValue) {
    if (x < 0) {
         return - x;
     }
     if (x > maxValue) {
         return 2 * maxValue - x;
     }
     return x;
}

private ColorBgra getSafeSurfacePixel(Surface s, int x, int y) {
    if ((x >= 0) && (y >= 0) && (x < s.Width) && (y < s.Height)) {
        return s[x, y];
    }

    if (borderHandling > 0) {
        if (borderHandling == 1) {
            // Extend
            return s[surfaceExtend(x, s.Width - 1), surfaceExtend(y, s.Height - 1)];
        }
        // Mirror
        return s[surfaceMirror(x, s.Width - 1), surfaceMirror(y, s.Height - 1)];
    }
    // Background color
    return ColorBgra.Black;
}

private double[] convolve(Surface s, int x, int y, double[] m, int radius) {
    double[] bgr = new double[3];
    int dimension = 2 * radius - 1;
    int offset = 0;
    for (int j = 0; j < dimension; j++) {
        for (int i = 0; i < dimension; i++) {
            ColorBgra c = getSafeSurfacePixel(s, x + i - radius + 1, y + j - radius + 1);
            bgr[0] = bgr[0] + c.B * m[offset];
            bgr[1] = bgr[1] + c.G * m[offset];
            bgr[2] = bgr[2] + c.R * m[offset];
            offset = offset + 1;
        }
    }

    return bgr;
}

private double applyContrast(double x) {
    return (256 + 10 * (contrastSliderIndex - 5)) * x / 256 - (5 * (contrastSliderIndex - 5));
}

void PreRender(Surface dst, Surface src) {
    kernelMatrix = createLanczosKernel(kernelRadius, kernelShape);
    kernelNorm = getCoefficientSum(kernelMatrix);
}

void Render(Surface dst, Surface src, Rectangle rect) {
    for (int y = rect.Top; y < rect.Bottom; y++) {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++) {
            double[] bgr = convolve(src, x, y, kernelMatrix, kernelRadius);
            dst[x,y] = ColorBgra.FromBgr(to8bit(applyContrast(bgr[0] / kernelNorm)),
                    to8bit(applyContrast(bgr[1] / kernelNorm)),
                    to8bit(applyContrast(bgr[2] / kernelNorm)));
        }
    }
}

 

WaveToolBox.zip

  • Upvote 1
Link to comment
Share on other sites

Thank you for your efforts @loupasc and willingness to contribute. I understand that there is still some way to go before you're happy (as you mention lack of optimization), but I also understand that this is no a beta version, rather it is meant to be published for all to use. It would therefore receive more attention, downloads and use if you publish it in the Plugins - Publishing ONLY! section.

 

3 hours ago, loupasc said:

I place the plugin under Effects -> Render.

Maybe I don't really understand what the plugin does, but find the Render submenu odd. Would you perhaps consider the Stylize submenu instead?

Xkds4Lh.png

Link to comment
Share on other sites

Hello Djisves,

 

Thanks for the feedback !

I called this plugin tool box because it can be used for many things and I think you're right render may not be a good place (I hesitated a lot in fact).

I may change the location to advanced it would be more suitable.

 

I test what I deliver so I can say it's ok to be run. I will fix the menu issue and add a parameter to tune the Sinc filter before delivering in Plugins - Publishing ONLY!.

 

Have a nice day,

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...