一個簡單的驗證碼識別功能(只能識別純數字的簡單功能)


驗證碼樣例  

 

直接上代碼:

  1 package com.tdx;
  2 
  3 
  4 import java.awt.image.BufferedImage;
  5 import java.io.File;
  6 import java.io.IOException;
  7 import java.util.ArrayList;
  8 import java.util.List;
  9 
 10 import javax.imageio.ImageIO;
 11 
 12 import com.Common.VerifiCodeUtil;
 13 
 14 public class Test1 {
 15 
 16     public static String TEMP_DIR = "F:/develop_tool/workspace/DownloadFile";
 17     
 18     public static int num = 0;
 19     public static void main(String[] args) {
 20          
 21         
 22         
 23         //准備訓練庫的素材
 24         //1、下載驗證碼到本地
 25 //        for (int i = 0; i < 10; i++) {
 26 //            VerifiCodeUtil.downLoadImageCode();
 27 //        }
 28         //2、去除驗證碼的背景,將驗證碼轉換成黑白圖片(為了更加便於識別)
 29 //        for (int i = 0; i < 10; i++) {
 30 //            VerifiCodeUtil.removeBackground(TEMP_DIR + "/codeImg/down/" + i + ".png", TEMP_DIR + "/codeImg/bacground/" + i + ".png");
 31 //        }
 32         //3、將黑白圖片分割成單個的數字圖片(最好能盡可能的小,這樣匹配時精度更高)
 33 //        for (int i = 0; i < 10; i++) {
 34 //            try {
 35 //                handleImg(TEMP_DIR + "/codeImg/bacground/" + i + ".png");
 36 //            } catch (Exception e) {
 37 //                // TODO Auto-generated catch block
 38 //                e.printStackTrace();
 39 //            }
 40 //        }
 41         
 42         //4、人工在TEMP_DIR + "/codeImg/temp/temp"中找到合適的樣本,然后用畫圖工具稍微處理一下以增加精度
 43         //將處理好的樣本圖片按照圖片中的數字命名,刪除其它無用的圖片
 44         
 45         //5、驗證成功率如何
 46 //        for (int i = 0; i < 10; i++) {
 47 //            try {
 48 //                getValidateCode(new File(TEMP_DIR + "/codeImg/bacground/" + i + ".png"));
 49 //            } catch (Exception e) {
 50 //                e.printStackTrace();
 51 //            }
 52 //        }
 53         
 54         //訓練庫中的素材准備好了,就可以正式開始了(記得刪除之前1-5步准備階段的代碼和目錄(temp、result目錄))
 55         
 56         //大致流程是1、下載驗證碼2、將驗證碼轉換成黑白圖片3、進行圖片分割4、逐個與樣本圖片比對,差異最小的額就是這個圖片的數值。
 57         VerifiCodeUtil.getValidateCode();
 58     }
 59     
 60     public static String getValidateCode(File file) throws Exception {
 61         // 裝載圖片
 62         BufferedImage image = ImageIO.read(file);
 63         //VerifiCodeUtil.removeInterference(image);
 64         // 拆分圖片
 65         List<BufferedImage> digitImageList = splitImage(image);
 66 
 67         // 循環每一位數字圖進行比對
 68         StringBuilder sb = new StringBuilder();
 69         for (BufferedImage digitImage : digitImageList) {
 70             String result = "";
 71             int width = digitImage.getWidth();
 72             int height = digitImage.getHeight();
 73             
 74             // 最小的不同次數(初始值為總像素),值越小就越像。
 75             int minDiffCount = width * height;
 76             for (BufferedImage bi : VerifiCodeUtil.trainMap.keySet()) {
 77                 // 對每一位數字圖與字典中的進行按像素比較
 78                 int currDiffCount = 0; // 按像素比較不同的次數
 79                 outer : for (int x = 0; x < width; ++x) {
 80                     for (int y = 0; y < height; ++y) {
 81                         if (digitImage.getRGB(x, y) != bi.getRGB(x, y)) {
 82                             // 按像素比較如果不同,則加1;
 83                             currDiffCount++;
 84                             // 如果值大於minDiffCount,則不用再比較了,因為我們要找最小的minDiffCount。
 85                             if (currDiffCount >= minDiffCount) 
 86                                 break outer;
 87                         }
 88                     }
 89                 }
 90                 if (currDiffCount < minDiffCount) {
 91                     // 現在誰差別最小,就先暫時把值賦予給它
 92                     minDiffCount = currDiffCount;
 93                     result = VerifiCodeUtil.trainMap.get(bi);
 94                 }
 95             }
 96             sb.append(result);
 97         }        
 98         ImageIO.write(image, "PNG", new File(TEMP_DIR + "/codeImg/result/", sb.toString() + ".png"));
 99         
100         return sb.toString();
101     }
102     
103     
104     private static void handleImg(String imgUrl) throws Exception {
105         BufferedImage img = ImageIO.read(new File(imgUrl));
106         List<BufferedImage> list = splitImage(img);
107         for (int i = 0; i < 4; i++,num ++) {
108             
109              File file = new File(TEMP_DIR + "/codeImg/temp/temp" + num + ".png");
110              if (!file.exists())
111              {
112                  File dir = file.getParentFile();
113                  if (!dir.exists())
114                  {
115                      dir.mkdirs();
116                  }
117                  try
118                  {
119                      file.createNewFile();
120                  }
121                  catch (IOException e)
122                  {
123                      e.printStackTrace();
124                  }
125              }
126              else {
127                  file.delete();
128              }
129              
130             ImageIO.write(list.get(i), "png", file);
131         }
132     }
133     
134     // 3.判斷拆分驗證碼的標准:就是定義驗證碼中包含的各數字的x、y坐標值,及它們的寬度(width)、高度(height)。
135     private static List<BufferedImage> splitImage(BufferedImage image) throws Exception {
136         final int DIGIT_WIDTH = 9;
137         final int DIGIT_HEIGHT = 14;
138 
139         List<BufferedImage> digitImageList = new ArrayList<BufferedImage>();
140         digitImageList.add(image.getSubimage(3, 5, DIGIT_WIDTH, DIGIT_HEIGHT));
141         digitImageList.add(image.getSubimage(17, 8, DIGIT_WIDTH, DIGIT_HEIGHT));
142         digitImageList.add(image.getSubimage(31, 5, DIGIT_WIDTH, DIGIT_HEIGHT));
143         digitImageList.add(image.getSubimage(45, 8, DIGIT_WIDTH, DIGIT_HEIGHT));
144 
145         return digitImageList;
146     }
147     
148 }
package com.Common;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;

public class VerifiCodeUtil {

     public static final String ROOT_PATH = "F:/develop_tool/workspace/DownloadFile";
     
     // 存放下載驗證碼的目錄
     public static final String DOWNLOAD_DIR =ROOT_PATH + "/codeImg/down/verifycode.png";
    
    
   
    // 存放已經拆分開的單個數字圖片的目錄,供比對用
    private static final String TRAIN_DIR = ROOT_PATH + "/codeImg/reference";


    public static final String BACGROUND_DIR = ROOT_PATH + "/codeImg/bacground/verifycode.png";
    //時間戳可以考慮去除
    public static String CODE_URL = "獲取驗證碼的URL?t=";
    
    
 // 存放比對圖片與代表數字的Map
    public static Map<BufferedImage, String> trainMap = new HashMap<BufferedImage, String>();
    
    // 圖片過濾器,想要什么樣的圖片,傳進名稱即可。如:png/gif/.png
    static class ImageFileFilter implements FileFilter {
        private String postfix = ".png";
        
        public ImageFileFilter(String postfix) {
            if(!postfix.startsWith("."))
                postfix = "." + postfix;
            
            this.postfix = postfix;
        }
        
        @Override
        public boolean accept(File pathname) {
            return pathname.getName().toLowerCase().endsWith(postfix);
        }
    }

    static {
        try {
            // 將TRAIN_DIR目錄的供比對的圖片裝載進來
            
            File dir = new File(TRAIN_DIR);
            File[] files = dir.listFiles(new ImageFileFilter("png"));
            for (File file : files) {
                trainMap.put(ImageIO.read(file), file.getName().charAt(0) + "");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    //下載驗證碼到本地
    public static void downLoadImageCode() {
        HttpClient client = new HttpClient();
        String url_address = CODE_URL + new Date().getTime();
        GetMethod get = new GetMethod(url_address);
        

        get.setRequestHeader("Accept-Language", "Accept-Language");
        get.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        get.setRequestHeader("Accept-Encoding", "gzip, deflate");
        
        try {
            client.executeMethod(get);
            File storeFile = new File(DOWNLOAD_DIR);//驗證碼暫存本地位置
            if(storeFile.exists()) {
                storeFile.delete();
            }
            //FileOutputStream output = new FileOutputStream(storeFile);
            InputStream is = get.getResponseBodyAsStream();
            FileOutputStream fos = new FileOutputStream(storeFile);
            byte[] b = new byte[1024];
            while ((is.read(b)) != -1) {
                fos.write(b);
            }
            is.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
    // 取得指定位置的顏色是否為白色,如果超出邊界,返回true
    // 本方法是從removeInterference方法中摘取出來的。單獨調用本方法無意義。
    private static boolean isWhiteColor(BufferedImage image, int x, int y) throws Exception {
        if(x < 0 || y < 0) return true;
        if(x >= image.getWidth() || y >= image.getHeight()) return true;

        Color color = new Color(image.getRGB(x, y));
        
        int num1 = color.getRed()+color.getGreen()+color.getBlue();
//        System.out.println(num1);
        return (num1 > 740)?true:false;
    }
    
    // 4.判斷字體的顏色含義:正常可以用rgb三種顏色加起來表示,字與非字應該有顯示的區別,找出來。
    private static boolean isFontColor(int colorInt) {
        Color color = new Color(colorInt);

        return color.getRed() + color.getGreen() + color.getBlue() < 650;
    }
    
 // 2.去除圖像干擾像素(非必須操作,只是可以提高精度而已)。
    public static BufferedImage removeInterference(BufferedImage image)  
            throws Exception {  
        int width = image.getWidth();  
        int height = image.getHeight();  
        for (int x = 0; x < width; ++x) {  
            for (int y = 0; y < height; ++y) {  
                if (isFontColor(image.getRGB(x, y))) {
                    // 如果當前像素是字體色,則檢查周邊是否都為白色,如都是則刪除本像素。
                    int roundWhiteCount = 0;
                    if(isWhiteColor(image, x+1, y+1))
                        roundWhiteCount++;
                    if(isWhiteColor(image, x+1, y-1))
                        roundWhiteCount++;
                    if(isWhiteColor(image, x-1, y+1))
                        roundWhiteCount++;
                    if(isWhiteColor(image, x-1, y-1))
                        roundWhiteCount++;
                    if(roundWhiteCount == 4) {
                        image.setRGB(x, y, Color.WHITE.getRGB());
                    }
                } 
            }  
        }  
        return image;  
     }
    
    // 3.判斷拆分驗證碼的標准:就是定義驗證碼中包含的各數字的x、y坐標值,及它們的寬度(width)、高度(height)。
    public static List<BufferedImage> splitImage(BufferedImage image) throws Exception {
        final int DIGIT_WIDTH = 9;
        final int DIGIT_HEIGHT = 14;

        List<BufferedImage> digitImageList = new ArrayList<BufferedImage>();
        digitImageList.add(image.getSubimage(3, 5, DIGIT_WIDTH, DIGIT_HEIGHT));
        digitImageList.add(image.getSubimage(17, 8, DIGIT_WIDTH, DIGIT_HEIGHT));
        digitImageList.add(image.getSubimage(31, 5, DIGIT_WIDTH, DIGIT_HEIGHT));
        digitImageList.add(image.getSubimage(45, 8, DIGIT_WIDTH, DIGIT_HEIGHT));

        return digitImageList;
    }
    
    public static String getValidateCode() {
        
        //驗證碼下載到本地
        downLoadImageCode();
        //去除驗證碼的背景
        removeBackground(DOWNLOAD_DIR,BACGROUND_DIR);
        
        //對比獲取驗證碼
        String code = null;
        try {
            code = getValidateCode(new File(BACGROUND_DIR));
        }
        catch(Exception e) {
            
        }
        
        return code;
    }
    /**
     * 8.提供給外界接口調用。
     * @param file
     * @return
     * @throws Exception
     */
    public static String getValidateCode(File file) throws Exception {
        // 裝載圖片
        BufferedImage image = ImageIO.read(file);
        removeInterference(image);
        // 拆分圖片
        List<BufferedImage> digitImageList = splitImage(image);

        // 循環每一位數字圖進行比對
        StringBuilder sb = new StringBuilder();
        for (BufferedImage digitImage : digitImageList) {
            String result = "";
            int width = digitImage.getWidth();
            int height = digitImage.getHeight();
            
            // 最小的不同次數(初始值為總像素),值越小就越像。
            int minDiffCount = width * height;
            for (BufferedImage bi : trainMap.keySet()) {
                // 對每一位數字圖與字典中的進行按像素比較
                int currDiffCount = 0; // 按像素比較不同的次數
                outer : for (int x = 0; x < width; ++x) {
                    for (int y = 0; y < height; ++y) {
                        if (digitImage.getRGB(x, y) != bi.getRGB(x, y)) {
                            // 按像素比較如果不同,則加1;
                            currDiffCount++;
                            // 如果值大於minDiffCount,則不用再比較了,因為我們要找最小的minDiffCount。
                            if (currDiffCount >= minDiffCount) 
                                break outer;
                        }
                    }
                }
                if (currDiffCount < minDiffCount) {
                    // 現在誰差別最小,就先暫時把值賦予給它
                    minDiffCount = currDiffCount;
                    result = trainMap.get(bi);
                }
            }
            sb.append(result);
        }        
        //ImageIO.write(image, "PNG", new File(RESULT_DIR, sb.toString() + ".png"));
        
        return sb.toString();
    }
    
  //將驗證碼轉換成黑白照
    public static void removeBackground(String imgUrl, String resUrl){
        //定義一個臨界閾值
        int threshold = 610;
        try{
            BufferedImage img = ImageIO.read(new File(imgUrl));
            removeInterference(img);
            int width = img.getWidth();
            int height = img.getHeight();
            for(int i = 1;i < width;i++){
                for (int x = 0; x < width; x++){
                    for (int y = 0; y < height; y++){
                        Color color = new Color(img.getRGB(x, y));
                       // System.out.println("red:"+color.getRed()+" | green:"+color.getGreen()+" | blue:"+color.getBlue());
                        int num = color.getRed()+color.getGreen()+color.getBlue();
                        if(num >= threshold){
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }
                }
            }
            for(int i = 1;i<width;i++){
                Color color1 = new Color(img.getRGB(i, 1));
                //System.out.println("color1:" + color1);
                int num1 = color1.getRed()+color1.getGreen()+color1.getBlue();
               // System.out.println("num1:" + color1.getAlpha() + "===rgb:");
                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        Color color = new Color(img.getRGB(x, y));
 
                        int num = color.getRed()+color.getGreen()+color.getBlue();
                        if(num==num1){
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        }else{
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }
                }
            }
            File file = new File(resUrl);
            if (!file.exists())
            {
                File dir = file.getParentFile();
                if (!dir.exists())
                {
                    dir.mkdirs();
                }
                try
                {
                    file.createNewFile();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
            else {
                file.delete();
            }
            removeInterference(img);
            ImageIO.write(img, "png", file);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    
}

圖片的目錄:

 

 借鑒了許多網友的代碼,網上現在也有許多非常優秀的驗證碼識別工具。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM