本系列專題前兩篇分別探討了常見的幾種圖形處理算法和性能已經圖形的灰度處理、逆反處理和二值化處理,本文介紹霧化處理。
先上圖,先不談算法,其實很多東西來源於現實的。
設想你有塊透明的玻璃,朝上面哈幾口氣,然后將玻璃放到一張圖片上,看看有啥效果?應該和上圖差不多的霧化效果吧。下面來分析原理從而推導出算法。
玻璃上面哈氣后,有很多小水滴小水汽之類的,形狀不規則,因此發生光折射,由於水滴的不規則性,發生折射也是不規則的,也就是折射光的折射角不確定,但是肯定有個范圍,假設有像素A(i,j),(i和j分別表示橫坐標和縱坐標),折射后一定幾率在A(i+d,j+d)處,(-k<d<k),該點在原來的點為圓心的圓內。
從而得出霧化處理效果算法原理:
對每個像素A(i,j)進行處理,用其周圍一定范圍內隨機點A(i+d,j+d),(-k<d<k)的像素替代。顯然,以該點為圓心的圓半徑越大,則霧化效果越明顯。
/// <summary>
///霧化效果
/// 圖像的霧化處理不是基於圖像中像素點之間的計算,而是給圖像像素的顏色值引入一定的隨機值,
/// 使圖像具有毛玻璃帶水霧般的效果..
/// </summary>
public class FogImage:IImageProcessable
{
#region IImageProcessable 成員
public unsafe void ProcessBitmap(System.Drawing.Bitmap bmp)
{
int width = bmp.Width;
int height = bmp.Height;
Random rnd = new Random();
for (int x = 0; x < width - 1; x++)
{
for (int y = 0; y < height - 1; y++)
{
int k = rnd.Next(-12345,12345);
//像素塊大小
int dx = x + k %7;
int dy = y + k %7;
//處理溢出
if (dx >= width)
dx = width - 1;
if (dy >= height)
dy = height - 1;
if (dx < 0)
dx = 0;
if (dy < 0)
dy = 0;
Color c1 = bmp.GetPixel(dx, dy);
bmp.SetPixel(x, y, c1);
}
}
}
#endregion
#region IImageProcessable 成員
public unsafe void UnsafeProcessBitmap(Bitmap bmp)
{
UnsafeProcessBitmap(bmp, 7);
}
#endregion
public unsafe static void UnsafeProcessBitmap(Bitmap bmp,int N)
{
int width = bmp.Width;
int height = bmp.Height;
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte* ptr = (byte*)(bmpData.Scan0);
Random rnd = new Random();
for (int i = 0; i < height - 1; i++)
{
for (int j = 0; j < width - 1; j++)
{
int k = rnd.Next(-12345, 12345);
//像素塊大小 常量N的大小決定霧化模糊度
int dj = j + k % N;//水平向右方向像素偏移后
int di = i + k % N;//垂直向下方向像素偏移后
if (dj >= width) dj = width - 1;
if (di >= height) di = height - 1;
if (di < 0)
di = 0;
if (dj < 0)
dj = 0;
//針對Format32bppArgb格式像素,指針偏移量為4的倍數 4*dj 4*di
//g(i,j)=f(di,dj)
ptr[bmpData.Stride * i + j * 4 + 0] = ptr[bmpData.Stride * di + dj * 4 + 0];//B
ptr[bmpData.Stride * i + j * 4 + 1] = ptr[bmpData.Stride * di + dj * 4 + 1];//G
ptr[bmpData.Stride * i + j * 4 + 2] = ptr[bmpData.Stride * di + dj * 4 + 2];//R
// ptr += 4; 注意此處指針沒移動,始終以bmpData.Scan0開始
}
// ptr += bmpData.Stride - width * 4;
}
bmp.UnlockBits(bmpData);
}