验证码样例
直接上代码:
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(); } } }
图片的目录:
借鉴了许多网友的代码,网上现在也有许多非常优秀的验证码识别工具。