一个简单的验证码识别功能(只能识别纯数字的简单功能)


验证码样例  

 

直接上代码:

  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