java - 使用OpenCV + Tesseract识别图片验证码

目录
-
- Tesseract的使用
-
- 识别效果
- 借助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,识别准确率提升明显。





旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。
更多推荐