簡單驗證碼破解程序

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;
        }
    }
}
相關文章
相關標籤/搜索