Sign in to follow this  
loupasc

Slight Edge Boost

Recommended Posts

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

interface.png.e6edd00371b35b359e3efe85ca8c1c00.png

 

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

edgeBoost_demo.gif

 

 

Edited by loupasc
New version 1.6.7489.24864
  • Like 1
  • Upvote 2

Share this post


Link to post
Share on other sites

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 by loupasc
Syntax highlighting
  • Like 1

Share this post


Link to post
Share on other sites

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));
        }
    }
}

 

 

  • Like 3

Share this post


Link to post
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.

Sign in to follow this