文章首發(fā)QQ音樂技術(shù)公眾號(hào):
https://mp.weixin.qq.com/s/H9Tz1n4O2-Aawgu7p-XL5w
一勋磕、Android 尺寸壓縮邏輯
針對(duì)圖片尺寸的修改其實(shí)就是一個(gè)圖像重新采樣的過程揍障,放大圖像稱為上采樣(upsamping)啤月,縮小圖像稱為下采樣(downsampling),這里我們重點(diǎn)討論下采樣避消。
在 Android 中圖片重采樣提供了兩種方法低滩,一種叫做鄰近采樣(Nearest Neighbour Resampling)
,另一種叫做雙線性采樣(Bilinear Resampling)
岩喷。
除了 Android 中這兩種常用的重采樣方法之外,還有另外比較常見的兩種:雙立方/雙三次采樣(Bicubic Resampling)
和 Lanczos Resampling
监憎。除此之外纱意,還有一些其他個(gè)人或機(jī)構(gòu)發(fā)明的算法 Hermite Resampling
,Bell Resampling
鲸阔,Mitchell Resampling
偷霉。我們這里著重介紹前面提到的四種采樣方法。
二褐筛、鄰近采樣(Nearest Neighbour Resampling)
Nearest Neighbour Resampling(鄰近采樣)
类少,是 Android 中常用的壓縮方法之一,我們先來看看在 Android 中使用鄰近采樣的示例代碼:
BitmapFactory.Options options = new BitmapFactory.Options();
//或者 inDensity 搭配 inTargetDensity 使用渔扎,算法和 inSampleSize 一樣
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Bitmap compress = BitmapFactory.decodeFile("/sdcard/test.png", options);
來看看鄰近采樣的圖片效果:
原圖是每個(gè)像素紅綠相間的圖片硫狞,可以看到處理之后的圖片已經(jīng)完全變成了綠色,接著我們來看看 inSampleSzie 的官方描述:
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1. Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2.
從官方的解釋中我們可以看到 x(x 為 2 的倍數(shù))個(gè)像素最后對(duì)應(yīng)一個(gè)像素晃痴,由于采樣率設(shè)置為 1/2残吩,所以是兩個(gè)像素生成一個(gè)像素。鄰近采樣的方式比較粗暴倘核,直接選擇其中的一個(gè)像素作為生成像素泣侮,另一個(gè)像素直接拋棄,這樣就造成了圖片變成了純綠色紧唱,也就是紅色像素被拋棄活尊。
鄰近采樣采用的算法叫做鄰近點(diǎn)插值算法。
三漏益、雙線性采樣(Bilinear Resampling)
雙線性采樣(Bilinear Resampling)
在 Android 中的使用方式一般有兩種:
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Bitmap compress = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, true);
或者直接使用 matrix 進(jìn)行縮放
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);
看源碼可以知道 createScaledBitmap
函數(shù)最終也是使用第二種方式的 matrix 進(jìn)行縮放蛹锰,我們來看看雙線性采樣的表現(xiàn):
可以看到處理之后的圖片不是像鄰近采樣一樣純粹的一種顏色,而是兩種顏色的混合遭庶。雙線性采樣使用的是雙線性內(nèi)插值算法宁仔,這個(gè)算法不像鄰近點(diǎn)插值算法一樣,直接粗暴的選擇一個(gè)像素峦睡,而是參考了源像素相應(yīng)位置周圍 2x2 個(gè)點(diǎn)的值翎苫,根據(jù)相對(duì)位置取對(duì)應(yīng)的權(quán)重权埠,經(jīng)過計(jì)算之后得到目標(biāo)圖像。
雙線性內(nèi)插值算法在圖像的縮放處理中具有抗鋸齒功能, 是最簡(jiǎn)單和常見的圖像縮放算法煎谍,當(dāng)對(duì)相鄰 2x2 個(gè)像素點(diǎn)采用雙線性內(nèi)插值算法時(shí)攘蔽,所得表面在鄰域處是吻合的,但斜率不吻合呐粘,并且雙線性內(nèi)插值算法的平滑作用可能使得圖像的細(xì)節(jié)產(chǎn)生退化满俗,這種現(xiàn)象在上采樣時(shí)尤其明顯。
四作岖、鄰近采樣和雙線性采樣對(duì)比
我們這里來對(duì)比一下這兩種 Android 中經(jīng)常用到的圖片尺寸壓縮方法唆垃。
鄰近采樣的方式是最快的,因?yàn)樗苯舆x擇其中一個(gè)像素作為生成像素痘儡,但是生成的圖片可能會(huì)相對(duì)比較失真辕万,產(chǎn)生比較明顯的鋸齒,最具有代表性的就是處理文字比較多的圖片在展示效果上的差別沉删,對(duì)比:
原圖:這個(gè)對(duì)比就非常直觀了渐尿,鄰近采樣字的顯示失真對(duì)比雙線性采樣來說要嚴(yán)重很多。
五矾瑰、雙立方/雙三次采樣(Bicubic Resampling)
雙立方/雙三次采樣使用的是雙立方/雙三次插值算法砖茸。鄰近點(diǎn)插值算法的目標(biāo)像素值由源圖上單個(gè)像素決定,雙線性內(nèi)插值算法由源像素某點(diǎn)周圍 2x2 個(gè)像素點(diǎn)按一定權(quán)重獲得殴穴,而雙立方/雙三次插值算法更進(jìn)一步參考了源像素某點(diǎn)周圍 4x4 個(gè)像素凉夯。
這個(gè)算法在 Android 中并沒有原生支持,如果需要使用推正,可以通過手動(dòng)編寫算法或者引用第三方算法庫恍涂,幸運(yùn)的是這個(gè)算法在 ffmpeg 中已經(jīng)給到了支持,具體的實(shí)現(xiàn)在 libswscale/swscale.c
文件中:FFmpeg Scaler Documentation植榕。
雙立方/雙三次插值算法經(jīng)常用于圖像或者視頻的縮放再沧,它能比雙線性內(nèi)插值算法保留更好的細(xì)節(jié)質(zhì)量。我們看看這個(gè)算法的實(shí)際表現(xiàn)和與雙線性內(nèi)插值算法的下采樣對(duì)比:
原圖:就下采樣來說尊残,兩者表現(xiàn)很相近炒瘸,肉眼可見的差距不大,接下來比較一下這兩種算法的上采樣實(shí)際表現(xiàn):
原圖:這兩種算法的上采樣結(jié)果我們還是可以看見較為明顯的差距寝衫,雙立方/雙三次采樣的鋸齒是要小一些顷扩。
雙立方/雙三次插值算法在平時(shí)的軟件中是很常用的一種圖片處理算法,但是這個(gè)算法有一個(gè)缺點(diǎn)就是計(jì)算量會(huì)相對(duì)比較大慰毅,是前三種算法中計(jì)算量最大的隘截,軟件 photoshop 中的圖片縮放功能使用的就是這個(gè)算法。
六、Lanczos Resampling
Lanczos 采樣和 Lanczos 過濾是 Lanczos 算法的兩種常見應(yīng)用婶芭,它可以用作低通濾波器或者用于平滑地在采樣之間插入數(shù)字信號(hào)东臀,Lanczos 采樣一般用來增加數(shù)字信號(hào)的采樣率,或者間隔采樣來降低采樣率犀农。
Lanczos 采樣使用的 Lanczos 算法也可以用來作為圖片的縮放惰赋,Lanczos 算法和雙三次插值算法都是使用卷積核來通過輸入像素計(jì)算輸出像素,只不過在算法表現(xiàn)上稍有不同呵哨。關(guān)于卷積核的介紹赁濒,這里給一張簡(jiǎn)單的圖片幫助大家理解:
Lanczos 從算法角度講理論上會(huì)比雙三次/雙立方插值算法更好一點(diǎn),先來看看它和雙三次/雙立方采樣的圖片下采樣對(duì)比:
原圖:基本看不出差別孟害,然后是這兩種算法的上采樣對(duì)比:
原圖:這兩種算法的上下采樣結(jié)果從肉眼上看差距很小拒炎,但是從理論上來說 Lanczos 算法處理出來的圖片應(yīng)該是更加平滑少鋸齒的。
同樣的纹坐,Lanczos 算法在 ffmpeg 的 libswscale/swscale.c
中也有實(shí)現(xiàn)枝冀。其實(shí)不光 Lanczos 和上面的三種算法,ffmpeg 還提供了其他的圖像重采樣方法耘子,諸如 area averaging
、Gaussian
等等球切,通過編譯好的 ffmpeg 庫調(diào)用這些算法處理圖片的命令如下:
ffmpeg -s 600x500 -i input.jpg -s 300x250 -sws_flags lanczos lanczos.jpg
-sws_flags
參數(shù)根據(jù)采樣算法可以選擇 bilinear/bicubic/lanczos 等等谷誓。
七、四種算法對(duì)二值化圖片的處理表現(xiàn)
這四種圖片重采樣算法在處理二值化圖片上面的表現(xiàn)差異較大吨凑,我們先看看下采樣的對(duì)比:
原圖:下采樣的對(duì)比一目了然捍歪,從上到下的圖像表現(xiàn)效果逐漸變優(yōu),Lanczos 算法處理后的圖像質(zhì)量屬于最優(yōu)鸵钝,接著我們看看這四種算法的上采樣對(duì)比:
原圖:從圖像質(zhì)量上來看糙臼,和下采樣結(jié)果一致,鄰近采樣效果較差恩商,依次往下效果變優(yōu)变逃,Lanczos 效果最優(yōu)。
八怠堪、總結(jié)
上面主要介紹了常見的四種圖像重采樣算法揽乱,在 Android 中,前兩種采樣方法根據(jù)實(shí)際情況去選擇即可粟矿,如果對(duì)時(shí)間要求不高凰棉,傾向于使用雙線性采樣去縮放圖片。如果對(duì)圖片質(zhì)量要求很高陌粹,雙線性采樣也已經(jīng)無法滿足要求撒犀,則可以考慮引入另外幾種算法去處理圖片,但是同時(shí)需要注意的是后面兩種算法使用的都是卷積核去計(jì)算生成像素,計(jì)算量會(huì)相對(duì)比較大或舞,Lanczos 的計(jì)算量則是最大荆姆,在實(shí)際開發(fā)過程中根據(jù)需求進(jìn)行算法的選擇即可。