java 通过穷举方式识别图片验证码

 
  
第一个类
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识别图像验证码 本质上是通过穷举的方式来识别验证码, 首先拿到所有的图片可能,  然后对图片进行二值化处理, 生成对应的验证码库,  对于新得到的图片 首先进行二值化 处理,  然后去比较与库中图片的汉明距离, 根据汉明距离来进行验证码推测


你可能感兴趣的:(java 通过穷举方式识别图片验证码)