-
2021-03-09 19:27:33
1 import javax.imageio.ImageIO;
2 import java.awt.image.BufferedImage;
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.HashMap;
6 import java.util.Map;
7
8 public class Global {
9 public static final String LIB_PATH = "C:/lib";
10 public static final String LIB_NO = "C:/no";
11 public static final double SIMILARITY = 0.9;
12 public static Map trainedMap;
13 public static Map noTrainedMap = new HashMap<>();
14
15 static {
16 trainedMap = getMap(LIB_PATH);
17 noTrainedMap = getMap(LIB_NO);
18 }
19
20 private static Map getMap(String path) {
21 Map map = new HashMap<>();
22 File parentFile = new File(path);
23 for (String filePath : parentFile.list()) {
24 File file = new File(path + File.separator + filePath);
25 String fileName = file.getName();
26 String key = fileName.substring(0,fileName.indexOf(".")).trim();
27 try {
28 map.put(key, ImageIO.read(file));
29 } catch (IOException e) {
30 e.printStackTrace();
31 }
32 }
33 return map;
34 }
35 }
36 import javax.imageio.ImageIO;
37 import java.awt.*;
38 import java.awt.image.BufferedImage;
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.*;
42
43 /**
44 * 识别验证码
45 */
46 public class ImageProcess {
47 private String imgPath;
48
49 public ImageProcess(String imgPath) {
50 this.imgPath = imgPath;
51 }
52
53 public String getResult() {
54 java.util.List imgList = null;
55 try {
56 BufferedImage img = ImageIO.read(new File(imgPath));
57 imgList = splitImage(img);
58 } catch (IOException e) {
59 e.printStackTrace();
60 } catch (Exception e) {
61 e.printStackTrace();
62 }
63 return realize(imgList);
64 }
65
66 //分析识别
67 private String realize(java.util.List imgList) {
68 String resultStr = "";
69 for (BufferedImage img : imgList) {
70 String key = getKey(Global.trainedMap, img);
71 if (key == null) {
72 String noTrainedKey = getKey(Global.noTrainedMap, img);
73 if(noTrainedKey == null){
74 try {
75 ImageIO.write(img, "JPG", new File(Global.LIB_NO + File.separator + UUID.randomUUID() + ".jpg"));
76 } catch (IOException e) {
77 e.printStackTrace();
78 }
79 }
80 } else {
81 resultStr += key;
82 }
83 }
84 return resultStr;
85 }
86
87 //获取已知值
88 private String getKey(Map map, BufferedImage img){
89 String resultStr = null;
90 Set> entrySet = map.entrySet();
91 for (Map.Entry one : entrySet) {
92 if (isSimilarity(img, one.getValue())) {
93 resultStr = one.getKey();
94 break;
95 }
96 }
97 return resultStr;
98 }
99
100 //是否相似
101 private boolean isSimilarity(BufferedImage imageA, BufferedImage imageB) {
102 int widthA = imageA.getWidth();
103 int widthB = imageB.getWidth();
104 int heightA = imageA.getHeight();
105 int heightB = imageB.getHeight();
106 if (widthA != widthB || heightA != heightB) {
107 return false;
108 } else {
109 int[][] weightA = getImgWeight(imageA);
110 int[][] weightB = getImgWeight(imageB);
111 int count = 0;
112 for (int i = 0; i < widthA; i++) {
113 for (int j = 0; j < heightB; j++) {
114 if (weightA[i][j] != weightB[i][j]) {
115 count++;
116 }
117 }
118 }
119 if ((double) count / (widthA * widthB) > (1 - Global.SIMILARITY)) {
120 return false;
121 } else {
122 return true;
123 }
124 }
125 }
126
127 //分割图片
128 private java.util.List splitImage(BufferedImage originImg)
129 throws Exception {
130 java.util.List subImgList = new ArrayList<>();
131 int height = originImg.getHeight();
132 int[][] weight = getImgWeight(originImg);
133 int start = 0;
134 int end = 0;
135 boolean isStartReady = false;
136 boolean isEndReady = false;
137 for (int i = 0; i < weight.length; i++) {
138 boolean isBlank = isBlankArr(weight[i]);
139 if (isBlank) {
140 if (isStartReady && !isEndReady) {
141 end = i;
142 isEndReady = true;
143 }
144 } else {
145 if (!isStartReady) {
146 start = i;
147 isStartReady = true;
148 }
149 }
150 if (isStartReady && isEndReady) {
151 subImgList.add(originImg.getSubimage(start, 0, end - start, height));
152 isStartReady = false;
153 isEndReady = false;
154 }
155 }
156 return subImgList;
157 }
158
159 //颜色是否为空白
160 private boolean isBlank(int colorInt) {
161 Color color = new Color(colorInt);
162 return color.getRed() + color.getGreen() + color.getBlue() > 600;
163 }
164
165 //数组是不是全空白
166 private boolean isBlankArr(int[] arr) {
167 boolean isBlank = true;
168 for (int value : arr) {
169 if (value == 0) {
170 isBlank = false;
171 break;
172 }
173 }
174 return isBlank;
175 }
176
177 //获取图片权重数据
178 private int[][] getImgWeight(BufferedImage img) {
179 int width = img.getWidth();
180 int height = img.getHeight();
181 int[][] weight = new int[width][height];
182 for (int x = 0; x < width; ++x) {
183 for (int y = 0; y < height; ++y) {
184 if (isBlank(img.getRGB(x, y))) {
185 weight[x][y] = 1;
186 }
187 }
188 }
189 return weight;
190 }
191
192
193 public static void main(String[] args) throws Exception {
194 String result = new ImageProcess("C:/login.jpg").getResult();
195 System.out.println(result);
196
197 }
198 }
更多相关内容 -
java识别验证码
2019-12-10 14:48:27之前在做数据核对部分工作,需要获取厂商的...这里记录一下识别验证码的过程。 使用tess4j 1.下载tessdata和训练语言包 在tessract的github直接下载即可,下载地址戳我(只需要项目的 tessdata文件夹 )。这里,...之前在做数据核对部分工作,需要获取厂商的数据,有的厂商提供了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样本训练,提高验证码识别率
-
Java简单验证码识别(附源码)
2018-07-07 16:37:00java验证码识别 JAVA知识巩固 源码 java验证码识别 java验证码识别 -
JAVA识别验证码,支持变形,扭曲,斜线,噪点,支持自己训练
2019-06-18 16:02:52JAVA识别验证码,支持变形,扭曲,斜线,噪点,支持自己训练 -
Java识别验证码和图像处理
2020-12-20 14:39:17① Java识别验证码② Tess4J的使用和OCR识别③ JavaCV的使用二值化和灰度测试④ Java裁剪和缩放图片⑤ 如何生成数字&字母验证码⑥ ... 这里老样子先说几句无关的话,去年毕业的时候帮同学做了一个验证码识别系统...阅读本文可能会解决的问题:① Java识别验证码
② Tess4J的使用和OCR识别
③ JavaCV的使用二值化和灰度测试
④ Java裁剪和缩放图片
⑤ 如何生成数字&字母验证码
⑥ ...这里老样子先说几句无关的话,去年毕业的时候帮同学做了一个验证码识别系统,按部就班的参考了大神的操作,对于二值化这些操作都没有深入了解,最近做项目遇到了要实现机器登录的操作,验证码自然绕不过去所以就又翻出来参考总结一下,方便自己也分享给大家。同时我也发现现在除了一些政府网站外,基本很少有采用下面我列出的这种数字&字母验证码了,所以针对项目需求做的这个识别可能目前并没有什么参考价值,但是在学习的过程中也了解了很多比如Tess4J和JavaCV这些技术,图像处理比如缩放和裁剪等,话不多说,下面就搞起吧。
一、常见的验证码识别
1.常见验证码
这里我主要针对第四种验证码进行识别,其实试验后我发现前面三个都是可以识别的,只是裁剪和二值化的阈值需要调整。
2.识别思路
输入原始验证码之后,先对图片进行干扰像素去除,再裁剪边角,最后利用Tess4J进行识别。
3.主要代码
main方法调用具体实现方法
其中removeBackground方法去除验证码噪点,首先我定义了一个临界阈值,这个值代表像素点的亮度,我们在实际扫描验证码的每一个像素块时通过判断该像素块的亮度(获取该像素块的三原色)是否超过该自定义值,从而判断出是否删除或保留该像素块,因此针对不同的验证码我们可以调整这个阈值,比如像我上面列出的几种验证码波动一般都在100~600之间,通过测试就得出一个比较适中的阈值可以大大提高验证码的提纯度。public static void main(String[] args){ //原始验证码地址 String OriginalImg = "C:\\mysoftware\\images\\upload\\OcrImg\\oi.jpg"; //识别样本输出地址 String ocrResult = "C:\\mysoftware\\images\\upload\\OcrResult\\or.jpg"; //去噪点 ImgUtils.removeBackground(OriginalImg, ocrResult); //裁剪边角 ImgUtils.cuttingImg(ocrResult); //OCR识别 String code = Tess4J.executeTess4J(ocrResult); //输出识别结果 System.out.println("Ocr识别结果: \n" + code); }
public static void removeBackground(String imgUrl, String resUrl){ //定义一个临界阈值 int threshold = 300; try{ BufferedImage img = ImageIO.read(new File(imgUrl)); int width = img.getWidth(); int height = img.getHeight(); for(int i = 1;i < width;i++){ for (int x = 0; x < width; x++){ for (int y = 0; y < height; y++){ Color color = new Color(img.getRGB(x, y)); System.out.println("red:"+color.getRed()+" | green:"+color.getGreen()+" | blue:"+color.getBlue()); int num = color.getRed()+color.getGreen()+color.getBlue(); if(num >= threshold){ img.setRGB(x, y, Color.WHITE.getRGB()); } } } } for(int i = 1;i<width;i++){ Color color1 = new Color(img.getRGB(i, 1)); int num1 = color1.getRed()+color1.getGreen()+color1.getBlue(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { Color color = new Color(img.getRGB(x, y)); int num = color.getRed()+color.getGreen()+color.getBlue(); if(num==num1){ img.setRGB(x, y, Color.BLACK.getRGB()); }else{ img.setRGB(x, y, Color.WHITE.getRGB()); } } } } File file = new File(resUrl); if (!file.exists()) { File dir = file.getParentFile(); if (!dir.exists()) { dir.mkdirs(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } ImageIO.write(img, "jpg", file); }catch (Exception e){ e.printStackTrace(); } }
这样就能得到一个降噪之后的验证码,再后续通过颜色反转将验证码的内容显示出来就完成了干扰像素的去除工作,接下来非必要的一项工作就是将验证码进行裁边。为什么要裁边呢,通过观察我们发现验证码的边角部分没有干扰素涂抹,在我们的去除干扰素过程中可能会影响到这部分像素块导致最后边角有噪点,这时将边角裁掉几个像素就可以了。
其中裁剪图片我们需要预先读取宽高度,确认需要裁剪的像素宽度,我这里将60*20的图像宽度两边各裁剪4像素,高度上下各裁剪2像素,输出就得到了一个清晰的图像。到这里基本完成了对验证码的简单处理,不出意外的话验证码由public static void cuttingImg(String imgUrl){ try{ File newfile=new File(imgUrl); BufferedImage bufferedimage=ImageIO.read(newfile); int width = bufferedimage.getWidth(); int height = bufferedimage.getHeight(); if (width > 52) { bufferedimage=ImgUtils.cropImage(bufferedimage,(int) ((width - 52) / 2),0,(int) (width - (width-52) / 2),(int) (height)); if (height > 16) { bufferedimage=ImgUtils.cropImage(bufferedimage,0,(int) ((height - 16) / 2),52,(int) (height - (height - 16) / 2)); } }else{ if (height > 16) { bufferedimage=ImgUtils.cropImage(bufferedimage,0,(int) ((height - 16) / 2),(int) (width),(int) (height - (height - 16) / 2)); } } ImageIO.write(bufferedimage, "jpg", new File(imgUrl)); }catch (IOException e){ e.printStackTrace(); } }
变为了
到这里就很简单了,我们可以利用裁切将图片平均剪成四份,再通过简单的训练比对完成匹配,详细操作可以看这里,我就不再赘述了。我这里提供另一个思路,就是要说的Tess4J文本识别工具,通过这个工具我可以直接对当前生成的验证码进行OCR识别,准确率接近100%,注意我项目里是采用Tess4J进行最终的验证码图片内容识别的。
二、Tess4J的使用
1.首先需要到官网下载Tess4J的压缩包,下载后先将解压出来的包内tessdata文件拷入项目根目录,然后引入Tess4J\lib包,最后还要单独引入Tess4J\dist\tess4j-3.4.8.jar包
2.编写简单的识别代码
public static String executeTess4J(String imgUrl){ String ocrResult = ""; try{ ITesseract instance = new Tesseract(); //instance.setLanguage("chi_sim"); File imgDir = new File(imgUrl); //long startTime = System.currentTimeMillis(); ocrResult = instance.doOCR(imgDir); }catch (TesseractException e){ e.printStackTrace(); } return ocrResult; }
ocrResult 即为最终是别的结果,代码相当简单,其中我注释掉的 instance.setLanguage("chi_sim"); 这部分是指定需要识别文本的语言,如果是识别中文按照我注释的chi_sim填写即可,当然这是一个中文包,需要先在这里找到中文包下载后放到Tess4J\tessdata目录下即可使用。
到这里基本就完成了对这个验证码的简单识别,在这个过程中基本没遇到什么有难度的工作,毕竟是站在大佬们的肩膀上操作,其实还是学到了很多东西的,需求这东西说不定哪天就遇到了呢。接下来我在研究验证码识别的过程中了解到了JavaCV,比较感兴趣就记录下来,方便之后使用吧。
三、JavaCV的使用
1. 首先就是去官网下载包啦,点 这里,引入很简单,解压后将javacv-bin包引入项目即可(可以看到封装了OpenCV)
2. 基本操作:获取灰度图像、获取二值化处理图像
public static void main(String[] args) { //图片地址 String imgUrl = "C:\\mysoftware\\images\\upload\\OcrImg\\20180607004153.png"; //得到灰度图像 getHuidu(imgUrl); //得到二值化处理图像 getErzhihua(imgUrl); } //得到灰度图像 public static void getHuidu(String imgUrl){ Mat image=imread(imgUrl,CV_LOAD_IMAGE_GRAYSCALE); //读入一个图像文件并转换为灰度图像(由无符号字节构成) Mat image1=imread(imgUrl,CV_LOAD_IMAGE_COLOR); //读取图像,并转换为三通道彩色图像,这里创建的图像中每个像素有3字节 //如果输入图像为灰度图像,这三个通道的值就是相同的 System.out.println("image has "+image1.channels()+" channel(s)"); //channels方法可用来检查图像的通道数 flip(image,image,1);//就地处理,参数1表示输入图像,参数2表示输出图像 //在一窗口显示结果 namedWindow("输入图片显示窗口");//定义窗口 imshow("输入图片显示窗口",image);//显示窗口 waitKey(0);//因为他是控制台窗口,会在mian函数结束时关闭;0表示永远的等待按键,正数表示等待指定的毫秒数 } //得到二值化处理图像 public static void getErzhihua(String imgUrl){ // TODO Auto-generated method stub Mat image=imread(imgUrl); //加载图像 if(image.empty()) { System.out.println("图像加载错误,请检查图片路径!"); return ; } imshow("原始图像",image); Mat gray=new Mat(); cvtColor(image,gray,COLOR_RGB2GRAY); //彩色图像转为灰度图像 imshow("灰度图像",gray); Mat bin=new Mat(); threshold(gray,bin,120,255,THRESH_TOZERO); //图像二值化 imshow("二值图像",bin); waitKey(0); }
看一下效果:
四、生成验证码
生成验证码大致思路:加载登录界面时向后台请求一个随机验证码code存入session,并在页面以图形显示出来,用户输入登录信息后点击登录,后台读出session的验证码code比对是否一致,不一致返回错误信息,一致则进行登录账号密码校验。
1. 首先在login的页面添加一个img
其中为了方便用户使用,需要做点击图片自动获取新的验证码,所以changeValidateCode()方法是动态去后台请求验证码并设置到页面上<div class="form-group"> <label class="col-sm-2 control-label">验证</label> <div class="col-sm-10"> <input id = "code" type="text" name="vcode" value="请输入验证码" maxlength="4" tabindex="3" style="width:123px;padding: 6px 12px;border: 1px solid #ccc;font-size: 14px;line-height: 1.42857143;color: #555;background-color: #fff;border-radius: 4px;box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;" οnfοcus="if(this.value=='请输入验证码'){this.value='';}" οnblur="if(this.value==''){this.value='请输入验证码';}"/> <a href="#" id="js-get_mobile_vcode" class="button btn-disabled"> <img src="../recordHome/getRand" id="randImg" οnclick="changeValidateCode(this)"/> </a> </div> </div>
请求时添加时间戳是为了防止浏览器缓存导致获取失败function changeValidateCode() { var timestamp = new Date().getTime(); $("#randImg").attr('src','../recordHome/getRand?flag='+timestamp); }
2. 后台生成验证码
使用ContextHolderUtils.getSession().setAttribute("rand", sRand);将验证码存入session中/** * 随机生成4位验证码 * @param httpSession * @param request * @param response */ @RequestMapping(value="/getRand") public void rand(HttpSession httpSession, HttpServletRequest request, HttpServletResponse response){ // 在内存中创建图象 int width = 65, height = 20; BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB); // 获取图形上下文 Graphics g = image.getGraphics(); // 生成随机类 Random random = new Random(); // 设定背景色 g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); // 设定字体 g.setFont(new Font("Times New Roman", Font.PLAIN, 18)); // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到 g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } // 取随机产生的认证码(6位数字) String sRand = ""; for (int i = 0; i < 4; i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; // 将认证码显示到图象中 g.setColor(new Color(20 + random.nextInt(110), 20 + random .nextInt(110), 20 + random.nextInt(110))); // 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成 g.drawString(rand, 13 * i + 6, 16); } // 将认证码存入SESSION ContextHolderUtils.getSession().setAttribute("rand", sRand); //httpSession.setAttribute("rand", sRand); // 图象生效 g.dispose(); try{ ImageIO.write(image, "JPEG", response.getOutputStream()); response.getOutputStream().flush(); }catch (Exception e){ } } /** * 给定范围获得随机颜色 * @param fc * @param bc * @return */ private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) fc = 255; if (bc > 255) bc = 255; int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); }
登录时进行校验,就酱so easy@RequestMapping("/login") public String login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password, @RequestParam String vcode) { if(!vcode.equals(ContextHolderUtils.getSession().getAttribute("rand"))){ request.setAttribute("failMsg", "验证码不正确!"); return "/base/login"; } logger.info("用户登录用户:" + username ); ... }
最后是全部代码,这里说到的我都集成到了项目里,直接下载就可以运行,欢迎大家指正。
有用的话可以☆star一下哦
https://github.com/jwwam/OcrImage.git
转载请注明,谢谢。 -
java识别验证码 仅限字母数字 附带bmp图片
2011-06-03 14:22:20java识别验证码 仅限字母数字 附带bmp图片 验证码图片位置随机变动 -
java验证码识别
2011-11-22 22:06:01java 验证码识别 ocr java 验证码识别 ocr -
Java使用OCR技术识别验证码实现自动化登陆方法
2020-08-25 17:31:21在本篇文章里小编给大家分享的是关于Java 如何使用 OCR 技术识别验证码实现自动化登陆的相关知识点内容,需要的朋友们学习下。 -
【Java】验证码识别解决方案
2021-02-12 09:35:50对于类似以下简单的验证码的识别方案:1、234、1、.../**** author:chzeze* 识别验证码并返回* train_path 验证码字母图库位置* 验证码图片缓存位置:Configuration.getProperties("web_save_path")+"/captcha.jpg"...对于类似以下简单的验证码的识别方案:
1、
2
3
4、
1、建库:切割验证码为单个字符,人工标记,比如:A。
2、识别:给一个验证码:切割为单个字符,在库中查询识别。
/***
* author:chzeze
* 识别验证码并返回
* train_path 验证码字母图库位置
* 验证码图片缓存位置:Configuration.getProperties("web_save_path")+"/captcha.jpg"
*/
public class AmGetCaptchaTest {
private static Logger logger = Logger.getLogger(AmGetCaptchaTest.class);
private static String train_path = "/data/sata/share_sata/AmazonCrawl/amazonWeb/captcha";
private static Map trainMap = null;
private static int index = 0;
private static int imgnum = 0;
private static MultiThreadedHttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager();
private static HttpClient client = new HttpClient(httpConnectionManager);
/* static {
//每主机最大连接数和总共最大连接数,通过hosfConfiguration设置host来区分每个主机
client.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost(8);
client.getHttpConnectionManager().getParams().setMaxTotalConnections(48);
client.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
client.getHttpConnectionManager().getParams().setSoTimeout(10000);
client.getHttpConnectionManager().getParams().setTcpNoDelay(true);
client.getHttpConnectionManager().getParams().setLinger(1000);
//失败的情况下会进行3次尝试,成功之后不会再尝试
client.getHttpConnectionManager().getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
}*/
public static int isBlack(int colorInt) {
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() <= 100) {
return 1;
}
return 0;
}
public static int isWhite(int colorInt) {
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() > 600) {
return 1;
}
return 0;
}
public static BufferedImage removeBackgroud(String picFile)
throws Exception {
BufferedImage img = ImageIO.read(new File(picFile));
img = img.getSubimage(1, 1, img.getWidth() - 2, img.getHeight() - 2);
int width = img.getWidth();
int height = img.getHeight();
double subWidth = width / 5.0;
for (int i = 0; i < 5; i++) {
Map map = new HashMap();
for (int x = (int) (1 + i * subWidth); x < (i + 1) * subWidth
&& x < width - 1; ++x) {
for (int y = 0; y < height; ++y) {
if (isWhite(img.getRGB(x, y)) == 1)
continue;
if (map.containsKey(img.getRGB(x, y))) {
map.put(img.getRGB(x, y), map.get(img.getRGB(x, y)) + 1);
} else {
map.put(img.getRGB(x, y), 1);
}
}
}
int max = 0;
int colorMax = 0;
for (Integer color : map.keySet()) {
if (max < map.get(color)) {
max = map.get(color);
colorMax = color;
}
}
for (int x = (int) (1 + i * subWidth); x < (i + 1) * subWidth
&& x < width - 1; ++x) {
for (int y = 0; y < height; ++y) {
if (img.getRGB(x, y) != colorMax) {
img.setRGB(x, y, Color.WHITE.getRGB());
} else {
img.setRGB(x, y, Color.BLACK.getRGB());
}
}
}
}
return img;
}
public static BufferedImage removeBlank(BufferedImage img) throws Exception {
int width = img.getWidth();
int height = img.getHeight();
int start = 0;
int end = 0;
Label1: for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (isBlack(img.getRGB(x, y)) == 1) {
start = y;
break Label1;
}
}
}
Label2: for (int y = height - 1; y >= 0; --y) {
for (int x = 0; x < width; ++x) {
if (isBlack(img.getRGB(x, y)) == 1) {
end = y;
break Label2;
}
}
}
return img.getSubimage(0, start, width, end - start + 1);
}
public static List splitImage(BufferedImage img)
throws Exception {
List subImgs = new ArrayList();
int width = img.getWidth();
int height = img.getHeight();
List weightlist = new ArrayList();
for (int x = 0; x < width; ++x) {
int count = 0;
for (int y = 0; y < height; ++y) {
if (isBlack(img.getRGB(x, y)) == 1) {
count++;
}
}
weightlist.add(count);
}
for (int i = 0; i < weightlist.size();i++) {
int length = 0;
while (i < weightlist.size() && weightlist.get(i) > 0) {
i++;
length++;
}
if (length > 2) {
subImgs.add(removeBlank(img.getSubimage(i - length, 0,
length, height)));
}
}
return subImgs;
}
public static Map loadTrainData() throws Exception {
if (trainMap == null) {
Map map = new HashMap();
File dir = new File(train_path);
File[] files = dir.listFiles();
for (File file : files) {
map.put(ImageIO.read(file), file.getName().charAt(0) + "");
}
trainMap = map;
}
return trainMap;
}
public static String getSingleCharOcr(BufferedImage img,
Map map) {
String result = "#";
int width = img.getWidth();
int height = img.getHeight();
int min = width * height;
for (BufferedImage bi : map.keySet()) {
int count = 0;
if (Math.abs(bi.getWidth()-width) > 2)
continue;
int widthmin = width < bi.getWidth() ? width : bi.getWidth();
int heightmin = height < bi.getHeight() ? height : bi.getHeight();
Label1: for (int x = 0; x < widthmin; ++x) {
for (int y = 0; y < heightmin; ++y) {
if (isBlack(img.getRGB(x, y)) != isBlack(bi.getRGB(x, y))) {
count++;
if (count >= min)
break Label1;
}
}
}
if (count < min) {
min = count;
result = map.get(bi);
}
}
return result;
}
public static String getAllOcr(String file) throws Exception {
BufferedImage img = removeBackgroud(file);//去除重影
List listImg = splitImage(img);//切割图片
Map map = loadTrainData();
String result = "";
for (BufferedImage bi : listImg) {
result += getSingleCharOcr(bi, map);
}
//ImageIO.write(img, "JPG", new File("result6\\" + result + ".jpg"));
return result;
}
/***
* 下载验证码图片暂时保存供识别程序使用
* @param imgurl 验证码图片url
*/
public static void downloadimg(String imgurl)
{
//HttpClient httpClient = new HttpClient();
//httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
//httpClient.getHttpConnectionManager().getParams().setSoTimeout(10000);
GetMethod getMethod = new GetMethod(imgurl);
try {
int statusCode = client.executeMethod(getMethod);
System.out.println(statusCode);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("("+statusCode+")Method failed: "+ getMethod.getStatusLine());
logger.info("("+statusCode+")Method failed: "+ getMethod.getStatusLine());
}
InputStream inputStream = getMethod.getResponseBodyAsStream();
OutputStream outStream = new FileOutputStream("/data/sata/share_sata/AmazonCrawl/amazonWeb/captcha.jpg");
IOUtils.copy(inputStream, outStream);
inputStream.close();
outStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
//logger.info(new Date()+"captcha appear exception:"+e.getMessage());
try {
//若遇到异常则睡眠20秒后继续重试
Thread.sleep(20000);
} catch (InterruptedException e1) {
logger.error(e1);
}
e.printStackTrace();
}finally {
getMethod.releaseConnection();
}
}
/***
* 抽取页面验证码并返回
* @param stringBuffer
* @return 验证码字符串
*/
public static String GetCaptcha(StringBuilder html){
String captcha_str="######";//未识别则为#
Document doc = Jsoup.parse(html.toString());
String imgurl = doc.select("div[class=a-row a-text-center]").get(0).child(0).attr("src");
//System.out.println(imgurl);
downloadimg(imgurl);
try {
captcha_str = getAllOcr("/data/sata/share_sata/AmazonCrawl/amazonWeb/captcha.jpg");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return captcha_str;
}
}
后记:复杂验证码识别
对于复杂的验证码识别:目前的最简单的方案就是交给第三方人工打码平台:可以参考我做的EBay多线程打码兔验证码解决方案:
更专业的可以采用机器学习、模式识别等方法去实现,但是识别成功率,我目前测试的结果不是很理想,复杂的验证码,正确率在百分之二三十上下,但是我的训练样本库不是很大,提高训练的样本可能结果会好一点。
-
基于Java的简单图形验证码识别
2019-08-07 07:33:40基于 Java 的简单图形验证码识别 -
Java识别图像、验证码
2021-03-06 03:51:24前言这是一个学习案例,看最后一张图,识别率应该有90%以上吧!提高识别度代码处理,放大图像不清晰的图像,像素不好的,如果已经是最好效果了,建议不要处理(放大),就这样子直接识别。/** 对图片进行处理 - 提高... -
Java使用J4L识别验证码
2021-02-12 09:35:231、首先要下载j4l的相应文件和jar 2、下载完成之后解压,文件目录结构如下 3、环境准备 下面配置环境变量,如果我们已经提前配置好JDK的环境变量,那么我们可以直接... 经过测试,J4L识别验证码有时还是会出现偏差。 -
验证码识别-Java版
2021-01-20 10:36:56前段时间用Java写了个爬虫爬教务处网站,于是有朋友问我是怎么实现验证码识别的,在此将这个小方法分享出来! -
使用Java调用Ocr识别验证码
2016-10-09 15:34:52资源包括Java代码以及引用包、tesseract安装包和使用说明 -
Java识别简单的数字验证码
2021-02-12 17:27:19import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import javax.i... -
Java实现验证码识别
2019-07-17 11:40:35java中识别验证码比较简单,使用的软件是tesseractocr,这个软件需要安装在本地中,傻瓜式安装(方便调用)。 github下载地址https://github.com/tesseract-ocr/tessdata 博主是在官网下载的。 该软件默认的识别... -
Java 验证码识别(1)使用 Tess4J 进行 OCR 识别
2021-12-21 15:39:17Java 验证码识别 Tess4J Tesseract OCR -
Java使用test4j识别验证码
2021-02-12 09:35:23Java使用test4j识别验证码test4j的集成此处使用了拷入test4j的项目文件集成到自己的项目中去。1.去test4j官网下载test4j的zip文件(我下载的是3.4.8的版本);2.解压缩文件,得到以下目录需要拷贝到项目中的文件.png3.把... -
java解析图片验证码
2014-01-20 10:31:13java解析图片验证码 -
java识别验证码.docx
2012-08-12 18:43:12Access restriction: The type JPEGImageEncoder is not accessible due to restriction on required library C:\Java\jre1.6.0_07\lib\rt.jar 此时解决办法: Eclipse默认把这些受访问限制的API设成了ERROR。只要把... -
java 验证码识别 OCR
2019-04-08 18:27:45java 验证码识别 OCR。 -
一个纯java的验证码识别算法
2019-04-03 01:41:32NULL 博文链接:https://lvhuiqing.iteye.com/blog/1474186 -
java ocr 识别验证码 aocr.jar
2017-02-07 10:21:33java技术解析验证码时会用的jar包 -
JAVA实现验证码(完整代码)
2013-05-07 10:20:27JAVA实现验证码(完整代码),一个完整地验证码例子。 -
java+selenium+Tesseract-OCR识别图片验证码
2021-07-15 09:22:424、截图,获得验证码图片 WebElement yanzhengmaPic = driver.findElement(By.xpath()); // 截图整个页面 File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); BufferedImage img = ... -
Java 识别图片验证码
2021-02-28 17:32:17import java.awt.image.BufferedImage;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import j...