简单验证码破解程序

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;
        }
    }
}
相关文章
相关标签/搜索