Question

I'm attempting to perform Gaussian blur calculations based on source from the AForge framework. At present though I have something wrong with my calculations in that I am getting the same pixel data out of the process as I am putting in. (Something to do, I think with calculating the divider)

The process comes in two parts:

  1. Create a Gaussian filter based on a kernel created with the set size.

  2. Process the filter against an array of pixels (rgba structure) to return the transformed pixels which are later converted to a bitmap.

The method that I have for creating the original kernel that is converted to an integer based one has been tested against other programs and is correct in its implementation.

Any help would be greatly appreciated. I've been working on this for the last 12 hours.

The method that creates a filter

/// <summary>
/// Create a 2 dimensional Gaussian kernel using the Gaussian G(x y)  
/// function for blurring images.
/// </summary>
/// <param name="kernelSize">Kernel Size</param>
/// <returns>A Gaussian Kernel with the given size.</returns>
public double[,] CreateGuassianBlurFilter(int kernelSize)
{
    // Create kernel
    double[,] kernel = this.CreateGaussianKernel2D(kernelSize);
    double min = kernel[0, 0];

    // Convert to integer blurring kernel. First of all the integer kernel
    // is calculated from Kernel2D
    // by dividing all elements by the element with the smallest value.
    double[,] intKernel = new double[kernelSize, kernelSize];
    int divider = 0;

    for (int i = 0; i < kernelSize; i++)
    {
        for (int j = 0; j < kernelSize; j++)
        {
            double v = kernel[i, j] / min;

            if (v > ushort.MaxValue)
            {
                v = ushort.MaxValue;
            }

            intKernel[i, j] = (int)v;

            // Collect the divider
            divider += (int)intKernel[i, j];
        }
    }

    // Update filter
    this.Divider = divider;
    return intKernel;
}

And the method that performs the convolution:

/// <summary>
/// Processes the given kernel to produce an array of pixels representing a 
/// bitmap.
/// </summary>
/// <param name="pixels">The raw pixels of the image to blur</param>
/// <param name="kernel">
/// The Gaussian kernel to use when performing the method</param>
/// <returns>An array of pixels representing the bitmap.</returns>
public Pixel[,] ProcessKernel(Pixel[,] pixels, double[,] kernel)
{
    int width = pixels.GetLength(0);
    int height = pixels.GetLength(1);
    int kernelLength = kernel.GetLength(0);
    int radius = kernelLength >> 1;
    int kernelSize = kernelLength * kernelLength;
    Pixel[,] result = new Pixel[width, height];

    // For each line
    for (int y = 0; y < height; y++)
    {
        // For each pixel
        for (int x = 0; x < width; x++)
        {
            // The number of kernel elements taken into account
            int processedKernelSize;

            // Colour sums
            double blue;
            double alpha;
            double divider;
            double green;
            double red = green = blue = alpha = divider = 
                         processedKernelSize = 0;

            // For each kernel row
            for (int i = 0; i < kernelLength; i++)
            {
                int ir = i - radius;
                int position = y + ir;

                // Skip the current row
                if (position < 0)
                {
                    continue;
                }

                // Outwith the current bounds so break.
                if (position >= height)
                {
                    break;
                }

                // For each kernel column
                for (int j = 0; j < kernelLength; j++)
                {
                    int jr = j - radius;
                    position = x + jr;

                    // Skip the column
                    if (position < 0)
                    {
                        continue;
                    }

                    if (position < width)
                    {
                        double k = kernel[i, j];
                        Pixel pixel = pixels[x, y];

                        divider += k;

                        red += k * pixel.R;
                        green += k * pixel.G;
                        blue += k * pixel.B;
                        alpha += k * pixel.A;

                        processedKernelSize++;
                    }
                }
            }

            // Check to see if all kernel elements were processed
            if (processedKernelSize == kernelSize)
            {
                // All kernel elements are processed; we are not on the edge.
                divider = this.Divider;
            }
            else
            {
                // We are on an edge; do we need to use dynamic divider or not?
                if (!this.UseDynamicDividerForEdges)
                {
                    // Apply the set divider.
                    divider = this.Divider;
                }
            }

            // Check and apply the divider
            if ((long)divider != 0)
            {
                red /= divider;
                green /= divider;
                blue /= divider;
                alpha /= divider;
            }

            // Add any applicable threshold.
            red += this.Threshold;
            green += this.Threshold;
            blue += this.Threshold;
            alpha += this.Threshold;

            result[x, y].R = (byte)((red > 255) 
                           ? 255 : ((red < 0) ? 0 : red));               
            result[x, y].G = (byte)((green > 255) 
                           ? 255 : ((green < 0) ? 0 : green));
            result[x, y].B = (byte)((blue > 255) 
                           ? 255 : ((blue < 0) ? 0 : blue));
            result[x, y].A = (byte)((alpha > 255) 
                           ? 255 : ((alpha < 0) ? 0 : alpha));
        }
    }

    return result;
}
Était-ce utile?

La solution

The issue was in the selection of the correct pixel to multiply by the kernel value. Instead of the appropriate offset I was choosing the same pixel.

The corrected method is as follows.

/// <summary>
/// Processes the given kernel to produce an array of pixels representing a 
/// bitmap.
/// </summary>
/// <param name="pixels">The raw pixels of the image to blur</param>
/// <param name="kernel">
/// The Gaussian kernel to use when performing the method</param>
/// <returns>An array of pixels representing the bitmap.</returns>
public Pixel[,] ProcessKernel(Pixel[,] pixels, double[,] kernel)
{
    int width = pixels.GetLength(0);
    int height = pixels.GetLength(1);
    int kernelLength = kernel.GetLength(0);
    int radius = kernelLength >> 1;
    int kernelSize = kernelLength * kernelLength;
    Pixel[,] result = new Pixel[width, height];

    // For each line
    for (int y = 0; y < height; y++)
    {
        // For each pixel
        for (int x = 0; x < width; x++)
        {
            // The number of kernel elements taken into account
            int processedKernelSize;

            // Colour sums
            double blue;
            double alpha;
            double divider;
            double green;
            double red = green = blue = alpha = divider = 
                         processedKernelSize = 0;

            // For each kernel row
            for (int i = 0; i < kernelLength; i++)
            {
                int ir = i - radius;
                int iposition = y + ir;

                // Skip the current row
                if (iposition < 0)
                {
                    continue;
                }

                // Outwith the current bounds so break.
                if (iposition >= height)
                {
                    break;
                }

                // For each kernel column
                for (int j = 0; j < kernelLength; j++)
                {
                    int jr = j - radius;
                    int jposition = x + jr;

                    // Skip the column
                    if (jposition < 0)
                    {
                        continue;
                    }

                    if (jposition < width)
                    {
                        double k = kernel[i, j];
                        Pixel pixel = pixels[jposition, iposition];

                        divider += k;

                        red += k * pixel.R;
                        green += k * pixel.G;
                        blue += k * pixel.B;
                        alpha += k * pixel.A;

                        processedKernelSize++;
                    }
                }
            }

            // Check to see if all kernel elements were processed
            if (processedKernelSize == kernelSize)
            {
                // All kernel elements are processed; we are not on the edge.
                divider = this.Divider;
            }
            else
            {
                // We are on an edge; do we need to use dynamic divider or not?
                if (!this.UseDynamicDividerForEdges)
                {
                    // Apply the set divider.
                    divider = this.Divider;
                }
            }

            // Check and apply the divider
            if ((long)divider != 0)
            {
                red /= divider;
                green /= divider;
                blue /= divider;
                alpha /= divider;
            }

            // Add any applicable threshold.
            red += this.Threshold;
            green += this.Threshold;
            blue += this.Threshold;
            alpha += this.Threshold;

            result[x, y].R = (byte)((red > 255) 
                           ? 255 : ((red < 0) ? 0 : red));               
            result[x, y].G = (byte)((green > 255) 
                           ? 255 : ((green < 0) ? 0 : green));
            result[x, y].B = (byte)((blue > 255) 
                           ? 255 : ((blue < 0) ? 0 : blue));
            result[x, y].A = (byte)((alpha > 255) 
                           ? 255 : ((alpha < 0) ? 0 : alpha));
        }
    }

    return result;
}

Autres conseils

The only quick way to find out why it is happening is to set breakpoints and track the values changing. This helps you to catch the code with errors operatively. You could forget some calculations or method may return an untouched copy instead of modified result, or modified calculations may cut precision, anyway, this is not a kind of a problem to delegate to others.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top