需驗證碼識別,對常用論壇的驗證碼識別的時(shí)候大家用來(lái)做群發(fā)是最合適不過(guò)了。一個(gè)非常有意義的參考
注:非crazycoder原裝,文章是轉載的,原文出處不祥了,很多地方都有,找不到原出處了
驗證碼(captcha)是伴隨自動(dòng)提交程序(spam)的出現而出現的?,F在各種論壇、博客、投票等程序都帶有驗證碼功能。大部分驗證碼都比較容 易識別,只需要簡(jiǎn)單對照一下特征碼就可以得到百分之百準確的結果。也有稍微復雜一點(diǎn)的,比如phpwind和discuz的驗證碼。
前段時(shí)間做了phpwind和discuz的驗證碼識別,phpwind6.0以前的驗證碼和discuz最新的驗證碼如果在不改變后臺驗證碼配置的情況下,識別正確率幾乎可以達到100%,現在跟大家分享一下識別方法。
驗證碼識別一般分為以下幾個(gè)步驟:
各種驗證碼在具體的步驟上操作會(huì )有所不同。
我的程序是用VC++寫(xiě)的,這里不貼詳細代碼了,只講識別方法。

先說(shuō)phpwind驗證碼的識別方法:
具體分為這幾步:
因為這個(gè)驗證碼的字符分布幾乎是等寬的,所以我們首先把圖片切為4份,這樣方便取出每一個(gè)字符。分成4塊后,通過(guò)每一塊中的顏色值對比可以很快得到 字符的特征。因為每一塊中字符的顏色值比雜點(diǎn)顏色值要多很多,而且字符是純色的,所以只要統計出最多的一種顏色,然后去除其它顏色就可以得到只有字符的干 凈圖片。然后對圖片二值化(即構造一個(gè)二維數組對應圖片上有顏色的點(diǎn),把有顏色的點(diǎn)的數組值置為1,無(wú)顏色的置為0),與樣本比對即可得到字符。當然首先 要得到樣本。樣本的制作與上面分析的步驟一致。經(jīng)過(guò)測試,上面這種方法的識別率是100%的,不會(huì )有差錯。

discuz的驗證碼識別較之phpwind要稍難一點(diǎn)。因為圖片帶有不太容易去掉的背景。字符也不是單純的字符,有陰影邊框,而且不等寬,位置不確定。我們具體分為以下幾步:
這個(gè)背景色是漸變的而字符的顏色是不變的。首先去除對角線(xiàn)上找不到相同顏色的點(diǎn),然后統計出每一種顏色占用的區域的寬度和高度。去除占用區域高度小 于圖片總高度1/5或大于圖片總高度2/3的點(diǎn),因為一個(gè)字符不可能達到這種尺寸。再去除密度(即顏色點(diǎn)數/顏色所占的區域寬高的積)小于15%的點(diǎn)。剩 下的就只有干凈的字符的顏色點(diǎn)了。

把這些點(diǎn)分為4份。(分割的辦法為從左到右用一條豎直的線(xiàn)掃描,掃描線(xiàn)經(jīng)過(guò)的連續區域就是字符區域。)分成4個(gè)字符塊后,我們就可以對每一個(gè)字符塊進(jìn)行輪廓特征取值。
什么是輪廓法?我是由berg(berg是網(wǎng)易社區的牛人,對我幫助不少)那里獲知驗證碼識別中的輪廓法。即將一個(gè)字模點(diǎn)陣,以四條直線(xiàn)由上下左右 4個(gè)方向向字符中心掃描,遇到點(diǎn)即停下,把每一條線(xiàn)通過(guò)的路徑長(cháng)度記下。然后以比路徑長(cháng)度和其它一些相關(guān)的參數得到正確的字符。
我稍微變換了一下輪廓法。即把4個(gè)方向上的路徑長(cháng)度變?yōu)椴ǖ男问?。波峰記?,波谷記為0,最后得到一個(gè)由1和0組成的特征串,與樣本串比較即可得 到匹配結果。有幾個(gè)字符,如V和Y、H和M、4和6等,得到的特征串可能是一樣的,這樣需要通過(guò)其它的一些參數來(lái)輔助得到結果。
下面是我計算的特征串和字符的對照樣本:
10-10-10-10- X
1-1010-1-10101- W
-1010--- W
1--1-101- T
-1-1010-10- R
--10-10- R
101-101-1010-1010- Q
-1-101-1- P
--1-1- P
-10--1010- M
-10-10-10- K
10-10-1-1- J
10-10--- J
101-101-1010-10- G
--101-1- F
--1010-- E
101-101-10101-101- C
101-10-10-10- C
-1-10101-1- B
---- B
10101-101-101-101- 9
1---10- 9
-101--- 8
10--1-101- 7
-1-10-- 6
1-101-10-101- 4
1010---- 3
1010-101-1010-- 2
10--10-- 2
//below is equivocal
1-10-1-101- V //Y
10-10-10-- G //Q
-10--10- H //M
10101-101-10101-101- 3 //8
101-101-101-101- 4 //6
上面這種方法,對discuz的默認驗證碼,即如圖所示的驗證碼識別正確率為100%
上面對phpwind和discuz的驗證碼識別方法均沒(méi)有用到高級的算法,更加沒(méi)有用到人工智能的知識,不免有點(diǎn)遺憾,不過(guò)準確率相當高,也容易看懂。
在實(shí)際應用中可能遇到一些問(wèn)題,比如discuz驗證碼可以在后臺設為gif圖片格式。如何把gif動(dòng)畫(huà)中那一個(gè)字符幀轉為bmp圖片呢?下面是VC里面的方法:
BOOL CCaptchaBreak::Gif2Bmp(CString &sPath)
{
ULONG_PTR GdippToken;
GdiplusStartupInput GdippStart;
GdiplusStartup(&GdippToken,&GdippStart,0);
BSTR bsTemp = sPath.AllocSysString();
Bitmap bmp(bsTemp);
::SysFreeString(bsTemp);
int FrameCount,FramePos,size,pause;
PropertyItem* pPropItem;
GUID pageGuid;
GUID* pDimID;
UINT count;
count=bmp.GetFrameDimensionsCount();
pDimID=new GUID[count];
bmp.GetFrameDimensionsList(pDimID,count);
FrameCount=bmp.GetFrameCount(&pDimID[0]);
if (1==FrameCount) //if is bmp then exit
{
delete[]pDimID;
return FALSE;
}
size=bmp.GetPropertyItemSize(PropertyTagFrameDelay);
pPropItem=(PropertyItem*)malloc(size);
bmp.GetPropertyItem(PropertyTagFrameDelay,size,pPropItem);
delete[]pDimID;
pageGuid=FrameDimensionTime;
FramePos=0;
int iFontFramePos = 0;
int iMaxPause = 0;
while (FramePos
return TRUE;
}
BOOL CCaptchaBreak::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return FALSE;
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return FALSE;
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return TRUE; // Success
}
}
free(pImageCodecInfo);
return FALSE;
}
聯(lián)系客服