時間過得真快啊,轉眼今年就要過去了,大半年都沒有寫博客了,要說時間嘛,花在泡妹子和搞英語去了,哈哈。。。前幾天老大問我html
怎麼這麼長時間都沒寫博客了,好吧,繼續堅持,繼續分享個人心得體會。算法
這個系列咱們玩玩aforge.net,套用官方都話就是一個專門爲開發者和研究者基於C#框架設計的,這個框架提供了不一樣的類庫和關於類庫的網絡
資源,還有不少應用程序例子,包括計算機視覺與人工智能,圖像處理,神經網絡,遺傳算法,機器學習,機器人等領域,這個系列研究的重點框架
就是瞎幾把搞下AForge.Imaging這個命名空間下面的東東,下載網址:http://www.aforgenet.com/framework/downloads.html機器學習
對了,不知道有多少公司是用得仕卡做爲員工的福利卡,咱們公司就是這樣的,每月公司都會充值一些money,而後咱們這些屁碼農每一個學習
月15號就都開心的去看看發了多少。字體
上去看了後,喲呵~ 還有個90年代的驗證碼,我想這年頭估計找到這樣驗證碼的網站已經很少了,若是懂一點圖像處理都話,這張驗證碼網站
跟沒有一個樣,謝謝。。。這篇咱們看看怎麼去識別它。人工智能
一: 驗證碼處理spa
1. 通常處理原則
這種驗證碼爲何說跟沒有同樣,第一點:字體規範工整,第二點:不旋轉扭曲粘連,第三點:字體顏色單一,下面看處理步驟
這裏要注意的是,aforge只接受像素格式爲24/32bpp的像素格式圖片,因此處理前,先進行格式轉化。
//轉化圖片像素格式 var bnew = new Bitmap(b.Width, b.Height, PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(bnew); g.DrawImage(b, 0, 0); g.Dispose();
<1>圖片灰度化
這是圖像識別一般都要走的第一步,圖片灰度化有助於減小後續對rgb的計算量,同時也方便咱們進行二值化,在aforge中咱們有
專門的類一步搞定,簡潔方便。
//灰度化 b = new Grayscale(0.2125, 0.7154, 0.0721).Apply(b);
<2>二值化
二值化顧名思義就是二種值,好比非白即黑,非黑即白,那麼白和黑的標準就須要提供一個閾值,大於或者小於怎麼樣,在aforge一樣
也有類似的類進行處理
//二值化 b = new Threshold(50).Apply(b);
<3> 去噪點
從上面的圖片能夠發現有不少紅點點,搞得像皮膚病同樣,仔細觀察能夠看到這種噪點具備獨立,體積小的特徵,因此判斷的標準就是若是
圖中某個區塊的大小在我設置的閾值內,就將其去掉,一樣也有專門的類進行處理。
//去噪點 new BlobsFiltering(1, 1, b.Width, b.Height).Apply(b);
這裏具體怎麼傳遞參數,後續系列會慢慢解讀。
<4>切割圖片
切圖片的好處在於咱們須要知道真正要識別的元素的有效範圍是多大,同時也方便咱們將這些圖片做爲模板保存下來。
代碼以下:
1 /// <summary> 2 /// 按照 Y 軸線 切割 3 /// (丟棄等於號) 4 /// </summary> 5 /// <param name="?"></param> 6 /// <returns></returns> 7 public List<Bitmap> Crop_Y(Bitmap b) 8 { 9 var list = new List<Bitmap>(); 10 11 //統計每一列的「1」的個數,方便切除 12 int[] cols = new int[b.Width]; 13 14 /* 15 * 縱向切割 16 */ 17 for (int x = 0; x < b.Width; x++) 18 { 19 for (int y = 0; y < b.Height; y++) 20 { 21 //獲取當前像素點像素 22 var pixel = b.GetPixel(x, y); 23 24 //說明是黑色點 25 if (pixel.R == 0) 26 { 27 cols[x] = ++cols[x]; 28 } 29 } 30 } 31 32 int left = 0, right = 0; 33 34 for (int i = 0; i < cols.Length; i++) 35 { 36 //說明該列有像素值(爲了防止像素干擾,去噪後出現空白的問題,因此多判斷一下,防止切割成多個) 37 if (cols[i] > 0 || (i + 1 < cols.Length && cols[i + 1] > 0)) 38 { 39 if (left == 0) 40 { 41 //切下來圖片的橫座標left 42 left = i; 43 } 44 else 45 { 46 //切下來圖片的橫座標right 47 right = i; 48 } 49 } 50 else 51 { 52 //說明已經有切割圖了,下面咱們進行切割處理 53 if ((left > 0 || right > 0)) 54 { 55 Crop corp = new Crop(new Rectangle(left, 0, right - left + 1, b.Height)); 56 57 var small = corp.Apply(b); 58 59 //居中,將圖片放在20*50的像素裏面 60 61 list.Add(small); 62 } 63 64 left = right = 0; 65 } 66 } 67 68 return list; 69 } 70 71 /// <summary> 72 /// 按照 X 軸線 切割 73 /// </summary> 74 /// <param name="b"></param> 75 /// <returns></returns> 76 public List<Bitmap> Crop_X(List<Bitmap> list) 77 { 78 var corplist = new List<Bitmap>(); 79 80 //再對分割的圖進行上下切割,取出上下的白邊 81 foreach (var segb in list) 82 { 83 //統計每一行的「1」的個數,方便切除 84 int[] rows = new int[segb.Height]; 85 86 /* 87 * 橫向切割 88 */ 89 for (int y = 0; y < segb.Height; y++) 90 { 91 for (int x = 0; x < segb.Width; x++) 92 { 93 //獲取當前像素點像素 94 var pixel = segb.GetPixel(x, y); 95 96 //說明是黑色點 97 if (pixel.R == 0) 98 { 99 rows[y] = ++rows[y]; 100 } 101 } 102 } 103 104 int bottom = 0, top = 0; 105 106 for (int y = 0; y < rows.Length; y++) 107 { 108 //說明該行有像素值(爲了防止像素干擾,去噪後出現空白的問題,因此多判斷一下,防止切割成多個) 109 if (rows[y] > 0 || (y + 1 < rows.Length && rows[y + 1] > 0)) 110 { 111 if (top == 0) 112 { 113 //切下來圖片的top座標 114 top = y; 115 } 116 else 117 { 118 //切下來圖片的bottom座標 119 bottom = y; 120 } 121 } 122 else 123 { 124 //說明已經有切割圖了,下面咱們進行切割處理 125 if ((top > 0 || bottom > 0) && bottom - top > 0) 126 { 127 Crop corp = new Crop(new Rectangle(0, top, segb.Width, bottom - top + 1)); 128 129 var small = corp.Apply(segb); 130 131 corplist.Add(small); 132 } 133 134 top = bottom = 0; 135 } 136 } 137 } 138 139 return corplist; 140 }
<5> 圖片精處理
這裏要注意的是,好比數字「2」,切除上下左右的空白後,再加上噪點的干擾,不必定每次切下來的圖片大小都同樣,因此這裏
爲了方便更好的識別,咱們須要重置下圖片的大小,而且將「數字2」進行文字居中。
1 /// <summary> 2 /// 重置圖片的指定大小而且居中 3 /// </summary> 4 /// <param name="list"></param> 5 /// <returns></returns> 6 public List<Bitmap> ToResizeAndCenterIt(List<Bitmap> list, int w = 20, int h = 20) 7 { 8 List<Bitmap> resizeList = new List<Bitmap>(); 9 10 11 for (int i = 0; i < list.Count; i++) 12 { 13 //反轉一下圖片 14 list[i] = new Invert().Apply(list[i]); 15 16 int sw = list[i].Width; 17 int sh = list[i].Height; 18 19 Crop corpFilter = new Crop(new Rectangle(0, 0, w, h)); 20 21 list[i] = corpFilter.Apply(list[i]); 22 23 //再反轉回去 24 list[i] = new Invert().Apply(list[i]); 25 26 //計算中心位置 27 int centerX = (w - sw) / 2; 28 int centerY = (h - sh) / 2; 29 30 list[i] = new CanvasMove(new IntPoint(centerX, centerY), Color.White).Apply(list[i]); 31 32 resizeList.Add(list[i]); 33 } 34 35 return resizeList; 36 }
其實精處理後,這些圖片就能夠做爲咱們的模板庫的圖片了,能夠將每張模板圖都標記下具體的數字,後續咱們再遇到時,計算下其類似度
就能夠了,下面就是已經制做好的模板。
<6> 模板匹配識別
既然模板圖片都製做好了,一切都差很少水到渠成了,下次來的驗證碼我都切好後作成精圖片後跟模板進行匹配,在afroge裏面
有一個ExhaustiveTemplateMatching,專門用來進行模板匹配用的,很方便。
ExhaustiveTemplateMatching templateMatching = new ExhaustiveTemplateMatching(0.9f);
這裏的0.9f就是設定的閾值,只有大於0.9的閾值,我才認爲該模板與目標圖片類似,而後在全部大於0.9的類似度中取到最大的一個做爲
咱們最後識別的圖像。
1 var files = Directory.GetFiles(Environment.CurrentDirectory + "\\Template\\"); 2 3 var templateList = files.Select(i => { return new Bitmap(i); }).ToList(); 4 var templateListFileName = files.Select(i => { return i.Substring(30, 1); }).ToList(); 5 6 var result = new List<string>(); 7 8 ExhaustiveTemplateMatching templateMatching = new ExhaustiveTemplateMatching(0.9f); 9 10 //這裏面有四張圖片,進行四張圖的模板匹配 11 for (int i = 0; i < list.Count; i++) 12 { 13 float max = 0; 14 int index = 0; 15 16 for (int j = 0; j < templateList.Count; j++) 17 { 18 var compare = templateMatching.ProcessImage(list[i], templateList[j]); 19 20 if (compare.Length > 0 && compare[0].Similarity > max) 21 { 22 //記錄下最類似的 23 max = compare[0].Similarity; 24 index = j; 25 } 26 } 27 28 result.Add(templateListFileName[index]); 29 }
最後的效果仍是不錯的,識別率基本100%吧。