驗證碼樣例
直接上代碼:
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(); } } }
圖片的目錄:
借鑒了許多網友的代碼,網上現在也有許多非常優秀的驗證碼識別工具。