1. 程式人生 > >驗證碼去除干擾線

驗證碼去除干擾線

驗證碼是防止和阻止非人為性的請求訪問的一道屏障,有時候要去網上“瀏覽一些資料”,當遇到驗證碼的時候不得不突破驗證碼這道屏障。但是驗證碼的種類繁多,這不得不說給破解驗證碼的盆友們帶來了巨大的阻礙。

我之前發表的幾篇部落格也在講破解不同種類驗證碼的演算法,還有源程式,有興趣的可以看看。

1、干擾線顏色和驗證碼字元顏色不同

http://shixin.court.gov.cn/captchaNew.do?captchaId=ce1fcc27aeab4b8ab769d043b5b9a4ac&random=0.4211629195644244(個別的驗證碼帶干擾線),像這種帶干擾線的有一個特點,每一個驗證碼字元基本都是一種顏色,干擾線是另外不同的顏色。
那麼這樣的驗證碼就可以用顏色來區別哪些是字元哪些是干擾線,經過我大量的分析發現乃至90%以上的驗證碼都有一個共同的特徵,四種驗證碼字元所帶顏色是整個驗證碼中比例最高的前四。這樣一來就可以統計驗證碼中佔有分量最高的前四位,並且把RGB顏色記錄下裡,如果不是這種顏色統統的填充成白色,這樣一來就解決了干擾線的問題。
但是如果你要去認真做的話你會發現由於干擾線被填充成了白色,字元也被白線分割成了兩截,在做字元分割的時候很有可能把一個字元從白色的干擾線部分分割成兩截,導致分割失敗。
那麼為了解決這個問題我又寫了一個演算法來修補去幹擾線留下來的“後遺症”。如果驗證碼被從中間分割兩截的話,被截斷的地方上下兩個方向都會有字元,我利用這個現象在驗證碼二值化之後做了修復的工作。

public static BufferedImage reline(BufferedImage curImg) {
        if (curImg != null) {
            int width = curImg.getWidth();
            int height = curImg.getHeight();
            int px = 3;
            Map<Integer, Integer> map = new HashMap<Integer, Integer>();
            for
(int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int argb = curImg.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); int sum = r + g + b; if (!map.containsKey(sum)) { map.put(sum, 1); } else { int num = map.get(sum); map.remove(sum); map.put(sum, num + 1); } } } List<Integer> list = new ArrayList<Integer>(); for (Integer in : map.keySet()) { // map.keySet()返回的是所有key的值 Integer n = map.get(in);// 得到每個key多對用value的值 list.add(n); } Collections.sort(list); // 四種顏色的rgb int num1 = 0; int num2 = 0; int num3 = 0; int num4 = 0; if (list.size() > 4) { num1 = list.get(list.size() - 5); num2 = list.get(list.size() - 4); num3 = list.get(list.size() - 3); num4 = list.get(list.size() - 2); } List<Integer> keylist = new ArrayList<Integer>(); for (Integer key : map.keySet()) { if (map.get(key) == num1 || map.get(key) == num2 || map.get(key) == num3 || map.get(key) == num4) { keylist.add(key); } } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int argb = curImg.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); int sum = r + g + b; int sum1 = 0; int sum2 = 0; int sum3 = 0; int sum4 = 0; int sum5 = 0; int sum6 = 0; boolean flag = true; for (int i = 1; i <= px && y + i < height && y - i > 0 && x - i > 0 && x + i < width; i++) { int upargb = curImg.getRGB(x, y - i); int endargb = curImg.getRGB(x, y + i); int rightupargb = curImg.getRGB(x + i, y + i); int leftupargb = curImg.getRGB(x - i, y + i); int leftdownargb = curImg.getRGB(x - i, y - i); int rightdownargb = curImg.getRGB(x + i, y - i); int r1 = (int) (((upargb >> 16) & 0xFF) * 1.1 + 30); int g1 = (int) (((upargb >> 8) & 0xFF) * 1.1 + 30); int b1 = (int) (((upargb >> 0) & 0xFF) * 1.1 + 30); sum1 = r1 + g1 + b1; int r2 = (int) (((endargb >> 16) & 0xFF) * 1.1 + 30); int g2 = (int) (((endargb >> 8) & 0xFF) * 1.1 + 30); int b2 = (int) (((endargb >> 0) & 0xFF) * 1.1 + 30); sum2 = r2 + g2 + b2; int r3 = (int) (((rightupargb >> 16) & 0xFF) * 1.1 + 30); int g3 = (int) (((rightupargb >> 8) & 0xFF) * 1.1 + 30); int b3 = (int) (((rightupargb >> 0) & 0xFF) * 1.1 + 30); sum3 = r3 + g3 + b3; int r4 = (int) (((leftupargb >> 16) & 0xFF) * 1.1 + 30); int g4 = (int) (((leftupargb >> 8) & 0xFF) * 1.1 + 30); int b4 = (int) (((leftupargb >> 0) & 0xFF) * 1.1 + 30); sum4 = r4 + g4 + b4; int r5 = (int) (((leftdownargb >> 16) & 0xFF) * 1.1 + 30); int g5 = (int) (((leftdownargb >> 8) & 0xFF) * 1.1 + 30); int b5 = (int) (((leftdownargb >> 0) & 0xFF) * 1.1 + 30); sum5 = r5 + g5 + b5; int r6 = (int) (((rightdownargb >> 16) & 0xFF) * 1.1 + 30); int g6 = (int) (((rightdownargb >> 8) & 0xFF) * 1.1 + 30); int b6 = (int) (((rightdownargb >> 0) & 0xFF) * 1.1 + 30); sum6 = r6 + g6 + b6; if (keylist.contains(sum1) || keylist.contains(sum2) || keylist.contains(sum3) || keylist.contains(sum4) || keylist.contains(sum5) || keylist.contains(sum6)) { flag = false; } } if (!(keylist.contains(sum)) && flag) { curImg.setRGB(x, y, Color.white.getRGB()); } } } } return curImg; }

2、干擾線顏色和驗證碼字元顏色一樣

https://s.nacao.org.cn/servlet/ValidateCode?time=這種驗證碼看上去明顯複雜了很多,通過分析發現這個驗證碼每個字元基本上都保持一定的位置,最前面和最後面一段沒有字元內容,我的想法是果斷截斷只保留有字元的部分。剩下的不難看出,干擾線要比字元看上去細點,通過粗細很容易判斷出哪是字元哪是干擾線,設定一個px 的大小值,過濾掉干擾線,然後再用修復演算法把隔斷的白色部分給補上。

private static BufferedImage removeLine(BufferedImage img, int px) {
        if (img != null) {
            int width = img.getWidth();
            int height = img.getHeight();

            for (int x = 0; x < width; x++) {
                List<Integer> list = new ArrayList<Integer>();
                for (int y = 0; y < height; y++) {
                    int count = 0;
                    while (y < height - 1 && isBlack(img.getRGB(x, y))) {
                        count++;
                        y++;
                    }
                    if (count <= px && count > 0) {
                        for (int i = 0; i <= count; i++) {
                            list.add(y - i);
                        }
                    }
                }
                if (list.size() != 0) {
                    for (int i = 0; i < list.size(); i++) {
                        img.setRGB(x, list.get(i), Color.white.getRGB());
                    }
                }
            }
        }
        return img;

    }

    public static boolean isBlack(int rgb){
    Color c = new Color(rgb);
    int b = c.getBlue();
    int r = c.getRed();
    int g = c.getGreen();
    int sum = r+g+b
    if(sum<10){
        return true;
    }
    return false;
    //sum的值越小(最小為零,黑色)顏色越重,
    //sum的值越大(最大值是225*3)顏色越淺,
    //sum的值小於10就算是黑色了.
    }

下載的圖片這裡寫圖片描述

截斷後的圖片這裡寫圖片描述

去背景後的圖片這裡寫圖片描述

去幹擾線後的圖片這裡寫圖片描述

看上去有點傷啊。但是隻要不影響識別就OK!

如果對您有所幫助,那就說明我分享的東西有價值了,感謝您的閱讀,感覺還可以的話就點撥贊,給我提供寫下去的動力。