精华内容
下载资源
问答
  • 使用机器学习端到端图片验证码识别,通杀所有图片类型验证码类型(包括复杂的连在一起、重叠的验证码),支持java,python, c#等语言, 识别精度达95%以上。 机器学习识别验证码,提供了一个完整的图片验证码识别...
  • NULL 博文链接:https://lvhuiqing.iteye.com/blog/1474186
  • JAVA识别复杂验证码+图像处理

    千次阅读 2018-12-13 17:42:45
    先对验证码进行简单的处理噪点和纯色 例未曾处理的图片 public static void main(String[] args) throws Exception { //源文件 String picName = "C:\\Users\\syxy101\\Desktop\\6.jpg"; File file...

     先对验证码进行简单的处理噪点和纯色

    例未曾处理的图片

    public static void main(String[] args) throws Exception {
       //源文件
      String picName = "C:\\Users\\syxy101\\Desktop\\6.jpg";
    
     File filepic=new File(picName);
    //去噪点 并处理为纯色
      BufferedImage bufferedImage = removeBackgroud(picName);
    //处理后图片地址
    File w2 = new File("C:\\Users\\syxy101\\Desktop\\5.png");//可以是jpg,png格式
    ImageIO.write(bufferedImage, "jpg", w2);//不管输出什么格式图片,此处不需改动
    
    
    }
    
    //去除图片噪点  并将彩色转为纯色(黑色)
      public static BufferedImage removeBackgroud(String picFile)
                throws Exception {
            BufferedImage img = ImageIO.read(new File(picFile));
            int width = img.getWidth();
            int height = img.getHeight();
            for (int x = 0; x < width; ++x) {
                for (int y = 0; y < height; ++y) {
                    if (isWhite(img.getRGB(x, y)) == 1) {
                        img.setRGB(x, y, Color.WHITE.getRGB());
                    } else {
                        img.setRGB(x, y, Color.BLACK.getRGB());
                    }
                }
            }
            return img;
        }
    //设置颜色阀值
     public static int isWhite(int colorInt) {
            Color color = new Color(colorInt);
            if (color.getRed() + color.getGreen() + color.getBlue() > 200 && color.getRed() + color.getGreen() + color.getBlue()<500) {
                return 0;
            }
            return 1;
        }

    处理后

    可以看到已经清晰了不少  当然现在还是无法识别的

    一下步 强加黑色 处理掉横线斜线 噪点

    package com.adc.da.sync.web;
    
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.HttpStatus;
    import org.apache.commons.httpclient.methods.GetMethod;
    import org.apache.commons.io.IOUtils;
    
    import javax.imageio.ImageIO;
    import java.awt.Color;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * //TODO 添加类/接口功能描述
     *
     * @author zhangsj
     * @date 2018-12-13
     */
    public class ImagePreProcess4 {
    
    
    
            public static void main(String[] args) throws IOException
            {
                //源文件
                File testDataDir = new File("C:\\Users\\syxy101\\Desktop\\5.png");
                //处理后路径
                final String destDir = "C:\\Users\\syxy101\\Desktop\\ss";
                    cleanLinesInImage(testDataDir, destDir);
            }
    
            /**
             *
             * @param sfile
             *            需要去噪的图像
             * @param destDir
             *            去噪后的图像保存地址
             * @throws IOException
             */
            public static void cleanLinesInImage(File sfile, String destDir)  throws IOException{
                File destF = new File(destDir);
                if (!destF.exists())
                {
                    destF.mkdirs();
                }
    
                BufferedImage bufferedImage = ImageIO.read(sfile);
                int h = bufferedImage.getHeight();
                int w = bufferedImage.getWidth();
    
                // 灰度化
                int[][] gray = new int[w][h];
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
                        int argb = bufferedImage.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);
                        if (r >= 255)
                        {
                            r = 255;
                        }
                        if (g >= 255)
                        {
                            g = 255;
                        }
                        if (b >= 255)
                        {
                            b = 255;
                        }
                        gray[x][y] = (int) Math
                                .pow((Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2)
                                        * 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2);
                    }
                }
    
                // 二值化
                int threshold = ostu(gray, w, h);
                BufferedImage binaryBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
                        if (gray[x][y] > threshold)
                        {
                            gray[x][y] |= 0x00FFFF;
                        } else
                        {
                            gray[x][y] &= 0xFF0000;
                        }
                        binaryBufferedImage.setRGB(x, y, gray[x][y]);
                    }
                }
    
                //去除干扰线条
                for(int y = 1; y < h-1; y++){
                    for(int x = 1; x < w-1; x++){
                        boolean flag = false ;
                        if(isBlack(binaryBufferedImage.getRGB(x, y))){
                            //左右均为空时,去掉此点
                            if(isWhite(binaryBufferedImage.getRGB(x-1, y)) && isWhite(binaryBufferedImage.getRGB(x+1, y))){
                                flag = true;
                            }
                            //上下均为空时,去掉此点
                            if(isWhite(binaryBufferedImage.getRGB(x, y+1)) && isWhite(binaryBufferedImage.getRGB(x, y-1))){
                                flag = true;
                            }
                            //斜上下为空时,去掉此点
                            if(isWhite(binaryBufferedImage.getRGB(x-1, y+1)) && isWhite(binaryBufferedImage.getRGB(x+1, y-1))){
                                flag = true;
                            }
                            if(isWhite(binaryBufferedImage.getRGB(x+1, y+1)) && isWhite(binaryBufferedImage.getRGB(x-1, y-1))){
                                flag = true;
                            }
                            if(flag){
                                binaryBufferedImage.setRGB(x,y,-1);
                            }
                        }
                    }
                }
    
    
                // 矩阵打印
                for (int y = 0; y < h; y++)
                {
                    for (int x = 0; x < w; x++)
                    {
                        if (isBlack(binaryBufferedImage.getRGB(x, y)))
                        {
                            System.out.print("*");
                        } else
                        {
                            System.out.print(" ");
                        }
                    }
                    System.out.println();
                }
    
                ImageIO.write(binaryBufferedImage, "jpg", new File(destDir, sfile
                        .getName()));
            }
    
            public static boolean isBlack(int colorInt)
            {
                Color color = new Color(colorInt);
                if (color.getRed() + color.getGreen() + color.getBlue() <= 300)
                {
                    return true;
                }
                return false;
            }
    
            public static boolean isWhite(int colorInt)
            {
                Color color = new Color(colorInt);
                if (color.getRed() + color.getGreen() + color.getBlue() > 300)
                {
                    return true;
                }
                return false;
            }
    
            public static int isBlackOrWhite(int colorInt)
            {
                if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730)
                {
                    return 1;
                }
                return 0;
            }
    
            public static int getColorBright(int colorInt)
            {
                Color color = new Color(colorInt);
                return color.getRed() + color.getGreen() + color.getBlue();
            }
    
            public static int ostu(int[][] gray, int w, int h)
            {
                int[] histData = new int[w * h];
                // Calculate histogram
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
                        int red = 0xFF & gray[x][y];
                        histData[red]++;
                    }
                }
    
                // Total number of pixels
                int total = w * h;
    
                float sum = 0;
                for (int t = 0; t < 256; t++)
                    sum += t * histData[t];
    
                float sumB = 0;
                int wB = 0;
                int wF = 0;
    
                float varMax = 0;
                int threshold = 0;
    
                for (int t = 0; t < 256; t++)
                {
                    wB += histData[t]; // Weight Background
                    if (wB == 0)
                        continue;
    
                    wF = total - wB; // Weight Foreground
                    if (wF == 0)
                        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;
            }
        }
    
    

     

     

    已经去掉多余的横线和噪点了 

    这时候识别度已经很高了 

    为了达到识别度百分之80

    需要把每个字母隔开 分开识别

      public static void main(String[] args) throws Exception {
    
       int x=6;
          for(int i=0;i<4;i++){
                if(i==0){
                    x=6;
                }else if(i==1){
                    x=22;
                }else if(i==2){
                    x=36;
                }else if(i==3){
                    x=52;
                }
                imageCut(x,0,15,30,picName,"D:\\ccccc"+i+".jpg");
    
            }
    
    }
       /**
         * 图片剪裁
         * @param x 距离左上角的x轴距离
         * @param y 距离左上角的y轴距离
         * @param width 宽度
         * @param height 高度
         * @param sourcePath 图片源
         * @param descpath 目标位置
         */
        public static void imageCut(int x, int y, int width, int height, String sourcePath, String descpath) {
            FileInputStream is = null;
            ImageInputStream iis = null;
            try {
                is = new FileInputStream(sourcePath);
                String fileSuffix = sourcePath.substring(sourcePath.lastIndexOf(".") + 1);
                Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(fileSuffix);
                ImageReader reader = it.next();
                iis = ImageIO.createImageInputStream(is);
                reader.setInput(iis, true);
                ImageReadParam param = reader.getDefaultReadParam();
                Rectangle rect = new Rectangle(x, y, width, height);
                param.setSourceRegion(rect);
                BufferedImage bi = reader.read(0, param);
                ImageIO.write(bi, fileSuffix, new File(descpath));
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    is = null;
                }
                if (iis != null) {
                    try {
                        iis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    iis = null;
                }
            }
        }

     

    换了个验证码剪辑

    这时候识别度已经到百分之90了

    展开全文
  • JAVA智能验证码识别,可训练提高识别率,带干扰线的也可以识别到,绝对可用
  • 这种方案对验证码的处理相对较少,运用相对普遍,很多验证码图片可以通过这个方式得到识别,当然还需要一部分的降噪处理。什么是图片二值化处理:简单也就是把一张五颜六色的验证码处理成一张只由黑白构成的验证码,...

    Python中有专门的图像处理技术比如说PIL,可以对验证码一类的图片进行二值化处理,然后对图片进行分割,进行像素点比较得到图片中的数字。这种方案对验证码的处理相对较少,运用相对普遍,很多验证码图片可以通过这个方式得到识别,当然还需要一部分的降噪处理。

    什么是图片二值化处理:简单也就是把一张五颜六色的验证码处理成一张只由黑白构成的验证码,这个是为了方便后期我们和保存的黑白单一数字、字母进行像素点比较。

    什么是降噪处理:简单的解释就是把验证码中的干扰去掉一部分,降噪不可能完全降,但是可以处理一大部分就是对识别的一种进步,毕竟如果降噪处理不行,对后期的像素点比较和结果值影响比较大。

    今天我们用图片的RGB的色彩比对技术,用JAVA对图片进行一次二值化处理,然后识别。

    原图片:

    831a39865f6bc5635416eb645a174887.png

    二值化后图片:

    f63e7088a556990eb154376dab9bb870.png

    我们针对这个网页的验证码需要在自己库中保存的模板类型:

    b6ea08c1116643084a473d4b4fcfcc22.png

    1f96211b39446ea2f2d4b680e4d21c67.png.....这一类是用于后期像素点比较得到图片本身数值的准备。

    那么基本流程我们知道了,我们就开始

    第一步:图片下载:

    网页的抓取有时候会有验证码的识别,这样我们就需要对http请求的包进行解析,有的验证码可以在js中解析得到,有的是直接返回该网页页面,反正可以找到这个img图片进行下载到本地就行,此处不一一赘述。

    第二部:对下载到本地的图片进行二值化处理:

    在这里我自己写了一个脚本,供大家使用和参考:

    public class MyImgDel {

    //todo splitNums可以根据你给到的图片色差进行调整,在你自己使用时,可以针对splitNums做一个循环,每次加多少,得到不同的色差比的二值化后的图片,因为不同的图片可能干扰线、干扰点颜色原因,二值化后会有差异

    //todo splitWidthNum:把图片根据长度切分的分数,这个可以根据你图片中的数字个数进行切分

    public static final int splitNums=4000000;

    public static final int splitWidthNum=5;

    public static void main(String[] args) {

    String path="F://test1.png";

    try{

    BufferedImage img=removeBackgroud(path);

    ImageIO.write(img, "PNG", new File("F://test1-1.png"));

    }catch (Exception e){

    e.printStackTrace();

    }

    }

    public static BufferedImage removeBackgroud(String picFile)

    throws Exception {

    BufferedImage img = ImageIO.read(new File(picFile));

    img = img.getSubimage(1, 1, img.getWidth()-2, img.getHeight()-2);

    int width = img.getWidth();

    int height = img.getHeight();

    double subWidth = (double) width/(splitWidthNum+0.0);

    Map map = new HashMap();

    for (int i = 0; i < splitWidthNum; i++) {

    //todo 以下是对图片进行二值化处理,在这里我的思路是规定,色差范围在splitNums到负splitNums之间的,算是同色,放入同一个色值,放入一个map中,map中的Key放色值,value放这个色值得个数,后期就根据这个色值来对验证码进行二值化

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

    for (int y = 0; y < height; ++y) {

    if (isWhite(img.getRGB(x, y)) == 1){

    continue;

    }

    Map map2 = new HashMap();

    for (Integer color : map.keySet()) {

    map2.put(color,map.get(color));

    }

    for (Integer color : map2.keySet()) {

    System.out.println(Math.abs(color)-Math.abs(img.getRGB(x, y)));

    if (Math.abs(color)-Math.abs(img.getRGB(x, y))-splitNums){

    map.put(color, map.get(color) + 1);

    }else{

    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;

    }

    }

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

    for (int y = 0; y < height; ++y) {

    int ress=Math.abs(img.getRGB(x, y))-Math.abs(colorMax);

    if (ress-splitNums) {

    img.setRGB(x, y, Color.WHITE.getRGB());

    } else {

    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;

    }

    }

    处理到这里我们就可以得到一个二值化后的图片了。

    然后我们就要开始对二值化后的图进行等分,然后和我们样本库中的图片进行一次像素比对。

    展开全文
  • java验证码识别--3

    2021-02-27 20:01:58
    )前面的验证码背景都比较简单,用亮度稍微区分一下就可以去掉背景来看个稍微复杂一点的1。图片预处理怎么去掉背景干扰呢。可以注意到每个验证码数字或字母都是同一颜色,所以把验证码平均分成5份计算每个区域的颜色...

    (本文仅用于学习研究图像匹配识别原理,不得用于其他用途。)

    前面的验证码背景都比较简单,用亮度稍微区分一下就可以去掉背景

    来看个稍微复杂一点的

    0_12813668667PUx.gif

    1。图片预处理

    怎么去掉背景干扰呢。

    可以注意到每个验证码数字或字母都是同一颜色,所以把验证码平均分成5份

    0_1281367162flTG.gif

    计算每个区域的颜色分布,除了白色之外,颜色值最多的就是验证码的颜色

    因此很容易将背景去掉

    代码:

    public static BufferedImage removeBackgroud(String picFile)

    throws Exception {

    BufferedImage img = ImageIO.read(new File(picFile));

    img = img.getSubimage(1, 1, img.getWidth() - 2, img.getHeight() - 2);

    int width = img.getWidth();

    int height = img.getHeight();

    double subWidth = (double) width / 5.0;

    for (int i = 0; i < 5; i++) {

    Map map = new HashMap();

    for (int x = (int) (1 + i * subWidth); x < (i + 1) * subWidth

    && x < width - 1; ++x) {

    for (int y = 0; y < height; ++y) {

    if (isWhite(img.getRGB(x, y)) == 1)

    continue;

    if (map.containsKey(img.getRGB(x, y))) {

    map.put(img.getRGB(x, y), map.get(img.getRGB(x, y)) + 1);

    } else {

    map.put(img.getRGB(x, y), 1);

    }

    }

    }

    int max = 0;

    int colorMax = 0;

    for (Integer color : map.keySet()) {

    if (max < map.get(color)) {

    max = map.get(color);

    colorMax = color;

    }

    }

    for (int x = (int) (1 + i * subWidth); x < (i + 1) * subWidth

    && x < width - 1; ++x) {

    for (int y = 0; y < height; ++y) {

    if (img.getRGB(x, y) != colorMax) {

    img.setRGB(x, y, Color.WHITE.getRGB());

    } else {

    img.setRGB(x, y, Color.BLACK.getRGB());

    }

    }

    }

    }

    return img;

    }

    0_12813668719Agt.gif

    处理成这样了,基本就跟验证码识别--2一样了,只是黑白换了一下而已。

    2。分割原理和3。训练原理,4。识别都水到渠成了。

    识别结果如下,识别率100%

    上面的处理之后的图片和识别结果,下面是原始图片

    0_1281367570Rfz8.gif

    完整源码:

    public class ImagePreProcess3 {

    private static Map trainMap = null;

    private static int index = 0;

    public static int isBlack(int colorInt) {

    Color color = new Color(colorInt);

    if (color.getRed() + color.getGreen() + color.getBlue() <= 100) {

    return 1;

    }

    return 0;

    }

    public static int isWhite(int colorInt) {

    Color color = new Color(colorInt);

    if (color.getRed() + color.getGreen() + color.getBlue() > 600) {

    return 1;

    }

    return 0;

    }

    public static BufferedImage removeBackgroud(String picFile)

    throws Exception {

    BufferedImage img = ImageIO.read(new File(picFile));

    img = img.getSubimage(1, 1, img.getWidth() - 2, img.getHeight() - 2);

    int width = img.getWidth();

    int height = img.getHeight();

    double subWidth = (double) width / 5.0;

    for (int i = 0; i < 5; i++) {

    Map map = new HashMap();

    for (int x = (int) (1 + i * subWidth); x < (i + 1) * subWidth

    && x < width - 1; ++x) {

    for (int y = 0; y < height; ++y) {

    if (isWhite(img.getRGB(x, y)) == 1)

    continue;

    if (map.containsKey(img.getRGB(x, y))) {

    map.put(img.getRGB(x, y), map.get(img.getRGB(x, y)) + 1);

    } else {

    map.put(img.getRGB(x, y), 1);

    }

    }

    }

    int max = 0;

    int colorMax = 0;

    for (Integer color : map.keySet()) {

    if (max < map.get(color)) {

    max = map.get(color);

    colorMax = color;

    }

    }

    for (int x = (int) (1 + i * subWidth); x < (i + 1) * subWidth

    && x < width - 1; ++x) {

    for (int y = 0; y < height; ++y) {

    if (img.getRGB(x, y) != colorMax) {

    img.setRGB(x, y, Color.WHITE.getRGB());

    } else {

    img.setRGB(x, y, Color.BLACK.getRGB());

    }

    }

    }

    }

    return img;

    }

    public static BufferedImage removeBlank(BufferedImage img) throws Exception {

    int width = img.getWidth();

    int height = img.getHeight();

    int start = 0;

    int end = 0;

    Label1: for (int y = 0; y < height; ++y) {

    for (int x = 0; x < width; ++x) {

    if (isBlack(img.getRGB(x, y)) == 1) {

    start = y;

    break Label1;

    }

    }

    }

    Label2: for (int y = height - 1; y >= 0; --y) {

    for (int x = 0; x < width; ++x) {

    if (isBlack(img.getRGB(x, y)) == 1) {

    end = y;

    break Label2;

    }

    }

    }

    return img.getSubimage(0, start, width, end - start + 1);

    }

    public static List splitImage(BufferedImage img)

    throws Exception {

    List subImgs = new ArrayList();

    int width = img.getWidth();

    int height = img.getHeight();

    List weightlist = new ArrayList();

    for (int x = 0; x < width; ++x) {

    int count = 0;

    for (int y = 0; y < height; ++y) {

    if (isBlack(img.getRGB(x, y)) == 1) {

    count++;

    }

    }

    weightlist.add(count);

    }

    for (int i = 0; i < weightlist.size();i++) {

    int length = 0;

    while (i < weightlist.size() && weightlist.get(i) > 0) {

    i++;

    length++;

    }

    if (length > 2) {

    subImgs.add(removeBlank(img.getSubimage(i - length, 0,

    length, height)));

    }

    }

    return subImgs;

    }

    public static Map loadTrainData() throws Exception {

    if (trainMap == null) {

    Map map = new HashMap();

    File dir = new File("train3");

    File[] files = dir.listFiles();

    for (File file : files) {

    map.put(ImageIO.read(file), file.getName().charAt(0) + "");

    }

    trainMap = map;

    }

    return trainMap;

    }

    public static String getSingleCharOcr(BufferedImage img,

    Map map) {

    String result = "#";

    int width = img.getWidth();

    int height = img.getHeight();

    int min = width * height;

    for (BufferedImage bi : map.keySet()) {

    int count = 0;

    if (Math.abs(bi.getWidth()-width) > 2)

    continue;

    int widthmin = width < bi.getWidth() ? width : bi.getWidth();

    int heightmin = height < bi.getHeight() ? height : bi.getHeight();

    Label1: for (int x = 0; x < widthmin; ++x) {

    for (int y = 0; y < heightmin; ++y) {

    if (isBlack(img.getRGB(x, y)) != isBlack(bi.getRGB(x, y))) {

    count++;

    if (count >= min)

    break Label1;

    }

    }

    }

    if (count < min) {

    min = count;

    result = map.get(bi);

    }

    }

    return result;

    }

    public static String getAllOcr(String file) throws Exception {

    BufferedImage img = removeBackgroud(file);

    List listImg = splitImage(img);

    Map map = loadTrainData();

    String result = "";

    for (BufferedImage bi : listImg) {

    result += getSingleCharOcr(bi, map);

    }

    ImageIO.write(img, "JPG", new File("result3//" + result + ".jpg"));

    return result;

    }

    public static void downloadImage() {

    HttpClient httpClient = new HttpClient();

    GetMethod getMethod = new GetMethod("http://game.tom.com/checkcode.php");

    for (int i = 0; i < 30; i++) {

    try {

    // 执行getMethod

    int statusCode = httpClient.executeMethod(getMethod);

    if (statusCode != HttpStatus.SC_OK) {

    System.err.println("Method failed: "

    + getMethod.getStatusLine());

    }

    // 读取内容

    String picName = "img3//" + i + ".jpg";

    InputStream inputStream = getMethod.getResponseBodyAsStream();

    OutputStream outStream = new FileOutputStream(picName);

    IOUtils.copy(inputStream, outStream);

    outStream.close();

    System.out.println(i + "OK!");

    } catch (Exception e) {

    e.printStackTrace();

    } finally {

    // 释放连接

    getMethod.releaseConnection();

    }

    }

    }

    public static void trainData() throws Exception {

    File dir = new File("temp3");

    File[] files = dir.listFiles();

    for (File file : files) {

    BufferedImage img = removeBackgroud("temp3//" + file.getName());

    List listImg = splitImage(img);

    if (listImg.size() == 5) {

    for (int j = 0; j < listImg.size(); ++j) {

    ImageIO.write(listImg.get(j), "JPG", new File("train3//"

    + file.getName().charAt(j) + "-" + (index++)

    + ".jpg"));

    }

    }

    }

    }

    /**

    * @param args

    * @throws Exception

    */

    public static void main(String[] args) throws Exception {

    //trainData();

    // downloadImage();

    for (int i = 0; i < 30; ++i) {

    String text = getAllOcr("img3//" + i + ".jpg");

    System.out.println(i + ".jpg = " + text);

    }

    }

    }

    展开全文
  • Java实现超简单验证码识别

    万次阅读 2018-06-09 23:03:49
    闲来想实现程序模拟登陆一个系统,说白了,就是写个简单的爬虫,但是无奈,遇到了数字图片验证码,在查阅了一些方案以后,遂决定自己手写代码实现验证码识别,分享一下整个过程。 图片验证码是什么 图片验证码...

    闲来想实现程序模拟登陆一个系统,说白了,就是写个简单的爬虫,但是无奈,遇到了数字图片验证码,在查阅了一些方案以后,遂决定自己手写代码实现验证码识别,分享一下整个过程。

    图片验证码是什么

    图片验证码,这个大家应该都见过。最普遍的图片验证码就是一张图片上面有4-6个歪歪扭扭的数字字母,图片还有点看不清楚,但是基本可以肉眼识别出上面的数字字母。那为什么要有这个东东呢?

    其实验证码的出现为了区分人与机器。对于歪歪妞妞还有点看不清的数字字母图片,由于人脑的特殊构造,是可以完全无障碍识别的,但是想让奇迹识别出这些字母数字,就会出现识别错误。那为什么要区别人与机器呢?假如一个一个系统没有验证码,我知道了你的用户名,并且知道你的登录密码是8位的数字,那我完全可以写个脚本程序穷举出所有的8位数组合,挨个去尝试登录,这个过程对于人来说可能耗时耗力,但是对于程序来说,so easy。所以验证码的出现就会阻止程序进行这样的穷举登录。

    随着技术的发展,现在很多的验证码系统都可以通过图像处理、机器学习深度学习等方式进行攻破,图片验证码已经不再安全,即使是非常有名的12306验证码,也已经被利用深度学习达到了很高的识别精度。所以也出现了手机验证码、拖动滑块图片到指定位置的验证码等各种验证码。下面展示的就是几种常见的验证码。

    这里写图片描述 这里写图片描述
    这里写图片描述 这里写图片描述

    超简单验证码

    为什么说是超简单呢?因为这次需要处理的验证码,就是简单的数字图片验证码,并且图片很干净,没有干扰元素,数字也很规整,没有扭曲、变形和移位。如下图所示。
    这里写图片描述
    看到图片可能很多人就说了,这不就是个简单的图像处理问题吗,太简单了。

    先说说我看到这个图片验证码的第一想法,不是自己手动实现,我先想到的是OCR(光学字符识别)。因为图片上的数字太规整了,OCR识别是最快、最省力的,只需要调用接口即可。但是查了一下目前的OCR接口,找到了腾讯的OCR接口,但是一个月只有1000次免费调用,感觉用在爬虫上不太够,而且我这个验证码是gif图片,腾讯的接口不支持gif。所以就干脆自己写一个识别程序。

    首先说一下,对于这个程序的要求,识别速度要快,识别准确度要高,程序要尽量简单,尽量不涉及图像处理的内容。换句话说就是用最低的成本实现这个验证码的识别。

    分析思路

    实现的思路其实很简单,由于数字图片验证码只有0-9这10个数字,场景很少,加之数字很规整,所以可以先收集到包含有0-9这10个数字的图片。然后用程序进行图片裁剪,裁剪出0-9这10个单个数字的形态的图片并存储。然后对于一张新的验证码图片,我们可以采用先裁剪为4张单个数字图片,然后与我们事先准备好的10个数字图片进行相似度对比,最相似的即为正确的数字。

    具体实现

    下面看看具体的代码实现。

    1.图片边缘空白裁剪

    这一步主要是把图片边缘的空白裁减掉,让剩余的图片刚好包含四个数字即可。图片的原始大小是60px*36px,将其导入ps中查看需要裁剪的部分,然后用程序进行裁剪。如图,就是把红色框之外的部分裁减掉。
    这里写图片描述
    这里为了程序尽可能简单,所以不使用第三方的Java包,知识用Java本身内置的ImageIO工具类进行图片的读写和简单裁剪,封装的函数如下图所示:

    /**
     * 裁剪图片
     * @param srcPath 原始图片路径
     * @param readImageFormat 读取图片的格式
     * @param x 裁剪的x坐标
     * @param y 裁剪的y坐标
     * @param width 裁剪后图片宽度
     * @param height 裁剪后图片高度
     * @param writeImageFormat 保存裁剪后图片的格式
     * @param isSave 是否保存裁剪后的图片到本地[不保存会返回裁剪后图片的字节数组]
     * @param toPath 裁剪后的图片保存路径
     *
     * @return byte[] 如果图片不保存在本地,则返回裁剪后图片的字节数组
     */
    public static byte[] cropImg(String srcPath, String readImageFormat, int x, int y,
                               int width, int height, String writeImageFormat, boolean isSave, String toPath) {
        FileInputStream fis = null;
        ImageInputStream iis = null;
        try {
            //读取图片文件
            fis = new FileInputStream(srcPath);
            Iterator it = ImageIO.getImageReadersByFormatName(readImageFormat);
            ImageReader reader = (ImageReader) it.next();
            //获取图片流
            iis = ImageIO.createImageInputStream(fis);
            reader.setInput(iis, true);
            ImageReadParam param = reader.getDefaultReadParam();
            //定义一个矩形
            Rectangle rect = new Rectangle(x, y, width, height);
            //提供一个 BufferedImage,将其用作解码像素数据的目标。
            param.setSourceRegion(rect);
            BufferedImage bi = reader.read(0, param);
    
            if (isSave){
                //保存新图片
                ImageIO.write(bi, writeImageFormat, new File(toPath));
                return null;
            }else {
                //返回字节数组
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(100);
                ImageIO.write(bi, writeImageFormat, byteArrayOutputStream);
                return byteArrayOutputStream.toByteArray();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    为了提升性能,我们在部分情况下无需将裁剪后的图片图片保存到本地,而是直接转化为字节数组然后进行处理即可。
    使用下面的代码调用上面的函数即可完成对图片边缘空白的裁剪。

    File sourceImgDir = new File("./sourceimg/");
        File[] sourceImgList = sourceImgDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith("gif")){
                    return true;
                }
                return false;
            }
        });
        if (sourceImgList == null) {
            return;
        }
    
        for (File file : sourceImgList) {
            try {
                BufferedImage bufferedImage = ImageIO.read(file);
                System.out.println("width = " + bufferedImage.getWidth() + "\theight = " + bufferedImage.getHeight());
                //进行图片边缘空白的裁剪
                ImgUtil.cropImg(file.getPath(), "gif", 6, 16, 44, 10, "png", true, file.getPath() + ".png");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    裁剪完成后的对比如下:
    这里写图片描述 这里写图片描述

    2.图片分割为单个数字

    由于图片非常规整,每个数字的宽度也是一致的,所以我们可以继续使用上面的裁剪函数进行图片裁剪,即可将包含四个数字的图片裁剪为单个的数字的图片,代码如下:

    int oneW = (bufferedImage.getWidth() - 15) / 4;
    for (int i = 0; i < 4; i++) {
        cropImg(file.getPath(), "png", i * (oneW + 5), 0, oneW, 10, "png", file.getPath() + i + ".png");
    }

    经过上面的步骤,我们就获得0-9这10个数字的单个图片,如下图:
    这里写图片描述

    3.验证码对比识别

    经过查看,由于图片非常规整,我们每次裁剪出来的数字图片都是一样的,也就是同一个数字的两张图片的每一个字节都是相同的,并且经过裁剪后的图片其实非常小,所以我们的识别其实就是对比,只需要将待识别的图片裁剪为4张小图,然后与我们提前准备好的单张数字图片对比即可。代码如下:

    /**
     * 比较两个图片字节数组是否相同
     * @param img1Byte
     * @param img2Byte
     * @return
     */
    public static boolean compareImg(byte[] img1Byte, byte[] img2Byte) {
        if (img1Byte == null || img2Byte == null){
            return false;
        }
    
        if (img1Byte.length == 0 || img2Byte.length == 0){
            return false;
        }
    
        if (img1Byte.length != img2Byte.length){
            return false;
        }
    
        for (int i = 0; i < img1Byte.length; i++) {
            if (img1Byte[i] != img2Byte[i]){
                return false;
            }
        }
        return true;
    }
    
    /**
     * 通过比较字节来获得是该图片是数字几
     * @param imgData 原始的0-9这10张图片的字节信息
     * @param srcBytes 待识别的图片字节
     * @return
     */
    public static int chooseImg(List<byte[]> imgData, byte[] srcBytes){
        if (imgData == null || imgData.size() == 0
                || srcBytes == null || srcBytes.length == 0){
            return -1;
        }
        for (int i = 0; i < imgData.size(); i++) {
            if (compareImg(imgData.get(i), srcBytes)){
                return i;
            }
        }
        return -1;
    }

    上面的函数就是对比字节的函数。当然在对比之前,我们还需要将我们的10张数字图片加载到内存中,便于后续对比,代码如下:

    /**
     * 图片文件转字节数组
     * @param imgFile
     * @return
     */
    public static byte[] imgToBytes(File imgFile){
        if (imgFile == null){
            return null;
        }
        try {
            FileInputStream inputStream = new FileInputStream(imgFile);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(200);
            byte[] bytes = new byte[200];
            int n;
            while ((n = inputStream.read(bytes)) != -1){
                outputStream.write(bytes, 0, n);
            }
            inputStream.close();
            outputStream.close();
            return outputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 载入图片数据[装载需要进行比较的图片数据]
     * @param imgPath
     * @return
     */
    public static List<byte[]> loadImgData(String imgPath){
        if (imgPath == null || "".equals(imgPath)){
            return null;
        }
        File imgDir = new File(imgPath);
        List<byte[]> imgData = new ArrayList<>(10);
        //获得0-9的图片数据
        File[] imgs = imgDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".png")){
                    return true;
                }
                return false;
            }
        });
        if (imgs == null){
            return null;
        }
    
        for (File file : imgs){
            imgData.add(imgToBytes(file));
        }
        return imgData;
    }

    这里的载入我们是按顺序载入的,也就是下标为0的位置存放的就是数字0这个图片的字节数组,以此来推。

    下面就是我们载入图片,并进行对比识别的代码:

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //载入0-9这10个数字的单张图片
        List<byte[]> imgData = ImgUtil.loadImgData("./one/");
    
        File[] imgsPath = new File("./sourceimg/").listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".gif")){
                    return true;
                }
                return false;
            }
        });
        String distImgPath = "./distimg/dist.png";
        String srcImgPath = "";
        if (imgsPath == null){
            return;
        }
        for (File f : imgsPath) {
            srcImgPath = f.getPath();
            try {
                //裁剪图片并存储在本地
                //先做图片一次裁剪,裁剪掉边缘空白
                ImgUtil.cropImg(srcImgPath, "gif", 6, 16, 44, 10, "png", true, distImgPath);
                BufferedImage bufferedImage = ImageIO.read(new File(distImgPath));
    
                int oneW = (bufferedImage.getWidth() - 15) / 4;
                StringBuilder stringBuilder = new StringBuilder();
                //循环裁剪4个数字
                for (int i = 0; i < 4; i++) {
                    //裁剪出每个数字
                    byte[] bytes = ImgUtil.cropImg("./distimg/dist.png", "png", i * (oneW + 5), 0, oneW, 10, "png", false, null);
                    //对比裁剪出的数字
                    stringBuilder.append(ImgUtil.chooseImg(imgData, bytes));
                }
                //打印出识别结结果
                System.out.println(stringBuilder.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("用时" + System.currentTimeMillis() - start + "毫秒");
    }

    经过测试,7张图片的识别时间为2200毫秒左右,识别准确率为100%。

    写在最后

    上面介绍的这种方法只能用于特定的场合,由于不需要做图像处理,所以处理效率肯定是较高的,并且没有使用第三方库,所以项目依赖少。后续会陆续介绍稍微复杂验证码的识别处理方式。

    展开全文
  • java 开源 验证码识别

    2011-04-27 12:44:41
    增加了细化算法。 增加了注释 界面稍微变了变
  • java识别验证码 仅限字母数字 附带bmp图片 验证码图片位置随机变动
  • 基于惯性大水滴滴水算法和支持向量机的验证码识别Inertial big drop fall algorithm, libsvmIntroduction:Functions:1.Download the CAPTCHA from specified websites2.Segment and recognize the distorted and ...
  • 1,目前简单的验证码识别可以使用tess4J进行图像识别,前提是需要将验证码图片处理好(清晰,黑白色,无其他干扰素,字体大小一样,间距合适),这样才能保证识别正确 2,简单验证码图片处理方法,二值化,去干扰线...
  • 0x00 基本环境 ...0x01 JwxtCaptchaCrack.java import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import net.coobird.thumbnailator.Thumbnails; import org.tensorflow.fr...
  • 图片验证码识别JAVA源码

    热门讨论 2012-10-18 16:02:33
    图片验证码识别的测试例子,经测试中英字符识别率90%以上
  • 无论是想要学习Spring 5 、Spark、Cassandra等这样的系统,还是通过学习Netty来构建自己的基于Java的高性能网络框架,或者是更加具体的高性能Web或者游戏服务器等,本书都将是你的超强拍档。 本书共分为4个部分: ...
  • Java使用test4j识别验证码test4j的集成此处使用了拷入test4j的项目文件集成到自己的项目中去。1.去test4j官网下载test4j的zip文件(我下载的是3.4.8的版本);2.解压缩文件,得到以下目录需要拷贝到项目中的文件.png3.把...
  • 对于类似以下简单的验证码识别方案:1、234、1、建库:切割验证码为单个字符,人工标记,比如:A。2、识别:给一个验证码:切割为单个字符,在库中查询识别。/**** author:chzeze* 识别验证码并返回* train_path ...
  • java验证码识别--4

    2021-03-16 01:41:57
    )验证码识别如果识别率都是100%,那验证码也就没存在的必要了。其实很多验证码能达到10%的识别率就不错了。下面来一个稍微复杂一点的,识别率85%左右。看验证码挑一张来看放大看,我们会发现干扰线是纯黑色的,因此...
  • 样式比较单一,基本上只有数字和英文字母,位置相对固定,可以使用如下方法,识别结果比较准确(对于复杂验证码识别不好), 引入maven依赖 (也许以后会用到,先保存到csdn上) <dependency> <...
  • 1.完美验证码识别系统制作字库简单流程 2.支持多线程并发识别,识别无需加许可. 3.识别速度是某时代的10倍以上.制作字库也比某时代快多了.某时代可以做的码,本工具一定可以做,而本工具做得了的,某代不一定可以做 4...
  • 内容包括:Java概述、Java基本语法、Java 执行控制流程、面向对象、访问控制权限、接口和抽象类、异常、内部类、集合、泛形、反射、枚举、I/O、关于 null 的几种处理方式、思维导图。 1、Java概述 2、Java基本语法 ...
  • 对于最简单的无干扰背景的验证码,使用tesseract-ocr可以识别,但是带有干扰像素点的验证码,tesseract-ocr识别不出来,可以用机器学习尝试破解,然而短时间可以尝试以下更简单的方法,以下方法,前提是主机上已经...
  • 全自动图形图片验证码识别工具。 在以前的互联网是没有验证码的,后来随着一些自动程序的出现(如自动注册邮箱、自动发布信息等),对网络的正常使用产生越来越严重的影响,便产生了验证码。 使用验证码的目的是通过...
  • 复杂 java 图片验证码

    2009-01-04 18:07:25
    希望对大家有用,里面有源码,跟Google的查不多
  • java识别验证码

    2019-07-05 11:52:41
    1.pom.xml添加依赖 <groupId>net.sourceforge.tess4j</groupId> <artifactId>tess4j <version>3.4.8 ...import java.io.File;...注:普通验证码英文都能识别复杂的无法识别,不能保证成功
  • 在正常使用Tesseract-OCR的默认eng去识别复杂验证码失败率很高,这时候就需要自己训练出自己需要的语言来提高识别成功率。如何训练呢? 训练提高识别率 从jTessBoxEditor:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,560
精华内容 1,824
关键字:

java复杂验证码识别

java 订阅