之前在做數據覈對部分工作,需要獲取廠商的數據,有的廠商提供了api,可以直接通過api拿到數據;有的就沒api,這部分,只能去它們後臺獲取了,那就需要爬蟲,但是,過程中,又碰到登陸的驗證碼。這裏記錄一下識別驗證碼的過程。
使用tess4j
1.下載tessdata和訓練語言包
在tessract的github直接下載即可,下載地址戳我(只需要項目的 tessdata文件夾 )。這裏,我下載後放在 D:\tesseract\tessdata文件夾。
訓練語言包:tessdata支持多語言,每個語言不同,比如,英文數字類型的驗證碼,對應的是 eng.traineddata
訓練語言包,下載地址戳我
下載完,需要把文件放到步驟一的tessdata文件夾下面。我的是放在 D:\tesseract\tessdata下面。
2.加入maven依賴
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>4.1.1</version>
<exclusions>
<exclusion>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
3.編寫代碼
public class TestImgV1 {
/** tessdata 路徑 */
private static final String TESSDATA_PATH = "D:\\tesseract\\tessdata";
/** 驗證碼圖片地址 */
private static final String url = "http://abc.com/validateCodeForIndex.do";
public static void main(String[] args) throws Exception {
BufferedImage codeImage = ImageIO.read(new URL(url));
// 本地做個備份,好對比
write2disk(codeImage);
Tesseract tessreact = new Tesseract();
tessreact.setDatapath(TESSDATA_PATH);
String result = tessreact.doOCR(codeImage);
System.out.println("測驗結果:[" + result.replace(" ", "").trim() + "]");
}
private static void write2disk(BufferedImage image) throws IOException {
File file = new File("d:\\tesseract\\code.jpg");
ImageIO.write(image, "jpg", file);
}
}
4.驗證結果
運行main方法,可以看到,已經正確解析出來了。
如下圖,左側是檢驗結果,右邊的是驗證碼原圖。
我隨便試了10次,識別出了6次。但是,如果拿那種1和l、g和q、0和o這種相似度比較高的,識別度驟降。
有沒有提高識別率的方案?有的,可以使用tesseract-ocr
使用tesseract-ocr
tesseract-ocr在tess4j的基礎上,增加了對驗證碼去噪點、二值化等操作。要想使用tesseract-ocr,具體步驟如下。
1.安裝tesseract-ocr
我是windows,下載的是exe安裝文件,官網下載地址戳我
下載完成,需要加入系統環境變量 Path 和 TESSDATA_PREFIX
在cmd裏,執行 tesseract –version ,如果能看到輸出的版本號,那配置就沒問題
執行 tesseract code.jpg result ,它會解析當前目錄下的 code.jpg 驗證碼圖片,把解析結果寫入到 result.txt 文件(txt後綴是它自動加上的)
詳細安裝步驟,可以參考這篇文章 Windows安裝Tesseract-OCR 4.00並配置環境變量
2.加入maven依賴
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>4.1.1</version>
<exclusions>
<exclusion>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>3.2.0-0</version>
</dependency>
</dependencies>
3.編寫代碼
public class TestImgV2 {
/**tessdata 路徑*/
private static final String TESSDATA_PATH = "D:\\tesseract\\tesseract-ocr\\tessdata";
/** 驗證碼路徑*/
private static final String IMAGE_CODE_PATH = "d:\\tesseract\\code.jpg";
/** 用來調用OpenCV庫文件,必須添加 */
static {
nu.pattern.OpenCV.loadShared();
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// 驗證碼圖片
File imageFile = new File(IMAGE_CODE_PATH);
// 去噪點、二值化
filterPic(imageFile);
imageFile = new File(IMAGE_CODE_PATH);
String result = getResult(imageFile);
System.out.println("測驗結果:[" + result.replace(" ", "").trim() + "]");
}
// 圖片處理及處理後的圖片儲存
public static void filterPic(File imageFile) {
// 圖片去噪
Mat src = Imgcodecs.imread(imageFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
Mat dst = new Mat(src.width(), src.height(), CvType.CV_8UC1);
if (src.empty()) {
System.out.println("圖片不存在");
return;
}
Imgproc.boxFilter(src, dst, src.depth(), new Size(3.2, 3.2));
Imgcodecs.imwrite(imageFile.getAbsolutePath(), dst);
// 圖片閾值處理,二值化
Mat src1 = Imgcodecs.imread(imageFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
Mat dst1 = new Mat(src1.width(), src1.height(), CvType.CV_8UC1);
Imgproc.threshold(src1, dst1, 165, 200, Imgproc.THRESH_TRUNC);
Imgcodecs.imwrite(imageFile.getAbsolutePath(), dst1);
// 圖片截取
Mat src2 = Imgcodecs.imread(imageFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
Rect roi = new Rect(4, 2, src2.cols() - 7, src2.rows() - 4); // 參數:x座標,y座標,截取的長度,截取的寬度
Mat dst2 = new Mat(src2, roi);
Imgcodecs.imwrite(imageFile.getAbsolutePath(), dst2);
}
// 獲取解析結果
public static String getResult(File imageFile) {
if (!imageFile.exists()) {
System.out.println("圖片不存在");
}
Tesseract tessreact = new Tesseract();
tessreact.setDatapath(TESSDATA_PATH);
String result;
try {
result = tessreact.doOCR(imageFile);
} catch (TesseractException e) {
e.printStackTrace();
return null;
}
return result;
}
}
4.驗證結果
在tess4j的基礎上,增加了去噪、二值化,解析效果確實比tess4j高一點,但是,字符之間的間距很小或者存在局部重疊的情況,那基本是解析錯誤。
不過,如果只是用來識別管理後臺的驗證碼,用tess4j就已經差不多夠用了。
如果要更進一步,那就要使用 JTessBoxEditorFX 提高識別率,或者,針對性的生成自己的訓練庫了。有興趣的可以參考這個大佬的文章 利用jTessBoxEditor工具進行Tesseract3.02.02樣本訓練,提高驗證碼識別率