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

网友投稿 538 2022-10-09


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

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

1、百度接口用过(可以自己去百度开发者申请,免费的),识别率吧,还可以,但也不是百分百的,但是次数使用有限制,虽然也是够用,但是被限制总是害怕超过不让用。

2、canvas的话是需要对图片做具体的处理,涉及到图片的翻转、置灰、文字间隔的设定等等,成功率很高,但是公司产品验证码是各式各样的,没办法用这种方法处理,所以暂时放弃了。

3、ocrad这个目前用过其.js版本,识别率还是比较低的,具体使用后面会再写一篇文章介绍一下的。

虽然,网上对于 Tessdata的技术介绍文章一搜一大片,但是其实小仙真正用起来的时候,还是费了点周折的。:fendou:

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

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

1、添加项目依赖

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

net.java.dev.jna

jna

4.1.0

net.sourceforge.tess4j

tess4j

2.0.1

com.sun.jna

jna

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 = ImageIhttp://O.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)) && http://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;

}

http://

这一部分由于项目问题,贴在这里做了特殊处理,原码有一点点区别。大家使用,如果有什么问题,欢迎反馈!

6、成果

这里简单放个对照,图片将就看一下效果,识别结果大概90%以上吧:


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:VTI知识点回顾(第二次修改)
下一篇:主动防御技术之蜜罐(蜜罐攻击方式)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~