QQ泡沫乐园 · 免费提供游戏辅助,破解软件,活动资讯,喜欢记得收藏哦!
综合软件_线报活动_游戏辅助_最新电影_最优质的的辅助分享平台

阅读本文可能会解决的问题:Java识别验证码②Tess4J

泡沫乐园 2022-05-23 21:02

阅读本文可能解决的问题:

① Java识别验证码

② 使用 Tess4J 和 OCR 识别

③ JavaCV二值化和灰度测试的使用

④ Java 裁剪和缩放图片

⑤ 如何生成数字&字母验证码

⑥ ...

这里我先说几句无关紧要的话。去年毕业的时候,我帮同学搭建了一个验证码识别系统。我一步步跟着大神的操作。我对二值化的这些操作没有深入的了解。在机器登录的操作中,验证码是绕不过去的,所以翻出来供参考,总结一下,分享给大家。同时我也发现除了一些政府网站外,我下面列出的数字&字母验证码很少使用,所以这个对于项目需要的标识目前可能没有任何参考价值,但是我在学习的过程中,我也学习了很多Tess4J、JavaCV等技术,以及缩放、裁剪等图像处理。

一、常用验证码识别

1.常用验证码

这里我主要识别第四类验证码。其实经过测试,我发现前三个都是可以识别的,但是裁剪和二值化的阈值需要调整一下。

2.识别思路

输入原始验证码后,先去除图片的干扰像素,再裁剪边角,最后使用Tess4J进行识别。

3.主要代码

主方法调用具体实现方法

    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);
 
    }

removeBackground 方法去除了验证码的噪音。首先,我定义了一个临界阈值,它代表像素的亮度。当我们实际扫描验证码的每个像素块时,判断像素块的亮度(获取像素块的三基色)是否超过了自定义值,从而判断是删除还是保留像素块,所以我们可以针对不同的验证码调整这个阈值。比如上面列出的几个验证码的波动一般在100到600之间,这段时间通过测试得到一个适中的阈值,可以大大提高验证码的纯度。

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

这样就可以得到降噪后的验证码,然后通过反色显示验证码的内容,完成干扰像素的去除。接下来的非必要工作是修剪验证码。为什么我们需要切割边缘?通过观察,我们发现验证码一角没有干扰素涂片。在我们去除干扰素的过程中,这部分像素块可能会受到影响,导致最终角落出现噪点。这时候,角落会被切掉。只需降低几个像素。

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();
        }
    }

其中,要裁剪图像,我们需要提前读取宽度和高度,并确定要裁剪的像素宽度。在这里,我将 60*20 的图像宽度裁剪为每边 4 个像素,上下裁剪高度为 2 个像素验证码看不清楚怎么办,输出将得到清晰的图像。至此,验证码的简单处理基本完成。如无意外,验证码由

变成了

这里很简单。我们可以使用cropping将图片平均切割成四个部分验证码看不清楚怎么办,然后通过简单的训练比较完成匹配。详细操作可以看这里,不再赘述。这里我再提供一个思路,就是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);
    }

看看效果:

四、生成验证码

生成验证码的大致思路:

在加载登录界面时,会从后台请求一个随机的验证码存储在会话中,并以图形的形式显示在页面上。用户输入登录信息点击登录后,后台读取session的验证码是否一致,不一致则返回错误信息。如果相同,则进行登录账号和密码验证。

1. 首先在登录页面添加一个img

为了方便用户使用,需要点击图片自动获取新的验证码,所以changeValidateCode()方法是动态到后台请求验证码并在页面设置

function changeValidateCode() {
    var timestamp = new Date().getTime();
    $("#randImg").attr('src','../recordHome/getRand?flag='+timestamp);
}

请求时加时间戳是为了防止浏览器缓存导致抓取失败

2. 后台生成验证码

/**
	 * 随机生成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);
	}

使用 ContextHolderUtils.getSession().setAttribute("rand", sRand); 将验证码存储在会话中

@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 );
		...
	}

登录时查看,很简单

最后是所有代码。我已经把它集成到这里提到的项目中了,直接下载就可以运行了。欢迎大家指正。

文件下载

附:项目源码【可运行】

文件大小:48M

更新时间:

立即下载

上一篇:2014-07-26摩旅天水国际邮币卡电子盘 下一篇:没有了