zoukankan      html  css  js  c++  java
  • 验证码处理算法(一)

           在面对那种有许多干扰线或者干扰点的验证码,或者各种各样的验证码的时候,往往一个阀值是无法精确的处理图形验证码的,这里,我们主要使用一个范围缩圈进行像素点的比对,因为之前有使用计算像素的平均值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色和背景颜色相差不大时,得到的也是失真的图片。

    当然,后期如果有什么更好的方案处理图片,会不断更新,也欢迎各种观众大佬爷提出建议,不断精进!

  • 相关阅读:
    PL/SQL database character set(AL32UTF8) and Client character set(ZHS16GBK) are different
    pl sql 无法解析指定的连接标识符
    【转】几个常用的Oracle存储过程
    .NET 条件查询实现--类似网上商城宝贝搜索
    SQL Server 中大小写区分的处理
    .NET DataGrid 导出Excel 无分页
    C# 读书笔记之访问虚方法、重写方法和隐藏方法
    人工智能技术在中小学课堂中的应用
    dzzoffice协同办公平台与onlyoffice在线协作平台安装与部署
    一本通 确定进制
  • 原文地址:https://www.cnblogs.com/asd529735325/p/10216022.html
Copyright © 2011-2022 走看看