標籤:style blog http color ar os 使用 sp for
驗證碼的智能識別是一項比較複雜的工作,甚至需要掌握點映像學的知識。
當然對於寫程式的來說不用那麼深入,只需要掌握幾個常規步驟就行了。
驗證碼Image Recognition步驟:1、擷取映像 2、清除邊框 3、灰階處理 4、二值化處理 5、噪點處理 6、映像分割 7、識別單個數字 8、拼接驗證碼
一、擷取映像
映像一般是遠端,所以需要用到WebRequest:
public Bitmap GetImg(string imgUrl) { WebRequest wreq = WebRequest.Create(imgUrl); wreq.Timeout = 10000; HttpWebResponse wresp = (HttpWebResponse)wreq.GetResponse(); Stream s = wresp.GetResponseStream(); return new Bitmap(s); }
二、清除邊框
很多驗證碼周圍都有一圈黑色的邊框,因此需要用到以下操作:
public Bitmap ClearBorder(Bitmap bm) { //去邊框 width for (int i = 0; i < bm.Width; i++) { bm.SetPixel(i, 0, Color.White); bm.SetPixel(i, bm.Height - 1, Color.White); } //去邊框 height for (int j = 0; j < bm.Height; j++) { bm.SetPixel(0, j, Color.White); bm.SetPixel(bm.Width - 1, j, Color.White); } return bm; }
三、灰階處理
所謂的灰階處理即讓五彩繽紛的映像變成深淺度不同的灰色映像。之所以如此是為了接下去的二值化處理。先看灰階處理:
public Bitmap MakeGray(Bitmap bm) { for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { Color c = bm.GetPixel(i, j);//原始背景顏色 int gray = (int)(c.R * 0.11 + c.G * 0.59 + c.B * 0.3);//計算灰階 bm.SetPixel(i, j, Color.FromArgb(gray, gray, gray)); } } return bm; }
它的原理是遍曆映像上的像素點,根據當前像素點的RGB顏色得到灰階,然後將灰階重新賦給當前像素點。
灰階處理通常有以下三個方法(本例採用的公式2.2):
(2.1)
(2.2)
(2.3)
公式(2.1)取RGB通道的平均值,得到的映像相對比較柔和,同時也縮小了目標和背景的平均亮度差,不利於後續的閥值處理。
公式(2.2)考慮了人眼對綠色的適應度最強,藍色次之,紅色最差。在處理綠色調和藍色調的驗證碼映像時,公式(2.2)的效果令人滿意,但在處理紅色調的映像時,因為公式中紅色的權值很小,灰階化後目標像素和背景像素的亮度差值被嚴重縮小,效果還不如公式(2.1)。
公式(2.3)基於一個前提,那就是有限保留目標像素的亮度資訊,利於後續的閥值分割。
有關理論方面的可以參考此連結:http://www.cnblogs.com/chaosimple/archive/2013/07/18/3197720.html (感謝作者提供寶貴的資料)
四、二值化處理
二值化處理即讓所有深淺度不同的灰色像素點變為黑白兩種像素點,即二值化。
在此需要找到一個臨界值。大於該值的為白(背景),小於的為白(驗證碼)。
public Bitmap MakeBlackWhite(Bitmap bm) { for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { Color c = bm.GetPixel(i, j);//背景顏色 if (c.B > 37) //當前像素點與臨界值判斷 { bm.SetPixel(i, j, Color.White); } else { bm.SetPixel(i, j, Color.Black); } } } return bm; }
五、噪點處理
所謂的噪點處理,就是把一些零零碎碎的點去掉,使其得到乾淨整潔的映像。
public Bitmap ClearPieces(Bitmap bm) { for (int i = 1; i < bm.Width - 1; i++) { for (int j = 1; j < bm.Height - 1; j++) { Color c = bm.GetPixel(i, j);//原始背景顏色 Color cUp = bm.GetPixel(i, j - 1); Color cDown = bm.GetPixel(i, j + 1); Color cLeft = bm.GetPixel(i - 1, j); Color cRight = bm.GetPixel(i + 1, j); //Response.Write(c.R + " " + c.G + " " + c.B + " <br />"); if (c.R == 0 && cUp.R != 0 && cDown.R != 0 && cLeft.R != 0 && cRight.R != 0) { bm.SetPixel(i, j, Color.White); } } } return bm; }
六、映像切割
public Bitmap SplitImg(Bitmap bm,int pointX,int pointY) { Bitmap first = new Bitmap(cutWidth, cutHeight, PixelFormat.Format32bppRgb);for (int i = 0; i < first.Width; i++) { for (int j = 0; j < first.Height; j++) { Color c = bm.GetPixel(pointX + i, pointY + j); first.SetPixel(i, j, c); } }
return first; }
這樣就得到了驗證碼中的只包含一個數位圖片。
七、識別單個數字
public string GetOneNumber(Bitmap first) { StringBuilder strFir = new StringBuilder(""); for (int i = 0; i < first.Width; i++) { for (int j = 0; j < first.Height; j++) { Color c = bm.GetPixel( i, j); if (c.R == 0) { strFir.Append("0"); } else { strFir.Append("1"); } } } int result = 0; string num = ""; List<string> numbers = verifyHelper.GetList(); for (int j = 0; j < numbers.Count(); j++) { result = 0; for (int i = 0; i < strFir.Length; i++) { if (strFir[i] == numbers[j][i]) { result++; } if (result > 110) { num = j.ToString(); return num; } } } return "-1";
八、最後拼接
result += GetOneNumber(bm, 8, 4); result += GetOneNumber(bm, 20, 2); result += GetOneNumber(bm, 35, 5); result += GetOneNumber(bm, 45, 2);
綜合起來需要做的操作是
public string GetNumbers(string imgUrl) { Bitmap bm = new Bitmap(GetImgStream(imgUrl)); bm = ClearBorder(bm); bm = MakeGray(bm); bm = MakeBlackWhite(bm); bm = ClearPieces(bm); string result = ""; result += GetOneNumber(bm, 8, 4); result += GetOneNumber(bm, 20, 2); result += GetOneNumber(bm, 35, 5); result += GetOneNumber(bm, 45, 2); if(result.Contains("-1")) { return "-1"; } else { return result; } }
簡單驗證碼的識別:Bitmap類的使用