Java实现去除文档阴影的示例代码
作者:ZackSock 发布时间:2023-08-31 11:45:48
一、前言
文稿扫描大家用的都比较频繁、想是各种证件、文件都可以通过扫描文稿功能保存到手机。相比直接拍照,在扫描文稿时,程序会对图像进行一些矫正。比如去除阴影、修正倾斜、旋转矫正等。进行这些处理后的图片要更加容易识别。今天就来讨论以下去除阴影的操作。
二、实现原理
1. 图像
在开始实现前,我们来了解一些图像相关的知识。这里讨论RGB图像,也就是我们俗称的彩色的图像。图像可以被看作是一个height×width的数组,每一个数表示一个像素。如果是彩色的图像,每个像素会包含RBG三个值,最低字节表示G、次低字节表示B、第三字节表示R。
比如像素值为:
0x00ff00
其RBG值分别为:
R: 0x00
G: 0xff
B: 0x00
如果想要从原像素中取RGB的值,可以使用按位与操作,示例如下:
// pixel是从图像中取出来的数
int[] rgb = new int[3];
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = (pixel & 0xff);
因为获取R和G的时候,保留的是高位,我们希望得到的是一个低位的数据,因此向右移一定位。
2. 灰度转换
有时候,为了方便处理会把图像转换成灰度图像。转换成灰度图像的方法有很多,一种非常简单的办法就是让rgb三个通道都为同样的值,这个值就是rgb三个值的均值。
3.阈值处理
阈值处理是今天关键部分,阈值处理的思想非常简单,就是当图像像素值大于阈值时将其处理为最大值,当像素小于等于阈值时将其处理为0。这样可以得到一张完全的黑白图像。
在文稿中,文字部分可以看作是黑色,背景部分可以看作是白色,而阴影则是介于黑白之间的值。如果想要去除阴影,则需要对图像进行阈值处理,把阈值设定为小于阴影的值。比如下图:
左图是原图,其中灰色部分为阴影,需要去除。这时我们对图像进行阈值处理,把阈值设定为50,那么阴影部分就会被设置成255,文字部分和背景部分变换都不大,这样就实现了文稿的阴影去除工作。
三、代码实现
1.读取图像
首先来看看如何读取图像以及如何访问图像的像素,这里使用ImageIO类。代码如下:
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class DocumentDealing {
public static void main(String[] args) throws Exception {
String imagePath = "D:/images/imgs/10000.jpeg";
BufferedImage bi = ImageIO.read(new File(imagePath));
//获取图片宽高
int width = bi.getWidth();
int height = bi.getHeight();
System.out.println("width:" + width + ",height:" + height);
//获取坐标为(0, 0)位置的像素
int pixel = bi.getRGB(0, 0);
System.out.println("pixel" + pixel);
//获取rgb值
int[] rgb = new int[3];
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = pixel & 0xff;
System.out.println(
"r:" + rgb[0] +
"\tg:" + rgb[1] +
"\tb:" + rgb[2]
);
}
public static int[] getRgb(BufferedImage bi, int x, int y) {
int[] rgb = new int[3];
int pixel = bi.getRGB(x, y);
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = (pixel & 0xff);
return rgb;
}
}
我们可以通过下面代码读取图片,其中imagePath是图片路径:
BufferedImage bi = ImageIO.read(new File(imagePath));
BufferedImage可以获取图片的宽、高、某个点的像素等。为了方便,编写一个getRgb来把pixel转成一个rgb数组。代码输出结果如下:
width:400,height:400
pixel-2853206
r:212 g:118 b:170
2.阈值处理
知道了上面的基本操作后,就可以开始进行阈值处理了。阈值处理就是求rgb均值mean,如果mean大于阈值,则把像素设置为0xffffff,否则设置为0。具体代码如下:
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class DocumentDealing {
public static void main(String[] args) throws Exception {
String imagePath = "C:/Users/Administrator/Desktop/document.jpg";
threshold(imagePath, "result.jpg", 50);
}
public static void threshold(String imagePath, String savePath, int threshold) throws Exception{
//读取图片
BufferedImage bi = ImageIO.read(new File(imagePath));
//读取宽高
int width = bi.getWidth();
int height = bi.getHeight();
//遍历图片像素
for(int y = 0; y < height; y ++){
for(int x = 0; x < width; x ++){
int[] rgb = getRgb(bi, x, y);
//计算rgb均值
int grayScale = (rgb[0] + rgb[1] + rgb[2]) / 3;
//如果均值大于阈值,则赋值将该像素设置为0xffffff(全白),否则赋值为0(全黑)
if(grayScale > threshold){
bi.setRgb(x, y, 0xffffff);
}else{
bi.setRgb(x, y, 0);
}
}
}
//保存图片
ImageIO.write(bi, "jpg", new File(savePath));
}
public static int[] getRgb(BufferedImage bi, int x, int y){
int[] rgb = new int[3];
int pixel = bi.getRGB(x, y);
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = (pixel & 0xff);
return rgb;
}
}
下图是原图和处理后的结果:
左图中有两处阴影,右侧则去除了阴影。最终效果图与设定的阈值有关系,当阈值设置不恰当时,会导致结果图比原图更糟糕,或者导致最终文字目标也被去除了。这里可以用循环来解决,代码如下:
public static void main(String[] args) throws Exception {
String imagePath = "C:/Users/Administrator/Desktop/document.jpg";
for (int i = 50; i < 127; i++) {
threshold(imagePath, "imgs/result" + i + ".jpg", i);
}
}
大家可以自行测试。有时候之间阈值处理不能很好的去除阴影,这个时候会结合一些其它办法。包括滤波操作、形态学处理等。
来源:https://blog.csdn.net/ZackSock/article/details/128225047
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- 目录使用格式化编辑手机号格式化编辑身份证号设置监听移除格式化的文本实现原理项目地址格式化编辑的需求一般是从编辑手机号开始的,UI 给出的效果
- 本文实例讲述了C#实现系统托盘通知的方法。分享给大家供大家参考。具体实现方法如下:namespace WindowsApplication1
- 今天被数据大神说了,对接第三方接口返回的json字段我想用驼峰形式,他说我这样不专业。所以就改了,认怂。记住以后再次对接rest接口,返回的
- 本文实例为大家分享了Android利用Canvas类绘制图形的具体代码,供大家参考,具体内容如下首先介绍一下相关基础知识。1.画笔(pain
- WPF中的ObservableCollection是一个非常常用的集合对象,我们可以通过将它绑定到ListBox之类的集合控件上时,当集合发
- 一、项目结构二、pom.xml<?xml version="1.0" encoding="UTF-8&q
- 1.spring配置文件<bean id="configproperties"  
- Executor接口基于以下方法可以完成增,删,改查以及事务处理等操作。事实上,mybatis中的所有数据库操作是通过调用这些方法实现的。p
- 1.方法重写子类写和父类一样的方法定义public void call(){System.out.println(“输出文字”); //父类
- String类型小数值转为Long类型数值分为小数和整数,当传入的类型为String,需要获取的类型为Long,这时候直接通过Long.va
- 首先我们上图: xml的代码如下,用于编写按钮:<?xml version="1.0" encoding
- 之前文章都是基于用户名密码登录,第六章图形验证码登录其实还是用户名密码登录,只不过多了一层图形验证码校验而已;Spring Security
- cmd调用phantomjs官方资料:http://phantomjs.org/quick-start.html手动执行从官方下载phant
- 利用源码编译Android系统Java类库1、编写Java项目和Android.mk文件 ├── Android.mk &nbs
- 最初的源文件样式如下:用默认的配置进行格式化之后如下:使用如下配置后,格式化之后的代码如下:最终修改成下面这样比较合适:来源:https:/
- 本文实例讲述了Android实现为Notification加上一个进度条的方法。分享给大家供大家参考,具体如下:package com.no
- 1、cmd指令,进入.svn目录,找到wc.db文件 sqlite 3 打开2、 对 svn源代码目录 右键, clean up, 稍等1至
- 从何说起前些天和朋友讨论一个问题,他们的应用有几十万会员然后对应有积分,现在想做积分排名的需求,问有没有什么好方案。这个问题也算常见,很多地
- 1.导入 EasyExcel Maven包<!--easyexcel 导出excel依赖--><dependency>
- 在基于Mybatis-plus实现多租户架构中,介绍了在多租户项目中如果要开启一个子线程,那么需要手动进行RequestAttributes