1. 程式人生 > >驗證碼處理演算法(一)

驗證碼處理演算法(一)

       在面對那種有許多幹擾線或者干擾點的驗證碼,或者各種各樣的驗證碼的時候,往往一個閥值是無法精確的處理圖形驗證碼的,這裡,我們主要使用一個範圍縮圈進行畫素點的比對,因為之前有使用計算畫素的平均值K作為閾值,但是會導致部分物件畫素或者背景畫素丟失,故這個方案暫且擱置。

       我們知道每一個畫素點都有自己的畫素值,但是對於一張驗證碼來說,同一個字母顏色或深或淺,其畫素點的值都會有差異,所以我們需要一個容錯範圍,我們可以指定,在某個範圍差內的值,都是這個圖片字母的值。然後我們讓這個閥值在一個範圍內不斷遞增,從而得到不同閥值下的二值化圖片。

這裡有一個處理前和處理後圖片的對比:

處理前的圖片

處理後我們得到很多張圖片,從中選出最完整的一張圖片:       

因為機器學習的前期圖片還是需要人工篩選一下的。

以下是我們處理圖片的程式碼:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class PicDeal {
    //todo splitNums可以根據你給到的圖片色差進行調整,在你自己使用時,可以針對splitNums做一個迴圈,每次加多少,得到不同的色差比的二值化後的圖片,因為不同的圖片可能干擾線、干擾點顏色原因,二值化後會有差異
    //todo splitWidthNum:把圖片根據長度切分的分數,這個可以根據你圖片中的數字個數進行切分
    public static int splitNums=2100000;

    public static int beginNum=100000;
    public static int endNum=4000000;
    public static final int splitWidthNum=1;
    public static void main(String[] args) {
        //todo 需要處理圖片的路徑
        String path="C://Users/admin/Desktop/微信搜狗驗證碼/yzmsg/3.jpg";
        try{
            splitPic(path);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //todo 分割圖片
    public static void splitPic(String picFile) throws Exception{
        //todo 分割圖片
//        BufferedImage img = ImageIO.read(new File(picFile));
        Image image = ImageIO.read(new File(picFile));

        for (int i = 0; i < splitWidthNum; i++) {
            String filename="F://test/test3_"+i+".jpg";
            ImageIO.write(toBufferedImage(image), "JPG", new File(filename));

            //todo 分割後的圖片處理
            for (int j=beginNum;j<=endNum;j+=25000){
                Image thisimg = ImageIO.read(new File(filename));
                BufferedImage imgDeal=removeBackgroud(thisimg,j,i);

                imgDeal=DeletePassPoint.deletepoints(imgDeal);

                //todo 分割後圖片寫出去
                String filename2="F://test/test3_"+i+"imgDeal_畫素分割_"+j+".jpg";
                ImageIO.write(imgDeal, "JPG", new File(filename2));
            }
        }
    }



    //todo 圖片處理演算法
    public static BufferedImage removeBackgroud(Image img2,int j,int i)throws Exception {

        BufferedImage img=toBufferedImage(img2);

        String filename="F://test/test3_"+i+"_畫素分割_"+j+".jpg";
        ImageIO.write(img, "JPG", new File(filename));


        splitNums=j;
        System.out.println("splitNums==========="+splitNums);

        img = img.getSubimage(1, 1, img.getWidth()-2, img.getHeight()-2);
        int width = img.getWidth();
        int height = img.getHeight();
        double subWidth = (double) width;
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        //todo 以下是對圖片進行二值化處理,在這裡我的思路是規定,色差範圍在splitNums到負splitNums之間的,算是同色,放入同一個色值,放入一個map中,
        //todo map中的Key放色值,value放這個色值得個數,後期就根據這個色值來對驗證碼進行二值化
        for (int x = (int) (1 + 0 * subWidth); x < (0 + 1) * subWidth && x < width - 1; ++x) {
            for (int y = 0; y < height; ++y) {
                if (isWhite(img.getRGB(x, y)) == 1){
                    continue;
                }
                Map<Integer, Integer> map2 = new HashMap<Integer, Integer>();
                for (Integer color : map.keySet()) {
                    map2.put(color,map.get(color));
                }

                boolean hasnewColor=false;
                for (Integer color : map2.keySet()) {
//                        System.out.println(Math.abs(color)-Math.abs(img.getRGB(x, y)));
//                        Math.abs(color)-Math.abs(img.getRGB(x, y)))<splitNums&&Math.abs(color)-Math.abs(img.getRGB(x, y))>-splitNums
                    if (Math.abs(color)-Math.abs(img.getRGB(x, y))<splitNums&&Math.abs(color)-Math.abs(img.getRGB(x, y))>-splitNums){
                        map.put(color, map.get(color) + 1);
                        hasnewColor=true;
                    }
                }

                if (!hasnewColor){
                    map.put(img.getRGB(x, y), 1);
                }

                if (map.isEmpty()){
                    map.put(img.getRGB(x, y), 1);
                }

            }
        }

        System.out.println("==============================");

        int max = 0;
        int colorMax = 0;
        for (Integer color : map.keySet()) {
            if (max < map.get(color)) {
                max = map.get(color);
                colorMax = color;
            }
        }
        map.remove(colorMax);

        max = 0;
        int colorMax2 = 0;
        for (Integer color : map.keySet()) {
            if (max < map.get(color)) {
                max = map.get(color);
                colorMax2 = color;
            }
        }
        map.remove(colorMax2);


//        int colorMax3 = 0;
//        for (Integer color : map.keySet()) {
//            if (max < map.get(color)) {
//                max = map.get(color);
//                colorMax3 = color;
//            }
//        }
//        colorMax=colorMax2;
//        colorMax2=colorMax3;


        //todo 核心演算法
        for (int x = (int) (1 + 0 * subWidth); x < (0 + 1) * subWidth&& x < width - 1; ++x) {
            for (int y = 0; y < height; ++y) {
                int ress=Math.abs(img.getRGB(x, y))-Math.abs(colorMax);
                int ress_2=Math.abs(Math.abs(colorMax)-Math.abs(img.getRGB(x, y)));
                int ress2=Math.abs(img.getRGB(x, y))-Math.abs(colorMax2);

                if (ress>0&&ress2>0){
                    if (ress>ress2){
                        if ((ress2<splitNums&&ress2>-splitNums)) {
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        } else {
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }else{
//                            img.setRGB(x, y, Color.WHITE.getRGB());
                        if (ress2<splitNums&&ress2>ress) {
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        } else {
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }
                }else if (ress<0&&ress2<0){
                    ress=Math.abs(ress);
                    ress2=Math.abs(ress2);
                    if (ress>ress2){
                        if ((ress2<splitNums&&ress2>-splitNums)) {
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        } else {
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }else{
                        if (ress2<splitNums&&ress2>ress) {
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        } else {
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }
                }else if (ress>0&&ress2<0){
                    if (Math.abs(ress)>Math.abs(ress2)){
                        if ((Math.abs(ress2)>splitNums&&ress2<-splitNums)) {
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        } else {
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }else{
                        img.setRGB(x, y, Color.WHITE.getRGB());
//                        if (splitNums<)
                    }

                }else{
                    if (Math.abs(ress)>Math.abs(ress2)){
                        if ((Math.abs(ress2)>splitNums&&ress2<-splitNums)) {
                            img.setRGB(x, y, Color.BLACK.getRGB());
                        } else {
                            img.setRGB(x, y, Color.WHITE.getRGB());
                        }
                    }else{
                        img.setRGB(x, y, Color.WHITE.getRGB());
//                        if (splitNums<)
                    }
//                    img.setRGB(x, y, Color.BLACK.getRGB());
                }
            }
        }
        return img;
    }



    //todo 判斷是否為白色的方法  爬蟲 驗證碼處理
    public static int isWhite(int colorInt) {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue()>600) {
            return 1;
        }
        return 0;
    }



    //todo image轉bufferImage
    public static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage)image;
        }
        // 載入所有畫素
        image = new ImageIcon(image).getImage();
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
            // 建立buffer影象
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(
                    image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
            e.printStackTrace();
        }
        if (bimage == null) {
            int type = BufferedImage.TYPE_INT_RGB;
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }
        // 複製
        Graphics g = bimage.createGraphics();
        // 賦值
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return bimage;
    }
}

 在這裡我們預設RGB最多的為背景色,所以依照RGB顏色第二多的為準進行歸類劃值。

在這裡面我們也進行了一波簡單的處理,就是對得到的圖片進行單獨點的刪除,因為處理後的圖片會有類似這張圖一樣的小點:

,針對這類的小點,我們預設去掉。

演算法其實很簡單,就是當判定上下左右其中3個方位為空時,此點算離散點。

程式碼如下:

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.File;

public class DeletePassPoint {
    

    public static BufferedImage deletepoints(BufferedImage img){
        //todo 為白色的畫素點rgb值
//        int[] whitePointNum={-257,-1,-770,-1027,-514};
        int width = img.getWidth();
        int height = img.getHeight();
        double subWidth = (double) width;

        ColorModel cm = img.getColorModel();
        BufferedImage imgNew=new BufferedImage(cm,cm.createCompatibleWritableRaster(img.getWidth(),img.getHeight()),cm.isAlphaPremultiplied(), null);

        for (int x = (int) (1 + 0 * subWidth); x < (0 + 1) * subWidth && x < width - 1; ++x) {
            for (int y = 1; y < height-1; ++y) {

                int leftRgb=img.getRGB(x-1,y);
                int rightRgb=img.getRGB(x+1,y);
                int buttonRgb=img.getRGB(x,y-1);
                int headRgb=img.getRGB(x,y+1);


                int judgy=0;
                //todo 對自己本身畫素點周邊進行檢測
                if (isWhite(leftRgb)==0){
                    judgy++;
                }
                if (isWhite(rightRgb)==0){
                    judgy++;
                }

                if (isWhite(buttonRgb)==0){
                    judgy++;
                }

                if (isWhite(headRgb)==0){
                    judgy++;
                }



                //todo 當判定上下左右其中3個方位為空時,此點算離散點
                if (judgy>=3){
//                    imgNew.setRGB(x, y, Color.BLACK.getRGB());
                    imgNew.setRGB(x, y, Color.WHITE.getRGB());
                }else{
//                    imgNew.setRGB(x, y, Color.WHITE.getRGB());
                    imgNew.setRGB(x, y, Color.BLACK.getRGB());
                }
            }
        }

        return imgNew;
    }

    //todo 判斷是否為白色的方法  爬蟲 驗證碼處理
    public static int isWhite(int colorInt) {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue()>600) {
            return 1;
        }
        return 0;
    }

}

這樣下來,處理出來的圖片就是我們剛才貼出來的樣子了。當然,這個方案還有很多需要改進的地方。

比如:當背景顏色RGB值並不是最多的時候,這樣就會出現誤刪除。還有就是驗證碼RGB色和背景顏色相差不大時,得到的也是失真的圖片。

當然,後期如果有什麼更好的方案處理圖片,會不斷更新,也歡迎各種觀眾大佬爺提出建議,不斷精進!