Java如何使用Tessdata做OCR图片文字说明,看这篇就够了~

Posted 恒生LIGHT云社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java如何使用Tessdata做OCR图片文字说明,看这篇就够了~相关的知识,希望对你有一定的参考价值。

说到文字识别,目前除了用一些现成的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%以上吧:

以上是关于Java如何使用Tessdata做OCR图片文字说明,看这篇就够了~的主要内容,如果未能解决你的问题,请参考以下文章

怎样做才能实现图片转文字

python有啥好的本地文字识别

Tesseract-OCR-03-图片文字识别

如何在迅捷OCR文字识别软件中进行图片局部识别

带字的图片如何转换成可编辑的文字?

迅捷OCR文字识别软件教你如何将图片转文字