loupasc Posted June 11, 2020 Share Posted June 11, 2020 (edited) Slight edge boost Last version 1.6.7489.24864 [ 06 July 2020 ] Download link > > > Download Location: Effects -> Photo Corresponding source is available in the last post. Plugin description This plugin enhances image edges using unsharp mask method. It provides the possibility to apply sharpen only to the image luminosity in order to avoid chromatic noise amplification. Illustration on a stenope (blurry) photography Spoiler Edited July 6, 2020 by loupasc New version 1.6.7489.24864 1 2 Quote Link to comment Share on other sites More sharing options...
loupasc Posted June 11, 2020 Author Share Posted June 11, 2020 (edited) Version 1.5.7467.22643 Source Spoiler // Name: Slight edge boost // Submenu: Photo // Author: Pascal Ollive // Title: Slight edge boost // Version: 1.5 // Desc: Gaussian unsharp mask // Keywords: Sharpening|Gaussian|filter // URL: https://forums.getpaint.net/profile/160055-loupasc/ // Help: #region UICode RadioButtonControl colorSpace = 0; // Select sharpen target|RGB (all)|YUV (Y only) IntSliderControl sliderValue = 5; // [0,10] Strength #endregion // 10 amounts depending of sliderValue private static byte[] AMOUNT = {2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233}; // 2 * Math.Exp(-6*(x^2+y^2)) + Math.Exp(-9*(x^2+y^2)/8) r=1.5 // sum(matrix) = 0 // factor = 65536 private static int[,] KERNEL7x7 = createKernelMatrix(); private static double gaussian(double x, double y) { double sqr = x * x + y * y; return 2 * Math.Exp(-6 * sqr) + Math.Exp(-9 * sqr / 8); } private static int[,] createKernelMatrix() { int[,] matrix = new int[7, 7]; for (int j = 0; j < 7; j++) { for (int i = 0; i < 7; i++) { // support 2 => 1.5 matrix[i, j] = (int) Math.Round(-22311.3 * gaussian(2 * (i - 3)/ 3.0, 2 * (j - 3)/ 3.0)); } } // // sum(matrix) = 0 matrix[3, 3] = 131068; return matrix; } private byte strength = 21; // [2,233] // --------------------------- // -- Color space functions -- // --------------------------- // -- RGB to YUV -- // out [0 .. 255] private static double bgrToY(ColorBgra c) { return 0.299 * c.R + 0.587 * c.G + 0.114 * c.B; } // out [-111.18 .. 111.18] private static double bgrToU(ColorBgra c) { return -0.14714 * c.R - 0.28886 * c.G + 0.436 * c.B; } // out [-156.825 .. 156.825] private static double bgrToV(ColorBgra c) { return 0.615 * c.R - 0.51499 * c.G - 0.10001 * c.B; } // -- YUV to RGB -- private static byte to8bit(double x) { if (x < 0) { return 0; } if (x > 255) { return 255; } return (byte) Math.Round(x); } private static ColorBgra yuvToBgr(double y, double u, double v) { byte b = to8bit(y + 2.032078 * u); // 0.299/0.14714 byte g = to8bit(y - 0.39465 * u - 0.5806 * v); byte r = to8bit(y + 1.139827 * v); // 0.587/0.51499 return ColorBgra.FromBgr(b, g, r); } // // --------------------------- // private static int surfaceExtend(int x, int maxValue) { if (x < 0) { return 0; } if (x < maxValue) { return x; } return maxValue - 1; } // Safe access to Color BGRA surface private static ColorBgra getSafeSurfacePixel(Surface s, int x, int y) { if ((x >= 0) && (y >= 0) && (x < s.Width) && (y < s.Height)) { return s[x, y]; } return s[surfaceExtend(x, s.Width), surfaceExtend(y, s.Height)]; } // Utility function to provide result in 8-bit range // strength 2^5 // factor 2^16 // 21-bit scale private static byte to8bit(long n) { if (n > 535822335) { return 255; } if (n < 0) { return 0; } return (byte) ((n + 1048576) >> 21); } private ColorBgra convolveRGB(Surface src, int x, int y, bool isCentralArea) { // strength 2^5 and factor 2^16 => 21-bit scale long r = src[x, y].R << 21; long g = src[x, y].G << 21; long b = src[x, y].B << 21; for (int j = 0; j < 7; j++) { int posY = y + j - 3; for (int i = 0; i < 7; i++) { int posX = x + i - 3; long coef = KERNEL7x7[i, j] * strength; ColorBgra srcPixel = (isCentralArea ? src[posX, posY] : getSafeSurfacePixel(src, posX, posY)); r = r + coef * srcPixel.R; g = g + coef * srcPixel.G; b = b + coef * srcPixel.B; } } return ColorBgra.FromBgr(to8bit(b), to8bit(g), to8bit(r)); } private ColorBgra convolveYUV(Surface src, int x, int y, bool isCentralArea) { // strength 2^5 and factor 2^16 => 2^21 (2097152) double totalLuma = bgrToY(src[x, y]) * 2097152.0; double u = bgrToU(src[x, y]); double v = bgrToV(src[x, y]); for (int j = 0; j < 7; j++) { int posY = y + j - 3; for (int i = 0; i < 7; i++) { int posX = x + i - 3; double luma = (isCentralArea ? bgrToY(src[posX, posY]) : bgrToY(getSafeSurfacePixel(src, posX, posY))); totalLuma = totalLuma + luma * KERNEL7x7[i, j] * strength; } } return yuvToBgr(totalLuma / 2097152.0, u, v); } void PreRender(Surface dst, Surface src) { // strength 5-bit precision [2/32 .. 233/32] strength = AMOUNT[sliderValue]; } void Render(Surface dst, Surface src, Rectangle rect) { bool isCentralArea = ((rect.Left >= 3) && (rect.Top >= 3) && (rect.Right <= src.Width - 3) && (rect.Bottom <= src.Height - 3)); for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { dst[x, y] = (colorSpace == 0 ? convolveRGB(src, x, y, isCentralArea) : convolveYUV(src, x, y, isCentralArea)); } } } Edited June 11, 2020 by loupasc Syntax highlighting 1 Quote Link to comment Share on other sites More sharing options...
Eli Posted June 11, 2020 Share Posted June 11, 2020 Thank you @loupasc, with sharp edges I don't have to change my eye glasses. 2 Quote Link to comment Share on other sites More sharing options...
Foxxey Posted June 24, 2020 Share Posted June 24, 2020 (edited) Cool plugin! 😮 Edited March 21, 2021 by Foxxey 1 Quote Link to comment Share on other sites More sharing options...
Seerose Posted June 24, 2020 Share Posted June 24, 2020 ❤️ @loupasc! Thank you for your effort. Quote Live as if you were to die tomorrow. Learn as if you were to live forever. Gandhi Link to comment Share on other sites More sharing options...
Seerose Posted June 24, 2020 Share Posted June 24, 2020 ❤️ @Foxxey! Welcome to Family! Thank you for your great result. 1 Quote Live as if you were to die tomorrow. Learn as if you were to live forever. Gandhi Link to comment Share on other sites More sharing options...
loupasc Posted June 25, 2020 Author Share Posted June 25, 2020 @Foxxey Danke @Seerose It's my pleasure to contribute here ! 1 Quote Link to comment Share on other sites More sharing options...
loupasc Posted July 6, 2020 Author Share Posted July 6, 2020 Version 1.6.7489.24864 Minor improvements (kernel matrix accuracy and to8bit clamp function a bit more efficient). Spoiler // Name: Slight edge boost // Submenu: Photo // Author: Pascal Ollive // Title: Slight edge boost // Version: 1.6 // Desc: Gaussian unsharp mask // Keywords: Sharpening|Gaussian|filter // URL: https://forums.getpaint.net/profile/160055-loupasc/ // Help: #region UICode RadioButtonControl colorSpace = 0; // Select sharpen target|RGB (all)|YUV (Y only) IntSliderControl sliderValue = 5; // [0,10] Strength #endregion // Scale double -> integer private const int KERNEL_COEF_SCALE = -22306; // 10 amounts depending of sliderValue private static byte[] AMOUNT = {2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233}; // Significant value in 9x9 border private static int EXTREME_BORDER_COEF = (int) Math.Round(KERNEL_COEF_SCALE * gaussian(0, -8 / 3.0)); // 2 * Math.Exp(-6*(x^2+y^2)) + Math.Exp(-9*(x^2+y^2)/8) r=1.5 // sum(matrix) = 0 // factor = 65536 private static int[,] KERNEL7x7 = createKernelMatrix(); private static double gaussian(double x, double y) { double sqr = x * x + y * y; return 2 * Math.Exp(-6 * sqr) + Math.Exp(-9 * sqr / 8); } private static int[,] createKernelMatrix() { int[,] matrix = new int[7, 7]; int sum = 4 * EXTREME_BORDER_COEF; for (int j = 0; j < 7; j++) { for (int i = 0; i < 7; i++) { // support 2 => 1.5 matrix[i, j] = (int) Math.Round(KERNEL_COEF_SCALE * gaussian(2 * (i - 3)/ 3.0, 2 * (j - 3)/ 3.0)); sum = sum + matrix[i, j]; } } // // sum(matrix) = 0 matrix[3, 3] = matrix[3, 3] - sum; return matrix; } private byte strength = 21; // [2,233] // --------------------------- // -- Color space functions -- // --------------------------- // -- RGB to YUV -- // out [0 .. 255] private static double bgrToY(ColorBgra c) { return 0.299 * c.R + 0.587 * c.G + 0.114 * c.B; } // out [-111.18 .. 111.18] private static double bgrToU(ColorBgra c) { return -0.14714 * c.R - 0.28886 * c.G + 0.436 * c.B; } // out [-156.825 .. 156.825] private static double bgrToV(ColorBgra c) { return 0.615 * c.R - 0.51499 * c.G - 0.10001 * c.B; } // -- YUV to RGB -- private static byte to8bit(double x) { if (x < 0) { return 0; } if (x > 255) { return 255; } return (byte) Math.Round(x); } private static ColorBgra yuvToBgr(double y, double u, double v) { byte b = to8bit(y + 2.032078 * u); // 0.299/0.14714 byte g = to8bit(y - 0.39465 * u - 0.5806 * v); byte r = to8bit(y + 1.139827 * v); // 0.587/0.51499 return ColorBgra.FromBgr(b, g, r); } // // --------------------------- // private static int surfaceExtend(int x, int maxValue) { if (x < 0) { return 0; } if (x < maxValue) { return x; } return maxValue - 1; } // Safe access to Color BGRA surface private static ColorBgra getSafeSurfacePixel(Surface s, int x, int y) { if ((x >= 0) && (y >= 0) && (x < s.Width) && (y < s.Height)) { return s[x, y]; } return s[surfaceExtend(x, s.Width), surfaceExtend(y, s.Height)]; } // Utility function to provide result in 8-bit range // strength 2^5 // factor 2^16 // 21-bit scale private static byte to8bit(long n) { if (n > 533725183) { return 255; } if (n < 0) { return 0; } return (byte) ((n + 1048576) >> 21); } private static int[] convolveRgbBorder(Surface src, int x, int y) { int[] rgb = { 0, 0, 0 }; ColorBgra c1 = getSafeSurfacePixel(src, x, y - 4); ColorBgra c2 = getSafeSurfacePixel(src, x - 4, y); ColorBgra c3 = getSafeSurfacePixel(src, x + 4, y); ColorBgra c4 = getSafeSurfacePixel(src, x, y + 4); rgb[0] = EXTREME_BORDER_COEF * (c1.R + c2.R + c3.R + c4.R); rgb[1] = EXTREME_BORDER_COEF * (c1.G + c2.G + c3.G + c4.G); rgb[2] = EXTREME_BORDER_COEF * (c1.B + c2.B + c3.B + c4.B); return rgb; } private ColorBgra convolveRGB(Surface src, int x, int y, bool isCentralArea) { int[] rgb = convolveRgbBorder(src, x, y); // strength 2^5 and factor 2^16 => 21-bit scale long r = (src[x, y].R << 21) + rgb[0] * strength; long g = (src[x, y].G << 21) + rgb[1] * strength; long b = (src[x, y].B << 21) + rgb[2] * strength; for (int j = 0; j < 7; j++) { int posY = y + j - 3; for (int i = 0; i < 7; i++) { int posX = x + i - 3; long coef = KERNEL7x7[i, j] * strength; ColorBgra srcPixel = (isCentralArea ? src[posX, posY] : getSafeSurfacePixel(src, posX, posY)); r = r + coef * srcPixel.R; g = g + coef * srcPixel.G; b = b + coef * srcPixel.B; } } return ColorBgra.FromBgr(to8bit(b), to8bit(g), to8bit(r)); } private static double convolveYuvBorder(Surface src, int x, int y) { return EXTREME_BORDER_COEF * (bgrToY(getSafeSurfacePixel(src, x, y - 4)) + bgrToY(getSafeSurfacePixel(src, x - 4, y)) + bgrToY(getSafeSurfacePixel(src, x + 4, y)) + bgrToY(getSafeSurfacePixel(src, x, y + 4))); } private ColorBgra convolveYUV(Surface src, int x, int y, bool isCentralArea) { // strength 2^5 and factor 2^16 => 2^21 (2097152) double totalLuma = bgrToY(src[x, y]) * 2097152.0; double u = bgrToU(src[x, y]); double v = bgrToV(src[x, y]); totalLuma = totalLuma + convolveYuvBorder(src, x, y) * strength; for (int j = 0; j < 7; j++) { int posY = y + j - 3; for (int i = 0; i < 7; i++) { int posX = x + i - 3; double luma = (isCentralArea ? bgrToY(src[posX, posY]) : bgrToY(getSafeSurfacePixel(src, posX, posY))); totalLuma = totalLuma + luma * KERNEL7x7[i, j] * strength; } } return yuvToBgr(totalLuma / 2097152.0, u, v); } void PreRender(Surface dst, Surface src) { // strength 5-bit precision [2/32 .. 233/32] strength = AMOUNT[sliderValue]; } void Render(Surface dst, Surface src, Rectangle rect) { bool isCentralArea = ((rect.Left >= 3) && (rect.Top >= 3) && (rect.Right <= src.Width - 3) && (rect.Bottom <= src.Height - 3)); for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { dst[x, y] = (colorSpace == 0 ? convolveRGB(src, x, y, isCentralArea) : convolveYUV(src, x, y, isCentralArea)); } } } 3 Quote Link to comment Share on other sites More sharing options...
Seerose Posted July 6, 2020 Share Posted July 6, 2020 ❤️ @loupasc! Thank you so much for the new version. Quote Live as if you were to die tomorrow. Learn as if you were to live forever. Gandhi Link to comment Share on other sites More sharing options...
loupasc Posted July 7, 2020 Author Share Posted July 7, 2020 You're welcome ! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.