loupasc Posted May 29, 2020 Share Posted May 29, 2020 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 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 1 Link to comment Share on other sites More sharing options...
Djisves Posted May 29, 2020 Share Posted May 29, 2020 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? Link to comment Share on other sites More sharing options...
loupasc Posted May 29, 2020 Author Share Posted May 29, 2020 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 More sharing options...
loupasc Posted May 29, 2020 Author Share Posted May 29, 2020 @Djisves New topic created : https://forums.getpaint.net/topic/116512-reconstruction-filter-tool-box/ Shall I destroy the current topic or let a moderator lock it ? Link to comment Share on other sites More sharing options...
Rick Brewster Posted May 29, 2020 Share Posted May 29, 2020 I can lock this thread Locked The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Recommended Posts