成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

用 JS 實現了識別網頁驗證碼的功能!

開發 前端
當時我寫代碼的時候,沒有找到純 js 可以用的代碼和庫,不能打包成 chrome 擴展,用起來還是不太方便的。所以在驗證了思路的可行性后,我就大致寫下來,給他人以方便吧。

 很高興大家喜歡!Github:leonof/imgRecJs[1],剛剛上傳,代碼還需要完善~因為有不少同學表示訓練和識別有疑問,我做了個小接口放在最后,可以方便大家先把流程走通。

后續會更新:將 js 代碼等打包成 chrome 擴展程序,這樣就可以讓瀏覽器自動識別,完全傻瓜式使用啦~!(更新啦:利用 chrome 擴展,讓瀏覽器執行我們的腳本[2])

其實整篇文章難度不高,網上也有很多 java、c 等的代碼。只是當時我寫代碼的時候,沒有找到純 js 可以用的代碼和庫,不能打包成 chrome 擴展,用起來還是不太方便的。所以在驗證了思路的可行性后,我就大致寫下來,給他人以方便吧。

目前有多種驗證碼識別思路,限于能力有限,我只好采用了最簡單的機器學習。目標驗證碼也比較簡單,如:

(含字母也一樣)

識別控制速度在 0.1 秒以內的話,正確率在 99.99999%(因為一直是識別正確哈哈哈)。

在動手之前,先梳理一下大致思路,方便比較獨立的同學自己嘗試完成代碼:

1、先分析網頁 DOM 結構,載入驗證碼圖片。

2、將圖片畫到 canvas 上,拿到圖片的像素數據。

3、先后對圖片進行二值化、腐蝕膨脹、切割、旋轉、縮放處理。

4、記錄處理后的單個數字的二值化數據,并人工錄入真實數字。

5、重復訓練。

6、識別時,用處理后的圖像與庫中數據對比,取得最相近的數據,得到真實數字。

(以下優化)

7、數據量大時,可以取前幾個相似數據,并按權重從中選出最可能的數字,以提高準確度。

8、也可查找到相似度足夠高時停止搜索,取其作為最后識別結果,以提升效率。

大神們可以直接去寫了,我這低級簡單的代碼會遭你們嘲笑的。。。比較急于求成的同學也可以不用看了,回頭直接拿 demo 去修改吧!

好吧既然你看到這里了,我就盡量說的清楚明白一點。

在動手之前,我簡單模擬一下需要輸入驗證碼的網站,效果如下:

好吧,是真的簡單…點擊圖片可以更換驗證碼,輸入框用來輸入,按鈕模擬提交,如下:

我們就假裝他作為我們要自動識別的目標。

一、分析網頁 DOM 結構,載入驗證碼圖片。

我們可以看到,驗證碼的 url 是:img/0.jpg。我這里的 url 會變化,是為了模擬更換驗

  1. ctx1.drawImage(img,0,0,img.width,img.height); 

證碼的過程。但實際上,由于驗證碼絕大多數為后臺生成的,所以地址是固定的。那么我們很容易就可以拿到圖片數據:new 一個 Image,賦值 url 即可(直接 get 到 img 元素也行)。參考代碼:

  1. var img = document.getElementById("img"); 

二、將圖片畫到 canvas 上,拿到圖片的像素數據。

要將圖片畫到 canvas 上,首先要創建一個 canvas 并初始化。參考代碼: 

  1. var canvas1 = document.createElement("canvas");  
  2. document.getElementsByTagName("body")[0].appendChild(canvas1);  
  3. canvas1.style.backgroundColor = "cornsilk"
  4.  var ctx1 = canvas1.getContext("2d"); 

隨后,將圖片繪制上去。參考代碼: 

  1. ctx1.drawImage(img,0,0,img.width,img.height); 

然后我們就可以利用 canvas,拿到圖片的像素數據。參考代碼: 

  1. var imgData = ctx1.getImageData(0,0,WIDTH,HEIGHT); 

三、先后對圖片進行二值化、腐蝕膨脹、切割、旋轉、縮放處理。

這部分是圖像識別的重點,直接影響到識別準確率和速度。復雜的驗證碼還應加上去躁等處理過程。比如可以檢測貫穿的橫線并消除,或者將顏色高度統一的背景去掉等等。我們的圖片幾乎沒有干擾,只有簡單的旋轉和縮放,故直接進行二值化操作(二值化也能去掉少量的干擾)。

1、二值化操作的思路是:計算圖片的平均灰度作為閾值,比閾值大的置為純黑,反之純白。參考代碼: 

  1. function toHex(fromImgData){//二值化圖像  
  2.     var fromPixelData = fromImgData.data;  
  3.     var greyAve = 0 
  4.     for(var j=0;j<WIDTH*HEIGHT;j++){  
  5.         var r = fromPixelData[4*j];  
  6.         var g = fromPixelData[4*j+1];  
  7.         var b = fromPixelData[4*j+2];  
  8.         greyAve += r*0.3 + g*0.59 + b*0.11;  
  9.     }  
  10.     greyAve /= WIDTH*HEIGHT;//計算平均灰度值。  
  11.     for(j=0;j<WIDTH*HEIGHT;j++){  
  12.         r = fromPixelData[4*j];  
  13.         g = fromPixelData[4*j+1];  
  14.         b = fromPixelData[4*j+2];  
  15.         var grey = r*0.333 + g*0.333 + b*0.333;//取平均值。  
  16.         greygrey = grey>greyAve?255:0;  
  17.         fromPixelData[4*j] = grey;  
  18.         fromPixelData[4*j+1] = grey;  
  19.         fromPixelData[4*j+2] = grey;  
  20.     }  
  21.     return fromImgData;  
  22. }//二值化圖像 

二值化后,效果如圖:

可以發現,簡單的背景色是可以去掉的。

二值化處理之后,就可以將圖片轉換成數組(存 0 或 1)來保存了。參考代碼如下: 

  1. function toXY(fromImgData){  
  2.     var result = new Array(HEIGHT);  
  3.     var fromPixelData = fromImgData.data;  
  4.     for(var j=0;j<HEIGHT;j++){  
  5.         result[j] = new Array(WIDTH);  
  6.         for(var k=0;k<WIDTH;k++){  
  7.             var r = fromPixelData[4*(j*WIDTH+k)];  
  8.             var g = fromPixelData[4*(j*WIDTH+k)+1];  
  9.             var b = fromPixelData[4*(j*WIDTH+k)+2];  
  10.             result[j][k] = (r+g+b)>500?0:1;//賦值0、1給內部數組  
  11.         }  
  12.     }  
  13.     return result;  
  14. }//圖像轉數組 

2、接下來是腐蝕、膨脹。腐蝕的基本思路在于,將所有白色周圍的像素都置成白色,以此來消除游離的個別黑色像素點噪聲。膨脹正好相反,將黑色周圍置成黑色,消除數字內部的個別白色。同時,腐蝕、膨脹的操作可以讓圖片更加平滑。參考代碼: 

  1. function corrode(fromArray){  
  2.     for(var j=1;j<fromArray.length-1;j++){  
  3.         for(var k=1;k<fromArray[j].length-1;k++){  
  4.             if(fromArray[j][k]==1&&fromArray[j-1][k]+fromArray[j+1][k]+fromArray[j][k-1]+fromArray[j][k+1]==0){  
  5.                 fromArray[j][k] = 0;  
  6.             }  
  7.         }  
  8.     }  
  9.     return fromArray;  
  10. }//腐蝕(簡單)  
  11. function expand(fromArray){  
  12.     for(var j=1;j<fromArray.length-1;j++){  
  13.         for(var k=1;k<fromArray[j].length-1;k++){  
  14.             if(fromArray[j][k]==0&&fromArray[j-1][k]+fromArray[j+1][k]+fromArray[j][k-1]+fromArray[j][k+1]==4){  
  15.                 fromArray[j][k] = 1;  
  16.             }  
  17.         }  
  18.     }  
  19.     return fromArray;  
  20. }//膨脹(簡單) 

由于我們的圖片背景干擾不是很強烈,所以基本看不出差別。不過對于計算機來說,還是有不同的喲~尤其是背景復雜的圖片,這一步很好用。

3、切割。

由于我們的圖片內各數字沒有粘連,所以切割時只需要從上至下,從左至右掃描圖片,發現圖片某一豎行均為白色,就切一刀。有粘連的驗證碼比較困難,暫時不討論了。參考代碼: 

  1. function split(fromArray,count){  
  2.     var numNow = 0 
  3.     var status = false 
  4.     var w = fromArray[0].length;  
  5.     for(var k=0;k<w;k++) {//遍歷圖像  
  6.         var sumUp = 0 
  7.         for (var j=0;j<fromArray.length;j++) //檢測整列是否有圖像  
  8.             sumUp += fromArray[j][k];  
  9.         if(sumUp == 0){//切割  
  10.             for (j=0;j<fromArray.length-1;j++)  
  11.                 fromArray[j].remove(k);  
  12.             w --;  
  13.             k --;  
  14.             status = false 
  15.             continue;  
  16.         }  
  17.         else{//切換狀態  
  18.             if(!status)  
  19.                 numNow ++;  
  20.             status = true 
  21.         }  
  22.         if(numNow!=count){//不是想要的數字  
  23.             for (j=0;j<fromArray.length-1;j++)  
  24.                 fromArray[j].remove(k);  
  25.             w --;  
  26.             k --;  
  27.         }  
  28.     }  
  29.     return fromArray;  
  30. }//切割,獲取特定數字 

切割后,左右的空白因為都被切了,就沒有了。但是上下仍然存在空白,所以進行處理。這里比較簡單,就不放代碼了,思路和切割類似,但簡單很多。

4、旋轉、縮放。

其實旋轉不是必要的。沒有旋轉的步驟,可以用更多的數據量訓練來彌補。同理,縮放也不是必須的。先大致講一下思路:旋轉和縮放都再次利用了 canvas,將圖片畫上去之后,利用 canvas 的方法操作圖片旋轉或縮放,之后再把數據拿下來,就像我們最開始讀圖片時做的一樣。旋轉時,取順時針逆時針各 90 度,取左右寬度最窄的角度,當作數字站立的旋轉角度。縮放時,直接按預設長寬畫圖即可。這里我就只寫了縮放。處理后再轉換回數組形式。參考代碼: 

  1. function zoomToFit(fromArray){  
  2.     var imgD = fromXY(fromArray);  
  3.     var w = lastWidth 
  4.     var h = lastHeight 
  5.     var tempc1 = document.createElement("canvas");  
  6.     var tempc2 = document.createElement("canvas");  
  7.     tempc1.width = fromArray[0].length;  
  8.     tempc1.height = fromArray.length;  
  9.     tempc2.width = w;  
  10.     tempc2.height = h;  
  11.     var tempt1 = tempc1.getContext("2d");  
  12.     var tempt2 = tempc2.getContext("2d");  
  13.     tempt1.putImageData(imgD,0,0,0,0,tempc1.width,tempc1.height);  
  14.     tempt2.drawImage(tempc1,0,0,w,h);  
  15.     var returnImageD = tempt2.getImageData(0,0,WIDTH,HEIGHT);  
  16.     fromArray = toXY(returnImageD);  
  17.     fromArray.length = h;  
  18.     for(var i=0;i<h;i++)  
  19.         fromArray[i].length = w 
  20.     return fromArray;  
  21. }//尺寸歸一化 

處理后效果如圖:

四、記錄處理后的單個數字的二值化數據,并人工錄入真實數字。

到這里,圖像處理就搞定了,后面的工作就比較簡單了。我們把上一步得到的數組和真實的數字一起保存起來。這個過程可以有很多方法。我當時采取了大家一起錄入的方式,所以搭建了 PHP+MySQL 的服務器,用數據庫存儲。這塊就不詳述了,大家各顯神威。

五、重復訓練

為了方便訓練,我直接在頁面里增加了手動輸入的地方,提交后刷新驗證碼,繼續提交。提交 20 個驗證碼(20*4=80 個數字)后,便經常可以正確識別出 4 位驗證碼,在單個數字的數據量在 300 左右時(大約需要 300/4=75 個驗證碼),識別效率已經在 95%以上。在 500 左右時已經基本見不到錯誤識別的情況了,這時候已經可以寫代碼實現自我訓練了。此時識別一次大約需要 0.06 秒。

六、識別時,用處理后的圖像與庫中數據對比,取得最相近的數據,得到真實數字。

這塊也比較簡單。訓練完成后,我將數據庫數據導出,保存成了一個大的數組,直接用 js 就可以讀了。識別時遍歷所有的數據,按像素點逐一比較。由于尺寸做了歸一化,所以直接數有多少像素匹配即可。匹配數量最多的即為識別出的結果。我只找到了最開始寫的 PHP 代碼,先放一下吧,有點懶得再寫 js 了…: 

  1. function check($str)  
  2.  
  3.     $str = str_split($str,1);  
  4.     $length = count($str);  
  5.     $tempNum = 0 
  6.     $tempSimmiar = 0 
  7.     $query = "SELECT * FROM numkeys" 
  8.     $sth = execSql($query);  
  9.     while ($RES = $sth->fetch()) {  
  10.         $thisSimmiar = 0 
  11.         $thisFeature = str_split($RES["feature"],1);  
  12.         $thisNum = $RES["resultnum"];  
  13.         for($i=0;$i<$length;$i++){  
  14.             if($thisFeature[$i]==$str[$i]){  
  15.                 $thisSimmiar ++;  
  16.             }  
  17.         }  
  18.         if($thisSimmiar>$tempSimmiar){  
  19.             $tempSimmiar = $thisSimmiar;  
  20.             $tempNum = $thisNum;  
  21.         }  
  22.     }  
  23.     return $tempNum;  

七、優化部分

這塊就大家自己看著來吧,因為我的圖片不是很復雜,數據量也不是很大(千條級別),所以也沒啥優化的必要,每次識別大約 0.1 秒吧。所以我只是沒事干,做了之前大綱里寫了那兩個優化。其實我感覺主要的優化方向還是圖像處理那塊,盡量減少干擾,才能提高效率,也能檢測更復雜的驗證碼。

PS:訓練和識別的接口:

訓練:POST 發送 username(用戶名)、password(密碼)、n1(第一個數組)、n2、n3

、n4、num(真實四位字符)至 http://www.leonszone.cn/test/yanzhengma/train.php[3]。參考代碼: 

  1. function sendData() {  
  2.     var str = prompt("請輸入驗證碼:", "");  
  3.     if(!str)  
  4.         return false;  
  5.     postData = {//整合數據包  
  6.         username: 'pdgzfx',  
  7.         password: 'pdgzfx',  
  8.         nums: str,  
  9.         n1: numsArray[0],  
  10.         n2: numsArray[1],  
  11.         n3: numsArray[2],  
  12.         n4: numsArray[3]  
  13.     };  
  14.     $.ajax({ 
  15.          url: 'http://www.leonszone.cn/test/yanzhengma/train.php',  
  16.         type: 'POST',  
  17.         data: postData,  
  18.         success: function (data) {  
  19.             console.log(data);  
  20.             setTimeout(function () {  
  21.                 location.reload();  
  22.             },1000);  
  23.         }  
  24.     });  

識別:POST 發送 username(用戶名)、password(密碼)、n1(第一個數組)、n2、n3、n4 至 http://www.leonszone.cn/test/yanzhengma/check.php[4]。參考代碼: 

  1. function getData() {  
  2.     postData = {//整合數據包  
  3.         username: 'pdgzfx',  
  4.         password: 'pdgzfx',  
  5.         nums: 'help!!!',  
  6.         n1: numsArray[0],  
  7.         n2: numsArray[1],  
  8.         n3: numsArray[2],  
  9.         n4: numsArray[3]  
  10.     };  
  11.     $.ajax({  
  12.         url: 'http://www.leonszone.cn/test/yanzhengma/check.php',  
  13.         type: 'POST', 
  14.         data: postData,  
  15.         success: function (data) {  
  16.             $("#Vercode").val(data);  
  17.             console.log(data);  
  18.         }  
  19.     });  

注冊用戶名密碼(防止大家的庫混淆):POST 或 GET 發送 username(用戶名)、password(密碼)至 http://www.leonszone.cn/test/yanzhengma/regist.php[5]。參考代碼: 

  1. function getData() {  
  2.     postData = {//整合數據包  
  3.         username: 'pdgzfx',  
  4.         password: 'pdgzfx',  
  5.         };  
  6.     $.ajax({  
  7.         url: 'http://www.leonszone.cn/test/yanzhengma/regist.php',  
  8.         type: 'POST',  
  9.         data: postData,  
  10.         success: function (data) {  
  11.             console.log(data); } }); } 

或直接瀏覽器訪問:http://www.leonszone.cn/test/yanzhengma/regist.php\?username= 你的用戶名 \&amp;amp;password=[6]你的密碼

好累,先休息下,看看有沒有人看吧…(我感覺應該沒多少人= = 還真的有人!!!) 

 

責任編輯:龐桂玉 來源: Web開發
相關推薦

2013-06-19 10:19:59

2022-05-11 07:41:31

Python驗證碼

2024-01-29 08:32:10

Python驗證碼識別

2009-06-26 15:17:27

jQuery

2022-02-02 20:21:24

短信驗證碼登錄

2015-09-21 15:31:05

php實現驗證碼

2014-04-24 10:09:05

驗證碼C#

2020-12-29 05:33:03

Serverless驗證碼架構

2023-10-27 08:53:13

Python驗證碼圖片識別

2022-09-21 08:40:04

OCR技術驗證碼

2024-05-10 08:38:16

2009-11-23 16:59:23

PHP圖形驗證碼

2021-01-19 10:29:34

短信驗證碼密碼

2022-02-11 07:10:15

驗證碼

2021-06-16 06:58:09

TensorFlow識別驗證碼

2009-12-16 15:46:41

Ruby on rai

2020-11-16 07:28:53

驗證碼

2011-11-02 12:43:33

2024-04-08 14:10:06

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 波多野结衣先锋影音 | 久久亚洲欧美日韩精品专区 | 九九av| 亚洲字幕在线观看 | 伊人久久综合 | 久久精品国产一区二区电影 | 国产精品伦一区二区三级视频 | 国产免费人成xvideos视频 | 亚洲成人av | 国产精品1区 | 在线播放亚洲 | 色视频成人在线观看免 | 亚洲国产精品网站 | 国产亚洲精品久久久久久豆腐 | 羞羞的视频在线看 | 久久国产精品免费 | 91精品国产色综合久久不卡98口 | 国产精品99久久久久久久久 | 国产精品96久久久久久 | 色免费在线视频 | 蜜桃视频在线观看免费视频网站www | 国产精品一区二区在线 | 亚洲第一天堂 | 日韩视频在线播放 | 免费在线看a | 国产女人第一次做爰毛片 | 免费成人午夜 | 午夜a√| 国产伦精品一区二区三区在线 | 国产精品久久久久久久久久久久久 | 在线免费观看欧美 | 欧美在线视频一区二区 | 国产日韩欧美精品一区二区三区 | 午夜男人视频 | 成人福利网站 | 精品久久香蕉国产线看观看亚洲 | 日韩欧美成人精品 | 亚洲bt 欧美bt 日本bt | 亚洲欧美日本在线 | av在线播放不卡 | 99久久久国产精品 |