说到马赛克,一些闷骚男都懂的,“有马”、“无马”,此马即马赛克简称,可别光往邪恶的一面想,马赛克也有和谐的一面,比如一些新闻之类的将关键不想展示给观众的给和谐掉,此即马赛克应用。
先看一张具有极低解析度的马赛克图案:
看到一块块的同颜色块、或许你就能猜出其算法了。
马赛克算法很简单,说白了就是把一张图片分割成若干个val * val像素的小区块(可能在边缘有零星的小块,但不影响整体算法,val越大,马赛克效果越明显),每个小区块的颜色都是相同的。为了方便起见,我们不妨让这个颜色就用该区域最左上角的那个点的颜色。当然还可以有其他方法,比如取区块中间点的颜色,或区块中随机点的颜色作代表等等。
下面的示意图就是取val=2的结果。
原图像素
ABCDEFG
HIJKLMN
OPQRSTU
VWXYZ01
2345678
马赛克处理后
AACCEEG
AACCEEG
OOQQSSU
OOQQSSU
2244668
原理就是那么简单。具体实现就看各人的思维习惯了。我的想法是:
当y(当前高度)是val的整数倍时:
扫描当前行中的每一点x,如果x也是val的整数倍,记录下当前x,y的颜色值;如果x不是val的整数倍,则沿用最近一次被记录的颜色值。
当y不是val的整数倍:
很简单,直接复制上一行。
因此,区块越大,处理效果越明显;也可得出,源图片(R)对处理后的图片(S)是多对一映射,也就是说:马赛克处理后的图片是不可逆的,不要试图用可逆算法复原。
/// <summary>
/// 马赛克
/// 是把一张图片分割成若干个N * N像素的小区块(可能在边缘有零星的小块,但不影响整体算法)
/// ,每个小区块的颜色都是相同的。
/// </summary>
public class MosaicImage:IImageProcessable
{
#region IImageProcessable 成员
public void ProcessBitmap(Bitmap bmp)
{
int width = bmp.Width;
int height = bmp.Height;
const int N = 5;//效果粒度,值越大码越严重
int r = 0, g = 0, b = 0;
Color c;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (y % N == 0)
{
if (x % N == 0)//整数倍时,取像素赋值
{
c = bmp.GetPixel(x, y);
r = c.R;
g = c.G;
b = c.B;
}
else
{
bmp.SetPixel(x, y, Color.FromArgb(r, g, b));
}
}
else //复制上一行
{
Color colorPreLine = bmp.GetPixel(x, y - 1);
bmp.SetPixel(x, y, colorPreLine);
}
}
}
}
public unsafe void UnsafeProcessBitmap(Bitmap bmp)
{
int width = bmp.Width;
int height = bmp.Height;
const int N = 5;//效果粒度,值越大码越严重
int r = 0, g = 0, b = 0;
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte* ptr = (byte*)(bmpData.Scan0);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (y % N == 0)
{
if (x % N == 0)
{
r = ptr[2];
g = ptr[1];
b = ptr[0];
}
else
{
ptr[2] = (byte)r;
ptr[1] = (byte)g;
ptr[0] = (byte)b;
}
}
else //复制上一行
{
ptr[0] = ptr[0 - bmpData.Stride];//b;
ptr[1] = ptr[1 - bmpData.Stride];//g;
ptr[2] = ptr[2 - bmpData.Stride];//r
}
ptr += 4;
}
ptr += bmpData.Stride - width * 4;
}
bmp.UnlockBits(bmpData);
}
#endregion
}
下面分别是区块大小为5和9的处理效果: