很多圖片處理的算法從原理上講其實(shí)非常簡(jiǎn)單,難點(diǎn)往往在如何去寫算法實(shí)現(xiàn)它,更加難的就是如何去優(yōu)化實(shí)現(xiàn)的算法。雖說我一向認(rèn)為程序員的效率比程序的效率更重要,但為了等處理一張自己拍攝的數(shù)碼照片,溜出去買杯奶茶順便再買張彩票回來發(fā)現(xiàn)還沒算好,無(wú)論如何都是不能忍受的。
馬賽克算法很簡(jiǎn)單,說白了就是把一張圖片分割成若干個(gè)val * val像素的小區(qū)塊(可能在邊緣有零星的小塊,但不影響整體算法),每個(gè)小區(qū)塊的顏色都是相同的。為了方便起見,我們不妨讓這個(gè)顏色就用該區(qū)域最左上角的那個(gè)點(diǎn)的顏色。當(dāng)然還可以有其他方法,比如取區(qū)塊中間點(diǎn)的顏色,或區(qū)塊中隨機(jī)點(diǎn)的顏色作代表等等。
下面的示意圖就是取val=2的結(jié)果。
原圖像素
ABCDEFG
HIJKLMN
OPQRSTU
VWXYZ01
2345678
馬賽克處理后
AACCEEG
AACCEEG
OOQQSSU
OOQQSSU
2244668
原理就是那么簡(jiǎn)單。具體實(shí)現(xiàn)就看各人的思維習(xí)慣了。我的想法是:
當(dāng)y(當(dāng)前高度)是val的整數(shù)倍時(shí):
掃描當(dāng)前行中的每一點(diǎn)x,如果x也是val的整數(shù)倍,記錄下當(dāng)前x,y的顏色值;如果x不是val的整數(shù)倍,則沿用最近一次被記錄的顏色值。
當(dāng)y不是val的整數(shù)倍:
很簡(jiǎn)單,直接復(fù)制上一行。
簡(jiǎn)單的說就是以線帶面,最終實(shí)現(xiàn)讓大家都看不清楚
下面就是源代碼。寫算法不是我的強(qiáng)項(xiàng),不過偶爾勉為其難的寫個(gè)可以跑跑的不求甚解版還是可以做到的,不指望可以幫到你,只希望沒有誤導(dǎo)你。
public static Bitmap KiMosaic(Bitmap b, int val)
{
if (b.Equals(null))
{
return null;
}
int w = b.Width;
int h = b.Height;
int stdR, stdG, stdB;
stdR = 0;
stdG = 0;
stdB = 0;
BitmapData srcData = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* p = (byte*)srcData.Scan0.ToPointer();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (y % val == 0)
{
if (x % val == 0)
{
stdR = p[2]; stdG = p[1]; stdB = p[0];
}
else
{
p[0] = (byte)stdB;
p[1] = (byte)stdG;
p[2] = (byte)stdR;
}
}
else
{
// 復(fù)制上一行
byte * pTemp = p - srcData.Stride;
p[0] = (byte)pTemp[0];
p[1] = (byte)pTemp[1];
p[2] = (byte)pTemp[2];
}
p += 3;
} // end of x
p += srcData.Stride - w * 3;
} // end of y
b.UnlockBits(srcData);
}
return b;
}
馬賽克算法很簡(jiǎn)單,說白了就是把一張圖片分割成若干個(gè)val * val像素的小區(qū)塊(可能在邊緣有零星的小塊,但不影響整體算法),每個(gè)小區(qū)塊的顏色都是相同的。為了方便起見,我們不妨讓這個(gè)顏色就用該區(qū)域最左上角的那個(gè)點(diǎn)的顏色。當(dāng)然還可以有其他方法,比如取區(qū)塊中間點(diǎn)的顏色,或區(qū)塊中隨機(jī)點(diǎn)的顏色作代表等等。
下面的示意圖就是取val=2的結(jié)果。
原圖像素
ABCDEFG
HIJKLMN
OPQRSTU
VWXYZ01
2345678
馬賽克處理后
AACCEEG
AACCEEG
OOQQSSU
OOQQSSU
2244668
原理就是那么簡(jiǎn)單。具體實(shí)現(xiàn)就看各人的思維習(xí)慣了。我的想法是:
當(dāng)y(當(dāng)前高度)是val的整數(shù)倍時(shí):
掃描當(dāng)前行中的每一點(diǎn)x,如果x也是val的整數(shù)倍,記錄下當(dāng)前x,y的顏色值;如果x不是val的整數(shù)倍,則沿用最近一次被記錄的顏色值。
當(dāng)y不是val的整數(shù)倍:
很簡(jiǎn)單,直接復(fù)制上一行。
簡(jiǎn)單的說就是以線帶面,最終實(shí)現(xiàn)讓大家都看不清楚
下面就是源代碼。寫算法不是我的強(qiáng)項(xiàng),不過偶爾勉為其難的寫個(gè)可以跑跑的不求甚解版還是可以做到的,不指望可以幫到你,只希望沒有誤導(dǎo)你。
public static Bitmap KiMosaic(Bitmap b, int val)
{
if (b.Equals(null))
{
return null;
}
int w = b.Width;
int h = b.Height;
int stdR, stdG, stdB;
stdR = 0;
stdG = 0;
stdB = 0;
BitmapData srcData = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* p = (byte*)srcData.Scan0.ToPointer();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (y % val == 0)
{
if (x % val == 0)
{
stdR = p[2]; stdG = p[1]; stdB = p[0];
}
else
{
p[0] = (byte)stdB;
p[1] = (byte)stdG;
p[2] = (byte)stdR;
}
}
else
{
// 復(fù)制上一行
byte * pTemp = p - srcData.Stride;
p[0] = (byte)pTemp[0];
p[1] = (byte)pTemp[1];
p[2] = (byte)pTemp[2];
}
p += 3;
} // end of x
p += srcData.Stride - w * 3;
} // end of y
b.UnlockBits(srcData);
}
return b;
}

