人生难得几回搏,此时不搏待何时。这篇文章主要讲述Java如何使用Tessdata做OCR图片文字说明,看这篇就够了~相关的知识,希望能为你提供帮助。
说到文字识别,目前除了用一些现成的api,大概就是 tessdata
、canvas
或者 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 = https://www.songbingjia.com/android/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 staticStringocrResult(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图片文字说明,看这篇就够了~】
文章图片
推荐阅读
- Cocos2d-x 3.8.1+Cocos Studio 2.3.2捉虫记之控制场景文件中的骨骼动画
- 如何在JavaScript中自定义Google Maps颜色和样式
- 如何使用JavaScript(WebRTC)在浏览器中录制带音频的视频
- 如何使用JavaScript将二进制字符串转换为可读字符串,反之亦然
- 如何区分HTML并使用JavaScript在HTML中生成输出
- 如何使用js-imagediff使用JavaScript区分2张图片
- 如何在局域网连接的设备上使用Artyom.js沙箱
- 如何在浏览器的JavaScript控制台中显示图像
- 如何使用JavaScript检测AdBlock是否已安装在访问者的设备上