java验证码识别技术

内容纲要

转自:http://blog.csdn.net/lmj623565791/article/details/23955883

本人程序用的tesseract,官方地址:https://code.google.com/p/tesseract-ocr/,不为别的,谁让它支持我们的天朝的文字呢~哈

下载好程序后解压:

大概可以看到这样一个目录,别见怪楼主里面一堆测试文件。

然后就开始我们的测试之旅:

tesseract的用法:

参数1:需要识别的文件

参数2:输出的文件名称,输出的是文本文件,里面保存了识别的信息

识别英文这两个参数就可以了,下面做个实验:

我们在命令行输入:tesseract 5.jpg 6 ,可以看到程序生成了一个6.txt ,里面保存着识别后的文本,怎么样简单又给力~

上面说道tesseract 是支持中文的,所以么,接下来看看如何使用tesseract 实现我们中文的识别,下面继续介绍其他参数

参数3:-l

参数4: 使用的语言库

参数3 -l应该是知道参数4所使用的语言库,默认英文,也就是为什么上面识别英文的例子,并没有输入参数3和参数4,也实现了识别。

下面继续我们的实验:

我们准备了一张图片,然后使用tesseract zhongwen.jpg  7  -l chi_sim 指明了中文语言,然后效果图上,还是很不错的,毕竟我们的中文是如此的博大精深,并且tesseract可以经过训练,然后识字的能力就会大幅度提升。

好了,由于一行代码没写,就不上传代码了,大家自己去官网下载。接下来我会使用Java带大家实现这样的小程序。

接着上一篇OCR所说的,上一篇给大家介绍了tesseract 在命令行的简单用法,当然了要继承到我们的程序中,还是需要代码实现的,下面给大家分享下java实现的例子。

拿代码扫描上面的图片,然后输出结果。主要思想就是利用Java调用系统任务。

下面是核心代码:

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. package com.zhy.test;  

  2.   

  3. import java.io.BufferedReader;  

  4.   

  5. import java.io.File;  

  6. import java.io.FileInputStream;  

  7. import java.io.InputStreamReader;  

  8. import java.util.ArrayList;  

  9. import java.util.List;  

  10.   

  11. import org.jdesktop.swingx.util.OS;  

  12.   

  13. public class OCRHelper  

  14. {  

  15.     private final String LANG_OPTION = "-l";  

  16.     private final String EOL = System.getProperty("line.separator");  

  17.     /** 

  18.      * 文件位置我防止在,项目同一路径 

  19.      */  

  20.     private String tessPath = new File("tesseract").getAbsolutePath();  

  21.   

  22.     /** 

  23.      * @param imageFile 

  24.      *            传入的图像文件 

  25.      * @param imageFormat 

  26.      *            传入的图像格式 

  27.      * @return 识别后的字符串 

  28.      */  

  29.     public String recognizeText(File imageFile) throws Exception  

  30.     {  

  31.         /** 

  32.          * 设置输出文件的保存的文件目录 

  33.          */  

  34.         File outputFile = new File(imageFile.getParentFile(), "output");  

  35.   

  36.         StringBuffer strB = new StringBuffer();  

  37.         List<String> cmd = new ArrayList<String>();  

  38.         if (OS.isWindowsXP())  

  39.         {  

  40.             cmd.add(tessPath + "\\tesseract");  

  41.         } else if (OS.isLinux())  

  42.         {  

  43.             cmd.add("tesseract");  

  44.         } else  

  45.         {  

  46.             cmd.add(tessPath + "\\tesseract");  

  47.         }  

  48.         cmd.add("");  

  49.         cmd.add(outputFile.getName());  

  50.         cmd.add(LANG_OPTION);  

  51. //      cmd.add("chi_sim");  

  52.         cmd.add("eng");  

  53.   

  54.         ProcessBuilder pb = new ProcessBuilder();  

  55.         /** 

  56.          *Sets this process builder's working directory. 

  57.          */  

  58.         pb.directory(imageFile.getParentFile());  

  59.         cmd.set(1, imageFile.getName());  

  60.         pb.command(cmd);  

  61.         pb.redirectErrorStream(true);  

  62.         Process process = pb.start();  

  63.         // tesseract.exe 1.jpg 1 -l chi_sim  

  64.         // Runtime.getRuntime().exec("tesseract.exe 1.jpg 1 -l chi_sim");  

  65.         /** 

  66.          * the exit value of the process. By convention, 0 indicates normal 

  67.          * termination. 

  68.          */  

  69. //      System.out.println(cmd.toString());  

  70.         int w = process.waitFor();  

  71.         if (w == 0)// 0代表正常退出  

  72.         {  

  73.             BufferedReader in = new BufferedReader(new InputStreamReader(  

  74.                     new FileInputStream(outputFile.getAbsolutePath() + ".txt"),  

  75.                     "UTF-8"));  

  76.             String str;  

  77.   

  78.             while ((str = in.readLine()) != null)  

  79.             {  

  80.                 strB.append(str).append(EOL);  

  81.             }  

  82.             in.close();  

  83.         } else  

  84.         {  

  85.             String msg;  

  86.             switch (w)  

  87.             {  

  88.             case 1:  

  89.                 msg = "Errors accessing files. There may be spaces in your image's filename.";  

  90.                 break;  

  91.             case 29:  

  92.                 msg = "Cannot recognize the image or its selected region.";  

  93.                 break;  

  94.             case 31:  

  95.                 msg = "Unsupported image format.";  

  96.                 break;  

  97.             default:  

  98.                 msg = "Errors occurred.";  

  99.             }  

  100.             throw new RuntimeException(msg);  

  101.         }  

  102.         new File(outputFile.getAbsolutePath() + ".txt").delete();  

  103.         return strB.toString().replaceAll("\\s*""");  

  104.     }  

  105. }  

代码很简单,中间那部分ProcessBuilder其实就类似Runtime.getRuntime().exec("tesseract.exe 1.jpg 1 -l chi_sim"),大家不习惯的可以使用Runtime。

测试代码:

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. package com.zhy.test;  

  2.   

  3. import java.io.File;  

  4.   

  5. public class Test  

  6. {  

  7.     public static void main(String[] args)  

  8.     {  

  9.         try  

  10.         {  

  11.               

  12.             File testDataDir = new File("testdata");  

  13.             System.out.println(testDataDir.listFiles().length);  

  14.             int i = 0 ;   

  15.             for(File file :testDataDir.listFiles())  

  16.             {  

  17.                 i++ ;  

  18.                 String recognizeText = new OCRHelper().recognizeText(file);  

  19.                 System.out.print(recognizeText+"\t");  

  20.   

  21.                 if( i % 5  == 0 )  

  22.                 {  

  23.                     System.out.println();  

  24.                 }  

  25.             }  

  26.               

  27.         } catch (Exception e)  

  28.         {  

  29.             e.printStackTrace();  

  30.         }  

  31.   

  32.     }  

  33. }  


输出结果:

对比第一张图片,是不是很完美~哈哈 ,当然了如果你只需要实现验证码的读写,那么上面就足够了。下面继续普及图像处理的知识。



-------------------------------------------------------------------我的分割线--------------------------------------------------------------------

当然了,有时候图片被扭曲或者模糊的很厉害,很不容易识别,所以下面我给大家介绍一个去噪的辅助类,绝对碉堡了,先看下效果图。

来张特写:

一个类,不依赖任何jar,把图像中的干扰线消灭了,是不是很给力,然后再拿这样的图片去识别,会不会效果更好呢,嘿嘿,大家自己实验~

代码:

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. package com.zhy.test;  

  2.   

  3. import java.awt.Color;  

  4. import java.awt.image.BufferedImage;  

  5. import java.io.File;  

  6. import java.io.IOException;  

  7.   

  8. import javax.imageio.ImageIO;  

  9.   

  10. public class ClearImageHelper  

  11. {  

  12.   

  13.     public static void main(String[] args) throws IOException  

  14.     {  

  15.   

  16.           

  17.         File testDataDir = new File("testdata");  

  18.         final String destDir = testDataDir.getAbsolutePath()+"/tmp";  

  19.         for (File file : testDataDir.listFiles())  

  20.         {  

  21.             cleanImage(file, destDir);  

  22.         }  

  23.   

  24.     }  

  25.   

  26.     /** 

  27.      *  

  28.      * @param sfile 

  29.      *            需要去噪的图像 

  30.      * @param destDir 

  31.      *            去噪后的图像保存地址 

  32.      * @throws IOException 

  33.      */  

  34.     public static void cleanImage(File sfile, String destDir)  

  35.             throws IOException  

  36.     {  

  37.         File destF = new File(destDir);  

  38.         if (!destF.exists())  

  39.         {  

  40.             destF.mkdirs();  

  41.         }  

  42.   

  43.         BufferedImage bufferedImage = ImageIO.read(sfile);  

  44.         int h = bufferedImage.getHeight();  

  45.         int w = bufferedImage.getWidth();  

  46.   

  47.         // 灰度化  

  48.         int[][] gray = new int[w][h];  

  49.         for (int x = 0; x < w; x++)  

  50.         {  

  51.             for (int y = 0; y < h; y++)  

  52.             {  

  53.                 int argb = bufferedImage.getRGB(x, y);  

  54.                 // 图像加亮(调整亮度识别率非常高)  

  55.                 int r = (int) (((argb >> 16) & 0xFF) * 1.1 + 30);  

  56.                 int g = (int) (((argb >> 8) & 0xFF) * 1.1 + 30);  

  57.                 int b = (int) (((argb >> 0) & 0xFF) * 1.1 + 30);  

  58.                 if (r >= 255)  

  59.                 {  

  60.                     r = 255;  

  61.                 }  

  62.                 if (g >= 255)  

  63.                 {  

  64.                     g = 255;  

  65.                 }  

  66.                 if (b >= 255)  

  67.                 {  

  68.                     b = 255;  

  69.                 }  

  70.                 gray[x][y] = (int) Math  

  71.                         .pow((Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2)  

  72.                                 * 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2);  

  73.             }  

  74.         }  

  75.   

  76.         // 二值化  

  77.         int threshold = ostu(gray, w, h);  

  78.         BufferedImage binaryBufferedImage = new BufferedImage(w, h,  

  79.                 BufferedImage.TYPE_BYTE_BINARY);  

  80.         for (int x = 0; x < w; x++)  

  81.         {  

  82.             for (int y = 0; y < h; y++)  

  83.             {  

  84.                 if (gray[x][y] > threshold)  

  85.                 {  

  86.                     gray[x][y] |= 0x00FFFF;  

  87.                 } else  

  88.                 {  

  89.                     gray[x][y] &= 0xFF0000;  

  90.                 }  

  91.                 binaryBufferedImage.setRGB(x, y, gray[x][y]);  

  92.             }  

  93.         }  

  94.   

  95.         // 矩阵打印  

  96.         for (int y = 0; y < h; y++)  

  97.         {  

  98.             for (int x = 0; x < w; x++)  

  99.             {  

  100.                 if (isBlack(binaryBufferedImage.getRGB(x, y)))  

  101.                 {  

  102.                     System.out.print("*");  

  103.                 } else  

  104.                 {  

  105.                     System.out.print(" ");  

  106.                 }  

  107.             }  

  108.             System.out.println();  

  109.         }  

  110.   

  111.         ImageIO.write(binaryBufferedImage, "jpg"new File(destDir, sfile  

  112.                 .getName()));  

  113.     }  

  114.   

  115.     public static boolean isBlack(int colorInt)  

  116.     {  

  117.         Color color = new Color(colorInt);  

  118.         if (color.getRed() + color.getGreen() + color.getBlue() <= 300)  

  119.         {  

  120.             return true;  

  121.         }  

  122.         return false;  

  123.     }  

  124.   

  125.     public static boolean isWhite(int colorInt)  

  126.     {  

  127.         Color color = new Color(colorInt);  

  128.         if (color.getRed() + color.getGreen() + color.getBlue() > 300)  

  129.         {  

  130.             return true;  

  131.         }  

  132.         return false;  

  133.     }  

  134.   

  135.     public static int isBlackOrWhite(int colorInt)  

  136.     {  

  137.         if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730)  

  138.         {  

  139.             return 1;  

  140.         }  

  141.         return 0;  

  142.     }  

  143.   

  144.     public static int getColorBright(int colorInt)  

  145.     {  

  146.         Color color = new Color(colorInt);  

  147.         return color.getRed() + color.getGreen() + color.getBlue();  

  148.     }  

  149.   

  150.     public static int ostu(int[][] gray, int w, int h)  

  151.     {  

  152.         int[] histData = new int[w * h];  

  153.         // Calculate histogram  

  154.         for (int x = 0; x < w; x++)  

  155.         {  

  156.             for (int y = 0; y < h; y++)  

  157.             {  

  158.                 int red = 0xFF & gray[x][y];  

  159.                 histData[red]++;  

  160.             }  

  161.         }  

  162.   

  163.         // Total number of pixels  

  164.         int total = w * h;  

  165.   

  166.         float sum = 0;  

  167.         for (int t = 0; t < 256; t++)  

  168.             sum += t * histData[t];  

  169.   

  170.         float sumB = 0;  

  171.         int wB = 0;  

  172.         int wF = 0;  

  173.   

  174.         float varMax = 0;  

  175.         int threshold = 0;  

  176.   

  177.         for (int t = 0; t < 256; t++)  

  178.         {  

  179.             wB += histData[t]; // Weight Background  

  180.             if (wB == 0)  

  181.                 continue;  

  182.   

  183.             wF = total - wB; // Weight Foreground  

  184.             if (wF == 0)  

  185.                 break;  

  186.   

  187.             sumB += (float) (t * histData[t]);  

  188.   

  189.             float mB = sumB / wB; // Mean Background  

  190.             float mF = (sum - sumB) / wF; // Mean Foreground  

  191.   

  192.             // Calculate Between Class Variance  

  193.             float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);  

  194.   

  195.             // Check if new maximum found  

  196.             if (varBetween > varMax)  

  197.             {  

  198.                 varMax = varBetween;  

  199.                 threshold = t;  

  200.             }  

  201.         }  

  202.   

  203.         return threshold;  

  204.     }  

  205. }  

发表回复