Question

I'm attempting to implement a gaussian blur method for a library of mine based on an article and sample at http://softwarebydefault.com/2013/06/08/calculating-gaussian-kernels/

For some reason I keep getting a black border around my generated images that encroaches on the original image as shown below.

blurred image with black border

The border thickness increases as the Gaussian kernel length increases. This image was generated with a matrix of 15x15.

Can anyone shed any light as to what might be happening?

My code; apologies for the length:

The processing method.

/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">The the current instance of the
///  <see cref="T:ImageProcessor.ImageFactory" /> class containing
/// the image to process.</param>
/// <returns>
/// The processed image from the current instance of the
/// <see cref="T:ImageProcessor.ImageFactory" /> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
    Bitmap newImage = null;
    Bitmap image = (Bitmap)factory.Image;

    try
    {
        double[,] filterMatrix = this.Calculate((int)this.DynamicParameter, 10);

        // We could implement factor and bias here as parameters if 
        // we move this to a separate method.
        const double Factor = 1;
        const double Bias = 0;

        BitmapData sourceData = image.LockBits(
        new Rectangle(0, 0, image.Width, image.Height),
        ImageLockMode.ReadOnly,
        PixelFormat.Format32bppArgb);

        byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
        byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];

        Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
        image.UnlockBits(sourceData);

        int filterWidth = filterMatrix.GetLength(1);
        int filterHeight = filterMatrix.GetLength(0);
        int filterOffsetWidth = (filterWidth - 1) / 2;
        int filterOffsetHeight = (filterHeight - 1) / 2;

        for (int offsetY = filterOffsetHeight; offsetY < image.Height - filterOffsetHeight; offsetY++)
        {
            for (int offsetX = filterOffsetWidth; offsetX < image.Width - filterOffsetWidth; offsetX++)
            {
                double blue = 0;
                double green = 0;
                double red = 0;

                int byteOffset = (offsetY * sourceData.Stride) + (offsetX * 4);

                for (int filterY = -filterOffsetWidth; filterY <= filterOffsetWidth; filterY++)
                {
                    for (int filterX = -filterOffsetWidth; filterX <= filterOffsetWidth; filterX++)
                    {
                        int calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);

                        blue += pixelBuffer[calcOffset]
                                * filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];

                        green += pixelBuffer[calcOffset + 1]
                                    * filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];

                        red += pixelBuffer[calcOffset + 2]
                                * filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
                    }
                }

                blue = (Factor * blue) + Bias;
                green = (Factor * green) + Bias;
                red = (Factor * red) + Bias;

                blue = blue > 255 ? 255 : (blue < 0 ? 0 : blue);
                green = green > 255 ? 255 : (green < 0 ? 0 : green);
                red = red > 255 ? 255 : (red < 0 ? 0 : red);

                resultBuffer[byteOffset] = (byte)blue;
                resultBuffer[byteOffset + 1] = (byte)green;
                resultBuffer[byteOffset + 2] = (byte)red;
                resultBuffer[byteOffset + 3] = 255;
            }
        }

        // ReSharper disable once UseObjectOrCollectionInitializer
        newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);
        newImage.Tag = image.Tag;

        BitmapData resultData = newImage.LockBits(
            new Rectangle(0, 0, newImage.Width, newImage.Height),
            ImageLockMode.WriteOnly,
            PixelFormat.Format32bppArgb);

        Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
        newImage.UnlockBits(resultData);

        image.Dispose();
        image = newImage;

        // Save the image to ensure that nothing else is going on downstream.
        newImage.Save("C:\\Users\\James\\Desktop\\image.jpg", ImageFormat.Jpeg);
    }
    catch
    {
        if (newImage != null)
        {
            newImage.Dispose();
        }
    }

    return image;
}

The Gaussian calculator.

/// <summary>
/// Calculates a Gaussian kernel with the given .
/// </summary>
/// <param name="length">
/// The length.
/// </param>
/// of the kernel to produce
/// <param name="weight">
/// The weight of the kernel.
/// </param>
/// <returns>
/// The <see><cref>double[,]</cref></see> containing the Gaussian kernel.
/// </returns>
private double[,] Calculate(int length, double weight)
{
    double[,] kernel = new double[length, length];
    double sumTotal = 0;

    int kernelRadius = length / 2;

    double calculatedEuler = 1.0 /
    (2.0 * Math.PI * Math.Pow(weight, 2));

    for (int filterY = -kernelRadius;
            filterY <= kernelRadius; filterY++)
    {
        for (int filterX = -kernelRadius; filterX <= kernelRadius; filterX++)
        {
            double distance = ((filterX * filterX) + (filterY * filterY)) / (2 * (weight * weight));

            kernel[filterY + kernelRadius,
                    filterX + kernelRadius] =
                    calculatedEuler * Math.Exp(-distance);

            sumTotal += kernel[filterY + kernelRadius,
                                filterX + kernelRadius];
        }
    }

    for (int y = 0; y < length; y++)
    {
        for (int x = 0; x < length; x++)
        {
            kernel[y, x] = kernel[y, x] *
                            (1.0 / sumTotal);
        }
    }

    return kernel;
}
Était-ce utile?

La solution

You have a black border, because you're not rendering any pixels there.

int filterOffsetWidth = (filterWidth - 1) / 2;
int filterOffsetHeight = (filterHeight - 1) / 2;

for (int offsetY = filterOffsetHeight; 
     offsetY < image.Height - filterOffsetHeight; 
     offsetY++)
{
    for (int offsetX = filterOffsetWidth; 
         offsetX < image.Width - filterOffsetWidth; 
         offsetX++)

If you want the black border to be removed, you'll have to calculate some values for offsetY < filterOffsetHeightand so on as well.

UPDATE:

So, for a matrix size of 15, filterOffsetHeight will be 7. Your outer loop starts at offsetY = 7 and you never calculate any values for row 0 to 6. The pixels in that area will have their default values of 0 for red, green and blue, which shows up as a black border at the top of the image. The same thing happens at the other borders.

Obviously, you cannot just run your calculation in those borders. So you have two options: either crop your image, or use a different algorithm to calculate the borders. If you want to take the second option, the easiest is to assume that pixels outside the original image have the same color as the nearest pixel on the image border.

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