Captchacker.java
package com.hjr.apollo.image; import com.hjr.apollo.image.ImageUtils.RGBVector; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * 图片验证码破解 */ public class Captchacker { public static void main(String[] args) throws IOException { String capthcaDir = "d:\\captcha\\"; String captchaUrl = "http://118.122.127.40:8989/servlet/ValidateCodeServlet?d=" + System.currentTimeMillis(); BufferedImage captcha = ImageUtils.readImageFromUrl(captchaUrl); ImageUtils.saveImage(captcha, capthcaDir + System.currentTimeMillis() + ".png"); // 图片二值化 BufferedImage binaryImage = toBinary(captcha); ImageUtils.saveImage(binaryImage, capthcaDir + System.currentTimeMillis() + "-b.png"); // 删除边线 BufferedImage noBorderImage = clearBorder(binaryImage); ImageUtils.saveImage(noBorderImage, capthcaDir + System.currentTimeMillis() + "-nb.png"); // 去噪点 BufferedImage noNoicePointImage = clearNoicePoint(noBorderImage); ImageUtils.saveImage(noNoicePointImage, capthcaDir + System.currentTimeMillis() + "-cp.png"); // 去干扰线 BufferedImage noNoiceLingImage = clearNoiceLine(noNoicePointImage); ImageUtils.saveImage(noNoiceLingImage, capthcaDir + System.currentTimeMillis() + "-cl.png"); // 验证码字符切割 List<BufferedImage> subImgs = splitByChar(noNoiceLingImage);//流水算法 for (int i = 0; i < subImgs.size(); i++) { BufferedImage bi = subImgs.get(i); ImageUtils.saveImage(bi, capthcaDir + System.currentTimeMillis() + "-sub" + i + ".png"); } // 大小归一化 } /** * 把图片进行二值化处理 * @param image 待处理的图片 * @return */ public static BufferedImage toBinary(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); int whiteRGB = Color.WHITE.getRGB(); int blackRGB = Color.BLACK.getRGB(); for (int i = 0 ; i < width ; i++) { for (int j = 0 ; j < height; j++) { RGBVector rgb = ImageUtils.parseRGBVector(image.getRGB(i, j)); int min = Math.min(rgb.getRed(), rgb.getGreen()); min = Math.min(min, rgb.getBlue()); if (min > 200) { binaryImage.setRGB(i, j, whiteRGB); } else { binaryImage.setRGB(i, j, blackRGB); } } } return binaryImage; } /** * 删除图片边线 * @param image 待处理图片 * @return */ public static BufferedImage clearBorder(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); int whiteRGB = Color.WHITE.getRGB(); for (int i = 0 ; i < width ; i++) { for (int j = 0 ; j < height; j++) { // 边线点设为白色点 if (i == 0 || j == 0 || i == width - 1 || j == height - 1) { binaryImage.setRGB(i, j, whiteRGB); continue; } binaryImage.setRGB(i, j, image.getRGB(i, j)); } } return binaryImage; } /** * 删除噪点:八邻域 * @param image 待处理图片 * @return */ public static BufferedImage clearNoicePoint(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); int whiteRGB = Color.WHITE.getRGB(); int blackRGB = Color.BLACK.getRGB(); for (int i = 0 ; i < width; i++) { for (int j = 0 ; j < height; j++) { int rgb = image.getRGB(i, j); if (i == 0 || j == 0 || i == width - 1 || j == height - 1) { binaryImage.setRGB(i, j, rgb); continue; } if (rgb == whiteRGB) { binaryImage.setRGB(i, j, whiteRGB); continue; } // 判断八邻域是否有黑点,有则保存 if (blackRGB == image.getRGB(i-1, j) || blackRGB == image.getRGB(i-1, j-1) || blackRGB == image.getRGB(i-1, j+1) || blackRGB == image.getRGB(i, j-1) || blackRGB == image.getRGB(i, j+1) || blackRGB == image.getRGB(i+1, j) || blackRGB == image.getRGB(i+1, j-1) || blackRGB == image.getRGB(i+1, j+1)) { binaryImage.setRGB(i, j, blackRGB); continue; } binaryImage.setRGB(i, j, whiteRGB); } } return binaryImage; } /** * 删除干扰线:八邻域 * @param image 待处理图片 * @return */ public static BufferedImage clearNoiceLine(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); int whiteRGB = Color.WHITE.getRGB(); int blackRGB = Color.BLACK.getRGB(); for (int i = 0 ; i < width; i++) { for (int j = 0 ; j < height; j++) { int rgb = image.getRGB(i, j); if (i == 0 || j == 0 || i == width - 1 || j == height - 1) { binaryImage.setRGB(i, j, rgb); continue; } if (rgb == whiteRGB) { binaryImage.setRGB(i, j, whiteRGB); continue; } int sum = blackRGB == image.getRGB(i-1, j) ? 1 : 0; sum += blackRGB == image.getRGB(i-1, j-1) ? 1 : 0; sum += blackRGB == image.getRGB(i-1, j+1) ? 1 : 0; sum += blackRGB == image.getRGB(i, j-1) ? 1 : 0; sum += blackRGB == image.getRGB(i, j+1) ? 1 : 0; sum += blackRGB == image.getRGB(i+1, j) ? 1 : 0; sum += blackRGB == image.getRGB(i+1, j-1) ? 1 : 0; sum += blackRGB == image.getRGB(i+1, j+1) ? 1 : 0; // 8邻域黑点数小于4个断定为干扰线上的点 binaryImage.setRGB(i, j, sum < 4 ? whiteRGB : blackRGB); } } return binaryImage; } /** * 流水算法"精华部分.有空隙的图完全拆开(好比"回"字会被拆成大小俩"口"字) * @param image 须要拆解的图(java.awt.image.BufferedImage) */ private static List<BufferedImage> splitByChar(BufferedImage image) { // 用于装填拆解后的图片碎块 List<BufferedImage> subImgs = new ArrayList<BufferedImage>(); int minPix = 50;// 单个图片最小的像素数,下面会抛弃小于这个像素数的小图块 // 获取图片宽高 int width = image.getWidth(); int height = image.getHeight(); // 用于装填每一个图块的点数据 List<HashMap<Point, Integer>> pointList = new ArrayList<HashMap<Point, Integer>>(); // 根据宽高轮询图片中的全部点进行计算 for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { //找到一个点 label: if (isBlack(image.getRGB(x, y))) { Point point = new Point(x, y);//java.awt.Point for (HashMap<Point, Integer> pointMap : pointList) { if (pointMap.get(point) != null) { break label;//跳到标签处,此时不会再执行下面的内容. } } HashMap<Point, Integer> pointMap = new HashMap<Point, Integer>(); // 这个用法很关键,根据Map的KEY值不能重复的特色避免重复填充point pointMap.put(point, 1); // 这里就是在流水啦... get4Point(x, y, image, pointMap); pointList.add(pointMap); break; } } } // 根据提取出来的point建立各个碎图块 for (int i = 0; i < pointList.size(); ++i) { HashMap<Point, Integer> pointMap = pointList.get(i); // 图片的左,上,右,下边界以及宽,高 int l = 0, t = 0, r = 0, b = 0, w = 0, h = 0, index = 0; for (Point p : pointMap.keySet()) { if (index == 0) { // 用第一个点来初始化碎图的四个边界 l = p.x; t = p.y; r = p.x; b = p.y; } else { // 再根据每一个点与原有的点进行比较取舍四个边界的值 l = Math.min(l, p.x); t = Math.min(t, p.y); r = Math.max(r, p.x); b = Math.max(b, p.y); } index++; } w = r - l + 1; h = b - t + 1; // 去除杂点(小于50像素数量的点集不要) if (w * h < minPix) continue; // 建立个图片空壳子(里面的全部点值都是0,即黑色) BufferedImage imgCell = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); // 先将全部点替换成白色(反正我没有找到new BufferedImage的时候能够初始化像素色值的) for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { imgCell.setRGB(x, y, 0xffffffff);//图片换成白底 } } // 对应点换成黑色(若是上面换成白底的一步不作的话下面这里能够替换成白色,就变成了黑底白色的碎图了) for (Point p : pointMap.keySet()) { imgCell.setRGB(p.x - l, p.y - t, 0); } // 将切好的图放入上文传入的容器中(不传入容器的话这里能够用于返回) subImgs.add(imgCell); } // 耗时 return subImgs; } /** * 填进来上下左右中不是白色的点 * 递归 * @return */ private static void get4Point(int x, int y, BufferedImage img, HashMap<Point, Integer> pointMap) { // 左边 Point pl = new Point(x - 1, y); if (x - 1 >= 0 && isBlack(img.getRGB(x - 1, y)) && pointMap.get(pl) == null) { pointMap.put(pl, 1); get4Point(x - 1, y, img, pointMap); } // 右边 Point pr = new Point(x + 1, y); if (x + 1 < img.getWidth() && isBlack(img.getRGB(x + 1, y)) && pointMap.get(pr) == null) { pointMap.put(pr, 1); get4Point(x + 1, y, img, pointMap); } // 上边 Point pt = new Point(x, y - 1); if (y - 1 >= 0 && isBlack(img.getRGB(x, y - 1)) && pointMap.get(pt) == null) { pointMap.put(pt, 1); get4Point(x, y - 1, img, pointMap); } // 下边 Point pb = new Point(x, y + 1); if (y + 1 < img.getHeight() && isBlack(img.getRGB(x, y + 1)) && pointMap.get(pb) == null) { pointMap.put(pb, 1); get4Point(x, y + 1, img, pointMap); } } /** * 判断是否是黑色[这里的黑色指暗色],实际上本程序处理过的颜色,黑就是纯黑,值=0 * @param colorInt * @return */ public static boolean isBlack(int colorInt) { int threshold = 150;// 色域,用于界定多少范围的色值是噪色 Color color = new Color(colorInt); return color.getRed() + color.getGreen() + color.getBlue() <= threshold * 3; } }
ImageUtils.java
package com.hjr.apollo.image; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; /** * 图片处理工具集 */ public class ImageUtils { /** * 从指定连接加载图片信息 * @param url 图片连接 * @return */ public static BufferedImage readImageFromUrl(String url) throws IOException { return ImageIO.read(new URL(url)); } /** * 把图片保存到指定路径 * @param im 图片信息 * @param imagePath 图片待保存路径 * @throws IOException */ public static void saveImage(RenderedImage im, String imagePath) throws IOException { saveImage(im, "png", imagePath); } /** * 把图片按指定格式,保存到指定路径 * @param im 图片信息 * @param formatName 图片待保存格式 * @param imagePath 图片待保存路径 * @throws IOException */ public static void saveImage(RenderedImage im, String formatName, String imagePath) throws IOException { ImageIO.write(im,formatName, new File(imagePath)); } /** * 解析R、G、B三个份量的值 * @param argb * @return */ public static RGBVector parseRGBVector(int argb) { return RGBVector.parseRGBVector(argb); } public static class RGBVector { private int red; private int green; private int blue; public static RGBVector parseRGBVector(int argb) { return new RGBVector(argb); } public RGBVector() { } public RGBVector(int argb) { red = argb >> 16 & 0xFF; green = argb >> 8 & 0xFF; blue = argb & 0xFF; } public int getRed() { return red; } public void setRed(int red) { this.red = red; } public int getGreen() { return green; } public void setGreen(int green) { this.green = green; } public int getBlue() { return blue; } public void setBlue(int blue) { this.blue = blue; } } }