Java去除文档阴影

news2025/1/20 5:53:32

Java去除文档阴影

一、前言

文稿扫描大家用的都比较频繁、想是各种证件、文件都可以通过扫描文稿功能保存到手机。相比直接拍照,在扫描文稿时,程序会对图像进行一些矫正。比如去除阴影、修正倾斜、旋转矫正等。进行这些处理后的图片要更加容易识别。今天就来讨论以下去除阴影的操作。

二、实现原理

1. 图像

在开始实现前,我们来了解一些图像相关的知识。这里讨论RGB图像,也就是我们俗称的彩色图像。图像可以被看作是一个height×width的数组,每一个数表示一个像素。如果是彩色图像,每个像素会包含RBG三个值,最低字节表示G、次低字节表示B、第三字节表示R。

图片2.png

比如像素值为:

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。这样可以得到一张完全的黑白图像。

在文稿中,文字部分可以看作是黑色,背景部分可以看作是白色,而阴影则是介于黑白之间的值。如果想要去除阴影,则需要对图像进行阈值处理,把阈值设定为小于阴影的值。比如下图:

图片4.png

左图是原图,其中灰色部分为阴影,需要去除。这时我们对图像进行阈值处理,把阈值设定为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;
    }

}

下图是原图和处理后的结果:

图片1.png

左图中有两处阴影,右侧则去除了阴影。最终效果图与设定的阈值有关系,当阈值设置不恰当时,会导致结果图比原图更糟糕,或者导致最终文字目标也被去除了。这里可以用循环来解决,代码如下:

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

读者可以自行测试。有时候之间阈值处理不能很好的去除阴影,这个时候会结合一些其它办法。包括滤波操作、形态学处理等。感兴趣的读者可以去了解以下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/69308.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习下c++原来它和Java有很多相似的地方

Java和CJava和C区别简单学习下C语法C 是什么&#xff1f;C工作原理&#xff1a;C标识符C基本数据类型C关键字封装&#xff0c;继承&#xff0c;多态简单回顾下Java语法Java的基础语法&#xff1a;Java注释Java标识符Java修饰符Java 接口和继承Java8 新增的特性Java和C区别 Java…

VTK-vtkPointInterpolator/vtkInterpolatorKernel

欢迎大家加入社区&#xff0c;雪易VTK社区-CSDN社区云 前言&#xff1a;目前在做模型的ReMesh&#xff0c;在研究这个接口&#xff0c;希望能有所帮助。 vtkPointInterpolator 描述&#xff1a; 变量&#xff1a; Strategy&#xff1a;MASK_POINTS, NULL_VALUE, CLOSEST_POI…

【css遇到的问题】vue中使用select下拉框,数据绑定但是默认不显示问题

文章目录问题描述原因分析问题描述 在vue中使用原生的select下拉框的时候&#xff0c;绑定数据内容但是发现其中默认显示第一条的并不显示 需求实现效果 实际实现效果 循环出select内的数据以后&#xff0c;发现原本默认显示第一条的select框变成了空白&#xff0c;要选择后…

【Mysql】事务

文章目录一.什么是事务1.1. 事物的属性1.2. 事务的版本支持1.3. 事务的提交方式1.4. 事务常见操作证明事务的回滚事务崩溃情况下验证回滚结论二.事务隔离级别2.1. 如何理解隔离性2.2. 隔离性级别2.3. 脏读,幻读,不可重复读2.4. 查看,设置隔离级别2.5. 隔离性验证三.一致性(Cons…

T046基于51单片机无线蓝牙控制8位LED灯亮灭proteus仿真原理图PCB

功能&#xff1a; 0.本项目采用STC89C52作为单片机系统的控制MCU 1.通过蓝牙发送指令控制LED灯 2.通过手机APP可以控制8路LED灯的亮灭&#xff0c;可以全亮全灭。 3.通过手机APP可以控制8路LED灯的亮度。每个灯的亮度有3档。具体控制指令如下 a)发送Ox&#xff0c;开启指定LED灯…

日常学习之:Yaml 和 Json 有什么区别

安装 json 是 python 内置 yaml 需要安装 pip install pyyaml格式 对于同样一段数据&#xff1a; test_data {"金山中学":{"101班":{"王宁": {"语文":33,"数学":22,"英语":18}}}}我们用下面的代码分别存入 ya…

ALE的基本介绍、使用与配置

本文将介绍ale插件的基本使用与配置&#xff0c;将从linter的介绍到具体插件的使用与bug的修复~ 本文仅仅时抛砖引玉&#xff0c;更多的使用技巧与功能可以查看项目介绍 ALE的基本介绍、使用与配置ale的介绍ale的基本配置与使用查看你可用的linter安装iverilog——for windowal…

Redisson之lock()和tryLock()的区别

Redisson之lock()和tryLock()的区别和原理解析 在Redisson中 lock() 方法 与 tryLock() 方法是有区别的&#xff01; 我们先来阐述两者的区别&#xff0c;再分析它们的源码。 lock() 与 tryLock() 的区别 &#xff08;1&#xff09;返回值&#xff1a; lock() 是没有返回值…

Vue中的diff算法深度解析

模板tamplate经过parse&#xff0c;optimize&#xff0c;generate等一些列操作之后&#xff0c;把AST转为render function code进而生成虚拟VNode,模板编译阶段基本已经完成了&#xff0c;那么这一章&#xff0c;我们来探讨一下Vue中的一个算法策略–dom diff 首先来介绍下什么…

Java8 遍历List 使用stream().parallel()并发安全

1. parallelStream是什么&#xff1a; java 8引入了并行流的概念来进行并行处理&#xff0c;而并行流(Parallel Stream)利用所有可用CPU内核的优势&#xff0c;并行处理任务。其原理(Parallel Stream)是可以把大任务分成多个小任务执行, 最后再把执行结果进行合并, ForkJoinPoo…

数仓DWS层之旁路缓存优化

优化原因&#xff1a; 外部数据源的查询常常是流式计算的性能瓶颈。以本程序为例&#xff0c;每次查询都要连接 Hbase&#xff0c;数据传输需要做序列化、反序列化&#xff0c;还有网络传输&#xff0c;严重影响时效性。可以通过旁路缓存对查询进行优化。 旁路缓存模式是一种非…

利用Python海龟绘图画一个世界杯的足球

利用Python海龟绘图画一个世界杯的足球 花有重开日 人无再少年 四年一次的世界杯快要结束&#xff0c;为了纪念此次世界杯&#xff0c;特意用Python画了一个足球。 1.设计思路以及实现效果 世界杯足球实现思路&#xff1a; 首先使用海龟画一个圆形作为足球的外边框。然后在足…

3天带你走向实战!阿里顶配版Spring全家桶面试进阶笔记有多强?

Spring框架自从诞生以来就一直备受开发者青睐&#xff0c;它涵盖了Spring、Springboot、SpringCloud等诸多解决方案&#xff0c;一般我们都会统称为Spring全家桶&#xff01;出于Spring框架在Java开发者心中中的统治地位&#xff0c;所以不管是面试还是工作&#xff0c;Spring都…

夜神模拟器+fiddler抓包(抓取APPhttps请求,删除sll证书校验)

1.安装fiddler https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe &#xff08;下载不了直接去官网找&#xff09; 2.配置 开启https请求抓取&#xff0c;不抓https可忽略2.修改或查看端口&#xff08;使用默认8888端口&#xff0c;要自定义端口可修改&#…

Arduino 定时器中断

Arduino 定时器中断 Circuits Arduino 查看原文 简介&#xff1a;Arduino 定时器中断 奥雷里&#xff08;地球、月亮和太阳&#xff09; 立式兰花播种机 胶合板书柜扬声器 计时器中断允许您以非常特定的时间间隔执行任务&#xff0c;而不管代码中发生了什么其他事情。我…

Unity ILRuntime Debugger使用及常见问题

目录前言1.安装2.使用3.常见问题前言 ILRuntime支持在VS中断点调试&#xff0c;下面说一下ILRuntime Debugger的使用及常见问题。 1.安装 需要下载对应版本的ILRuntime Debugger VS插件。我是在Unity中PackageManager安装的ILRuntime&#xff0c;可以在插件信息中查看版本。…

记SQL插入emoji成功,但是程序插入失败问题

在执行单测时&#xff0c;碰到了以下熟悉的问题 org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: Incorrect string value: \xF0\x9F\x92\x8B for column name at row 1 ### The error may involve com.*…

Java入门教程(16)——条件判断语句

文章目录1. if结构1.1 if 单分支结构1.2 if-else 双分支结构1.3 if-else if-else 多分支结构switch 语句switch 多分支结构1. if结构 1.1 if 单分支结构 语法结构: if(布尔表达式){ 语句块 }实例&#xff1a;掷色子游戏 这里给大家扩展一个Math函数 Math.Random()&#xff0c…

动态规划算法

1.简介 1.动态规划(Dynamic Programming)算法的核心思想是: 将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法; 2.动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解; 3.与分治法不同…

项目统一规范包管理器

一般来说每个团队都会统一规定项目内只使用一个包管理器&#xff0c;譬如&#xff1a;npm、yarn、pnpm等&#xff0c;我们可以在文档中或者项目根目录REDEM.md中进行描述来形成共识&#xff0c;但毕竟是文档&#xff0c;并不能真正的进行约束&#xff0c;如果有项目成员没有看文…