上一篇,我已经实现了简单的图片验证码。但是该图片验证码看上去比较简单没有什么特效,不像其他网站上的验证码具有扭曲,和图片背景有噪音点的功能,这次我就准备实现这个功能。
一、图片背景噪音点的实现
实现思路:就是在内存图片上通过setPixel方法,设置指定像素点的颜色。
//字符串数组,存放颜色
private static string[] BrushName = new string[]{"OliveDrab","ForestGreen", "DarkCyan", "LightSlateGray", "RoyalBlue",
"SlateBlue", "DarkViolet","MediumVioletRed","IndianRed","Firebrick",
"Chocolate", "Peru","Goldenrod", "Teal", "DarkGreen",
"MediumBlue", "Black" };
"MediumBlue", "Black" };
/// <summary>
/// 绘制图片噪音点
/// </summary>
/// <param name="image">图片对象</param>
public void stainImage(Bitmap image)
{
Random rnd = new Random();
/// 绘制图片噪音点
/// </summary>
/// <param name="image">图片对象</param>
public void stainImage(Bitmap image)
{
Random rnd = new Random();
//随机选择颜色
int index = rnd.Next(BrushName.Length);
//在图形上画20个点数字可以进行修改
for (int n = 0; n < 20; n++)
{
int x = rnd.Next(image.Width-2);
int y = rnd.Next(image.Height-2);
image.SetPixel(x, y, Color.FromName(BrushName[index]));
}
}
int index = rnd.Next(BrushName.Length);
//在图形上画20个点数字可以进行修改
for (int n = 0; n < 20; n++)
{
int x = rnd.Next(image.Width-2);
int y = rnd.Next(image.Height-2);
image.SetPixel(x, y, Color.FromName(BrushName[index]));
}
}
二、扭曲图片实现
实现思路:按列循环整张图片,获取每个像素点的颜色,并通过正弦曲线获取另一个像素点的位置,用原始像素点的颜色替换正弦像素点的颜色,就可以得到一张扭曲的图片。余弦以此类推。
/// <summary>
/// 正弦曲线Wave扭曲图片
/// </summary>
/// <param name="srcBmp"></param>
/// <param name="bXDir">true左右波动,false上下波动</param>
/// <param name="nMultValue">波形的幅度倍数</param>
/// <returns></returns>
public System.Drawing.Bitmap twistImage(Bitmap srcBmp, bool bXDir, double dMultValue)
{
Random rnd = new Random();
double dPhase = rnd.Next(0, 6);
System.Drawing.Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
/// 正弦曲线Wave扭曲图片
/// </summary>
/// <param name="srcBmp"></param>
/// <param name="bXDir">true左右波动,false上下波动</param>
/// <param name="nMultValue">波形的幅度倍数</param>
/// <returns></returns>
public System.Drawing.Bitmap twistImage(Bitmap srcBmp, bool bXDir, double dMultValue)
{
Random rnd = new Random();
double dPhase = rnd.Next(0, 6);
System.Drawing.Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
// 将位图背景填充为白色
System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destBmp);
graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destBmp.Width, destBmp.Height);
graph.Dispose();
System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destBmp);
graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destBmp.Width, destBmp.Height);
graph.Dispose();
double dBaseAxisLen = bXDir ? (double)destBmp.Height : (double)destBmp.Width;
for (int i = 0; i < destBmp.Width; i++)
{
for (int j = 0; j < destBmp.Height; j++)
{
double dx = 0;
dx = bXDir ? (Math.PI * 2 * (double)j) / dBaseAxisLen : (Math.PI * 2 * (double)i) / dBaseAxisLen;
dx += dPhase;
double dy = Math.Sin(dx);
{
for (int j = 0; j < destBmp.Height; j++)
{
double dx = 0;
dx = bXDir ? (Math.PI * 2 * (double)j) / dBaseAxisLen : (Math.PI * 2 * (double)i) / dBaseAxisLen;
dx += dPhase;
double dy = Math.Sin(dx);
// 取得当前点的颜色
int nOldX = 0, nOldY = 0;
nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
nOldY = bXDir ? j : j + (int)(dy * dMultValue);
int nOldX = 0, nOldY = 0;
nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
nOldY = bXDir ? j : j + (int)(dy * dMultValue);
System.Drawing.Color color = srcBmp.GetPixel(i, j);
if (nOldX >= 0 && nOldX < destBmp.Width
&& nOldY >= 0 && nOldY < destBmp.Height)
{
destBmp.SetPixel(nOldX, nOldY, color);
}
}
}
return destBmp;
}
if (nOldX >= 0 && nOldX < destBmp.Width
&& nOldY >= 0 && nOldY < destBmp.Height)
{
destBmp.SetPixel(nOldX, nOldY, color);
}
}
}
return destBmp;
}
总结:在图片处理时,我们大量使用了setpixel和getpixel方法,这仅仅适用于处理小图片,因为这2个方法处理十分缓慢,如果是大图片的话,推荐使用unsafe代码使用指针来进行操作。
LockBits();在内存中锁定
Scan0//获取首地址指针
UnlockBits();在内存中解锁