淺析Android平臺(tái)圖像壓縮方案

在介紹Android平臺(tái)的壓縮方案之前,先了解一下Bitmap的幾個(gè)主要概念捏膨。

像素密度

像素密度指的是每英寸像素?cái)?shù)目甘有,在Bitmap里用mDensity/mTargetDensity版保,mDensity默認(rèn)是設(shè)備屏幕的像素密度,mTargetDensity是圖片的目標(biāo)像素密度悉抵,在加載圖片時(shí)就是 drawable 目錄的像素密度茬贵。

色彩模式->色彩模式是數(shù)字世界中表示顏色的一種算法,在Bitmap里用Config來表示沼头。

  • ARGB_8888:每個(gè)像素占四個(gè)字節(jié)爷绘,A、R进倍、G土至、B 分量各占8位,是 Android 的默認(rèn)設(shè)置猾昆;
  • RGB_565:每個(gè)像素占兩個(gè)字節(jié)陶因,R分量占5位,G分量占6位毡庆,B分量占5位坑赡;
  • ARGB_4444:每個(gè)像素占兩個(gè)字節(jié),A么抗、R、G亚铁、B分量各占4位蝇刀,成像效果比較差;
  • Alpha_8: 只保存透明度徘溢,共8位吞琐,1字節(jié);

Bitmap的計(jì)算方式

memory=scaledWidth*scaledHeight*每個(gè)像素所占字節(jié)數(shù)

其中
scaledWidth : widthtargetDensity/density+0.5
scaledHeight: height
targetDensity/density+0.5

  • scaledWidth表示水平方向的像素值,
  • width表示屏幕寬度,
  • targetDensity表示手機(jī)的像素密度,這個(gè)值一般跟手機(jī)相關(guān),
  • density表示decodingBitmap 的 density,這個(gè)值一般跟圖片放置的目錄有關(guān)(hdpi/xxhdpi)

scaledHeight同理

每個(gè)像素所占字節(jié)數(shù):這個(gè)值跟色彩模式相關(guān)然爆,默認(rèn) ARGB_8888 則是4個(gè)字節(jié)站粟,

在Bitmap種有兩個(gè)獲取內(nèi)存占用大小的方法

  • getByteCount():API12 加入,代表存儲(chǔ) Bitmap 的像素需要的最少內(nèi)存曾雕。
  • getAllocationByteCount():API19 加入奴烙,代表在內(nèi)存中為 Bitmap 分配的內(nèi)存大小,代替了 getByteCount() 方法。

兩者的區(qū)別:

在不復(fù)用 Bitmap 時(shí)切诀,getByteCount() 和 getAllocationByteCount 返回的結(jié)果是一樣的揩环。在通過復(fù)用 Bitmap 來解碼圖片時(shí),那么 getByteCount() 表示新解碼圖片占用內(nèi)存的大小幅虑,getAllocationByteCount() 表示被復(fù)用 Bitmap真實(shí)占用的內(nèi)存大蟹峄(即 mBuffer 的長(zhǎng)度)。

圖片壓縮方式

質(zhì)量壓縮

質(zhì)量壓縮的關(guān)鍵在于Bitmap.compress()函數(shù)倒庵,該函數(shù)不會(huì)改變圖像的大小褒墨,但是可以降低圖像的質(zhì)量,從而降低存儲(chǔ)大小擎宝,進(jìn)而達(dá)到壓縮的目的貌亭。

這里提到的圖像的質(zhì)量主要指的是圖片的色彩空間

一般圖像的色彩空間為RGB,主要通過RGB三原色通道來描述圖片,其中又有ARGB格式,比起RGB多了一個(gè)透明度的通道。

Android下的質(zhì)量壓縮主要通過下面這個(gè)函數(shù)來實(shí)現(xiàn)的认臊。

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);

三個(gè)參數(shù)

  • CompressFormat format:壓縮格式,它有JPEG圃庭、PNG、WEBP三種選擇失晴,JPEG是有損壓縮剧腻,PNG是無損壓縮,WEBP是Google推出的圖像格式.
  • int quality:0~100可選涂屁,數(shù)值越大书在,質(zhì)量越高,圖像越大拆又。
  • OutputStream stream:壓縮后圖像的輸出流儒旬。

其中PNG是無損格式的,壓縮效果不太理想,而WEBP會(huì)存在兼容性的問題。出于兼容性和效果來看帖族,一般會(huì)選擇JPEG作為壓碎格式栈源。

實(shí)例代碼

// R.drawable.thumb 為 png 圖片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.thumb);
try {
    //保存壓縮圖片到本地
    File file = new File(Environment.getExternalStorageDirectory(), "aaa.jpg");
    if (!file.exists()) {
        file.createNewFile();
    }
    FileOutputStream fs = new FileOutputStream(file);
    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fs);
    Log.i(TAG, "onCreate: file.length " + file.length());
    fs.flush();
    fs.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
//查看壓縮之后的 Bitmap 大小
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
byte[] bytes = outputStream.toByteArray();
Bitmap compress = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i(TAG, "onCreate: bitmap.size = " + bitmap.getByteCount() + "   compress.size = " + compress.getByteCount());

我們?cè)賮砜纯?code>quality參數(shù)被設(shè)置為50前后,兩張圖片的對(duì)比.
壓縮前的圖片

image

壓縮后的圖片

image

從上述兩圖可以明顯圖片質(zhì)量的差別,另外再通過log打印查看會(huì)壓縮前后圖片的所占用的大小是一樣的。

bitmap.size = compress.size

Q:這里可能有人就會(huì)有疑惑,為什么壓縮過后,兩張圖片的大小還會(huì)是一樣的呢竖般?

A:因?yàn)閳D片在內(nèi)存中的存儲(chǔ)方式和文件中的存儲(chǔ)方式是不一樣的甚垦。圖片壓縮只會(huì)影響文件的大小,在這個(gè)例子中,壓縮過后存到磁盤的文件大小會(huì)比壓縮之前的文件大小減小很多。

內(nèi)存中所占的大小沒有變化是因?yàn)閎itmap沒有變化的原因涣雕。

文章最開始提到Bitmap的計(jì)算方式

memory=scaledWidth*scaledHeight*每個(gè)像素所占字節(jié)數(shù)

因?yàn)槭菈嚎s的質(zhì)量,所有寬高都不變,而每個(gè)像素所占的字節(jié)數(shù)跟色彩空間有關(guān),默認(rèn)是ARGB_8888.寬高不變,色彩空間不重新設(shè)置,那么bitmap所占的大小就不會(huì)發(fā)生改變艰亮。

說道這里可能又會(huì)有個(gè)新疑問

Q:bitmap占用的大小不變,那為什么圖片質(zhì)量下降了呢?這是因?yàn)閳D片被壓縮過了啊!

A:首先要知道JPEG格式是有損壓縮的,JPEG格式的圖片是不支持透明色彩的,這也是JPEG的大小會(huì)比PNG小很大,圖片質(zhì)量會(huì)比PNG差的原因挣郭。
在經(jīng)過了bitmap.compress()這個(gè)流程時(shí),JPEG會(huì)舍去透明屬性.這樣存放到磁盤時(shí)的文件大小就減小了.然后這個(gè)時(shí)候再通過BitmapFactory.decodeByteArray()把圖片加載回來時(shí),加載的是舍去了透明通道的圖片,按理說應(yīng)該采用 RGB_565或者RGB_888這樣的色彩空間加載,但是你沒有另外設(shè)置這個(gè)參數(shù)的話,加載的色彩格式會(huì)是默認(rèn)ARGB_8888.圖片都沒有透明的色彩空間了,你再給它分配內(nèi)存就只是浪費(fèi)內(nèi)存而已迄埃。

這也是為什么壓縮前后,bitmap所占的大小相同,圖片質(zhì)量卻有所差距的原因。

補(bǔ)充一個(gè)有趣的事件,在早期的Android平臺(tái)下,對(duì)一張圖片進(jìn)行多次質(zhì)量壓縮,會(huì)得到一張變綠的圖片兑障。詳情鏈接

補(bǔ)充一些Android下各格式圖片的存儲(chǔ)方式

WebP

Webp圖片格式是Google推出的一個(gè)支持alpha通道的有損壓縮格式侄非,據(jù)Google官方表明蕉汪,同質(zhì)量情況下Webp圖像要比JPEG、PNG圖像小25%~45%左右彩库,在支持上Android4.0+版本提供原生支持肤无,使用libwebp庫(kù)進(jìn)行編解碼。

GIF

GIF圖像最廣泛的應(yīng)用是用于顯示動(dòng)畫圖像骇钦,它具備文件小且支持alpha通道的優(yōu)點(diǎn)宛渐,不過它是由8位進(jìn)行表示每個(gè)像素的色彩,僅支持256色眯搭,所以在對(duì)色彩要求比較高的場(chǎng)合不太適合窥翩。

Stream

圖片的存儲(chǔ)形式從File轉(zhuǎn)到內(nèi)存中時(shí),圖片內(nèi)容以字節(jié)方式存儲(chǔ)在Stream中鳞仙,此時(shí)所占的內(nèi)存大小為File文件大小寇蚊。

Bitmap

在Android中,任何圖片資源的顯示對(duì)象都是通過bitmap來顯示的棍好,除了xml資源則是通過Canvas來繪制的仗岸,所以,對(duì)于某些純色或者規(guī)則類的圖像借笙,可以通過xml進(jìn)行描述或Canvas來繪制扒怖,這樣所占用的內(nèi)存比通過bitmap來顯示將少幾個(gè)等級(jí)。

Bitmap與Drawable的聯(lián)系

關(guān)于Bitmap和Drawable的關(guān)系业稼,可以看官方的解釋盗痒,Drawable是一個(gè)抽象的概念,來描述某些具備可繪制的的對(duì)象低散,它是一個(gè)抽象類俯邓,而Bitmap是一個(gè)最簡(jiǎn)單的Drawable實(shí)體對(duì)象,Bitmap并不繼承于Drawable熔号,它們之間建立關(guān)聯(lián)最終是通過BitmapDrawable對(duì)象稽鞭,該對(duì)象會(huì)把具體的Bitmap實(shí)例對(duì)象渲染到Canvas上。Drawable更注重描述的是某繪制的行為跨嘉,而Bitmap則是注重存儲(chǔ)著圖像的像素信息川慌。

Bitmap存儲(chǔ)空間

隨著版本的變化以及存儲(chǔ)空間的變化,Bitmap的存儲(chǔ)空間主要有三個(gè)地方

Native Memory
Android2.3以下版本祠乃,bitmap像素?cái)?shù)據(jù)存儲(chǔ)在native內(nèi)存中,釋放內(nèi)存需主動(dòng)調(diào)用recycle()方法

Dalvik Heap
Android3.0+版本兑燥,在Android2.3版本引入了并發(fā)的垃圾回收器后亮瓷,在3.0以后的版本bitmap的像素?cái)?shù)據(jù)則存儲(chǔ)在虛擬機(jī)堆中,不需要主動(dòng)調(diào)用recycle()來回收內(nèi)存降瞳,gc會(huì)主動(dòng)回收

Ashmem
匿名共享內(nèi)存空間嘱支,說到這個(gè)蚓胸,就會(huì)聯(lián)想起大名鼎鼎的Fresco圖片庫(kù),它巧妙的利用了這一空間來進(jìn)行Bitmap對(duì)象的存儲(chǔ)除师,對(duì)于Ashmem空間沛膳,首先想到的是與App進(jìn)程空間是隔離且互不影響的,這點(diǎn)在Android4.4以下版本是這樣的汛聚,在Android4.4+后版本锹安,Ashmem空間將會(huì)包含在App所占用的內(nèi)存空間中∫幸ǎ看Fresco源碼也可以看出叹哭,對(duì)于4.4+版本,對(duì)于Bitmap的解碼使用了另外的解碼器痕貌。在Android4.4以下版本如何使用Ashmem進(jìn)行bitmap的存儲(chǔ)呢风罩?通過DecodeOptions:

options.inPurgeable = true;
options.inInputShareable = true;

以及通過MemoryFile可將圖片的字節(jié)數(shù)據(jù)存儲(chǔ)在Ashmem中。

尺寸壓縮

尺寸壓縮本質(zhì)上就是一個(gè)重新采樣的過程舵稠,放大圖像稱為上采樣超升,縮小圖像稱為下采樣,Android提供了兩種圖像采樣方法哺徊,鄰近采樣和雙線性采樣室琢。

鄰近采樣

鄰近采樣采用鄰近點(diǎn)插值算法,用一個(gè)像素點(diǎn)代替鄰近的像素點(diǎn)唉工,

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Bitmap compress = BitmapFactory.decodeFile("/sdcard/test.png", options);

其中options.inSampleSize的值代表著壓縮后一個(gè)像素點(diǎn)代替原來的幾個(gè)像素點(diǎn),比如options.inSampleSize=2,一個(gè)像素點(diǎn)會(huì)代替原來的2個(gè)像素點(diǎn),注意這里的2個(gè)像素點(diǎn)僅僅指水平方向或者豎直方向上的研乒。即原來2x2的像素,壓縮后僅使用一個(gè)像素點(diǎn)來代替。

網(wǎng)上找了張圖

壓縮前的圖片

image

壓縮后的圖片

image

壓縮前紅綠相間的圖片,經(jīng)過壓縮后,完全變成了綠色.這時(shí)因?yàn)?strong>鄰近點(diǎn)插值算法直接選擇其中一個(gè)像素作為生成像素,另外一個(gè)像素直接拋棄,這樣才會(huì)造成圖片變成純綠色的情況淋硝。

考慮到鄰近采樣的方法有些暴力,Android平臺(tái)提供了另一種尺寸壓縮方案

雙線性采樣

雙線性采樣采用雙線性插值算法雹熬,相比鄰近采樣簡(jiǎn)單粗暴的選擇一個(gè)像素點(diǎn)代替其他像素點(diǎn),雙線性采樣參考源像素相應(yīng)位置周圍2x2個(gè)點(diǎn)的值谣膳,根據(jù)相對(duì)位置取對(duì)應(yīng)的權(quán)重竿报,經(jīng)過計(jì)算得到目標(biāo)圖像。

使用實(shí)例

Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Bitmap compress = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, true);

或者

Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
bm = Bitmap.createBitmap(bitmap, 0, 0, bit.getWidth(), bit.getHeight(), matrix, true);

壓縮效果

壓縮前

image

壓縮后

image

可以看出壓縮后的圖片不會(huì)像鄰近采樣那般只有純粹的一種顏色,而是參考了像素源周圍2x2個(gè)點(diǎn)的像素继谚,并取其權(quán)重得到目標(biāo)圖像烈菌。

雙線性采樣相比鄰近采樣而言,圖片的保真度會(huì)高些,但壓縮的速率不及前者,因?yàn)榍罢卟恍枰?jì)算直接選擇了其中一個(gè)像素作為生成像素。

雙立方/雙三次采樣 (Android原生不支持)

雙立方/雙三次采樣使用的是雙立方/雙三次插值算法花履。雙立方/雙三次插值算法參考了源像素某點(diǎn)周圍 4x4 個(gè)像素芽世。

雙立方/雙三次插值算法經(jīng)常用于圖像或者視頻的縮放,它能比雙線性內(nèi)插值算法保留更好的細(xì)節(jié)質(zhì)量诡壁。

雙立方/雙三次插值算法在平時(shí)的軟件中是很常用的一種圖片處理算法济瓢,但是這個(gè)算法有一個(gè)缺點(diǎn)就是計(jì)算量會(huì)相對(duì)比較大,是前三種算法中計(jì)算量最大的妹卿,軟件 photoshop 中的圖片縮放功能使用的就是這個(gè)算法旺矾。

Lanczos 采樣 (原生不支持)###

Lanczos 采樣和 Lanczos 過濾是 Lanczos 算法的兩種常見應(yīng)用蔑鹦,它可以用作低通濾波器或者用于平滑地在采樣之間插入數(shù)字信號(hào),Lanczos 采樣一般用來增加數(shù)字信號(hào)的采樣率箕宙,或者間隔采樣來降低采樣率嚎朽。

采樣效果 從低到高依次

鄰近采樣--雙線性采樣--雙立方/雙三次采樣--Lanczos 采樣

Android平臺(tái)圖像壓縮方案
QQ音樂團(tuán)隊(duì)分享:Android中的圖片壓縮技術(shù)詳解
也談圖片壓縮
為什么圖片反復(fù)壓縮后會(huì)普遍會(huì)變綠而不是其他顏色
Android之優(yōu)雅地加載大圖片
內(nèi)存占用/GPU渲染性能優(yōu)化手記

另外

個(gè)人的github
閑暇之余寫的故事

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市柬帕,隨后出現(xiàn)的幾起案子哟忍,更是在濱河造成了極大的恐慌,老刑警劉巖雕崩,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魁索,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盼铁,警方通過查閱死者的電腦和手機(jī)粗蔚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饶火,“玉大人鹏控,你說我怎么就攤上這事》羟蓿” “怎么了当辐?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鲤看。 經(jīng)常有香客問我缘揪,道長(zhǎng),這世上最難降的妖魔是什么义桂? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任找筝,我火速辦了婚禮,結(jié)果婚禮上慷吊,老公的妹妹穿的比我還像新娘袖裕。我一直安慰自己,他們只是感情好溉瓶,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布急鳄。 她就那樣靜靜地躺著,像睡著了一般堰酿。 火紅的嫁衣襯著肌膚如雪疾宏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天触创,我揣著相機(jī)與錄音灾锯,去河邊找鬼。 笑死嗅榕,一個(gè)胖子當(dāng)著我的面吹牛顺饮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凌那,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼兼雄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了帽蝶?” 一聲冷哼從身側(cè)響起赦肋,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎励稳,沒想到半個(gè)月后佃乘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驹尼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年趣避,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片新翎。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡程帕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出地啰,到底是詐尸還是另有隱情愁拭,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布亏吝,位于F島的核電站岭埠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蔚鸥。R本人自食惡果不足惜惜论,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望株茶。 院中可真熱鬧来涨,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卧抗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳖粟,已是汗流浹背社裆。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留向图,地道東北人泳秀。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓标沪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嗜傅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子金句,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 摘要:對(duì)android 上圖片壓縮,其實(shí)總結(jié)起來基本可以分為兩類壓縮:尺寸壓縮和質(zhì)量壓縮吕嘀, 尺寸壓縮其實(shí)也可以理解...
    男爵是只貓丶閱讀 8,790評(píng)論 2 14
  • 如以上DEMO截圖所示效果偶房,我們對(duì)于這種類似的功能肯定不算陌生趁曼,因?yàn)檫@可以說是實(shí)際開發(fā)中一類非常常見的功能需求了。...
    Machivellia閱讀 2,053評(píng)論 1 13
  • 之前有個(gè)萌新在技術(shù)群里問圖片壓縮棕洋,然后我竟然還要查資料才回答他挡闰,沒辦法,誰讓我也是個(gè)萌新拍冠,所以打算寫一篇文章來復(fù)習(xí)...
    鍵盤上的麒麟臂閱讀 5,629評(píng)論 11 18
  • 7.1 壓縮圖片 一尿这、基礎(chǔ)知識(shí) 1、圖片的格式 jpg:最常見的圖片格式庆杜。色彩還原度比較好射众,可以支持適當(dāng)壓縮后保持...
    AndroidMaster閱讀 2,522評(píng)論 0 13
  • 三月潮濕的江南 綠苔已爬上了門楣 窄窄的門廳,光滑的石階 再未曾響起匆匆的足音 這里的事物都老了 朱漆的小門也已斑...
    代暮秋閱讀 273評(píng)論 0 1