我正在为我的项目制作一个卷积过滤器,我设法为任何大小的矩阵制作它,但随着它变大,我注意到并不是所有的位都改变了。 以下是显示问题的图片:
如您所见,有一个小条纹从未改变,随着矩阵变大,条纹也变大(在 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
...
}
过滤函数:
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
)的限制计算不应该涉及矩阵的大小,它们应该等于位图的大小。
执行此操作时,如果您想象一下在这种情况下将矩阵的中心点放在每个像素上时会发生什么(因为您需要从所有大小的像素中读取)矩阵将部分位于图像之外靠近边缘。
关于在边缘附近做什么有几种方法,但一种合理的方法是将坐标固定在边缘。 IE。当您最终从位图外部读取像素时,只需获取距离边缘(大小或角)最近的像素。
我也不明白为什么你需要五个循环——你似乎在矩阵的高度上循环了两次。那看起来不对。总而言之,一般结构应该是这样的:
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;
}
}
您可能需要一个额外的循环来处理需要单独处理的每个颜色 channel 。我无法立即从所有神秘变量中看出您可能在代码中的哪个位置执行此操作。
关于未完全应用任何大小矩阵(1x1、3x3、5x5,...)的 C# 卷积过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35855950/