在图像处理领域,经常会遇到需要改变图像外观的需求。通常,这些改变是通过修改像素的颜色值来实现的。然而,今天要探讨的是一种不同的方法——通过改变每个像素的位置来改变图像。这种方法被称为位移滤波器。
位移滤波器的实现需要一个框架,这个框架允许创建各种滤波器。基本方法是创建一个二维点数组,数组的大小与图像相同,每个点存储该索引处像素的新位置。将以两种方式实现这一点:一种是存储相对位置,另一种是存储绝对位置。最后,将创建自己的点结构,它包含两个双精度浮点数而不是整数,将使用它来实现执行双线性滤波的实现。
在C#中,可以使用二维数组来实现这一功能。代码如下:
Point[,] pt = new Point[nWidth, nHeight];
这将动态创建一个二维数组,可以使用类似于pt[2, 3]
的语法来访问像素,而不是C++中的pt[2][3]
。这不仅比C++更整洁,而且Point[,]
是一个有效的参数,可以在编译时未知大小的数组中传递。
位移滤波器的第一个辅助函数将接受一个相对位置。例如,如果想要将像素2,4移动到位置5,2,那么pt[2, 4]
将等于3,-2。可以使用Set/GetPixel
来实现这一点,但将继续使用直接访问,这可能更快。
public static bool OffsetFilter(Bitmap b, Point[,] offset)
{
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 scanline = 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 = bmData.Stride - b.Width * 3;
int nWidth = b.Width;
int nHeight = b.Height;
int xOffset, yOffset;
for (int y = 0; y < nHeight; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
xOffset = offset[x, y].X;
yOffset = offset[x, y].Y;
p[0] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3)];
p[1] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3) + 1];
p[2] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3) + 2];
p += 3;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
bSrc.UnlockBits(bmSrc);
return true;
}
会注意到,框架中有一个布尔成功代码,但实际上并没有真正使用。
位移滤波器的基本格式是创建一个数组,用值(位移或绝对)填充它,然后将位图和数组传递给适当的函数。这些滤波器中的许多都涉及到大量的三角学,不会详细讨论,而是专注于滤波器的作用和它的参数。
如果想要移动像素,最明显的事情就是翻转图像。将展示这个例子的代码,因为它是一个简单的例子,将更多地突出底层过程,而不是后来的例子,如漩涡。最终结果很明显,所以不会用示例来占用带宽。
这个滤波器接受一个数字,并在该数字的范围内随机移动每个像素。这出奇地有效,多次这样做最终会产生相当有效的油画效果。
这个滤波器是个人圣杯,也是想出这些东西的动力。基本上,它从中间开始,以圆圈的形式移动,随着旋转程度的增加,半径也在增加。由于使用了三角学,它从双线性滤波器中受益匪浅,这是一个选项。将展示正常和双线性过滤的例子,然后所有其他提供滤波器的例子,将展示滤波器打开的情况。传入的参数是一个非常小的数字,例如0.05。
球体滤波器是通过玩耍创建的一个例子。试图实现图像被包裹在球体上的效果。认为这并不十分有效,但它很有趣,是这样一个想法的起点。
另一个有趣的滤波器,这个滤波器使图像在远处消失时发生扭曲。示例使用了15的因子。
在玩漩涡的想法时,发现如果增加半径向外移动的速率,可以要么得到一个宽漩涡,要么在正确的参数下,产生莫尔效应。示例使用了3的因子。
一个更有用的滤波器是使事物看起来像在水下。这可以通过添加额外的伪影,如环和涟漪来改进。实际上,这个滤波器在x和y方向上通过水传递正弦波。
这是一个可以通用完成但最好用特定代码完成的滤波器的例子。像素化是指当图像放大时,曲线变得块状。这个滤波器通过创建与其左上角颜色相同的块来提供马赛克效果,并且还可以绘制线条来标记个别瓷砖。一个更好的实现将使用块内存在的平均颜色,而不是左上角,但这仍然相当有效。