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

时间:2013-12-26 16:22:01

标签: c# gdi+ gaussian

我正在尝试根据http://softwarebydefault.com/2013/06/08/calculating-gaussian-kernels/上的文章和示例为我的图书馆实施高斯模糊方法

出于某种原因,我不断在生成的图像周围出现黑色边框,侵入原始图像,如下所示。

blurred image with black border

边界厚度随着高斯核长度的增加而增加。该图像是用15x15的矩阵生成的。

任何人都可以了解可能发生的事情吗?

我的代码;道歉:

处理方法。

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

高斯计算器。

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

1 个答案:

答案 0 :(得分:1)

你有一个黑色边框,因为你没有在那里渲染任何像素。

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++)

如果要删除黑色边框,则必须计算offsetY < filterOffsetHeight的某些值,依此类推。

更新:

因此,对于矩阵大小为15,filterOffsetHeight将为7.您的外循环从offsetY = 7开始,您永远不会计算第0行到第6行的任何值。该区域中的像素将具有它们的默认值为0,分别为红色,绿色和蓝色,在图像顶部显示为黑色边框。同样的事情发生在其他边界。

显然,您不能只在这些边界中运行计算。因此,您有两种选择:裁剪图像,或使用其他算法计算边框。如果您想采用第二个选项,最简单的方法是假设原始图像外的像素与图像边框上最近的像素具有相同的颜色。