天天看點

去驗證碼幹擾線-java

首先看看去幹擾線的結果(java)

原始圖檔

去驗證碼幹擾線-java
去驗證碼幹擾線-java
去驗證碼幹擾線-java
去驗證碼幹擾線-java

去掉幹擾線以後的效果

去驗證碼幹擾線-java
去驗證碼幹擾線-java
去驗證碼幹擾線-java
去驗證碼幹擾線-java

這裡說下開發過程中遇到的問題

1.在網上使用了各種java類型的算法,直接對BufferedImage進行操作,但是都不理想

2.在使用Tesseract工具進行ocr識别的時候識别率也不高

解決第一個問題,我結合了網上的去幹擾線算法,以及使用了opencv算法。使用的opencv也是借鑒一篇網上的部落格。

解決第二個問題,是實用Tesseract工具針對我要識别的驗證碼進行獨立的訓練,而不是使用原始的訓練資料進行識别,這樣子可以明顯的提升識别率。

源碼

// 這裡是調用的核心方法
public class ImageCleanPlanOpencv implements ImageClean{

    Logger logger = LoggerFactory.getLogger(ImageCleanPlanOpencv.class);

    public BufferedImage clean(BufferedImage oriBufferedImage) {
        try {
            BufferedImage cleanedBufferedImage = null;
            //這裡可以看到去燥的方法反複調用了幾次,是為了得更好的去幹擾線結果,這裡可以根據自己的驗證碼情況來編寫調用的次數,必須是偶數次,因為opencv的api會進行圖像反色
            cleanedBufferedImage = cleanLinesInImage(oriBufferedImage);
            cleanedBufferedImage=cleanLinesInImage(cleanedBufferedImage);
            cleanedBufferedImage=cleanLinesInImage(cleanedBufferedImage);
            cleanedBufferedImage=cleanLinesInImage(cleanedBufferedImage);
//            try {
//                ImageUtil.generateImage(cleanedBufferedImage, ImageConstant.url,"new_","");
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
            return cleanedBufferedImage;
        } catch (IOException e) {
            logger.error("去噪過程異常",e);
            e.printStackTrace();
        }
        return null;
    }

    /**
     *
     * @param oriBufferedImage 需要去噪的圖像
     * @throws IOException
     */
    public BufferedImage cleanLinesInImage(BufferedImage oriBufferedImage)  throws IOException{

        BufferedImage bufferedImage = oriBufferedImage;
        int h = bufferedImage.getHeight();
        int w = bufferedImage.getWidth();

        // 灰階化
        int[][] gray = new int[w][h];
        for (int x = ; x < w; x++)
        {
            for (int y = ; y < h; y++)
            {
                int argb = bufferedImage.getRGB(x, y);
                // 圖像加亮(調整亮度識别率非常高)
                int r = (int) (((argb >> ) & ) *  + );
                int g = (int) (((argb >> ) & ) *  + );
                int b = (int) (((argb >> ) & ) *  + );
                if (r >= )
                {
                    r = ;
                }
                if (g >= )
                {
                    g = ;
                }
                if (b >= )
                {
                    b = ;
                }
                gray[x][y] = (int) Math
                        .pow((Math.pow(r, ) *  + Math.pow(g, )
                                *  + Math.pow(b, ) * ),  / );
            }
        }

        // 二值化
        int threshold = ostu(gray, w, h);
        BufferedImage binaryBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
        for (int x = ; x < w; x++)
        {
            for (int y = ; y < h; y++)
            {
                if (gray[x][y] > threshold)
                {
                    gray[x][y] |= ;
                } else
                {
                    gray[x][y] &= ;
                }
                binaryBufferedImage.setRGB(x, y, gray[x][y]);
            }
        }


        //這裡開始是利用opencv的api進行處理
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat mat = bufferedImageToMat(binaryBufferedImage);
        //第一次用opencv,這裡不太明白這個size對象如何使用,針對我要識别的驗證碼圖檔,調整為4,4的效果比較好,如果是不同的驗證碼圖檔,可以嘗試先不用這段opencv的代碼,或者微調這裡的參數
        Mat kelner = Imgproc.getStructuringElement(MORPH_RECT, new Size(, ), new Point(-, -));
        //腐蝕
        Imgproc.erode(mat,mat,kelner);
        //膨脹
        Imgproc.dilate(mat,mat,kelner);
        //圖像反色
        Core.bitwise_not(mat,mat);
        //去噪點
//        Imgproc.morphologyEx(mat,mat, MORPH_OPEN, kelner,new Point(-1,-1),1);
        binaryBufferedImage = mat2BufImg(mat,".png");

        cleanImage(binaryBufferedImage,h,w );


        return binaryBufferedImage;
    }

    public void cleanImage(BufferedImage binaryBufferedImage,int h ,int w ){
        //去除幹擾線條
        for(int y = ; y < h-; y++){
            for(int x = ; x < w-; x++){
                boolean flag = false ;
                if(isBlack(binaryBufferedImage.getRGB(x, y))){
                    //左右均為空時,去掉此點
                    if(isWhite(binaryBufferedImage.getRGB(x-, y)) && isWhite(binaryBufferedImage.getRGB(x+, y))){
                        flag = true;
                    }
                    //上下均為空時,去掉此點
                    if(isWhite(binaryBufferedImage.getRGB(x, y+)) && isWhite(binaryBufferedImage.getRGB(x, y-))){
                        flag = true;
                    }
                    //斜上下為空時,去掉此點
                    if(isWhite(binaryBufferedImage.getRGB(x-, y+)) && isWhite(binaryBufferedImage.getRGB(x+, y-))){
                        flag = true;
                    }
                    if(isWhite(binaryBufferedImage.getRGB(x+, y+)) && isWhite(binaryBufferedImage.getRGB(x-, y-))){
                        flag = true;
                    }
                    if(flag){
                        binaryBufferedImage.setRGB(x,y,-);
                    }
                }
            }
        }
    }

    public Mat bufferedImageToMat(BufferedImage bi) {
        Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC1);

        byte[] white = new byte[] { (byte)  };
        byte[] black = new byte[] { (byte)  };

        for (int x=; x<bi.getWidth(); x++) {
            for (int y=; y<bi.getHeight(); y++) {
                if (bi.getRGB(x, y) == Color.BLACK.getRGB()) {
                    mat.put(y, x, black);
                } else {
                    mat.put(y, x, white);
                }
            }
        }
        return mat;
    }

    /**
     * Mat轉換成BufferedImage
     *
     * @param matrix
     *            要轉換的Mat
     * @param fileExtension
     *            格式為 ".jpg", ".png", etc
     * @return
     */
    public BufferedImage mat2BufImg (Mat matrix, String fileExtension) {
        // convert the matrix into a matrix of bytes appropriate for
        // this file extension
        MatOfByte mob = new MatOfByte();
        Imgcodecs.imencode(fileExtension, matrix, mob);
        // convert the "matrix of bytes" into a byte array
        byte[] byteArray = mob.toArray();
        BufferedImage bufImage = null;
        try {
            InputStream in = new ByteArrayInputStream(byteArray);
            bufImage = ImageIO.read(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufImage;
    }

    public boolean isBlack(int colorInt)
    {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue() <= )
        {
            return true;
        }
        return false;
    }

    public boolean isWhite(int colorInt)
    {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue() > )
        {
            return true;
        }
        return false;
    }

    public int isBlackOrWhite(int colorInt)
    {
        if (getColorBright(colorInt) <  || getColorBright(colorInt) > )
        {
            return ;
        }
        return ;
    }

    public int getColorBright(int colorInt)
    {
        Color color = new Color(colorInt);
        return color.getRed() + color.getGreen() + color.getBlue();
    }

    public int ostu(int[][] gray, int w, int h)
    {
        int[] histData = new int[w * h];
        // Calculate histogram
        for (int x = ; x < w; x++)
        {
            for (int y = ; y < h; y++)
            {
                int red =  & gray[x][y];
                histData[red]++;
            }
        }

        // Total number of pixels
        int total = w * h;

        float sum = ;
        for (int t = ; t < ; t++)
            sum += t * histData[t];

        float sumB = ;
        int wB = ;
        int wF = ;

        float varMax = ;
        int threshold = ;

        for (int t = ; t < ; t++)
        {
            wB += histData[t]; // Weight Background
            if (wB == )
                continue;

            wF = total - wB; // Weight Foreground
            if (wF == )
                break;

            sumB += (float) (t * histData[t]);

            float mB = sumB / wB; // Mean Background
            float mF = (sum - sumB) / wF; // Mean Foreground

            // Calculate Between Class Variance
            float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);

            // Check if new maximum found
            if (varBetween > varMax)
            {
                varMax = varBetween;
                threshold = t;
            }
        }

        return threshold;
    }



}
           

這段代碼除開opencv那段是我個人編寫的,其他的也是借鑒網上的源代碼。如果有編寫不對,或者可以修改的更好的建議請指出。

參考部落格

https://blog.csdn.net/firehood_/article/details/8433077

https://blog.csdn.net/shengfn/article/details/53582694

http://pinnau.blogspot.com/2016/06/java-create-opencv-mat-from.html

https://blog.csdn.net/qianmang/article/details/79158366