简体   繁体   English

从高斯模糊图像中去除黑色边框

[英]Removing black border from Gaussian blurred image

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/ 我正在尝试基于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. 由于某种原因,我在生成的图像周围不断出现黑色边框,这会侵蚀原始图像,如下所示。

带有黑色边框的模糊的图像

The border thickness increases as the Gaussian kernel length increases. 边界厚度随着高斯核长度的增加而增加。 This image was generated with a matrix of 15x15. 该图像是使用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;
}

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 < filterOffsetHeight and so on as well. 如果要删除黑色边框,则必须为offsetY < filterOffsetHeight等计算一些值。

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. 因此,对于15的矩阵大小, filterOffsetHeight将为7。您的外部循环从offsetY = 7开始,并且您从不为第0至6行计算任何值。该区域中的像素的红色,绿色的默认值为0和蓝色,在图像顶部显示为黑色边框。 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. 如果要选择第二个选项,最简单的方法是假定原始图像外部的像素与图像边界上最近的像素具有相同的颜色。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM