java實(shí)現(xiàn)1M圖片壓縮優(yōu)化到100kb實(shí)現(xiàn)示例
引言
坦白從寬吧,我就是那個(gè)花了兩天兩夜把 1M 圖片優(yōu)化到 100kb 的家伙——王小二!
自從因?yàn)橐黄獔?bào)道登上熱搜后,我差點(diǎn)抑郁,每天要靠 50 片安眠藥才能入睡。
網(wǎng)絡(luò)上曝光的那些關(guān)于一碼通的消息,有真有假,我這里就不再澄清了。就說(shuō)說(shuō)我是怎么把圖片從 1M 優(yōu)化到 100kb 的故事吧。
是的,由于系統(tǒng)群體規(guī)模和訪(fǎng)問(wèn)規(guī)模的特殊性,每一行代碼、每一張圖片、每一個(gè)技術(shù)文檔都反復(fù)核準(zhǔn),優(yōu)化再優(yōu)化,精益求精。為確保系統(tǒng)運(yùn)行得更高效,我們將一張圖片從1MB壓縮到500KB,再?gòu)?00KB優(yōu)化到100KB。
這樣的工作在外人看起來(lái),簡(jiǎn)單到就好像悄悄給學(xué)妹塞一張情書(shū)就能讓她做我女朋友一樣簡(jiǎn)單。
但殊不知,這其中蘊(yùn)含著極高的技術(shù)含量!
不信,我給你們普及下。
一、圖像壓縮
圖像壓縮是數(shù)據(jù)壓縮技術(shù)在數(shù)字圖像上的應(yīng)用,目的是減少圖像數(shù)據(jù)中的冗余信息,從而用更加高效的格式存儲(chǔ)和傳輸數(shù)據(jù)。
圖像壓縮可以是有損數(shù)據(jù)壓縮,也可以是無(wú)損數(shù)據(jù)壓縮。
怎么樣?
是不是感覺(jué)圖像壓縮技術(shù)沒(méi)有想象中那么簡(jiǎn)單了?
更多關(guān)于圖像壓縮的資料可參考以下鏈接。
http://www.dbjr.com.cn/article/150789.htm
二、Java數(shù)字圖像處理
作為這次“20 多萬(wàn)外包項(xiàng)目”的“主力開(kāi)發(fā)人員”,我這里就給大家介紹下 Java 數(shù)字圖像處理技術(shù)吧,一開(kāi)始我就是用它來(lái)處理圖片的。
數(shù)字圖像處理(Digital Image Processing)是通過(guò)計(jì)算機(jī)對(duì)圖像進(jìn)行去除噪聲、增強(qiáng)、復(fù)原、分割、提取特征等處理的方法和技術(shù)。
輸入的是圖像信號(hào),然后經(jīng)過(guò) DIP 進(jìn)行有效的算法處理后,輸出為數(shù)字信號(hào)。
為了壓縮圖像,我們需要讀取圖像并將其轉(zhuǎn)換成 BufferedImage 對(duì)象,BufferedImage 是 Image 類(lèi)的一個(gè)子類(lèi),描述了一個(gè)具有可訪(fǎng)問(wèn)的圖像數(shù)據(jù)緩沖區(qū),由 ColorModel 和 Raster 的圖像數(shù)據(jù)組成。
廢話(huà)我就不多說(shuō)了,直接進(jìn)入實(shí)戰(zhàn)吧!
三、圖像壓縮實(shí)戰(zhàn)
剛好我本地有一張之前用過(guò)的封面圖,離 1M 只差 236 KB,可以拿來(lái)作為測(cè)試用。
這其中要用到 ImageIO 類(lèi),這是一個(gè)靜態(tài)類(lèi),提供了一系列方法用來(lái)讀和寫(xiě)圖像,同時(shí)還可以對(duì)圖像進(jìn)行簡(jiǎn)單的編碼和解碼。
比如說(shuō)通過(guò) ImageIO.read()
可以將圖像讀取到 BufferedImage 對(duì)象:
File input = new File("ceshi.jpg"); BufferedImage image = ImageIO.read(input);
比如說(shuō)通過(guò) ImageIO.getImageWritersByFormatName()
可以返回一個(gè)Iterator,其中包含了通過(guò)命名格式對(duì)圖像進(jìn)行編碼的 ImageWriter。
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer = (ImageWriter) writers.next();
比如說(shuō)通過(guò) ImageIO.createImageOutputStream()
可以創(chuàng)建一個(gè)圖像的輸出流對(duì)象,有了該對(duì)象后就可以通過(guò) ImageWriter.setOutput()
將其設(shè)置為輸出流。
File compressedImageFile = new File("bbcompress.jpg"); OutputStream os =new FileOutputStream(compressedImageFile); ImageOutputStream ios = ImageIO.createImageOutputStream(os); writer.setOutput(ios);
緊接著,可以對(duì) ImageWriter 進(jìn)行一些參數(shù)配置,比如說(shuō)壓縮模式,壓縮質(zhì)量等等。
ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(0.01f);
壓縮模式一共有四種,MODE_EXPLICIT 是其中一種,表示 ImageWriter 可以根據(jù)后續(xù)的 set 的附加信息進(jìn)行平鋪和壓縮,比如說(shuō)接下來(lái)的 setCompressionQuality()
方法。
setCompressionQuality()
方法的參數(shù)是一個(gè) 0-1 之間的數(shù),0.0 表示盡最大程度壓縮,1.0 表示保證圖像質(zhì)量很重要。對(duì)于有損壓縮方案,壓縮質(zhì)量應(yīng)該控制文件大小和圖像質(zhì)量之間的權(quán)衡(例如,通過(guò)在寫(xiě)入 JPEG 圖像時(shí)選擇量化表)。 對(duì)于無(wú)損方案,壓縮質(zhì)量可用于控制文件大小和執(zhí)行壓縮所需的時(shí)間之間的權(quán)衡(例如,通過(guò)優(yōu)化行過(guò)濾器并在寫(xiě)入 PNG 圖像時(shí)設(shè)置 ZLIB 壓縮級(jí)別)。
整體代碼如下所示:
public class Demo { public static void main(String[] args) { try { File input = new File("ceshi.jpg"); BufferedImage image = ImageIO.read(input); Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer = (ImageWriter) writers.next(); File compressedImageFile = new File("bbcompress.jpg"); OutputStream os = new FileOutputStream(compressedImageFile); ImageOutputStream ios = ImageIO.createImageOutputStream(os); writer.setOutput(ios); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(0.01f); writer.write(null, new IIOImage(image, null, null), param); os.close(); ios.close(); writer.dispose(); } catch (IOException e) { e.printStackTrace(); } } }
執(zhí)行壓縮后,可以看到圖片的大小壓縮到了 19 KB:
可以看得出,質(zhì)量因子為 0.01f 的時(shí)候圖片已經(jīng)有些失真了,可以適當(dāng)提高質(zhì)量因子比如說(shuō) 0.5f,再來(lái)看一下。
圖片質(zhì)量明顯提高了,但大小依然只有 64 KB,壓縮效果還是值得信賴(lài)的。
四、其他開(kāi)源庫(kù)
接下來(lái),推薦一些可以輕松集成到項(xiàng)目中的圖像處理庫(kù)吧,它們?nèi)际敲赓M(fèi)的。
1)ImageJ,用 Java 編寫(xiě)的,可以編輯、分析、處理、保存和打印圖像。
2)Apache Commons Imaging,一個(gè)讀取和寫(xiě)入各種圖像格式的庫(kù),包括快速解析圖像信息(如大小,顏色,空間,ICC配置文件等)和元數(shù)據(jù)。
3)ImageMagick,可以讀取和寫(xiě)入超過(guò)100種格式的圖像,包括DPX、EXR、GIF、JPEG、JPEG-2000、PDF、PNG、Postscript、SVG和TIFF。還可以調(diào)整大小、翻轉(zhuǎn)、鏡像、旋轉(zhuǎn)、扭曲、剪切和變換圖像,調(diào)整圖像顏色,應(yīng)用各種特殊效果,包括繪制文本、線(xiàn)條、多邊形、橢圓和貝塞爾曲線(xiàn)。
4)OpenCV,由BSD許可證發(fā)布,可以免費(fèi)學(xué)習(xí)和商業(yè)使用,提供了包括 C/C++、Python 和 Java 等主流編程語(yǔ)言在內(nèi)的接口。OpenCV 專(zhuān)為計(jì)算效率而設(shè)計(jì),強(qiáng)調(diào)實(shí)時(shí)應(yīng)用,可以充分發(fā)揮多核處理器的優(yōu)勢(shì)。
這里就以 OpenCV 為例,來(lái)演示一下圖像壓縮。當(dāng)然了,OpenCV 用來(lái)壓縮圖像屬于典型的大材小用。
第一步,添加 OpenCV 依賴(lài)到我們的項(xiàng)目當(dāng)中,以 Maven 為例。
<dependency> <groupId>org.openpnp</groupId> <artifactId>opencv</artifactId> <version>4.5.1-2</version> </dependency>
第二步,要想使用 OpenCV,需要先初始化。
OpenCV.loadShared();
第三步,使用 OpenCV 讀取圖片。
Mat src = Imgcodecs.imread(imagePath);
第四步,使用 OpenCV 壓縮圖片。
MatOfInt dstImage = new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 1); Imgcodecs.imwrite("resized_image.jpg", sourceImage, dstImage);
MatOfInt 的構(gòu)造參數(shù)是一個(gè)可變參數(shù),第一個(gè)參數(shù) IMWRITE_JPEG_QUALITY 表示對(duì)圖片的質(zhì)量進(jìn)行改變,第二個(gè)是質(zhì)量因子,1-100,值越大表示質(zhì)量越高。
執(zhí)行代碼后得到的圖片如下所示:
借這個(gè)機(jī)會(huì),來(lái)對(duì)比下 OpenCV 和 JDK 原生 API 在壓縮圖像時(shí)所使用的時(shí)間。
這是我本機(jī)的配置情況,早年買(mǎi)的頂配 iMac,也是我的主力機(jī)。一開(kāi)始只有 16 G 內(nèi)存,后來(lái)加了一個(gè) 16 G 內(nèi)存條,不過(guò)最近半年電腦突然死機(jī)重啟的頻率明顯提高了,不知道是不是 Big Sur 這個(gè)操作系統(tǒng)的問(wèn)題還是電腦硬件老了。
結(jié)果如下所示:
opencvCompress壓縮完成,所花時(shí)間:1070
jdkCompress壓縮完成,所花時(shí)間:322
壓縮后的圖片大小差不多,都是 19 KB,并且質(zhì)量因子都是最低值。
五、一點(diǎn)點(diǎn)心聲
經(jīng)過(guò)上面的技術(shù)分析后,相信你們都明白了,把1M圖片優(yōu)化到100kb實(shí)在是一件“不太容易”的事情。。。。
100KB 很小了吧?只有原來(lái)的 1/10。
要知道,我可是連續(xù)加班了兩天兩夜,不眠不休。
累到最后,我趴在電腦上都睡著了。
沒(méi)想到哈喇子直接給電腦整短路了,我這才算是從夢(mèng)里面嚇醒來(lái)了!
??,生活不易,且行且珍惜吧~
GitHub https://github.com/itwanger/toBeBetterJavaer
以上就是java實(shí)現(xiàn)1M圖片壓縮優(yōu)化到100kb實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于java 1M圖片壓縮到100kb的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java經(jīng)典設(shè)計(jì)模式之觀察者模式原理與用法詳解
這篇文章主要介紹了Java經(jīng)典設(shè)計(jì)模式之觀察者模式,簡(jiǎn)單分析了觀察者模式的概念、原理并結(jié)合實(shí)例形式給出了java觀察者模式的具體用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-08-08詳解SpringBoot Controller接收參數(shù)的幾種常用方式
這篇文章主要介紹了詳解SpringBoot Controller接收參數(shù)的幾種常用方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10Java String類(lèi)簡(jiǎn)單用法實(shí)戰(zhàn)示例【字符串輸出、比較】
這篇文章主要介紹了Java String類(lèi)簡(jiǎn)單用法,結(jié)合具體實(shí)例形式分析了Java使用String類(lèi)實(shí)現(xiàn)字符串的輸出和比較功能相關(guān)操作技巧,需要的朋友可以參考下2019-07-07Java組件commons fileupload實(shí)現(xiàn)文件上傳功能
這篇文章主要為大家詳細(xì)介紹了Java組件commons fileupload實(shí)現(xiàn)文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10springboot 自定義權(quán)限標(biāo)簽(tld),在freemarker引用操作
這篇文章主要介紹了springboot 自定義權(quán)限標(biāo)簽(tld),在freemarker引用操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Springboot使用SPI注冊(cè)bean到spring容器的示例代碼
這篇文章主要介紹了Springboot使用SPI注冊(cè)bean到spring容器,主要包括mydriver接口,mysqldriver實(shí)現(xiàn)過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10SpringBoot?整合Redis?數(shù)據(jù)庫(kù)的方法
Redis是一個(gè)基于內(nèi)存的日志型可持久化的緩存數(shù)據(jù)庫(kù),保存形式為key-value格式,Redis完全免費(fèi)開(kāi)源,它使用ANSI?C語(yǔ)言編寫(xiě)。這篇文章主要介紹了SpringBoot?整合Redis?數(shù)據(jù)庫(kù)的方法,需要的朋友可以參考下2018-03-03