第一个类
package com.rpa.client.commons.util; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.*; /** * Created by duyizhen on 2018/4/28. */ public class SafeCodeUtilsForSZ { private static Map<BufferedImage, String> trainMap = null; // 训练库路径 private static String train_path = "D:\\RPA\\train2\\shenzhen"; private static String safeCodePath = "D:/RPA/safecode/"; public static String getSafeCode(String safeCodeName) { File testDataDir = new File(safeCodePath + safeCodeName); final String destDir = safeCodePath; // 去噪 try { // 验证码原图 二值化 binaryzationPic(safeCodePath + safeCodeName, safeCodePath + safeCodeName); // 进行图片验证 String allChar = getAllChar(safeCodePath + safeCodeName); if (allChar!=null&&allChar.length() == 4) { System.out.println("验证码读取完成!"); return allChar; } else { System.out.println("验证码读取失败!"); return null; } } catch (Exception e) { System.out.println("图片读取异常!"); e.printStackTrace(); } return null; } // 获取所有的字符 public static String getAllChar(String file) throws Exception { // 获取图像 File imageFileA = new File(file); BufferedImage image = ImageIO.read(imageFileA); java.util.List<BufferedImage> listImg = SafeCodeUtils.splitImage(image);// 切割图片 if(listImg.size()!=4) { return null; } // 缩小成32x32的缩略图 String result = ""; for (BufferedImage imageA : listImg) { imageA = SafeCodeUtils.scale(imageA); // 获取灰度像素数组 int[] pixelsA = SafeCodeUtils.getPixels(imageA); // 获取平均灰度颜色 int averageColorA = SafeCodeUtils.getAverageOfPixelArray(pixelsA); // 获取灰度像素的比较数组(即图像指纹序列) pixelsA = SafeCodeUtils.getPixelDeviateWeightsArray(pixelsA, averageColorA); trainMap = loadTrainData(); result += SafeCodeUtils.getSingleChar(pixelsA, trainMap); } // ImageIO.write(img, "JPG", new File("result6\\" + result + ".jpg")); return result; } // 加载训练库 public static Map<BufferedImage, String> loadTrainData() throws Exception { if (trainMap == null) { Map<BufferedImage, String> map = new HashMap<BufferedImage, String>(); File dir = new File(train_path); File[] files = dir.listFiles(); for (File file : files) { map.put(ImageIO.read(file), file.getName().charAt(0) + ""); } System.out.println("加载训练库完成"); trainMap = map; } return trainMap; } /** * 图片二值化 * 首先拿到原有图片的色彩矩阵数据 * 然后根据原有图片大小创建新的图片 * 通过每个点灰度与阙值进行对比来判断颜色设置 * @param startFilePath * @param endFilePath */ public static void binaryzationPic(String startFilePath, String endFilePath){ // 图片二值化 BufferedImage bi= null;//通过imageio将图像载入 try { bi = ImageIO.read(new File(startFilePath)); } catch (IOException e) { e.printStackTrace(); } int h = bi.getHeight();//获取图像的高 int w = bi.getWidth();//获取图像的宽 //int rgb = bi.getRGB(0, 0);//获取指定坐标的ARGB的像素值 int[][] gray = new int[w][h]; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { // 获取每个点的ARGB像素值 rgb三点度数的三分之一 gray[x][y]=getGray(bi.getRGB(x, y)); } } // 重新创建一个图像 BufferedImage nbi = new BufferedImage(w,h,BufferedImage.TYPE_BYTE_BINARY); int SW=160; // 灰度阙值 可以设定 for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { // 便利 设置颜色 if(getAverageColor(gray, x, y, w, h) > SW){ //int max = new Color(255,255,255).getRGB(); nbi.setRGB(x, y, new Color(255,255,255).getRGB()); }else{ //int min = new Color(0,0,0).getRGB(); nbi.setRGB(x, y, new Color(0,0,0).getRGB()); } } } try { ImageIO.write(nbi, "jpg", new File(endFilePath)); } catch (IOException e) { e.printStackTrace(); } } /** * 获取gray * @param rgb * @return */ public static int getGray(int rgb){ String str=Integer.toHexString(rgb); //int r = Integer.parseInt(str.substring(2,4),16); // 输出一个十进制数 radix表示要被转换的是什么进制 //int g = Integer.parseInt(str.substring(4,6),16); //int b = Integer.parseInt(str.substring(6,8),16); //or 直接new个color对象 Color c = new Color(rgb); int r = c.getRed(); int g = c.getGreen(); int b = c.getBlue(); int top= (r+g+b) / 3; return (int)(top); } /** * 自己加周围8个灰度值再除以9,算出其相对灰度值 * @param gray * @param x * @param y * @param w * @param h * @return */ public static int getAverageColor(int[][] gray, int x, int y, int w, int h) { // 边缘位置取255 int rs = gray[x][y] + (x == 0 ? 255 : gray[x - 1][y]) + (x == 0 || y == 0 ? 255 : gray[x - 1][y - 1]) + (x == 0 || y == h - 1 ? 255 : gray[x - 1][y + 1]) + (y == 0 ? 255 : gray[x][y - 1]) + (y == h - 1 ? 255 : gray[x][y + 1]) + (x == w - 1 ? 255 : gray[x + 1][ y]) + (x == w - 1 || y == 0 ? 255 : gray[x + 1][y - 1]) + (x == w - 1 || y == h - 1 ? 255 : gray[x + 1][y + 1]); return rs / 9; } }
第二个类
package com.rpa.client.commons.util; import javax.imageio.ImageIO; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.io.File; import java.io.IOException; import java.util.*; import java.util.List; public class SafeCodeUtils { private static Map<BufferedImage, String> trainMap = null; // 训练库路径 private static String train_path = "D:/RPA/train2"; private static String safeCodePath = "D:/RPA/safecode/"; public static String getSafeCode(String safeCodeName) { File testDataDir = new File(safeCodePath + safeCodeName); final String destDir = safeCodePath; // 去噪 try { cleanLinesInImage(testDataDir, destDir); String allChar = getAllChar(safeCodePath + safeCodeName); if (allChar!=null&&allChar.length() == 4) { System.out.println("验证码读取完成!"); return allChar; } else { System.out.println("验证码读取失败!"); return null; } } catch (Exception e) { System.out.println("图片读取异常!"); e.printStackTrace(); } return null; } /** * * @param sfile * 需要去噪的图像 * @param destDir * 去噪后的图像保存地址 * @throws IOException */ public static void cleanLinesInImage(File sfile, String destDir) throws IOException { File destF = new File(destDir); if (!destF.exists()) { destF.mkdirs(); } BufferedImage bufferedImage = ImageIO.read(sfile); int h = bufferedImage.getHeight(); int w = bufferedImage.getWidth(); // 灰度化 int[][] gray = new int[w][h]; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int argb = bufferedImage.getRGB(x, y); // 图像加亮(调整亮度识别率非常高) int r = (int) (((argb >> 16) & 0xFF) * 1.1 + 30); int g = (int) (((argb >> 8) & 0xFF) * 1.1 + 30); int b = (int) (((argb >> 0) & 0xFF) * 1.1 + 30); if (r >= 255) { r = 255; } if (g >= 255) { g = 255; } if (b >= 255) { b = 255; } gray[x][y] = (int) Math.pow( (Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2) * 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2); } } // 二值化 int threshold = ostu(gray, w, h); BufferedImage binaryBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { if (gray[x][y] > threshold) { gray[x][y] |= 0x00FFFF; } else { gray[x][y] &= 0xFF0000; } binaryBufferedImage.setRGB(x, y, gray[x][y]); } } // 去除干扰线条 for (int y = 1; y < h - 1; y++) { for (int x = 1; x < w - 1; x++) { boolean flag = false; if (isBlack(binaryBufferedImage.getRGB(x, y))) { // 左右均为空时,去掉此点 if (isWhite(binaryBufferedImage.getRGB(x - 1, y)) && isWhite(binaryBufferedImage.getRGB(x + 1, y))) { flag = true; } // 上下均为空时,去掉此点 if (isWhite(binaryBufferedImage.getRGB(x, y + 1)) && isWhite(binaryBufferedImage.getRGB(x, y - 1))) { flag = true; } // 斜上下为空时,去掉此点 if (isWhite(binaryBufferedImage.getRGB(x - 1, y + 1)) && isWhite(binaryBufferedImage.getRGB(x + 1, y - 1))) { flag = true; } if (isWhite(binaryBufferedImage.getRGB(x + 1, y + 1)) && isWhite(binaryBufferedImage.getRGB(x - 1, y - 1))) { flag = true; } if (flag) { binaryBufferedImage.setRGB(x, y, -1); } } } } // 矩阵打印 // for (int y = 0; y < h; y++) { // for (int x = 0; x < w; x++) { // if (isBlack(binaryBufferedImage.getRGB(x, y))) { // System.out.print("*"); // } else { // System.out.print(" "); // } // } // System.out.println(); // } ImageIO.write(binaryBufferedImage, "jpg", new File(destDir, sfile.getName())); } // 判断是否为黑 public static boolean isBlack(int colorInt) { Color color = new Color(colorInt); if (color.getRed() + color.getGreen() + color.getBlue() <= 300) { return true; } return false; } // 判断是否为白 public static boolean isWhite(int colorInt) { Color color = new Color(colorInt); if (color.getRed() + color.getGreen() + color.getBlue() > 300) { return true; } return false; } public static int isBlackOrWhite(int colorInt) { if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730) { return 1; } return 0; } public static int getColorBright(int colorInt) { Color color = new Color(colorInt); return color.getRed() + color.getGreen() + color.getBlue(); } public static int ostu(int[][] gray, int w, int h) { int[] histData = new int[w * h]; // Calculate histogram for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int red = 0xFF & gray[x][y]; histData[red]++; } } // Total number of pixels int total = w * h; float sum = 0; for (int t = 0; t < 256; t++) sum += t * histData[t]; float sumB = 0; int wB = 0; int wF = 0; float varMax = 0; int threshold = 0; for (int t = 0; t < 256; t++) { wB += histData[t]; // Weight Background if (wB == 0) continue; wF = total - wB; // Weight Foreground if (wF == 0) break; sumB += (float) (t * histData[t]); float mB = sumB / wB; // Mean Background float mF = (sum - sumB) / wF; // Mean Foreground // Calculate Between Class Variance float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF); // Check if new maximum found if (varBetween > varMax) { varMax = varBetween; threshold = t; } } return threshold; } // 获取相似度最高的字符 public static void getSingleChar(String imageAname, int[] pixelsA) throws Exception { Map<BufferedImage, String> map = loadTrainData(); Map<Double, String> similar = new HashMap<Double, String>(); double similarity = 0; for (BufferedImage bi : map.keySet()) { // File imageFileB = new File("D:/train2/j-14.jpg"); // Image imageB = ImageIO.read(imageFileB); BufferedImage imageB = scale(bi); int[] pixelsB = getPixels(imageB); int averageColorB = getAverageOfPixelArray(pixelsB); pixelsB = getPixelDeviateWeightsArray(pixelsB, averageColorB); // 获取两个图的汉明距离(假设另一个图也已经按上面步骤得到灰度比较数组) int hammingDistance = getHammingDistance(pixelsA, pixelsB); // 通过汉明距离计算相似度,取值范围 [0.0, 1.0] similarity = calSimilarity(hammingDistance); similar.put(similarity, map.get(bi)); if (similarity > 0.5) { System.out.println(imageAname + "与" + map.get(bi) + "的相似度:" + similarity); } } if (similarity > 0 && similar != null) { Set<Double> keySet = similar.keySet(); Object[] key = keySet.toArray(); Arrays.sort(key); double max = (double) key[keySet.size() - 1]; String name = similar.get(max); System.out.println(imageAname + "与" + name + "的相似度最大,最大相似度是:" + max); } } // 将任意Image类型图像转换为BufferedImage类型,方便后续操作 public static BufferedImage convertToBufferedFrom(Image srcImage) { BufferedImage bufferedImage = new BufferedImage(srcImage.getWidth(null), srcImage.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g = bufferedImage.createGraphics(); g.drawImage(srcImage, null, null); g.dispose(); return bufferedImage; } // 转换至灰度图 public static BufferedImage toGrayscale(Image image) { BufferedImage sourceBuffered = convertToBufferedFrom(image); ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); ColorConvertOp op = new ColorConvertOp(cs, null); BufferedImage grayBuffered = op.filter(sourceBuffered, null); return grayBuffered; } // 缩放至32x32像素缩略图 public static BufferedImage scale(BufferedImage image) { Image img = image; img = img.getScaledInstance(32, 32, Image.SCALE_SMOOTH); BufferedImage image2 = convertToBufferedFrom(img); return image2; } // 获取像素数组 public static int[] getPixels(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); int[] pixels = image.getRGB(0, 0, width, height, null, 0, width); return pixels; } // 获取灰度图的平均像素颜色值 public static int getAverageOfPixelArray(int[] pixels) { Color color; long sumRed = 0; for (int i = 0; i < pixels.length; i++) { color = new Color(pixels[i], true); sumRed += color.getRed(); } int averageRed = (int) (sumRed / pixels.length); return averageRed; } // 获取灰度图的像素比较数组(平均值的离差) public static int[] getPixelDeviateWeightsArray(int[] pixels, final int averageColor) { Color color; int[] dest = new int[pixels.length]; for (int i = 0; i < pixels.length; i++) { color = new Color(pixels[i], true); dest[i] = color.getRed() - averageColor > 0 ? 1 : 0; } return dest; } // 获取两个缩略图的平均像素比较数组的汉明距离(距离越大差异越大) public static int getHammingDistance(int[] a, int[] b) { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i] == b[i] ? 0 : 1; } return sum; } // 通过汉明距离计算相似度 public static double calSimilarity(int hammingDistance) { int length = 32 * 32; double similarity = (length - hammingDistance) / (double) length; // 使用指数曲线调整相似度结果 similarity = java.lang.Math.pow(similarity, 2); return similarity; } // 加载训练库 public static Map<BufferedImage, String> loadTrainData() throws Exception { if (trainMap == null) { Map<BufferedImage, String> map = new HashMap<BufferedImage, String>(); File dir = new File(train_path); File[] files = dir.listFiles(); for (File file : files) { map.put(ImageIO.read(file), file.getName().charAt(0) + ""); } System.out.println("加载训练库完成"); trainMap = map; } return trainMap; } // 获取所有的字符 public static String getAllChar(String file) throws Exception { // 获取图像 File imageFileA = new File(file); BufferedImage image = ImageIO.read(imageFileA); List<BufferedImage> listImg = splitImage(image);// 切割图片 if(listImg.size()!=4) { return null; } String result = ""; for (BufferedImage imageA : listImg) { // 缩小成32x32的缩略图 imageA = scale(imageA); // 获取灰度像素数组 int[] pixelsA = getPixels(imageA); // 获取平均灰度颜色 int averageColorA = getAverageOfPixelArray(pixelsA); // 获取灰度像素的比较数组(即图像指纹序列) pixelsA = getPixelDeviateWeightsArray(pixelsA, averageColorA); trainMap = loadTrainData(); result += getSingleChar(pixelsA, trainMap); } // ImageIO.write(img, "JPG", new File("result6\\" + result + ".jpg")); return result; } // 切割图片 /** * 测试每个 x 轴点上的y直线是否经过黑色 * @param img * @return * @throws Exception */ public static List<BufferedImage> splitImage(BufferedImage img) throws Exception { List<BufferedImage> subImgs = new ArrayList<BufferedImage>(); int width = img.getWidth(); int height = img.getHeight(); List<Integer> weightlist = new ArrayList<Integer>(); // 获取图片中含有字符部分的宽度 for (int x = 0; x < width; ++x) { int count = 0; for (int y = 0; y < height; ++y) { if (isBlackTo1(img.getRGB(x, y)) == 1) { count++; } } weightlist.add(count); } // 主流程 副流程 使用相同的判断标准 两种流程判断同时候向前走 // 副流程记录所需要的位置 for (int i = 0; i < weightlist.size(); i++) { int length = 0; while (i < weightlist.size() && weightlist.get(i) > 0) { i++; length++; } if (length > 2) { subImgs.add(removeBlank(img.getSubimage(i - length, 0, length, height))); } } return subImgs; } // 获取单个字符 public static String getSingleChar(int[] pixelsA, Map<BufferedImage, String> map) throws Exception { Map<Double, String> similar = new HashMap<Double, String>(); double similarity = 0; for (BufferedImage bi : map.keySet()) { // File imageFileB = new File("D:/train2/j-14.jpg"); // Image imageB = ImageIO.read(imageFileB); BufferedImage imageB = scale(bi); int[] pixelsB = getPixels(imageB); int averageColorB = getAverageOfPixelArray(pixelsB); pixelsB = getPixelDeviateWeightsArray(pixelsB, averageColorB); // 获取两个图的汉明距离(假设另一个图也已经按上面步骤得到灰度比较数组) int hammingDistance = getHammingDistance(pixelsA, pixelsB); // 通过汉明距离计算相似度,取值范围 [0.0, 1.0] similarity = calSimilarity(hammingDistance); similar.put(similarity, map.get(bi)); if (similarity > 0.5) { // System.out.println(imageAname + "与" + map.get(bi) + "的相似度:" + similarity); } } // if (similarity > 0 && similar != null) { Set<Double> keySet = similar.keySet(); Object[] key = keySet.toArray(); Arrays.sort(key); double max = (double) key[keySet.size() - 1]; String name = similar.get(max); // System.out.println(imageAname + "与" + name + "的相似度最大,最大相似度是:" + max); // } return name; } // 判断颜色为黑 public static int isBlackTo1(int colorInt) { Color color = new Color(colorInt); if (color.getRed() + color.getGreen() + color.getBlue() <= 100) { return 1; } return 0; } // 判断颜色为白 public static int isWhiteTo1(int colorInt) { Color color = new Color(colorInt); if (color.getRed() + color.getGreen() + color.getBlue() > 600) { return 1; } return 0; } // 移除空白 public static BufferedImage removeBlank(BufferedImage img) throws Exception { int width = img.getWidth(); int height = img.getHeight(); int start = 0; int end = 0; Label1: for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { if (isBlackTo1(img.getRGB(x, y)) == 1) { start = y; break Label1; } } } Label2: for (int y = height - 1; y >= 0; --y) { for (int x = 0; x < width; ++x) { if (isBlackTo1(img.getRGB(x, y)) == 1) { end = y; break Label2; } } } return img.getSubimage(0, start, width, end - start + 1); } }
java识别图像验证码 本质上是通过穷举的方式来识别验证码, 首先拿到所有的图片可能, 然后对图片进行二值化处理, 生成对应的验证码库, 对于新得到的图片 首先进行二值化 处理, 然后去比较与库中图片的汉明距离, 根据汉明距离来进行验证码推测