本系列专题前两篇分别探讨了常见的几种图形处理算法和性能已经图形的灰度处理、逆反处理和二值化处理,本文介绍雾化处理。
先上图,先不谈算法,其实很多东西来源于现实的。
设想你有块透明的玻璃,朝上面哈几口气,然后将玻璃放到一张图片上,看看有啥效果?应该和上图差不多的雾化效果吧。下面来分析原理从而推导出算法。
玻璃上面哈气后,有很多小水滴小水汽之类的,形状不规则,因此发生光折射,由于水滴的不规则性,发生折射也是不规则的,也就是折射光的折射角不确定,但是肯定有个范围,假设有像素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);
}