c#破解驗證碼示例程式碼
阿新 • • 發佈:2019-02-13
驗證碼破解是一個很大的課題,也有很多種不同的方式,下面程式碼採用的方法是首先準備驗證碼圖片的樣本圖片。然後將驗證碼圖片做灰度處理,並根據字元間距切割驗證碼圖片,最後將切割後的驗證碼小圖和樣本之間做餘袨值比較,從而計算出驗證碼圖片的字元。
如下是c#程式碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.IO; using System.Configuration; namespace ConsoleApplication4 { /// <summary> /// 根據樣本做驗證碼破解 /// /// 需要在.config檔案中的appSettings配置節中新增key為sampleOcr.sampleDir value設定為樣本圖片所在路徑 /// 驗證碼:https://investorservice.cfmmc.com/ https://investorservice.cfmmc.com/veriCode.do?t=1335521167762&ip=202.99.16.22 /// /// outofmemory.cn 20120427 /// 100個樣例準確數為88個,錯誤主要發生在389這三個字元的混淆上 /// </summary> public abstract class SampleOcr { /// <summary> /// 灰度中間值 /// </summary> static int MiddleGrayValue = 200; /// <summary> /// 分割圖片的差異容忍度 /// </summary> static int ColorToleranceForSplit = 30; /// <summary> /// 樣本字典 /// </summary> static Dictionary<string, Bitmap> _samples; /// <summary> /// 破解驗證碼 /// </summary> /// <param name="bm">驗證碼圖片</param> /// <returns>驗證碼文字</returns> public static string Ocr(Bitmap bm) { //做灰度處理 GrayByPixels(bm); bm = RemoveVerticalSpaceRegion(bm); Bitmap[] splitBms = SplitBitmaps(bm); char[] result = new char[splitBms.Length]; for (int i = 0; i < splitBms.Length; i++) { result[i] = OcrChar(splitBms[i]); splitBms[i].Dispose(); } return new string(result); } /// <summary> /// 分割圖片 /// </summary> /// <param name="bm">圖片</param> /// <returns>分割後的圖片物件</returns> static Bitmap[] SplitBitmaps(Bitmap bm) { //找出垂直分割線 List<int> removeXs = new List<int>(); for (int x = 0; x < bm.Width; x++) { bool hasDiffPoint = false; Color color = Color.White; for (int y = 0; y < bm.Height; y++) { if (y == 0) { color = bm.GetPixel(x, y); } else { Color currentColor = bm.GetPixel(x, y); int diff = CalculateColorDifference(currentColor, color); if (diff > ColorToleranceForSplit) { hasDiffPoint = true; break; } // color = currentColor; } } if (!hasDiffPoint) { removeXs.Add(x); } } //根據空白區域,計算各個字元的點陣圖 List<Rectangle> charRects = new List<Rectangle>(); for (int i = 1; i < removeXs.Count; i++) { int diff = removeXs[i] - removeXs[i - 1]; if (diff > 5) { if (diff >= 20) { Rectangle rect = new Rectangle(removeXs[i - 1], 0, diff / 2, bm.Height); charRects.Add(rect); rect = new Rectangle(removeXs[i - 1] + diff / 2, 0, diff / 2, bm.Height); charRects.Add(rect); } else { Rectangle rect = new Rectangle(removeXs[i - 1], 0, diff, bm.Height); charRects.Add(rect); } } } int count = charRects.Count; Bitmap[] charBms = new Bitmap[count]; int charBmIndex = 0; foreach (Rectangle item in charRects) { Bitmap bmChar = bm.Clone(item, bm.PixelFormat); charBms[charBmIndex] = bmChar; charBmIndex += 1; } return charBms; } /// <summary> /// 解析字元 /// </summary> /// <param name="bm">分割後的小圖</param> /// <returns>字元</returns> static char OcrChar(Bitmap bm) { Dictionary<string, Bitmap> samples = LoadSamples(); double diff = .0; string mayBe = null; foreach (string key in samples.Keys) { double diffRate = CalcImageDiffRate(samples[key], bm); if (diffRate == 1) return key[0]; if (diffRate > diff) { mayBe = key; diff = diffRate; } } if (mayBe == null) throw new ApplicationException(); return mayBe[0]; } /// <summary> /// 載入樣本字典 /// </summary> /// <returns>樣本字典</returns> private static Dictionary<string, Bitmap> LoadSamples() { if (_samples == null) { _samples = new Dictionary<string, Bitmap>(); string sampleDir = ConfigurationManager.AppSettings["sampleOcr.sampleDir"] ?? @"D:\SampleOcr\samples"; DirectoryInfo dirInfo = new DirectoryInfo(sampleDir); FileInfo[] files = dirInfo.GetFiles("*.jpg"); foreach (FileInfo item in files) { Bitmap bm = (Bitmap)Bitmap.FromFile(item.FullName); string key = Path.GetFileNameWithoutExtension(item.FullName); _samples.Add(key, bm); } } return _samples; } /// <summary> /// 根據RGB,計算灰度值 /// </summary> /// <param name="posClr">Color值</param> /// <returns>灰度值,整型</returns> static int GetGrayNumColor(System.Drawing.Color posClr) { return (posClr.R * 19595 + posClr.G * 38469 + posClr.B * 7472) >> 16; } /// <summary> /// 灰度轉換,逐點方式 /// </summary> static void GrayByPixels(Bitmap bm) { for (int i = 0; i < bm.Height; i++) { for (int j = 0; j < bm.Width; j++) { int tmpValue = GetGrayNumColor(bm.GetPixel(j, i)); bm.SetPixel(j, i, Color.FromArgb(tmpValue, tmpValue, tmpValue)); } } } /// <summary> /// 刪除垂直方向上的空白區域 /// </summary> /// <param name="bm">源圖片</param> /// <returns>刪除空白之後的圖片</returns> static Bitmap RemoveVerticalSpaceRegion(Bitmap bm) { int topSpaceHeight = 0; for (int y = 0; y < bm.Height; y++) { bool hasDiffPoint = false; Color color = Color.White; for (int x = 0; x < bm.Width; x++) { if (x == 0) { color = bm.GetPixel(x, y); } else { Color currentColor = bm.GetPixel(x, y); int diff = CalculateColorDifference(currentColor, color); if (diff > ColorToleranceForSplit) { hasDiffPoint = true; break; } } } if (hasDiffPoint) { break; } else { topSpaceHeight += 1; } } int bottomSpaceHeight = 0; for (int y = bm.Height - 1; y > 0; y--) { bool hasDiffPoint = false; Color color = Color.White; for (int x = 0; x < bm.Width; x++) { if (x == 0) { color = bm.GetPixel(x, y); } else { Color currentColor = bm.GetPixel(x, y); int diff = CalculateColorDifference(currentColor, color); if (diff > ColorToleranceForSplit) { hasDiffPoint = true; break; } color = currentColor; } } if (hasDiffPoint) { break; } else { bottomSpaceHeight += 1; } } Rectangle rectValid = new Rectangle(0, topSpaceHeight, bm.Width, bm.Height - topSpaceHeight - bottomSpaceHeight); Bitmap newBm = bm.Clone(rectValid, bm.PixelFormat); bm.Dispose(); return newBm; } private static double CalcImageDiffRate(Bitmap bmSample, Bitmap bmCalc) { int[] eSample = new int[bmSample.Height]; int[] eCalc = new int[bmSample.Height]; for (int y = 0; y < bmSample.Height; y++) { eSample[y] = GetHorizontalValue(bmSample, y); eCalc[y] = GetHorizontalValue(bmCalc, y); } return GetCosine(eSample, eCalc); } /// <summary> /// 獲得向量的cos值 /// </summary> /// <param name="e1"></param> /// <param name="e2"></param> /// <returns></returns> static double GetCosine(int[] e1, int[] e2) { double fenzi = 0; for (int i = 0; i < e1.Length; i++) { fenzi += e1[i] * e2[i]; } double fenmuLeft = 0; double fenmuRight = 0; for (int i = 0; i < e1.Length; i++) { fenmuLeft += e1[i] * e1[i]; fenmuRight += e2[i] * e2[i]; } double fenmu = Math.Sqrt(fenmuLeft) * Math.Sqrt(fenmuRight); if (fenmu == 0.0) return 0; return fenzi / fenmu; } /// <summary> /// 計算水平方向上的差異點數 /// </summary> /// <param name="bm">點陣圖</param> /// <param name="y">y座標值</param> /// <returns>差異點數</returns> private static int GetHorizontalValue(Bitmap bm, int y) { if (y >= bm.Height) return 0; int val = 0; for (int x = 0; x < bm.Width; x++) { Color color = bm.GetPixel(x, y); int grayVal = GetColorGrayValue(color); if (grayVal > MiddleGrayValue) { val |= (1 << x); } } return val; } static int GetColorGrayValue(Color color) { return (int)(.299 * color.R + .587 * color.G + .114 * color.B); } /// <summary> /// 計算顏色之間的差值,這個只是一個簡單的計算,真正的色差計算很複雜 /// </summary> /// <param name="colorA">A色</param> /// <param name="colorB">B色</param> /// <returns>差值</returns> static int CalculateColorDifference(Color colorA, Color colorB) { int diff = GetColorGrayValue(colorA) - GetColorGrayValue(colorB); return Math.Abs(diff); } } }