C# 任何大小矩阵(1x1、3x3、5x5...)的卷积滤波器未完全应用

Ale*_*pka 3 c# image-processing convolution

我正在为我的项目制作一个卷积滤波器,并且我设法将其制作为任何大小的矩阵,但随着它变得更大,我注意到并非所有位都发生了变化。以下是显示问题的图片:

第一张是原图 在此输入图像描述 滤镜:模糊 9x9

过滤器:边缘检测 9x9: 在此输入图像描述

正如您所看到的,有一个小条纹从未改变,并且随着矩阵变大,条纹也会变大(在 3x3 中它不可见)

我的卷积矩阵类:

    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
    ...
}
Run Code Online (Sandbox Code Playgroud)

过滤功能:

    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;
}
Run Code Online (Sandbox Code Playgroud)

请帮忙

Mat*_*nen 5

问题是您的代码明确地停止在边缘附近。nWidth外循环(和)的限制计算nHeight不应涉及矩阵的大小,它们应等于位图的大小。

当您执行此操作时,如果您想象在这种情况下将矩阵的中心点放置在每个像素上时会发生什么(因为您需要从所有大小的像素读取),则矩阵将部分位于边缘附近的图像外部。

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

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

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[sourceX, sourceY];
      }
    }

    // factor and clamp sum
    
    destination[x, y] = sum;
  }
}
Run Code Online (Sandbox Code Playgroud)

您可能需要一个额外的循环来处理需要单独处理的每个颜色通道。我无法立即从所有神秘变量中看出您可能在代码中的何处执行此操作。