验证码识别代码(Java)

此代码为自动识别12306火车货运验证码,由于12306火车货运验证码较简单,故本代码没有做图像上的过多变换,只是做了图像灰化,识别正确率90%以上,还可以让它学习更多的验证码样本来提高识别正确率。本代码测试通过后未做过多优化。有验证码识别需求的coder们可以参考下。话不多说,上源码

 

import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

import javax.imageio.ImageIO;

// 字库处理类
class DBOp {

	// 添加字库
	public static void insertRec(String psChar, String psBits)
			throws ClassNotFoundException, SQLException {
		// 1 建立数据库连接
		Class.forName("org.sqlite.JDBC");
		Connection loConn = null;
		loConn = DriverManager.getConnection("jdbc:sqlite:char_bits.db");
		Statement loExecuter = loConn.createStatement();
		loExecuter.setQueryTimeout(1);

		// 2 查找Char的记录
		ResultSet loChars = loExecuter
				.executeQuery("select * from chars where char ='" + psChar
						+ "'");
		if (loChars.next()) {
			int liCharId = loChars.getInt("id");
			ResultSet loBitsRecs = loExecuter
					.executeQuery("select char_bits from bits where char_id ="
							+ liCharId);
			while (loBitsRecs.next()) {
				String lsOldBits = loBitsRecs.getString("char_bits");
				if (lsOldBits.equals(psBits)) {
					loConn.close();
					return;
				}
			}
			loExecuter.executeUpdate("insert into bits values(" + liCharId
					+ ",'" + psBits + "')");
			loConn.close();
		} else {
			loConn.close();
			throw new SQLException("数据库格式错误");
		}
	}

	// 获取字库内所有字的bit码
	public static ArrayList<String> getAllBits(int piOffset, int piLimit)
			throws ClassNotFoundException, SQLException {
		// 1 建立数据库连接
		Class.forName("org.sqlite.JDBC");
		Connection loConn = null;
		loConn = DriverManager.getConnection("jdbc:sqlite:char_bits.db");
		Statement loExecuter = loConn.createStatement();
		loExecuter.setQueryTimeout(1);

		// 2 获取Bits
		ArrayList<String> loResults = new ArrayList<String>();
		ResultSet loBits = loExecuter
				.executeQuery("select char_bits from bits limit " + piLimit
						+ " offset " + piOffset);
		while (loBits.next())
			loResults.add(loBits.getString("char_bits"));
		loConn.close();
		return loResults;
	}

	// 根据字的bit码获取字
	public static String getCharByBits(String psBits)
			throws ClassNotFoundException, SQLException {
		// 1 建立数据库连接
		Class.forName("org.sqlite.JDBC");
		Connection loConn = null;
		loConn = DriverManager.getConnection("jdbc:sqlite:char_bits.db");
		Statement loExecuter = loConn.createStatement();
		loExecuter.setQueryTimeout(1);

		// 2 获取Bits
		ResultSet loCharIds = loExecuter
				.executeQuery("select char_id from bits where char_bits = '"
						+ psBits + "'");
		String lsResult = null;
		if (loCharIds.next()) {
			ResultSet loChars = loExecuter
					.executeQuery("select char from chars where id = "
							+ loCharIds.getString("char_id") + "");
			if (loChars.next())
				lsResult = loChars.getString("char");
		}
		loConn.close();
		return lsResult;
	}

	// 获取所有CHar
	public static ArrayList<String> getAllChars()
			throws ClassNotFoundException, SQLException {
		// 1 建立数据库连接
		Class.forName("org.sqlite.JDBC");
		Connection loConn = null;
		loConn = DriverManager.getConnection("jdbc:sqlite:char_bits.db");
		Statement loExecuter = loConn.createStatement();
		loExecuter.setQueryTimeout(1);

		// 2 获取Char
		ArrayList<String> loResults = new ArrayList<String>();
		ResultSet loChars = loExecuter.executeQuery("select * from chars");
		while (loChars.next())
			loResults.add(loChars.getString("char"));
		loConn.close();
		return loResults;
	}

	// 根据CHar获取所有字节记录
	public static ArrayList<String> getAllBitsByChar(String psChar)
			throws ClassNotFoundException, SQLException {
		// 1 建立数据库连接
		Class.forName("org.sqlite.JDBC");
		Connection loConn = null;
		loConn = DriverManager.getConnection("jdbc:sqlite:char_bits.db");
		Statement loExecuter = loConn.createStatement();
		loExecuter.setQueryTimeout(1);

		// 2 查找
		ArrayList<String> loResults = new ArrayList<String>();
		ResultSet loChars = loExecuter
				.executeQuery("select * from chars where char ='" + psChar
						+ "'");
		if (loChars.next()) {
			ResultSet loBitsRecs = loExecuter
					.executeQuery("select char_bits from bits where char_id ="
							+ loChars.getInt("id"));
			while (loBitsRecs.next())
				loResults.add(loBitsRecs.getString("char_bits"));
		} else
			throw new SQLException("数据库中找不到" + psChar + "的记录");
		loConn.close();

		return loResults;
	}
}

public class AuthRecg {

	private static final String PATH_IMGS = "F:\\Projects\\Test\\yanzhengma";
	private static final int THRESHOLD_BIT_EFFECT = 80;
	private static final float THRESHOLD_COS_SAME_CHAR = 0.87f;

	// 验证码识别公共方法
	public static String recg(BufferedImage poImg) {
		// 1 扫描图片
		ArrayList<String> laScanChars = scan(poImg);

		// 2 针对每个字符,分别识别
		String lsResult = "";
		for (String lsCharBits : laScanChars) {
			String lsRecgChar = recgChar(lsCharBits);
			lsResult += lsRecgChar == null ? "?" : lsRecgChar;
		}
		return lsResult;
	}

	// 产生灰色图片
	private static BufferedImage getGrayPicture(BufferedImage poImg) {
		BufferedImage loGrayPicture;
		int liWidth = poImg.getWidth();
		int liHight = poImg.getHeight();

		loGrayPicture = new BufferedImage(liWidth, liHight,
				BufferedImage.TYPE_3BYTE_BGR);
		ColorConvertOp loCco = new ColorConvertOp(
				ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
		loCco.filter(poImg, loGrayPicture);
		return loGrayPicture;
	}

	// 获取图像bits码
	private static ArrayList<String> scan(BufferedImage poImg) {
		ArrayList<String> loResults = new ArrayList<String>();
		BufferedImage loGrayImg = getGrayPicture(poImg);
		int liWidth = loGrayImg.getWidth();
		int liHeight = loGrayImg.getHeight();

		boolean lbStarted = false;
		int liMinX = -1, liMinY = -1, liMaxX = -1, liMaxY = -1;
		ArrayList<int[]> loCharCols = new ArrayList<int[]>();
		for (int x = 0; x < liWidth; x++) {
			int[] laCol = new int[liHeight];
			for (int y = 0; y < liHeight; y++) {
				int liValue = poImg.getRGB(x, y) % 0x100;
				if (liValue < -THRESHOLD_BIT_EFFECT) {
					laCol[y] = 1;
					lbStarted = true;
					if (liMinX == -1 || liMinX > x)
						liMinX = x;
					if (liMinY == -1 || liMinY > y)
						liMinY = y;
					if (liMaxX == -1 || liMaxX < x)
						liMaxX = x;
					if (liMaxY == -1 || liMaxY < y)
						liMaxY = y;
				} else
					laCol[y] = 0;
			}
			if (lbStarted) {
				loCharCols.add(laCol);
				lbStarted = false;
				if (x == liWidth - 1) {
					String lsCharBits = "";
					for (int i = liMinX; i <= liMaxX; i++) {
						int[] laColBits = loCharCols.get(i - liMinX);
						for (int j = liMinY; j <= liMaxY; j++) {
							lsCharBits += laColBits[j];
						}
					}
					lsCharBits += "|" + (liMaxX - liMinX + 1) + ","
							+ (liMaxY - liMinY + 1);
					loResults.add(lsCharBits);
				}
			} else {
				if (loCharCols.size() > 0) {
					String lsCharBits = "";
					for (int i = liMinX; i <= liMaxX; i++) {
						int[] laColBits = loCharCols.get(i - liMinX);
						for (int j = liMinY; j <= liMaxY; j++) {
							lsCharBits += laColBits[j];
						}
					}
					lsCharBits += "|" + (liMaxX - liMinX + 1) + ","
							+ (liMaxY - liMinY + 1);
					loResults.add(lsCharBits);

					liMinX = -1;
					liMinY = -1;
					liMaxX = -1;
					liMaxY = -1;
					loCharCols = new ArrayList<int[]>();
				}
			}
		}

		return loResults;
	}

	// 字库学习样本
	public static void study(BufferedImage poImg, String psChars) {
		ArrayList<String> loResults = scan(poImg);
		if (loResults.size() == psChars.length()) {
			char[] laChars = psChars.toCharArray();

			for (int i = 0; i < laChars.length; i++)
				try {
					DBOp.insertRec(String.valueOf(laChars[i]), loResults.get(i));
				} catch (SQLException e) {
					e.printStackTrace();
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
		}
	}

	// 向量余弦值计算
	private static float calcCos(char[] paBits1, char[] paBits2) {
		float lfMolecule = 0; // 两个向量内积
		int liLen1 = 0, liLen2 = 0; // 两个向量的长度
		for (int i = 0; i < paBits1.length; i++) {
			int liBit1 = Integer.parseInt("" + paBits1[i]);
			int liBit2 = Integer.parseInt("" + paBits2[i]);
			lfMolecule += liBit1 & liBit2;
			liLen1 += liBit1;
			liLen2 += liBit2;
		}
		float lfLen1 = (float) Math.sqrt(liLen1);
		float lfLen2 = (float) Math.sqrt(liLen2);
		float lfDenominator = lfLen1 * lfLen2;

		return lfDenominator == 0 ? 0 : lfMolecule / lfDenominator;
	}

	// 识别单一字
	private static String recgChar(String psBits) {
		String[] lsTwoParts = psBits.split("\\|");
		int liOffset = 0, liLimit = 50;
		while (true) {
			ArrayList<String> laBits = null;
			try {
				laBits = DBOp.getAllBits(liOffset, liLimit);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			for (String lsBits : laBits) {
				String[] lsParts = lsBits.split("\\|");
				if (lsTwoParts[1].equals(lsParts[1])) {
					if (calcCos(lsTwoParts[0].toCharArray(),
							lsParts[0].toCharArray()) >= THRESHOLD_COS_SAME_CHAR)
						try {
							return DBOp.getCharByBits(lsBits);
						} catch (ClassNotFoundException e) {
							e.printStackTrace();
						} catch (SQLException e) {
							e.printStackTrace();
						}
				}
			}
			if (laBits.size() < liLimit)
				break;
			liOffset += liLimit;
		}
		return null;
	}

	public static void main(String[] args) throws IOException,
			ClassNotFoundException {

		// 1 给出一些样本,供字库学习
		String[] laPics = new String[201];
		for (int i = 0; i < 201; i++)
			laPics[i] = "untitled" + i + ".png";
		String[] loStrs = new String[] { "piade", "eusts", "tafe", "snund",
				"baips", "gike", "fenes", "lein", "caws", "falo", "abys",
				"aift", "wiin", "hoeds", "pise", "hiker", "opes", "nain",
				"tene", "nased", "loop", "eaips", "camp", "traly", "hate",
				"noped", "coll", "rirs", "bolk", "modid", "thre", "knder",
				"bots", "safer", "thrds", "flins", "coory", "ilper", "juyer",
				"goll", "soar", "foen", "dewls", "slme", "flows", "baoks",
				"plugh", "tames", "lorm", "boler", "wift", "feded", "knfer",
				"lark", "chat", "tots", "barst", "ouard", "sots", "ouing",
				"neory", "bave", "buab", "baces", "mebar", "baof", "badly",
				"baast", "bages", "baed", "baue", "bager", "bogs", "baled",
				"beree", "balds", "baamp", "baugh", "bater", "beart", "bere",
				"caed", "daick", "cuual", "icer", "muack", "cave", "coeed",
				"ferce", "costs", "ceam", "clen", "caer", "cast", "wace",
				"coep", "paced", "half", "bofts", "loft", "fimy", "maaf",
				"foeam", "falys", "bafes", "soght", "yege", "args", "kiged",
				"geman", "stge", "geugh", "baing", "goner", "gocer", "gird",
				"gorns", "thger", "cluth", "hern", "thts", "derch", "hook",
				"kixed", "haown", "seach", "sath", "geve", "soaly", "wames",
				"yaows", "vies", "marm", "trows", "yased", "zoink", "liys",
				"evss", "soys", "tayed", "clove", "woows", "jute", "voled",
				"fewns", "saow", "roxes", "wiyal", "roves", "fiyed", "wigs",
				"waxes", "twter", "evrk", "evee", "piys", "voes", "zoced",
				"werry", "velps", "eyns", "evce", "vikes", "ovat", "guve",
				"slfe", "taes", "usice", "jaade", "knva", "saar", "cazac",
				"bems", "hafe", "trons", "uslp", "sexed", "faed", "tild",
				"toow", "toar", "drall", "hazes", "yaad", "seubt", "strns",
				"buap", "redly", "shgs", "sapt", "kins", "buee", "famed",
				"coack", "hamy", "puder", "puod", "jaons", "jarks", "poate",
				"thlks" };
		for (int i = 0; i < 201; i++) {
			System.out.println(i);
			study(ImageIO.read(new File(PATH_IMGS + "\\untitled" + i + ".png")),
					loStrs[i]);
		}

		// 2 获取11个验证码,进行识别
		for (int i = 0; i < 11; i++)
			System.out.println(recg(ImageIO.read(new File(
					"F:\\Projects\\Test\\tests\\untitled" + i + ".png"))));
	}

}

 

字库采用SQLITE数据库存储,表结构如下:

 

训练学习样本201个验证码图片:

 

测试图片:

 

执行结果:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
验证码识别是一项比较复杂的任务,需要使用图像处理、机器学习等技术。下面是一个简单的验证码识别训练的Java代码: 1. 首先需要准备验证码数据集,包括验证码图片和对应的标签。可以手动标注或者使用OCR工具进行自动标注。 2. 使用Java的图像处理库读取验证码图片并进行预处理,例如将图片转换为灰度图、二值化等。 3. 使用机器学习算法训练模型。常用的算法包括SVM、神经网络等。这里以SVM为例: ``` // 读取数据集 List<String> imagePaths = ...; List<String> labels = ...; List<Mat> images = new ArrayList<>(); for (String imagePath : imagePaths) { Mat image = Imgcodecs.imread(imagePath, Imgcodecs.IMREAD_GRAYSCALE); images.add(image); } // 特征提取 List<Mat> features = new ArrayList<>(); for (Mat image : images) { Mat feature = new Mat(); Imgproc.resize(image, feature, new Size(20, 20)); feature = feature.reshape(1, 1); feature.convertTo(feature, CvType.CV_32F); Core.divide(feature, new Scalar(255.0), feature); features.add(feature); } // 训练模型 Mat samples = new Mat(features.size(), features.get(0).cols(), CvType.CV_32F); for (int i = 0; i < features.size(); i++) { features.get(i).row(0).copyTo(samples.row(i)); } Mat labelsMat = new Mat(labels.size(), 1, CvType.CV_32SC1); for (int i = 0; i < labels.size(); i++) { labelsMat.put(i, 0, Integer.parseInt(labels.get(i))); } SVM svm = SVM.create(); svm.setType(SVM.C_SVC); svm.setKernel(SVM.RBF); svm.setC(1.0); svm.setGamma(0.1); svm.train(samples, Ml.ROW_SAMPLE, labelsMat); svm.save("svm.xml"); ``` 4. 使用训练好的模型对新的验证码进行识别。具体方法是将验证码图片进行预处理和特征提取,然后使用模型进行预测。 ``` // 读取模型 SVM svm = SVM.load("svm.xml"); // 识别验证码 Mat image = Imgcodecs.imread("captcha.png", Imgcodecs.IMREAD_GRAYSCALE); Mat feature = new Mat(); Imgproc.resize(image, feature, new Size(20, 20)); feature = feature.reshape(1, 1); feature.convertTo(feature, CvType.CV_32F); Core.divide(feature, new Scalar(255.0), feature); Mat response = new Mat(); svm.predict(feature, response, 0); int label = (int) response.get(0, 0)[0]; System.out.println("验证码识别结果:" + label); ``` 需要注意的是,验证码识别是一项比较复杂的任务,以上代码只是一个简单的示例,实际的应用可能需要更加复杂的算法和预处理方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值