服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java使用Tessdata做OCR图片文字识别的详细思路

Java使用Tessdata做OCR图片文字识别的详细思路

2021-10-28 10:42恒生LIGHT云社区 Java教程

这篇文章主要介绍了Java使用Tessdata做OCR图片文字识别的详细思路,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

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

1、百度接口用过(可以自己去百度开发者申请,免费的),识别率吧,还可以,但也不是百分百的,但是次数使用有限制,虽然也是够用,但是被限制总是害怕超过不让用。
2、canvas的话是需要对图片做具体的处理,涉及到图片的翻转、置灰、文字间隔的设定等等,成功率很高,但是公司产品验证码是各式各样的,没办法用这种方法处理,所以暂时放弃了。
3、ocrad这个目前用过其.js版本,识别率还是比较低的,具体使用后面会再写一篇文章介绍一下的。
虽然,网上对于 Tessdata的技术介绍文章一搜一大片,但是其实小仙真正用起来的时候,还是费了点周折的。:fendou:

思路:截全图–截取元素图片–处理–识别–输出

注意:图片截取格式统一为.jpg,用png会出问题。

1、添加项目依赖

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--<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、从全图中截取元素图片

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 元素截图
 
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、对截取图片进行处理:灰度化、二值化、去除干扰线等

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
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/下面。

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

Java使用Tessdata做OCR图片文字识别的详细思路

5、对图片进行识别

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* 图片识别
* @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图片文字识别的详细思路

到此这篇关于Java使用Tessdata做OCR图片文字识别的详细思路的文章就介绍到这了,更多相关java Tessdata图片文字内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.51cto.com/u_7932852/3221714

延伸 · 阅读

精彩推荐
  • Java教程java实现在普通类中注入service或mapper

    java实现在普通类中注入service或mapper

    这篇文章主要介绍了java实现在普通类中注入service或mapper的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    一个包子的成长10912021-10-18
  • Java教程java 利用java反射机制动态加载类的简单实现

    java 利用java反射机制动态加载类的简单实现

    下面小编就为大家带来一篇java 利用java反射机制动态加载类的简单实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看...

    服务器之家3192020-06-17
  • Java教程Java单测void类型的方法详解

    Java单测void类型的方法详解

    这篇文章主要给大家介绍了Java中单测void类型的方法,文中给出了详细的示例代码,相信对大家的理解和学习具有一定的参考借鉴价值,需要的朋友可以跟着...

    疯狂的蚂蚁4452020-07-24
  • Java教程全面解析Java中的引用类型

    全面解析Java中的引用类型

    在Java中对象以引用来指向JVM的内存区块,这里我们总结了强引用、软引用、弱引用和假象引用(幽灵引用),下面就具体来全面解析Java中的引用类型: ...

    成富5322020-05-07
  • Java教程Java实现复杂的进制转换器功能示例

    Java实现复杂的进制转换器功能示例

    这篇文章主要介绍了Java实现复杂的进制转换器功能,结合实例形式分析了java数学运算的相关实现技巧,需要的朋友可以参考下 ...

    aitaoke3092020-07-21
  • Java教程关于Java企业级项目开发思想

    关于Java企业级项目开发思想

    Java企业级项目开发思想。偶遇,读有所得,遂分享给大家,本文不涉及案例,只谈思想和理念,需要的朋友可以参考。...

    李社河8692021-01-13
  • Java教程详解SpringBoot Schedule配置

    详解SpringBoot Schedule配置

    本篇文章主要介绍了详解SpringBoot Schedule配置 ,可以实现定时任务,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    slimer7612020-08-31
  • Java教程Java的字符串中对子字符串的查找方法总结

    Java的字符串中对子字符串的查找方法总结

    这篇文章主要介绍了Java的字符串中对子字符串的查找方法总结,是Java入门学习中的基础知识,需要的朋友可以参考下 ...

    iceFin5212020-01-13