java实现的身份证照片脸部识别(头像截图) 以及OCR字体识别
断断续续地折腾了大半个月,终于把身份证照片脸部识别以及OCR字体识别功能用Java实现了,需求很简单:通过摄像头所照的一张放在黑色底板上的身份证照,识别照片上身份证里面的人名和地址(OCR中文),再截取身份证上的头像用Base64编码。生成一个规定格式的XML然后把人名,地址和头像照片的编码放到XML里面。
其中用到了OpenCV, Tesseract-OCR 还有一些对BufferedImage进行图像处理的东西。代码倒也不算很复杂,但是其中几个问题的确很烧脑细胞,花了不少时间才解决(Jedi本人辛苦开发+原创码字博文共享,希望能):
1. 相片里面身份证的位置不确定问题: 底板比较大身份证可以在上面随意位置摆放
解决方法很简单,人脸识别时候把人脸的位置坐标返回出来,利用这个坐标来确定身份证位置也大大缩小需要字体OCR识别的区域。不需要整张照片做OCR也节省了许多运算时间。
2. OpenCV人脸识别的容错率问题(有时候一张照片可以识别出三个头像来, 连个模糊的色块也能识别为一个头像,汗啊),当然了,识别人脸时候需要的lbpcascade_frontalface.xml是必须的。上网找就有不少地方有的下载。运行时需要这个xml位于你的可运行jar所在的同个路径下
解决方法是思路就是指定进行人脸识别的最大和最小像素范围minSize和maxSize(就是多大尺寸以内才去分析是不是人脸,当然要具体情况具体设置,minsize最好设大一点点,不然一个模糊小色块都会可能被误当作是人脸,T_T),然后设置参数scaleFactor,minNeighbors和flags来提高识别正确率,具体代码如下:
public int[] detectFace(String imageFileName) {
int[] RectPosition = new int[4];
CascadeClassifier faceDetector = new CascadeClassifier("lbpcascade_frontalface.xml");
Mat image = Highgui.imread(imageFileName);
MatOfRect faceDetections = new MatOfRect();
//指定人脸识别的最大和最小像素范围
Size minSize = new Size(120, 120);
Size maxSize = new Size(250, 250);
//参数设置为scaleFactor=1.1f, minNeighbors=4, flags=0 以此来增加识别人脸的正确率
faceDetector.detectMultiScale(image, faceDetections, 1.1f, 4, 0, minSize, maxSize);
//对识别出来的头像画个方框,并且返回这个方框的位置坐标和大小
for (Rect rect : faceDetections.toArray()) {
Core.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x
+ rect.width, rect.y + rect.height), new Scalar(0, 255, 0));
RectPosition[0]=rect.x;
RectPosition[1]=rect.y;
RectPosition[2]=rect.width;
RectPosition[3]=rect.height;
System.out.println(rect.x +" "+ rect.y + " "+rect.width+" "+rect.height);
}
// 下面注释掉的三行可以用来生成识别出的人脸图像,保存下来以便Debug用
// String filename = "face.png";
// System.out.println(String.format("Writing %s", filename));
// Highgui.imwrite(filename, image);
return RectPosition;
}
3. Tesseract-OCR对身份证上的字体识别率比较低的问题。Tesseract自带的chi_sim.traineddatad(识别库)对身份证上字体的识别率偏低,想了很多办法,也用了很多时间精力去用身份证专用的华文细黑字体库来做训练 (关于如何使用jTessBoxEditor-1.2来生成tif图片和矫正每个字体的识别,还有如何用命令行进行训练生成tessdata识别库的问题,学到的经验多到几乎可以写一大篇文档了。这个要等有时间再总结了),捣腾了很多次之后发现出来的tessdata并没有比原生自带的chi_sim识别率提高多少,可能是需要从大到小不同字体都进行训练才行。
4. 身份证照片解析度不高,1600x1200的摄像头照出来的效果不敢恭维而且身份证相片上面有很多弧形干扰线导致识别率更加低
为了提高解析度消除干扰线,我用了Tess4J自带的ImageHelper.convertImageToBinary()把图像处理成黑白照片,一次性解决了字体受干扰的问题,不过由照片上笔画比较多的字体笔画间隙出现了模糊,我在转换黑背之前加上了一个图像处理的步骤,消除字体笔画间隙的干扰让字体更加清晰,方法如下:
public BufferedImage replaceWithWhiteColor(BufferedImage bi) {
int[] rgb = new int[3];
int width = bi.getWidth();
int height = bi.getHeight();
int minx = bi.getMinX();
int miny = bi.getMinY();
/**
* 遍历图片的像素,为处理图片上的杂色,所以要把指定像素上的颜色换成目标白色 用二层循环遍历长和宽上的每个像素
*/
int hitCount = 0;
for (int i = minx; i < width-1; i++) {
for (int j = miny; j < height; j++) {
/**
* 得到指定像素(i,j)上的RGB值,
*/
int pixel = bi.getRGB(i, j);
int pixelNext = bi.getRGB(i+1, j);
/**
* 分别进行位操作得到 r g b上的值
*/
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = (pixel & 0xff);
/**
* 进行换色操作,我这里是要换成白底,那么就判断图片中rgb值是否在范围内的像素
*/
//经过不断尝试,RGB数值相互间相差15以内的都基本上是灰色,
//对以身份证来说特别是介于73到78之间,还有大于100的部分RGB值都是干扰色,将它们一次性转变成白色
if ((Math.abs(rgb[0] - rgb[1]) < 15)
&& (Math.abs(rgb[0] - rgb[2]) < 15)
&& (Math.abs(rgb[1] - rgb[2]) < 15) &&
(((rgb[0] > 73)&& (rgb[0] < 78))||(rgb[0] > 100))) {
//进行换色操作,0xffffff是白色
bi.setRGB(i, j, 0xffffff);
}
}
}
return bi;
}
5. 关于Java调用DLL的问题,OpenCV和Tesseract-OCR都不是Java原生的,需要去load外部的DLL动态链接库文件
OpenCV需要用到opencv_java2410.dll (这个DLL和相关的opencv-2410.jar可以下载并安装opencv-2.4.10.exe之后在openCV的安装路径下找到opencv\build\java\, 里面有64位和32位不同的版本,需要根据你运行时的JRE/JDK是否为64位来决定),而且需要在运行时候用System.load("C:\\opencv_java2410.dll") 导入,这里我用了绝对路径去load这个opencv_java2410.dll. 所以在发布的时候可以用一个bat判断是否在C:\根目录下是否存在这个dll,没有的话就立马copy一个过去再运行java。(bat批处理来调用java的时候可以指定jre,也可以避免java版本64位或者32位的问题)
而相对来说Tess4j就简单许多,首先只需要下载tesseract-ocr-setup-3.02.02.exe并安装上(默认的chi_sim. Traineddatad大概40M,安装程序自动连接到Google的服务器下载,没有FQ的话可能下载失败,需要的童鞋可以到CSDN下载http://download.csdn.net/download/java_mamad/7308769,下载了之后放到C:\Program Files (x86)\Tesseract-OCR\tessdata\就行。然后把安装目录下的liblept168.dll, liblept168d.dll 和libtesseract302.dll拷贝到你的java项目根目录就行了,打包的时候这三个文件也是必须的,必须放到你的可运行jar包的相同路径下。(刚发现tesseract-OCR的所有东西都可以到SourceForge下载:http://sourceforge.net/projects/tesseract-ocr-alt/files/ 不用FQ可以下得到)
6. 打包jar包发布的时候如果用一个大的jar包里面包含了所有的依赖包(包括jai-imageio.jar)就会出现
sun.misc.ServiceConfigurationError: javax.imageio.spi.ImageOutputStreamSpi: Provider com.sun.med
ia.imageioimpl.stream.ChannelImageOutputStreamSpi could not be instantiated: java.lang.IllegalArgumentException: vendorName == null!
这个是由于imageIO的调用失败导致的,我尝试了很多做法,譬如修改Manifest .MF文件都还是不行T_T,最后还是选择分开单独打包jar和依赖包的方法才行。一个可运行jar包走天下的愿望泡汤了。
7. 调试中发现一张大照片来做OCR识别字体的结果比你限制一个小的区域单独做OCR的效果差很多,所以如果有可能的话还是尽量缩小你要OCR识别字体的范围,不断识别率提高了而且处理起来速度也快很多。我就是通过定位身份证头像大致位置来估算身份证文字位置大小区域来实现的。
8. 在开发中我为了识别完每张图片生成XML之后删除图片,用了file.deleteOnExit(); 结果发现几张图处理完之后全部图都删除了剩下最后一张图没法删除。
研究了大半天才发现问题所在:BufferedImage image = ImageIO.read(new FileInputStream(imgPath));
这样写的代码是存在问题的,因为这个FileInputStream在被GC之前是没有办法去close掉。没有close就会导致了文件无法被删除而且file.deleteOnExie()是不会有IOException抛出的. 查找BUG会相当的confuse费时费力. 正确的写法应该是:
FileInputStream fins = new FileInputStream(imgPath);
BufferedImage image = ImageIO.read(fins);
…
最后用fins.close(); 关闭这个FileInputStream, 文件就能删除了,良好的编程代码习惯还是要慢慢养成,不然要花很多时间在Debug上面。
9. 另外我也试过了手机上的App扫描全能王和PC上的汉王OCR,感觉都是挺不错的成熟产品识别率挺高的,可惜没有提供第三方可调用的接口(当然不开放了,不然人家怎么赚钱^_^)Google的Tesseract可提升的空间还很大,如果有时间的话多用用不同大小的字体来训练可以获得更高的识别率。
java实现的身份证照片脸部识别(头像截图) 以及OCR字体识别的更多相关文章
- 头像截图上传三种方式之一(一个简单易用的flash插件)(asp.net版本)
flash中有版权声明,不适合商业开发.这是官网地址:http://www.hdfu.net/ 本文参考了http://blog.csdn.net/yafei450225664/article/det ...
- 「小程序JAVA实战」小程序头像图片上传(下)(45)
转自:https://idig8.com/2018/09/09/xiaochengxujavashizhanxiaochengxutouxiangtupianshangchuan44/ 接下来,我们应 ...
- 「小程序JAVA实战」小程序头像图片上传(上)(43)
转自:https://idig8.com/2018/09/08/xiaochengxujavashizhanxiaochengxutouxiangtupianshangchuan40/ 在微信小程序中 ...
- Selenium常用API的使用java语言之20-获取窗口截图
自动化用例是由程序去执行,因此有时候打印的错误信息并不十分明确.如果在脚本执行出错的时候能对当前窗口截图保存,那么通过图片就可以非常直观地看出出错的原因. WebDriver提供了截图函数getScr ...
- JAVA实现网页上传头像
大概实现就是在页面嵌入一个file类型的input控件,并且将之隐藏,点击上传传递到这个控件上面,选择文件,将图片以base64的方式传递到后台,后台解码器解码,保存图片,并且把图片名字保存到数据库或 ...
- 「小程序JAVA实战」小程序头像图片上传(中)(44)
转自:https://idig8.com/2018/09/09/xiaochengxujavashizhanxiaochengxutouxiangtupianshangchuan43/ 用户可以上传了 ...
- Java基础(57):Eclipse中环境配置(视图字体颜色行号调试快捷键等等)
1:Eclipse的基本配置 A:程序的编译和运行的环境配置(一般不改) window -- Preferences -- Java 编译环境:Compiler 默认选中的就是最高版本. 运行环境:I ...
- JAVA OCR图片识别
今天闲来无聊,尝试了一下OCR识别,尝试了以下三种方案: 1.直接使用业界使用最广泛的Tesseract-OCR. Tesseract项目最初由惠普实验室支持,1996年被移植到Windows上,19 ...
- 用百度AI的OCR文字识别结合JAVA实现了图片的文字识别功能
第一步可定要获取百度的三个东西 要到百度AI网站(http://ai.baidu.com/)去注册 然后获得 -const APP_ID = '请填写你的appid'; -const API_KEY ...
随机推荐
- Castle ActiveRecord学习实践
Castle是针对.NET平台的一个开源项目,从数据访问框架ORM到IOC容器,再到WEB层的MVC框架.AOP,基本包括了整个开发过程中的所有东西,为我们快速的构建企业级的应用程序提供了很好的服务. ...
- 对iOS中MVC的理解
总结于斯坦福大学的白头发老头的公开课 模型-控制器-视图(MVC)是一种将应用中给所有类组织起来的策略 模型(Model)实际上考虑的是“什么”的问题,即你的程序是什么? 以纸牌匹配游戏为例子,模型就 ...
- win7常用快捷键
Win+1:打开/显示超级任务栏第一个图标代表的程序Win+2:打开/显示超级任务栏第二个图标代表的程序(3.4.……如此类推)Win+Tab:3D切换窗口,你要是按住不松口,则所有窗口会轮流翻转Wi ...
- Codeforces Round #274 (Div. 1) C. Riding in a Lift 前缀和优化dp
C. Riding in a Lift Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/480/pr ...
- ResultSet转成java类对象
在做web开发时遇到一个事情: 需要从mysql数据表中查询数据并遍历查询结果 这样最简单的方式是:查询到结果根据表中字段列表的顺序来一个个获取字段,但这样需要记住字段的顺序,操作起来不是那么方便.因 ...
- C# 利用范型与扩展方法重构代码
在一些C#代码中常常可以看到 //An Simple Example By Ray Linn class CarCollection :ICollection { IList list; public ...
- 第29题:推断一个序列是否是还有一个push序列的pop序列
github:https://github.com/frank-cq/MyTest 第29题:输入两个整数序列,当中一个序列表示栈的push顺序,推断还有一个序列有没有可能是相应的pop顺序.为了简单 ...
- [置顶] 递归 加引用 实现tree 和 无限级菜单
<?php class k_model_menu_menu { private $data = array(); private $rdata = array(); pr ...
- mybatis0209 二级缓存
.1二级缓存 1.1.1原理 mybatis和spring整合后一级缓存就没有了,sqlSession在不关闭的前提下2次查询就会从缓存中取,一级缓存缓存在sqlSession对象里面,当多用户查询的 ...
- sql 事务处理
事务定义: 事务是单个的工作单元.如果某一事务成功,则在该事务中进行的所有数据更改均会 提交,成为数据库中的永久组成部分.如果事务遇到错误且必须取消或回滚,则所有 数据更改均被清除. 事务三种运行模式 ...