說到馬賽克,一些悶騷男都懂的,“有馬”、“無馬”,此馬即馬賽克簡稱,可別光往邪惡的一面想,馬賽克也有和諧的一面,比如一些新聞之類的將關鍵不想展示給觀眾的給和諧掉,此即馬賽克應用。
先看一張具有極低解析度的馬賽克圖案:
看到一塊塊的同顏色塊、或許你就能猜出其演算法了。
馬賽克演算法很簡單,說白了就是把一張圖片分割成若干個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的處理效果: