java - 使用OpenCV + Tesseract识别图片验证码
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。
* @author Alan
* @Email no008@foxmail.com
java - 使用OpenCV + Tesseract识别图片验证码

Tesseract的使用
1、引入tess4j依赖:
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>5.8.0</version>
</dependency>
2、下载相关语言的训练数据,放到项目指定目录下;下载地址:https://gitcode.net/mirrors/tesseract-ocr/tessdata
比如要识别简体中文,使用chi_sim.traineddata,要识别英文,则使用eng.traineddata。
3、编写代码:
public static void main(String[] args) throws IOException, TesseractException {
ITesseract tesseract = new Tesseract();
//设置语言包地址
tesseract.setDatapath("./src/main/resources/tessdata");
//选择语言包
tesseract.setLanguage("chi_sim");
BufferedImage bufferedImage = ImageIO.read(new File("C:\\Users\\xxx\\Downloads\\verificationCodePicture101.jfif"));
System.out.println(tesseract.doOCR(bufferedImage));
}
识别效果
原图:
chi_sim语言识别结果:心
z
使用eng语言包直接识别不到任何字符。
结论:直接使用默认的语言包识别效果完全无法满足要求,需要进行图像处理,以及考虑自定义模型训练。
借助OpenCV进行图像处理
1、下载、安装OpenCV,下载地址:https://opencv.org/releases/;从安装目录中获取到动态链接库文件,然后放到项目指定目录下。
动态链接库文件路径如下:
2、引入maven依赖:
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>4.7.0-0</version>
</dependency>
3、编写代码,对图片进行处理,突出字符特征。
package org.sohuerp.utils;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class OcrUtil {
public static String getVerificationCodeFromPicture(String imgPath) throws TesseractException, IOException {
//加载dll文件
String projectPath = System.getProperty("user.dir");
System.load(projectPath + "/src/main/resources/opencv_java470.dll");
//加载图片
Mat srcImg = Imgcodecs.imread(imgPath);
//裁剪
srcImg = new Mat(srcImg, new Rect(52, 1, 110, 48));
//灰度化
Mat grayImg = new Mat();
Imgproc.cvtColor(srcImg, grayImg, Imgproc.COLOR_BGR2GRAY);
//二值化(超过120的设置为255白色,不到120的设置为0黑色)
Mat binaryImg = new Mat();
Imgproc.threshold(srcImg, binaryImg, 120, 255, Imgproc.THRESH_BINARY);
BufferedImage bufferedImage = mat2BufferedImg(binaryImg);
int h = bufferedImage.getHeight();
int w = bufferedImage.getWidth();
//去噪点(如果黑点的上、下、左、右四面,有三面为白色,则将该点改为白色)
for (int x = 1; x < w - 1; x++) {
for (int y = 1; y < h - 1; y++) {
if (isBlack(bufferedImage.getRGB(x, y))) {
int n = 0;
if (isWhite(bufferedImage.getRGB(x - 1, y))) {
n++;
}
if (isWhite(bufferedImage.getRGB(x + 1, y))) {
n++;
}
if (isWhite(bufferedImage.getRGB(x, y - 1))) {
n++;
}
if (isWhite(bufferedImage.getRGB(x, y + 1))) {
n++;
}
if (n >= 3) {
bufferedImage.setRGB(x, y, -1);
}
}
}
}
//从下到上再来一次
for (int x = 1; x < w - 1; x++) {
for (int y = h - 2; y > 0; y--) {
if (isBlack(bufferedImage.getRGB(x, y))) {
int n = 0;
if (isWhite(bufferedImage.getRGB(x - 1, y))) {
n++;
}
if (isWhite(bufferedImage.getRGB(x + 1, y))) {
n++;
}
if (isWhite(bufferedImage.getRGB(x, y - 1))) {
n++;
}
if (isWhite(bufferedImage.getRGB(x, y + 1))) {
n++;
}
if (n >= 3) {
bufferedImage.setRGB(x, y, -1);
}
}
}
}
//去除横向干扰线(宽度不超过3个像素的黑线改为白色)
for (int x = 0; x < w; x++) {
List<Point> list = new ArrayList<>();
for (int y = 0; y < h; y++) {
if (isBlack(bufferedImage.getRGB(x, y))) {
list.add(new Point(x, y));
} else if (list.size() > 3) {
list.clear();
} else if (!list.isEmpty()) {
list.forEach(point -> {
bufferedImage.setRGB(point.x, point.y, Color.WHITE.getRGB());
});
break;
}
}
}
//断点修复(去干扰线的过程可能导致较细的验证码字符断开,进行一定程度的修复)
for (int x = 1; x < w - 1; x++) {
for (int y = 1; y < h - 1; y++) {
if (isWhite(bufferedImage.getRGB(x, y))
&& isBlack(bufferedImage.getRGB(x - 1, y))
&& isBlack(bufferedImage.getRGB(x + 1, y))) {
bufferedImage.setRGB(x, y, Color.BLACK.getRGB());
}
}
}
Mat tempMat = bufferedImg2Mat(bufferedImage);
Mat resultImg = new Mat();
Imgproc.threshold(tempMat, resultImg, 120, 255, Imgproc.THRESH_BINARY);
//保存处理后的图像
Imgcodecs.imwrite("./src/main/resources/result.jpg", resultImg);
//开始识别
ITesseract tesseract = new Tesseract();
tesseract.setDatapath("./src/main/resources/tessdata");
BufferedImage input = ImageIO.read(new File("./src/main/resources/result.jpg"));
tesseract.setLanguage("chi_sim");
return tesseract.doOCR(input);
}
public static BufferedImage mat2BufferedImg(Mat mat) throws IOException {
MatOfByte matOfByte = new MatOfByte();
Imgcodecs.imencode(".jpg", mat, matOfByte);
ByteArrayInputStream inputStream = new ByteArrayInputStream(matOfByte.toArray());
return ImageIO.read(inputStream);
}
public static Mat bufferedImg2Mat(BufferedImage image) {
Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
mat.put(0, 0, pixels);
return mat;
}
public static boolean isBlack(int colorInt) {
Color color = new Color(colorInt);
return color.getRed() + color.getGreen() + color.getBlue() < 300;
}
public static boolean isWhite(int colorInt) {
Color color = new Color(colorInt);
return color.getRed() + color.getGreen() + color.getBlue() >= 300;
}
public static void main(String[] args) throws TesseractException, IOException {
String imgPath = "C:\\Users\\xxx\\Downloads\\verificationCodePicture73.jfif";
System.out.println(getVerificationCodeFromPicture(imgPath));
}
}
识别效果
原图1:
处理结果:
chi_sim语言包识别结果:ZOtU,eng语言包识别结果:Zotu
原图2:
处理结果:
chi_sim语言包识别结果:小
4Q,eng语言包识别结果:4cqe
结论:经过处理后的验证码图片,字符特征比较明显,但是使用默认的语言包,识别的准确率仍然不高,需要使用处理后的图片进行训练,生成专用的训练数据,提高识别准确率。
Tesseract自定义模型训练
1、下载并安装Tesseract,下载地址:https://digi.bib.uni-mannheim.de/tesseract/,本文使用tesseract-ocr-w64-setup-5.3.1.20230401.exe版本;使用安装目录设置环境变量,如:D:\Tesseract。
2、下载jTessBoxEditor,下载地址:https://sourceforge.net/projects/vietocr/files/jTessBoxEditor/,将下载后的压缩包解压,双击jTessBoxEditorFX.jar即可运行。
3、准备一定数量的待训练样本图片,在图片所在目录下进入命令行窗口,按以下步骤生产训练数据:
1)打开jTessBoxEditorFX,点击Tools->Merge
TIFF选项,批量选择要合并的样本图片,点击"打开",如图:
然后输入要保存的tif文件名"vc.sim.exp0",进行保存。vc表示语言包名,等价于chi_sim、eng,可随意取,sim表示字体名,可随意取。
如图:
2)执行命令:tesseract vc.sim.exp0.tif vc.sim.exp0 --psm 7 batch.nochop makebox
目录下会生成与tif图片关联的box文件"vc.sim.exp0.box",
使用tesseract --help-psm命令,可查看–psm参数含义:
Page
segmentation modes:
0
Orientation and script detection (OSD) only.
1
Automatic page segmentation with OSD.
2
Automatic page segmentation, but no OSD, or OCR. (not implemented)
3
Fully automatic page segmentation, but no OSD. (Default)
4
Assume a single column of text of variable sizes.
5
Assume a single uniform block of vertically aligned text.
6
Assume a single uniform block of text.
7
Treat the image as a single text line.
8
Treat the image as a single word.
9
Treat the image as a single word in a circle.
10
Treat the image as a single character.
11
Sparse text. Find as much text as possible in no particular order.
12
Sparse text with OSD.
13
Raw line. Treat the image as a single text line,
bypassing
hacks that are Tesseract-specific.
3)使用jTessBoxEditorFX,点击"Box
Editor",打开合并后的tif文件,即可编辑与之关联的box文件,对每一页的识别结果进行校对,如下图:
此过程是一个手动重复且耗时的过程。
4)执行命令:tesseract vc.sim.exp0.tif vc.sim.exp0 --psm 7 nobatch
box.train
,生成.tr文件。
5)执行命令:echo "sim
0 0 0 0
0">font_properties
,生成font_properties文件(注意去掉文件内容中的双引号)。
6)执行命令:unicharset_extractor vc.sim.exp0.box
,生成unicharset文件。
7)执行命令:shapeclustering -F
font_properties -U unicharset -O vc.unicharset vc.sim.exp0.tr
8)执行命令:mftraining -F
font_properties -U unicharset -O vc.unicharset vc.sim.exp0.tr
9)执行命令:cntraining vc.sim.exp0.tr
10)重命名以上过程中生成的四个文件,前缀使用自定义的语言名vc,分别执行:rename normproto
vc.normproto
、rename inttemp
vc.inttemp
、rename pffmtable
vc.pffmtable
、rename shapetable
vc.shapetable
11)执行命令:combine_tessdata vc.
,生成最终的 vc.traineddata
训练数据。
识别效果
可以将训练数据vc.traineddata放到Tesseract安装目录的tessdata文件夹下,方便使用命令行调试,命令如下:
tesseract result101.jpg output --psm 7 -l vc
结果将输出到 output.txt文件中。
将vc.traineddata放到java项目中,取代之前的chi_sim和eng,识别效果如下:
原图:
识别结果:4c4e、2qnh,识别准确率提升明显。




更多推荐
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 工良出品 | 长文讲解 MCP 和案例实战
· 多年后再做Web开发,AI帮大忙
· centos停服,迁移centos7.3系统到新搭建的openEuler
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 上周热点回顾(4.14-4.20)
2022-03-30 摩羯座特殊的择偶标准
2022-03-30 SpringBoot + FreeMarker + FlyingSaucer 实现PDF在线预览、打印、下载
2022-03-30 navicat连接sqlserver报错 [IM002] [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序
2016-03-30 zyUpload---照片上传并显示效果
2016-03-30 js的隐含参数(arguments,callee,caller)使用方法
2016-03-30 js中callback.call()和callback()的区别
2016-03-30 理解javascript中的回调函数(callback)