卷积后图像保持不变

时间:2013-12-28 19:05:44

标签: c# image-processing gdi+ gaussian convolution

我正在尝试根据AForge框架中的来源执行高斯模糊计算。目前虽然我的计算有问题,因为我在处理过程中得到了相同的像素数据。(我想用计算分频器的事情)

该过程分为两部分:

  1. 根据使用设置大小创建的内核创建高斯过滤器。

  2. 针对像素数组(rgba结构)处理滤镜,以返回转换后的像素,这些像素稍后会转换为位图。

  3. 我创建原始内核并转换为基于整数的内核的方法已针对其他程序进行了测试,并且在实现方面是正确的。

    非常感谢任何帮助。我在过去的12个小时里一直在研究这个问题。

    创建过滤器的方法

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

    执行卷积的方法:

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

2 个答案:

答案 0 :(得分:1)

问题在于选择正确的像素乘以内核值。我没有选择适当的偏移,而是选择了相同的像素。

修正后的方法如下。

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

答案 1 :(得分:0)

找出问题原因的唯一快捷方法是to set breakpoints并跟踪值的变化。这有助于您有效地捕获错误代码。您可能会忘记某些计算或方法可能会返回未触及的副本而不是修改后的结果,或者修改后的计算可能会切割精度,无论如何,这不是一种委托问题对其他人。