Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional standard gradient calculation in Sobel filter #407

Open
GoogleCodeExporter opened this issue Apr 17, 2016 · 0 comments
Open

Optional standard gradient calculation in Sobel filter #407

GoogleCodeExporter opened this issue Apr 17, 2016 · 0 comments

Comments

@GoogleCodeExporter
Copy link

Instead

        Math.Sqrt( gx * gx + gy * gy )

, framework evaluates

        Math.Min( 255, Math.Abs(gx) + Math.Abs(gy) )

This produces differences in two ways

1.- Simpliffied expression is always a bit bigger. Differences are noticiable 
but not important.

gx + gy    <=>    Sqrt( (gx + gy)^2 )    <=>    Sqrt( gx^2 + 2*gx*gy + gy^2 )


2.- Evaluating "Min" before normalization, produces important differences in 
result.

We can see differences in this image that combines 4 results. Results has been 
inverted and thresholded (235) to clearly show differences  

* top-left: Min + Sum (current AForge implementation)
* top-right: Min + Sqrt
* bottom-left: Sum
* bottom-right: Sqrt

https://dl.dropboxusercontent.com/u/49778953/test_sobel_results.JPG

Differences between results clearly show that smoothing effect is lost when 
using "Min"

This is my test image
https://dl.dropboxusercontent.com/u/49778953/test_sobel.jpg


I suggest to introduce a new Property "StandardGradientCalculation" with 
default value = False. This way, behaviour will not change in production 
systems.


This is the function i've used. Note the use of 

float[,] gradients = new float[source.Height, source.Width];

, to avoid byte overflow


public unsafe void ProcessFilter(AForge.Imaging.UnmanagedImage source, 
AForge.Imaging.UnmanagedImage destination, Rectangle rect,
                                 bool takeMin, bool useSum)
{
    int[,] kernelX = {{-1, +0, +1},
                      {-2, +0, +2},
                      {-1, +0, +1}};

    int[,] kernelY = {{-1, -2, -1},
                      {+0, +0, +0},
                      {+1, +2, +1}};

    // 3x3 kernel => kernelX.GetUpperBound(0) = 2
    int kernelSize = kernelX.GetUpperBound(0) + 1;
    if (kernelSize % 2 == 0)
    {
        throw new ArgumentException("Size of kernel matrix must have odd");
    }
    int kernelRadius = (kernelSize - 1) / 2;
    byte srcBytesPerPixel = (byte)(System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat) / 8);
    byte dstBytesPerPixel = (byte)(System.Drawing.Bitmap.GetPixelFormatSize(destination.PixelFormat) / 8);

    // processing start and stop X,Y positions                
    int startX = rect.Left + kernelRadius;
    int startY = rect.Top + kernelRadius;
    int stopX = startX + rect.Width - 2 * kernelRadius;
    int stopY = startY + rect.Height - 2 * kernelRadius;

    int dstStride = destination.Stride;
    int srcStride = source.Stride;

    byte* srcCurrent = (byte*)source.ImageData.ToPointer() + srcStride * startY + srcBytesPerPixel * startX;
    byte* dstCurrent = (byte*)destination.ImageData.ToPointer() + dstStride * startY + dstBytesPerPixel * startX;

    byte* src = null;
    byte* dst = null;

    // variables for gradient calculation
    float g = 0;
    float max = 0;
    float[,] gradients = new float[source.Height, source.Width];

    // for each line
    for (int y = startY; y < stopY; y++)
    {
        // for each pixel
        src = srcCurrent;
        dst = dstCurrent;
        for (int x = startX; x < stopX; x++)
        {
            //All cells for clarity: kernelX[0, 1], kernelX[1, 1] and kernelX[2, 1] are 0 and can be removed in expression
            int gx = kernelX[0, 0] * src[-srcStride - 1] + kernelX[0, 1] * src[-srcStride]  + kernelX[0, 2] * src[-srcStride + 1] +
                     kernelX[1, 0] * src[-1]             + kernelX[1, 1] * src[0]           + kernelX[1, 2] * src[1] +
                     kernelX[2, 0] * src[srcStride - 1]  + kernelX[2, 1] * src[srcStride]   + kernelX[2, 2] * src[srcStride + 1];

            //All cells for clarity: kernelY[1, 0], kernelY[1, 1] and kernelY[1, 2] are 0 and can be removed in expression
            int gy = kernelY[0, 0] * src[-srcStride - 1] + kernelY[0, 1] * src[-srcStride] + kernelY[0, 2] * src[-srcStride + 1] +
                     kernelY[1, 0] * src[-1]             + kernelY[1, 1] * src[0]          + kernelY[1, 2] * src[+1] +
                     kernelY[2, 0] * src[srcStride - 1]  + kernelY[2, 1] * src[srcStride]  + kernelY[2, 2] * src[srcStride + 1];

            if (useSum)
                g = Math.Abs(gy) + Math.Abs(gx);
            else
                g = (float)Math.Sqrt(gx * gx + gy * gy);

            if (takeMin)
                g = Math.Min(255, g);

            if (g > max)
                max = g;

            gradients[y, x] = g;
            src += srcBytesPerPixel;
            dst += dstBytesPerPixel;
        }
        srcCurrent += srcStride;
        dstCurrent += dstStride;
    }

    // make the second pass for intensity scaling and byte casting
    float factor = (float)255.0 / max;
    dstCurrent = (byte*)destination.ImageData.ToPointer() + dstStride * startY + dstBytesPerPixel * startX;
    // for each line
    for (int y = startY; y < stopY; y++)
    {
        // for each pixel
        dst = dstCurrent;
        for (int x = startX; x < stopX; x++)
        {
            *dst = (byte)(factor * gradients[y, x]);
            dst += dstBytesPerPixel;
        }
        dstCurrent += dstStride;
    }

    // draw black rectangle to remove those pixels, which were not processed
    // (this needs to be done for those cases, when filter is applied "in place" -
    // source image is modified instead of creating new copy)
    AForge.Imaging.Drawing.Rectangle(destination, rect, Color.Black);
}

Original issue reported on code.google.com by [email protected] on 15 Jun 2015 at 2:08

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant