[C#] 第一次自己作 Captcha(驗證碼) 就上手(2)
2013-03-11
上一篇 ( 第一次自己作 Captcha(驗證碼) 就上手 ) 寫到將字合成到有雜點圖片上面製作自己的Captcha..
甚麼還是會被破解?!
這時候我們試圖再增加難度讓機器人更難去判斷
首先參考這篇文章 濾鏡筆記-漣漪效果 我們來透過液態化效果將文字再合成的更加凌亂..
並且這篇文章,我將字體大小的設置拆成parameter 傳入,不然中文字的算法跟英文是不太一樣的..
/// <summary>
/// 製作Captcha
/// </summary>
/// <param name="text">文字</param>
/// <param name="fontName">字體名稱</param>
/// <param name="backgroundImagePath">採樣的背景圖</param>
/// <param name="width">結果的圖片寬度</param>
/// <param name="height">結果的圖片高度</param>
/// <param name="fontSize"> 字體大小</param>
/// <param name="waterEffect">增加漣漪複雜度 </param>
/// <returns></returns>
public Bitmap GetCaptcha(string text, string fontName, string backgroundImagePath, int width, int height, int fontSize, short waterEffect = 10)
{
var backImage = new Bitmap(backgroundImagePath);
//如果原圖過小 就以原圖大小為主
if (backImage.Width < width || backImage.Height < height)
{
throw new Exception("結果尺寸不得小於採樣大小的圖片");
}
//隨機取一個位置 並且按造欲需求的大小裁切
//請注意隨機採樣範圍 記得為 圖片寬度-欲取的寬度 高度也是 ,不然會發生裁切超過範圍
backImage =
backImage.Clone(
new Rectangle(new Random().Next(0, backImage.Width - width),
new Random().Next(0, backImage.Height - height), width, height), backImage.PixelFormat);
Graphics graphics = Graphics.FromImage(backImage);
//合成的文字為黑色
Brush br = new SolidBrush(Color.Black);
//將字體畫上圖片
//字體的大小 為 自訂
graphics.DrawString(text, new Font(new FontFamily(fontName), fontSize), br, new PointF(0, 0));
//增加複雜度
backImage = AdjustRippleEffect(backImage, waterEffect);
return backImage;
}
/// <summary>
/// 水波紋效果
/// </summary>
/// <param name="src"></param>
/// <param name="nWave">坡度</param>
/// <returns></returns>
public Bitmap AdjustRippleEffect(Bitmap src, short nWave)
{
int nWidth = src.Width;
int nHeight = src.Height;
// 透過公式進行水波紋的採樣
FloatPoint[,] fp = new FloatPoint[nWidth, nHeight];
Point[,] pt = new Point[nWidth, nHeight];
Point mid = new Point();
mid.X = nWidth / 2;
mid.Y = nHeight / 2;
double newX, newY;
double xo, yo;
//先取樣將水波紋座標跟RGB取出
for (int x = 0; x < nWidth; ++x)
for (int y = 0; y < nHeight; ++y)
{
xo = ((double)nWave * Math.Sin(2.0 * 3.1415 * (float)y / 128.0));
yo = ((double)nWave * Math.Cos(2.0 * 3.1415 * (float)x / 128.0));
newX = (x + xo);
newY = (y + yo);
if (newX > 0 && newX < nWidth)
{
fp[x, y].X = newX;
pt[x, y].X = (int)newX;
}
else
{
fp[x, y].X = 0.0;
pt[x, y].X = 0;
}
if (newY > 0 && newY < nHeight)
{
fp[x, y].Y = newY;
pt[x, y].Y = (int)newY;
}
else
{
fp[x, y].Y = 0.0;
pt[x, y].Y = 0;
}
}
//進行合成
Bitmap bSrc = (Bitmap)src.Clone();
// 依照 Format24bppRgb 每三個表示一 Pixel 0: 藍 1: 綠 2: 紅
BitmapData bitmapData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
int scanline = bitmapData.Stride;
IntPtr Scan0 = bitmapData.Scan0;
IntPtr SrcScan0 = bmSrc.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
byte* pSrc = (byte*)(void*)SrcScan0;
int nOffset = bitmapData.Stride - src.Width * 3;
int xOffset, yOffset;
for (int y = 0; y < nHeight; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
xOffset = pt[x, y].X;
yOffset = pt[x, y].Y;
if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth)
{
p[0] = pSrc[(yOffset * scanline) + (xOffset * 3)];
p[1] = pSrc[(yOffset * scanline) + (xOffset * 3) + 1];
p[2] = pSrc[(yOffset * scanline) + (xOffset * 3) + 2];
}
p += 3;
}
p += nOffset;
}
}
src.UnlockBits(bitmapData);
bSrc.UnlockBits(bmSrc);
return src;
}
private struct FloatPoint
{
public double X;
public double Y;
}
中文字展示:
Helper helper=new Helper();
var result=helper.GetCaptcha("我是當麻", "微軟正黑體",Server.MapPath("bk.jpg"),120,40,20);
英文:
Helper helper=new Helper();
var result = helper.GetCaptcha("ABCDE", "Verdana", Server.MapPath("bk.jpg"), 100, 40, 20, 12);
有沒有覺得人類都快要看不出來了..當你在調高 漣漪影響的幅度會更加連人類都看不出來…