作者:悠悠做神仙

起源:恒生LIGHT云社区

说到文字辨认,目前除了用一些现成的api,大略就是 tessdatacanvas或者 ocrad等。

1、百度接口用过(能够本人去百度开发者申请,收费的),识别率吧,还能够,但也不是百分百的,然而次数应用有限度,尽管也是够用,然而被限度总是胆怯超过不让用。
2、canvas的话是须要对图片做具体的解决,波及到图片的翻转、置灰、文字距离的设定等等,成功率很高,然而公司产品验证码是各式各样的,没方法用这种办法解决,所以临时放弃了。
3、ocrad这个目前用过其.js版本,识别率还是比拟低的,具体应用前面会再写一篇文章介绍一下的。
尽管,网上对于 Tessdata的技术介绍文章一搜一大片,然而其实小仙真正用起来的时候,还是费了点周折的。:fendou:

思路:截全图–截取元素图片–解决–辨认–输入

留神:图片截取格局对立为.jpg,用png会出问题。

1、增加我的项目依赖

在我的项目的pom.xml文件中,增加以下依赖

<!--<tess4j图片辨认>--><dependency>    <groupId>net.java.dev.jna</groupId>    <artifactId>jna</artifactId>    <version>4.1.0</version></dependency><dependency>    <groupId>net.sourceforge.tess4j</groupId>    <artifactId>tess4j</artifactId>    <version>2.0.1</version>    <exclusions>        <exclusion>            <groupId>com.sun.jna</groupId>            <artifactId>jna</artifactId>        </exclusion>    </exclusions></dependency>

2、从全图中截取元素图片

// 元素截图public static String[] elementscreenShot(WebElement element )        throws Exception {    WrapsDriver wrapsDriver = (WrapsDriver) element;    long time = System.currentTimeMillis();    // 截图整个页面    File screen = ((TakesScreenshot) wrapsDriver.getWrappedDriver())            .getScreenshotAs(OutputType.FILE);    BufferedImage img = ImageIO.read(screen);    // 取得元素的高度和宽度    int width = element.getSize().getWidth();    int height = element.getSize().getHeight();    // 创立一个矩形应用下面的高度,和宽度    Rectangle rect = new Rectangle(width, height);    // 失去元素的坐标    Point p = element.getLocation();    BufferedImage dest = img.getSubimage(p.getX(), p.getY(),            (int) rect.getWidth(), (int) rect.getHeight());    // 存为png格局    ImageIO.write(dest, "png", screen);    DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");    FileSystemView fsv = FileSystemView.getFileSystemView();    File com = fsv.getHomeDirectory(); // 这便是读取桌面门路的办法了    String url = com.getPath() + "/test";    File location = new File(url);    if (!location.exists()) {        location.mkdirs();    }    String imgPath = location.getAbsolutePath() + File.separator + "pic_"            + time + ".jpg";    String cleanPath = location.getAbsolutePath();    //存了原图片和分明后图片的地址    String[] imgpath = { imgPath, cleanPath };    File targetFile = new File(imgPath);    try {        FileUtils.copyFile(screen, targetFile);    } catch (IOException e1) {        e1.printStackTrace();    }    //元素图片门路    return imgpath;}

3、对截取图片进行解决:灰度化、二值化、去除烦扰线等

以下是图像处理的类,其中对于去除烦扰线的操作还是慎用,可能会把文字也剔除掉。

public class CleanElementImage {    /**     *     * @param sfile     *            须要去噪的图像     * @param destDir     *            去噪后的图像保留地址     * @throws IOException     */    public static void handlImage(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);//                    }//                }//            }//        }    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;}}

4、筹备辨认的语言包

默认是英文(辨认字母和数字),如果要辨认中文(数字 + 中文),须要制订语言包。
语言包能够指定一个门路,有就能够了。
源码下载地址
能够下载源码,而后到上面这个门路找到语言包,把语言包放到一个门路:
例如:XXX/tessdata/上面。

tesseract.js-master.zip\tesseract.js-master\tests\assets\traineddata 

5、对图片进行辨认

/*** 图片辨认* @author wangy* @date 2019-08-26* @param parameter*/public static  String  ocrResult(WebElement element ) throws Exception {    FileSystemView fsv = FileSystemView.getFileSystemView();    File com=fsv.getHomeDirectory();    //这便是读取桌面门路的办法了    String url = "";    String os = System.getProperty("os.name");    //识别系统,找不同的语言包门路    if (os.indexOf("Windows") == -1) {        url = "/opt/google/";    } else {        url = com.getPath();    }    //获取元素截图的门路        String path[]=Screenshot.elementscreenShot(element);        //获取未解决的截图门路        String imgpath=path[0];    String result = null;    File imageFile = new File(imgpath);    //要对图片解决        CleanElementImage.handlImage(imageFile,path[1]);    ITesseract instance = new Tesseract();    //读取语言包的门路地址    instance.setDatapath(url + File.separator + "test" + File.separator                + "tessdata");    // 默认是英文(辨认字母和数字),如果要辨认中文(数字 + 中文),须要制订语言包,这里是数字,所以没用语言包        // instance.setLanguage("chi_sim");        //为了避免没截完图片就辨认,做了一个简略的循环    try{        String ocrResult=instance.doOCR(imageFile);        if(imageFile.exists()&&ocrResult!=""){            result=ocrResult;        }else {            while(true){                Thread.sleep(1000);                if(imageFile.exists()&&ocrResult!=""){                    result=ocrResult;                    break;                }            }        }    }catch(TesseractException e){        System.out.println(e.getMessage());    }    return result;}

这一部分因为我的项目问题,贴在这里做了非凡解决,原码有一点点区别。大家应用,如果有什么问题,欢送反馈!

6、成绩

这里简略放个对照,图片将就看一下成果,辨认后果大略90%以上吧: