使用C#的aforge類庫識別驗證碼實例
時間過得真快啊,轉(zhuǎn)眼今年就要過去了,大半年都沒有寫博客了,要說時間嘛,花在泡妹子和搞英語去了,哈哈。。。前幾天老大問我
怎么這么長時間都沒寫博客了,好吧,繼續(xù)堅持,繼續(xù)分享我的心得體會。
這個系列我們玩玩aforge.net,套用官方都話就是一個專門為開發(fā)者和研究者基于C#框架設(shè)計的,這個框架提供了不同的類庫和關(guān)于類庫的
資源,還有很多應(yīng)用程序例子,包括計算機(jī)視覺與人工智能,圖像處理,神經(jīng)網(wǎng)絡(luò),遺傳算法,機(jī)器學(xué)習(xí),機(jī)器人等領(lǐng)域,這個系列研究的重點
就是瞎幾把搞下AForge.Imaging這個命名空間下面的東東,下載網(wǎng)址:http://www.aforgenet.com/framework/downloads.html
對了,不知道有多少公司是用得仕卡作為員工的福利卡,我們公司就是這樣的,每個月公司都會充值一些money,然后我們這些屁碼農(nóng)每個月15號就都開心的去看看發(fā)了多少。
上去看了后,喲呵~ 還有個90年代的驗證碼,我想這年頭估計找到這樣驗證碼的網(wǎng)站已經(jīng)不多了,如果懂一點圖像處理都話,這張驗證碼
跟沒有一個樣,謝謝。。。這篇我們看看怎么去識別它。
一: 驗證碼處理
1. 一般處理原則
這種驗證碼為什么說跟沒有一樣,第一點:字體規(guī)范工整,第二點:不旋轉(zhuǎn)扭曲粘連,第三點:字體顏色單一,下面看處理步驟。
這里要注意的是,aforge只接受像素格式為24/32bpp的像素格式圖片,所以處理前,先進(jìn)行格式轉(zhuǎn)化。
//轉(zhuǎn)化圖片像素格式
var bnew = new Bitmap(b.Width, b.Height, PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(bnew);
g.DrawImage(b, 0, 0);
g.Dispose();
<1>圖片灰度化
這是圖像識別通常都要走的第一步,圖片灰度化有助于減少后續(xù)對rgb的計算量,同時也方便我們進(jìn)行二值化,在aforge中我們有專門的類一步搞定,簡潔方便。
//灰度化
b = new Grayscale(0.2125, 0.7154, 0.0721).Apply(b);
<2>二值化
二值化顧名思義就是二種值,比如非白即黑,非黑即白,那么白和黑的標(biāo)準(zhǔn)就需要提供一個閾值,大于或者小于怎么樣,在aforge同樣也有相似的類進(jìn)行處理
//二值化
b = new Threshold(50).Apply(b);
<3> 去噪點
從上面的圖片可以發(fā)現(xiàn)有很多紅點點,搞得像皮膚病一樣,仔細(xì)觀察可以看到這種噪點具有獨立,體積小的特征,所以判斷的標(biāo)準(zhǔn)就是如果圖中某個區(qū)塊的大小在我設(shè)置的閾值內(nèi),就將其去掉,同樣也有專門的類進(jìn)行處理。
//去噪點
new BlobsFiltering(1, 1, b.Width, b.Height).Apply(b);
這里具體怎么傳遞參數(shù),后續(xù)系列會慢慢解讀。
<4>切割圖片
切圖片的好處在于我們需要知道真正要識別的元素的有效范圍是多大,同時也方便我們將這些圖片作為模板保存下來。
代碼如下:
/// <summary>
/// 按照 Y 軸線 切割
/// (丟棄等于號)
/// </summary>
/// <param name="?"></param>
/// <returns></returns>
public List<Bitmap> Crop_Y(Bitmap b)
{
var list = new List<Bitmap>();
//統(tǒng)計每一列的“1”的個數(shù),方便切除
int[] cols = new int[b.Width];
/*
* 縱向切割
*/
for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
//獲取當(dāng)前像素點像素
var pixel = b.GetPixel(x, y);
//說明是黑色點
if (pixel.R == 0)
{
cols[x] = ++cols[x];
}
}
}
int left = 0, right = 0;
for (int i = 0; i < cols.Length; i++)
{
//說明該列有像素值(為了防止像素干擾,去噪后出現(xiàn)空白的問題,所以多判斷一下,防止切割成多個)
if (cols[i] > 0 || (i + 1 < cols.Length && cols[i + 1] > 0))
{
if (left == 0)
{
//切下來圖片的橫坐標(biāo)left
left = i;
}
else
{
//切下來圖片的橫坐標(biāo)right
right = i;
}
}
else
{
//說明已經(jīng)有切割圖了,下面我們進(jìn)行切割處理
if ((left > 0 || right > 0))
{
Crop corp = new Crop(new Rectangle(left, 0, right - left + 1, b.Height));
var small = corp.Apply(b);
//居中,將圖片放在20*50的像素里面
list.Add(small);
}
left = right = 0;
}
}
return list;
}
/// <summary>
/// 按照 X 軸線 切割
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
public List<Bitmap> Crop_X(List<Bitmap> list)
{
var corplist = new List<Bitmap>();
//再對分割的圖進(jìn)行上下切割,取出上下的白邊
foreach (var segb in list)
{
//統(tǒng)計每一行的“1”的個數(shù),方便切除
int[] rows = new int[segb.Height];
/*
* 橫向切割
*/
for (int y = 0; y < segb.Height; y++)
{
for (int x = 0; x < segb.Width; x++)
{
//獲取當(dāng)前像素點像素
var pixel = segb.GetPixel(x, y);
//說明是黑色點
if (pixel.R == 0)
{
rows[y] = ++rows[y];
}
}
}
int bottom = 0, top = 0;
for (int y = 0; y < rows.Length; y++)
{
//說明該行有像素值(為了防止像素干擾,去噪后出現(xiàn)空白的問題,所以多判斷一下,防止切割成多個)
if (rows[y] > 0 || (y + 1 < rows.Length && rows[y + 1] > 0))
{
if (top == 0)
{
//切下來圖片的top坐標(biāo)
top = y;
}
else
{
//切下來圖片的bottom坐標(biāo)
bottom = y;
}
}
else
{
//說明已經(jīng)有切割圖了,下面我們進(jìn)行切割處理
if ((top > 0 || bottom > 0) && bottom - top > 0)
{
Crop corp = new Crop(new Rectangle(0, top, segb.Width, bottom - top + 1));
var small = corp.Apply(segb);
corplist.Add(small);
}
top = bottom = 0;
}
}
}
return corplist;
}
<5> 圖片精處理
這里要注意的是,比如數(shù)字“2”,切除上下左右的空白后,再加上噪點的干擾,不一定每次切下來的圖片大小都一樣,所以這里為了方便更好的識別,我們需要重置下圖片的大小,并且將“數(shù)字2”進(jìn)行文字居中。
/// <summary>
/// 重置圖片的指定大小并且居中
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public List<Bitmap> ToResizeAndCenterIt(List<Bitmap> list, int w = 20, int h = 20)
{
List<Bitmap> resizeList = new List<Bitmap>();
for (int i = 0; i < list.Count; i++)
{
//反轉(zhuǎn)一下圖片
list[i] = new Invert().Apply(list[i]);
int sw = list[i].Width;
int sh = list[i].Height;
Crop corpFilter = new Crop(new Rectangle(0, 0, w, h));
list[i] = corpFilter.Apply(list[i]);
//再反轉(zhuǎn)回去
list[i] = new Invert().Apply(list[i]);
//計算中心位置
int centerX = (w - sw) / 2;
int centerY = (h - sh) / 2;
list[i] = new CanvasMove(new IntPoint(centerX, centerY), Color.White).Apply(list[i]);
resizeList.Add(list[i]);
}
return resizeList;
}
其實精處理后,這些圖片就可以作為我們的模板庫的圖片了,可以將每張模板圖都標(biāo)記下具體的數(shù)字,后續(xù)我們再遇到時,計算下其相似度就可以了,下面就是已經(jīng)制作好的模板。
<6> 模板匹配識別
既然模板圖片都制作好了,一切都差不多水到渠成了,下次來的驗證碼我都切好后做成精圖片后跟模板進(jìn)行匹配,在afroge里面
有一個ExhaustiveTemplateMatching,專門用來進(jìn)行模板匹配用的,很方便。
ExhaustiveTemplateMatching templateMatching = new ExhaustiveTemplateMatching(0.9f);
這里的0.9f就是設(shè)定的閾值,只有大于0.9的閾值,我才認(rèn)為該模板與目標(biāo)圖片相似,然后在所有大于0.9的相似度中取到最大的一個作為
我們最后識別的圖像。
var files = Directory.GetFiles(Environment.CurrentDirectory + "\\Template\\");
var templateList = files.Select(i => { return new Bitmap(i); }).ToList();
var templateListFileName = files.Select(i => { return i.Substring(30, 1); }).ToList();
var result = new List<string>();
ExhaustiveTemplateMatching templateMatching = new ExhaustiveTemplateMatching(0.9f);
//這里面有四張圖片,進(jìn)行四張圖的模板匹配
for (int i = 0; i < list.Count; i++)
{
float max = 0;
int index = 0;
for (int j = 0; j < templateList.Count; j++)
{
var compare = templateMatching.ProcessImage(list[i], templateList[j]);
if (compare.Length > 0 && compare[0].Similarity > max)
{
//記錄下最相似的
max = compare[0].Similarity;
index = j;
}
}
result.Add(templateListFileName[index]);
}
最后的效果還是不錯的,識別率基本100%吧。
相關(guān)文章
C# TextBox多行文本框的字?jǐn)?shù)限制問題
最近在使用C# TextBox多行文本框的時候,發(fā)現(xiàn)了其對字?jǐn)?shù)限制的一點問題,所以總結(jié)下在使用C# TextBox多行文本框要注意的的字?jǐn)?shù)限制問題,現(xiàn)在分享給大家,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-12-12C#中將ListView中數(shù)據(jù)導(dǎo)出到Excel的實例方法
首先 你需要添加引用Microsoft Excel 11.0 Object Library2013-04-04C#與C++?dll之間傳遞字符串string?wchar_t*?char*?IntPtr問題
C#與C++?dll之間傳遞字符串string?wchar_t*?char*?IntPtr問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11c# 編寫一個輕量級的異步寫日志的實用工具類(LogAsyncWriter)
這篇文章主要介紹了c# 如何編寫一個輕量級的異步寫日志的實用工具類(LogAsyncWriter),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-03-03