Java實(shí)現(xiàn)去除文檔陰影的示例代碼
一、前言
文稿掃描大家用的都比較頻繁、想是各種證件、文件都可以通過掃描文稿功能保存到手機(jī)。相比直接拍照,在掃描文稿時(shí),程序會(huì)對(duì)圖像進(jìn)行一些矯正。比如去除陰影、修正傾斜、旋轉(zhuǎn)矯正等。進(jìn)行這些處理后的圖片要更加容易識(shí)別。今天就來討論以下去除陰影的操作。
二、實(shí)現(xiàn)原理
1. 圖像
在開始實(shí)現(xiàn)前,我們來了解一些圖像相關(guān)的知識(shí)。這里討論RGB圖像,也就是我們俗稱的彩色的圖像。圖像可以被看作是一個(gè)height×width的數(shù)組,每一個(gè)數(shù)表示一個(gè)像素。如果是彩色的圖像,每個(gè)像素會(huì)包含RBG三個(gè)值,最低字節(jié)表示G、次低字節(jié)表示B、第三字節(jié)表示R。
比如像素值為:
0x00ff00
其RBG值分別為:
R: 0x00
G: 0xff
B: 0x00
如果想要從原像素中取RGB的值,可以使用按位與操作,示例如下:
// pixel是從圖像中取出來的數(shù) int[] rgb = new int[3]; rgb[0] = (pixel & 0xff0000) >> 16; rgb[1] = (pixel & 0xff00) >> 8; rgb[2] = (pixel & 0xff);
因?yàn)楂@取R和G的時(shí)候,保留的是高位,我們希望得到的是一個(gè)低位的數(shù)據(jù),因此向右移一定位。
2. 灰度轉(zhuǎn)換
有時(shí)候,為了方便處理會(huì)把圖像轉(zhuǎn)換成灰度圖像。轉(zhuǎn)換成灰度圖像的方法有很多,一種非常簡單的辦法就是讓rgb三個(gè)通道都為同樣的值,這個(gè)值就是rgb三個(gè)值的均值。
3.閾值處理
閾值處理是今天關(guān)鍵部分,閾值處理的思想非常簡單,就是當(dāng)圖像像素值大于閾值時(shí)將其處理為最大值,當(dāng)像素小于等于閾值時(shí)將其處理為0。這樣可以得到一張完全的黑白圖像。
在文稿中,文字部分可以看作是黑色,背景部分可以看作是白色,而陰影則是介于黑白之間的值。如果想要去除陰影,則需要對(duì)圖像進(jìn)行閾值處理,把閾值設(shè)定為小于陰影的值。比如下圖:
左圖是原圖,其中灰色部分為陰影,需要去除。這時(shí)我們對(duì)圖像進(jìn)行閾值處理,把閾值設(shè)定為50,那么陰影部分就會(huì)被設(shè)置成255,文字部分和背景部分變換都不大,這樣就實(shí)現(xiàn)了文稿的陰影去除工作。
三、代碼實(shí)現(xiàn)
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); //獲取坐標(biāo)為(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可以獲取圖片的寬、高、某個(gè)點(diǎn)的像素等。為了方便,編寫一個(gè)getRgb來把pixel轉(zhuǎn)成一個(gè)rgb數(shù)組。代碼輸出結(jié)果如下:
width:400,height:400
pixel-2853206
r:212 g:118 b:170
2.閾值處理
知道了上面的基本操作后,就可以開始進(jìn)行閾值處理了。閾值處理就是求rgb均值mean,如果mean大于閾值,則把像素設(shè)置為0xffffff,否則設(shè)置為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); //計(jì)算rgb均值 int grayScale = (rgb[0] + rgb[1] + rgb[2]) / 3; //如果均值大于閾值,則賦值將該像素設(shè)置為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; } }
下圖是原圖和處理后的結(jié)果:
左圖中有兩處陰影,右側(cè)則去除了陰影。最終效果圖與設(shè)定的閾值有關(guān)系,當(dāng)閾值設(shè)置不恰當(dāng)時(shí),會(huì)導(dǎo)致結(jié)果圖比原圖更糟糕,或者導(dǎo)致最終文字目標(biāo)也被去除了。這里可以用循環(huán)來解決,代碼如下:
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); } }
大家可以自行測(cè)試。有時(shí)候之間閾值處理不能很好的去除陰影,這個(gè)時(shí)候會(huì)結(jié)合一些其它辦法。包括濾波操作、形態(tài)學(xué)處理等。
到此這篇關(guān)于Java實(shí)現(xiàn)去除文檔陰影的示例代碼的文章就介紹到這了,更多相關(guān)Java去除文檔陰影內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Java swing組件實(shí)現(xiàn)簡易計(jì)算器
這篇文章主要介紹了基于Java swing組件實(shí)現(xiàn)簡易計(jì)算器,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Java連接并操作Sedna XML數(shù)據(jù)庫的方法
這篇文章主要介紹了Java連接并操作Sedna XML數(shù)據(jù)庫的方法,較為詳細(xì)的說明了Sedna XML數(shù)據(jù)庫的原理與功能,并給出了基于java操作Sedna XML數(shù)據(jù)庫的方法,需要的朋友可以參考下2015-06-06Java中實(shí)現(xiàn)線程的三種方式及對(duì)比_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文給大家分享了java實(shí)現(xiàn)線程的三種方式,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-05-05Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹
這篇文章主要介紹了Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Spring?Security中自定義cors配置及原理解析
在Spring框架中,通過自定義CORS配置可根據(jù)實(shí)際情況調(diào)整URL的協(xié)議、主機(jī)、端口等,以適應(yīng)"同源安全策略",配置原理涉及CorsConfigurer和CorsFilter,自定義配置需要注意@Configuration注解、方法名以及可能的@Autowired注解2024-10-10SpringBoot+kaptcha實(shí)現(xiàn)圖片驗(yàn)證碼功能詳解
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何結(jié)合kaptcha實(shí)現(xiàn)圖片驗(yàn)證碼功能,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考一下2024-01-01Springboot開發(fā)OAuth2認(rèn)證授權(quán)與資源服務(wù)器操作
這篇文章主要介紹了Springboot開發(fā)OAuth2認(rèn)證授權(quán)與資源服務(wù)器操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06java實(shí)現(xiàn)字符串轉(zhuǎn)String數(shù)組的方法示例
這篇文章主要介紹了java實(shí)現(xiàn)字符串轉(zhuǎn)String數(shù)組的方法,涉及java字符串的遍歷、分割、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10