1,Tess4j
最近在 GitHub 上看到一个图像识别的开源框架 https://github.com/nguyenq/tess4j , 而且是 Java 版的, 为此利用此框架来识别验证码中的信息, 利用它提供的字体库, 来提取信息, 对于没有什么干扰线的验证码准确率还是蛮高的, 对于有一些干扰线的就差一些, 不过也可以能通过训练字体库, 从而可以提高准确率的.
根据范例, 写了一个简单的提取验证码信息的工具类 VerificationCode:
主要是用这个类的 extract 方法, 这个方法有 3 个参数:
第 1 个参数是指定图片的路径
第 2 个参数是指定字体库的, 其中 chi_sim 表示中文简体, eng 表示英文
第 3 个参数是指定是否需要去除干扰线, true 表示需要, false 表示不需要
- package com.swnote.tess4j.test;
- import java.awt.image.BufferedImage;
- import java.io.File;
- import javax.imageio.ImageIO;
- import com.recognition.software.jdeskew.ImageDeskew;
- import.NET.sourceforge.tess4j.ITesseract;
- import.NET.sourceforge.tess4j.Tesseract;
- import.NET.sourceforge.tess4j.util.ImageHelper;
- /**
- * 识别验证码
- *
- * @author lzj
- * @date [2019-03-03]
- */
- public class VerificationCode {
- /**
- * 配置文件
- */
- private static String config_path = "src/main/resources";
- /**
- * 调整倾斜度的阈值
- */
- private static double deskew_threshold = 0.05d;
- /**
- * 提取验证码图片中的文字
- *
- * @param img_path
- * @param lang
- * @param clear
- * @return
- */
- public static String extract(String img_path, String lang, boolean clear) throws Exception {
- // 图片文件
- File img = new File(img_path);
- if (clear) {
- // 将去除干扰后的图片保存在同级目录下的 ext 目录下
- String ext_path = img.getParentFile().getPath() + "/ext";
- // 去除干扰
- CleanImage.cleanLinesInImage(img, ext_path);
- // 处理后的图片
- img = new File(ext_path, img.getName());
- }
- // 设置语言库
- ITesseract instance = new Tesseract();
- File directory = new File(config_path);
- String course_file = directory.getCanonicalPath();
- instance.setDatapath(course_file + "/tessdata");
- // chi_sim 表示中文简体
- // eng 表示英文
- instance.setLanguage(lang);
- BufferedImage buffer_img = ImageIO.read(img);
- ImageDeskew img_deskew = new ImageDeskew(buffer_img);
- double img_skew_angle = img_deskew.getSkewAngle();
- if ((img_skew_angle> deskew_threshold || img_skew_angle <-(deskew_threshold))) {
- buffer_img = ImageHelper.rotateImage(buffer_img, -img_skew_angle);
- }
- String result = instance.doOCR(buffer_img);
- return result;
- }
- }
其中 CleanImage 类是用于清楚验证码干扰线的, 这个类是我从网上找到的, 加上这个类有一定的效果, 但是不是特别理想, 希望大家能够找到更好的去除干扰线方式.
在此也贴一下 CleanImage 类的代码:
- package com.swnote.tess4j.test;
- import java.awt.Color;
- import java.awt.image.BufferedImage;
- import java.io.File;
- import java.io.IOException;
- import javax.imageio.ImageIO;
- /**
- * 网上找到清除图片干扰工具
- */
- public class CleanImage {
- public static void cleanLinesInImage(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);
- }
- }
- }
- }
- // 矩阵打印
- // for (int y = 0; y < h; y++) {
- // for (int x = 0; x < w; x++) {
- // if (isBlack(binaryBufferedImage.getRGB(x, y))) {
- // System.out.print("*");
- // } else {
- // System.out.print(" ");
- // }
- // }
- // System.out.println();
- // }
- 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;
- }
- }
2, 测试
首先测试一张没有干扰线的图片, 即:
然后调用工具类, 可以得到如下结果:
结果是正确的.
再一测试一个中文的, 同时具有干扰线的, 即:
测试结果为:
中文内容是识别出来了, 但是也识别了一些其它信息.
关注我
以你最方便的方式关注我:
微信公众号:
来源: https://www.cnblogs.com/atcloud/p/10635837.html