本文介绍了C#卷积滤波器适用于任何尺寸矩阵(1x1,3x3,5x5,...)未完全应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的项目制作卷积滤镜,我设法为任何大小的矩阵制作它,但随着它变大,我注意到并非所有的位都被改变了。
以下是显示问题的图片:

I'm making a convolution filter for my project and I managed to make it for any size of matrix but as it gets bigger I noticed that not all bits are changed.Here are the pictures showing the problem:

第一个是原始的

过滤:模糊9x9

First one is the originalFilter: Blur 9x9

过滤器:EdgeDetection 9x9:

Filter: EdgeDetection 9x9:

正如你所看到的,有一条小条纹永远不会改变,随着矩阵变大,条纹也会变大(3x3不可见)

As you can see, there is a little stripe that is never changed and as the matrix gets bigger, the stripe also gets bigger (in 3x3 it wasn't visible)

我的卷积矩阵类:

    public class ConvMatrix
{
    public int Factor = 1;
    public int Height, Width;
    public int Offset = 0;
    public int[,] Arr;

    //later I assign functions to set these variables
    ...
}

过滤函数:

    Bitmap Conv3x3(Bitmap b, ConvMatrix m)
{
    if (0 == m.Factor)
        return b;

    Bitmap bSrc = (Bitmap)b.Clone();
    BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
                        ImageLockMode.ReadWrite,
                        PixelFormat.Format24bppRgb);
    BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height),
                       ImageLockMode.ReadWrite,
                       PixelFormat.Format24bppRgb);
    int stride = bmData.Stride;

    System.IntPtr Scan0 = bmData.Scan0;
    System.IntPtr SrcScan0 = bmSrc.Scan0;

        unsafe
        {
            byte* p = (byte*)(void*)Scan0;
            byte* pSrc = (byte*)(void*)SrcScan0;
            int nOffset = stride - b.Width * m.Width;
            int nWidth = b.Width - (m.Size-1);
            int nHeight = b.Height - (m.Size-2);

            int nPixel = 0;

            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    for (int r = 0; r < m.Height; r++)
                    {
                        nPixel = 0;
                        for (int i = 0; i < m.Width; i++)
                            for (int j = 0; j < m.Height; j++)
                            {
                                nPixel += (pSrc[(m.Width * (i + 1)) - 1 - r + stride * j] * m.Arr[j, i]);
                            }

                        nPixel /= m.Factor;
                        nPixel += m.Offset;

                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        p[(m.Width * (m.Height / 2 + 1)) - 1 - r + stride * (m.Height / 2)] = (byte)nPixel;
                    }
                    p += m.Width;
                    pSrc += m.Width;
                }
                p += nOffset;
                pSrc += nOffset;
            }
        }

    b.UnlockBits(bmData);
    bSrc.UnlockBits(bmSrc);
    return b;
}

请帮助

推荐答案

问题是你的代码显然没有边缘。外部循环限制的计算( nWidth nHeight )不应该涉及矩阵的大小,它们应该等于位图的大小。

The problem is that your code explicitly stops short of the edges. The calculation for the limits for your outer loops (nWidth and nHeight) shouldn't involve the size of the matrix, they should be equal to the size of your bitmap.

当你这样做时,如果你想象当你将矩阵的中心点放在每个像素上时会发生什么case(因为你需要读取像素的所有大小),矩阵将部分地位于边缘附近的图像之外。

When you do this, if you imagine what happens when you lay the center point of your matrix over each pixel in this case (because you need to read from all sizes of the pixel) the matrix will partially be outside of the image near the edges.

关于什么,有几种方法在边缘附近做,但合理的是将坐标夹到边缘。即当你最终从位图外部读取一个像素时,只需从边缘(大小或角落)获取最近的像素。

There are a few approaches as to what to do near the edges, but a reasonable one is to clamp the coordinates to the edges. I.e. when you would end up reading a pixel from outside the bitmap, just get the nearest pixel from the edge (size or corner).

我也不明白为什么你需要五个循环 - 你似乎循环遍历矩阵的高度两次。这看起来不对。总而言之,一般结构应该是这样的:

I also don't understand why you need five loops - you seem to be looping through the height of the matrix twice. That doesn't look right. All in all the general structure should be something like this:

for (int y = 0; y < bitmap.Height; y++) {
  for (int x = 0; x < bitmap.Width; x++) {
    int sum = 0;

    for (int matrixY = -matrix.Height/2; matrixY < matrix.Height/2; matrixY++)
      for (int matrixX = -matrix.Width/2; matrixX < matrix.Width/2; matrixX++) {
        // these coordinates will be outside the bitmap near all edges
        int sourceX = x + matrixX;
        int sourceY = y + matrixY;

        if (sourceX < 0)
          sourceX = 0;

        if (sourceX >= bitmap.Width)
          sourceX = bitmap.Width - 1;

        if (sourceY < 0)
          sourceY = 0;

        if (sourceY >= bitmap.Height)
          sourceY = bitmap.Height - 1;

        sum += source[x, y];
      }
    }

    // factor and clamp sum

    destination[x, y] = sum;
  }
}

您可能需要一个额外的循环来处理每个颜色通道需要单独处理。我无法立即看到你的代码中你可能会从所有神秘的变量中做到这一点。

You might need an extra loop to handle each color channel which need to be processed separately. I couldn't immediately see where in your code you might be doing that from all the cryptic variables.

这篇关于C#卷积滤波器适用于任何尺寸矩阵(1x1,3x3,5x5,...)未完全应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 15:28