之前有個(gè)萌新在技術(shù)群里問(wèn)圖片壓縮,然后我竟然還要查資料才回答他,沒(méi)辦法烟央,誰(shuí)讓我也是個(gè)萌新,所以打算寫一篇文章來(lái)復(fù)習(xí)一下圖片相關(guān)的知識(shí)點(diǎn)这吻。
一.URI 和 圖片路徑
一般來(lái)說(shuō)從本地中拿到Bitmap就能展示圖片到imageview吊档,而且URI和圖片在本地的路徑都能拿到Bitmap,但是這兩個(gè)不是同一個(gè)東西唾糯。
比如一張本地的圖片:
URI:content://media/external/images/media/416651
路徑:/storage/emulated/0/DCIM/Camera/b229c722-be7b-45e4-adda-5d7894480871.jpg
雖然不同怠硼,但是我們能夠把URI轉(zhuǎn)換成路徑。
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
//picturePath就是圖片在儲(chǔ)存卡所在的位置
String picturePath = cursor.getString(columnIndex);
cursor.close();
有人說(shuō)這個(gè)方法只適用于API19以下的移怯,我這里在19以上也能正常獲取到香璃。如果19以上獲取不到路徑就只能分情況去寫。
二.獲取本地圖片并展示
1.首先要進(jìn)入一個(gè)選擇圖片的頁(yè)面
這樣的頁(yè)面可以自定義去寫舟误,但是一般系統(tǒng)都會(huì)提供一個(gè)簡(jiǎn)單的葡秒。
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, 0x1111);
2.然后在返回中做顯示圖片的操作
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 0x1111) {
Uri uri = data.getData();
ivShow.setImageURI(uri);
}
}
調(diào)用imageView.setImageURI(uri)方法就能直接用uri來(lái)顯示本地的圖片,但是有時(shí)候我們有其它的需求嵌溢,要用Bitmap
那么我們可以把uri轉(zhuǎn)成Bitmap之后再顯示眯牧,那么上面的代碼可以改成這樣。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 0x1111) {
Uri uri = data.getData();
try {
Bitmap photoBmp = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
ivShow.setImageBitmap(photoBmp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
但是有沒(méi)有發(fā)現(xiàn)這里AS默認(rèn)給我們加了個(gè)try-catch赖草,為什么学少?因?yàn)檫@樣搞可能會(huì)造成OOM,我們可以先來(lái)看看這個(gè)圖片的大小秧骑。
bitmap.getAllocationByteCount()計(jì)算出這張圖片的大小為
然后我們?cè)倏纯词謾C(jī)內(nèi)部他的大小版确,比較一下。
對(duì)計(jì)算的進(jìn)行大小轉(zhuǎn)換
如果沒(méi)算錯(cuò)的話這張圖片在內(nèi)存中占5.6MB乎折,在存儲(chǔ)空間中占2.97MB绒疗,也就是說(shuō)Bitmap和File不是同個(gè)東西,而且Bitmap是file的展開骂澄,也可以說(shuō)存儲(chǔ)時(shí)file對(duì)Bitmap進(jìn)行了壓縮吓蘑。這是一個(gè)細(xì)節(jié)的問(wèn)題,雖然影響不大坟冲,但是必須注意磨镶。
這和之后說(shuō)的圖片壓縮有很大的關(guān)系?壓縮為什么不止一種樱衷,為什么有的壓縮能讓文件變小棋嘲,但是圖片不會(huì)失真等等,要在那種情況下用哪種壓縮矩桂?我個(gè)人覺(jué)得要看這些之前首先要大概了解file和Bitmap的不同沸移。
http://blog.csdn.net/xjz729827161/article/details/53586273這里有篇文章痪伦,雖然講得內(nèi)容少,但是講得很好雹锣。
3.imageview展示圖片的方法
上面的例子中我用過(guò)imageview.setImageURI(uri)和imageview.setImageBitmap(bitmap)兩種方法來(lái)展示圖片到imageview中网沾,那說(shuō)明imageview肯定有其它的展示方法。我可以先找到源碼中的set方法
(1)setImageResource和setImageDrawable我想就不用再多說(shuō)了蕊爵。
(2)setIcon辉哥,Iocn就是應(yīng)用圖標(biāo),這個(gè)方法是在6.0之后才使用的攒射,也不是很普遍
(3)關(guān)于Tint的就是改變顏色用的
(4)setImageLevel這個(gè)方法是要配合level-list來(lái)用醋旦,主要是改變圖片的狀態(tài)。
三.存儲(chǔ)圖片到本地
上面講了從本地獲取問(wèn)題会放,這里就講存儲(chǔ)到本地(不講網(wǎng)絡(luò)的那個(gè)饲齐,主要講從圖片從內(nèi)存到本地存儲(chǔ)空間的操作)
保存圖片到本地就是操作文件的操作,那就會(huì)用到IO流咧最,其實(shí)你從本地拿出圖片轉(zhuǎn)Bitmap也用到IO流捂人,只是它內(nèi)部封裝起來(lái)了而已。不管是什么形式的矢沿,是bitmap還是文件或者是設(shè)么滥搭,都會(huì)轉(zhuǎn)成字節(jié)保存到本地形成本地文件,這是io的內(nèi)容捣鲸,不多說(shuō)瑟匆,那就只拿bitmap來(lái)做舉例。
1.把Bitmap保存到本地
既然之前的流程是:文件->bitmap->imagerView 摄狱。那么這里我們就反著弄脓诡,從imagerView 拿到bitmap再存到本地无午。
(1)imageView獲取Bitmap :
ivShow.setDrawingCacheEnabled(true);
Bitmap bm = ivShow.getDrawingCache();
我截了一張圖來(lái)表示輸入imageview的bitmap和從imageview拿到的bitmap有什么不同媒役。
注意:從圖中可以看出有兩種情況,第一種情況我是imageview太大沒(méi)有展示完宪迟,第二種情況展示完酣衷,會(huì)發(fā)現(xiàn)沒(méi)展示完的情況下,從imageview中獲取到的bitmap和傳進(jìn)去的不同次泽。這說(shuō)明兩個(gè)bitmap是不同的穿仪,和imageview的寬度和高度有關(guān),這里要注意一下意荤。
(2)bitmap用流存到本地
private void savePhoto(){
ivShow.setDrawingCacheEnabled(true);
Bitmap bm = ivShow.getDrawingCache();
Log.v("mmp","bm大邪∑:"+(bm.getRowBytes() * bm.getHeight()));
// 保存
File file = new File(Environment.getExternalStorageDirectory(),"newpic.jpg");
if (file.exists()){
file.delete();
}
try {
FileOutputStream outputStream = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
Log.v("mmp","圖片存到本地的大小:"+file.length());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
三張圖分別是原圖的大小,打印的文件大小和保存到本地的新突破的大小玖像。其實(shí)我也有疑問(wèn)為什么會(huì)不一樣紫谷?這個(gè)問(wèn)題雖然對(duì)我講的內(nèi)容沒(méi)多大關(guān)系,但是我還是找到答案后再更新補(bǔ)充,這里的獲取保存前后為什么會(huì)不一樣笤昨。
(3)改變imageview大小查看結(jié)果
如果我改變imageview的大凶媲(變小)瞒窒,可以看到保存到本地的圖片的大小也會(huì)不同捺僻。這是因?yàn)楦淖僫mageview的大小會(huì)改變獲取到的bitmap的大小不同,導(dǎo)致存儲(chǔ)到本地的圖片也不同崇裁,所以要慎用從imageview中獲取Bitmap
四.圖片壓縮
先看看壓縮的基本方法bitmap.compress()
這個(gè)方法有3個(gè)參數(shù):
(1)Bitmap.CompressFormat format 表示壓縮格式匕坯,一般選jpeg
(2)int quality 表示一個(gè)壓縮率,100表示不壓縮
(3)OutputStream stream 流
我覺(jué)得主要是研究三點(diǎn)拔稳,壓縮前后文件大小的變化醒颖、壓縮前后Bitmap的大小變化和圖片的質(zhì)量
1.改變壓縮率
之前寫photoBmp.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
改成photoBmp.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);壓縮一半看看。
(1)先對(duì)Bitmap的大小
從圖中可以看出壓縮前后Bitmap的大小不變壳炎。
(2)再對(duì)比文件的大小泞歉。
之前沒(méi)壓縮是100kb,這里顯然是壓縮后文件變小了匿辩。
(3)再看看展示的效果
為了能更好的顯示效果腰耙,我把壓縮率調(diào)得更低
10的情況
這樣也還不是很明顯,0的情況
這樣就很容易看出圖片炸了铲球。說(shuō)明改變壓縮率來(lái)壓縮圖片挺庞,會(huì)改變圖片的清晰度。
(4)總結(jié)
經(jīng)過(guò)一系列的對(duì)比我們得出這樣的結(jié)果:
改變壓縮率稼病,位圖的大小不變选侨,圖片的質(zhì)量變差(也可以說(shuō)失真),文件的大小變小然走,圖片的寬高不變援制。
那么這里我們就應(yīng)該有一個(gè)疑問(wèn),不對(duì)芍瑞,應(yīng)該說(shuō)必須要弄懂一個(gè)重要的問(wèn)題:為毛圖片質(zhì)量變差了晨仑,bitmap的大小卻不變,我們都知道bitmap = 單位長(zhǎng) X 寬 X 單位像素占用的字節(jié)數(shù)拆檬,那么這些都不變的情況下為什么圖片質(zhì)量變了
從圖中第一眼可以看出洪己,變得是什么?是顏色,有沒(méi)有一種彩圖變黑白的感覺(jué)竟贯。這種壓縮后會(huì)損壞圖片質(zhì)量的壓縮叫質(zhì)量壓縮也是一種有損壓縮答捕。
看看百度百科的定義,我覺(jué)得他默認(rèn)是進(jìn)行色度抽樣的操作屑那。如果你詳細(xì)了解這個(gè)過(guò)程拱镐,你需要深度去了解jpg格式晌缘、色彩等內(nèi)容,我這方面不是很了解痢站,但是我敢確定磷箕,jpg文件的大小會(huì)和色彩有關(guān)。隨便一說(shuō)阵难,這種有損壓縮一般是沒(méi)辦法還原的岳枷。
2.采樣率
在android中有一種壓縮方式成為采樣率壓縮,其實(shí)這種壓縮方法就是一種改變圖片的寬高呜叫,按照我們上面的說(shuō)法空繁,改變尺寸,那bitmap也會(huì)變小朱庆,文件也會(huì)變小盛泡,下圖中的第三張圖就是對(duì)第二張圖進(jìn)行采樣率壓縮的結(jié)果,會(huì)發(fā)現(xiàn)它的寬高都縮小了一半娱颊,那Bitmap的大小就變成了之前的1/4.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bm = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/newpic2.jpg"
, options);
Log.v("mmp","位圖采樣率壓縮后大邪了小:"+(bm.getRowBytes() * bm.getHeight()));
ivBeilv.setImageBitmap(bm);
但是不同于普通的縮小尺寸的方法,因?yàn)槠胀ǖ目s小尺寸的方法是箱硕,你先從文件中把圖片轉(zhuǎn)成bitmap到內(nèi)存拴竹,再縮小。這樣的話bitmap就是先會(huì)占很大內(nèi)存剧罩,而BitmapFactory.decodeFile是先獲取尺寸栓拜,然后再讀取圖片的像素,最后展示惠昔,這樣獲取的bitmap就直接是原來(lái)的1/4.
注意:關(guān)于像素幕与,是個(gè)細(xì)節(jié),直接這樣定義可能不是很清晰镇防,之后我會(huì)講講像素和展示圖片的關(guān)系啦鸣,這樣就能更清楚明白采樣率的原理了。
3.質(zhì)量壓縮 與 尺寸壓縮
上面我只是用口頭語(yǔ)言稍微解釋了一些可以實(shí)現(xiàn)壓縮的方法营罢,但是用android的時(shí)候我們會(huì)經(jīng)常聽說(shuō)不是用質(zhì)量壓縮就是用尺寸壓縮赏陵,那么這兩種壓縮方式分別是怎樣的饼齿。
其實(shí)第一種使用compress方法的壓縮就是質(zhì)量壓縮饲漾,而尺寸壓縮的方法很多,主要以改變圖片尺寸為目的的壓縮我覺(jué)得都應(yīng)該被稱為尺寸壓縮缕溉。
不多扯這些無(wú)關(guān)緊要的東西考传,很多地方會(huì)說(shuō)這樣的一句話,質(zhì)量壓縮是通過(guò)降低圖片的質(zhì)量证鸥,尺寸壓縮是通過(guò)降低圖片的像素僚楞。
(1)尺寸壓縮
就是通過(guò)改變尺寸來(lái)改變像素勤晚,像素變了,文件的大小自然會(huì)變泉褐。我們都知道赐写,手機(jī)的分辨率是固定的,假如圖像的分辨率是800 * 600膜赃,它長(zhǎng)寬變短一半后就變成了400 * 300挺邀,那么800 * 600個(gè)點(diǎn),怎么放到只有400 * 300個(gè)點(diǎn)歌點(diǎn)的位置呢跳座?那就是來(lái)相鄰的4個(gè)像素點(diǎn)端铛,通過(guò)某種算法變成一個(gè)像素點(diǎn),那就造成了像素的丟失疲眷,所以文件就變小了禾蚕。
擴(kuò)展一下,那如果是變大呢狂丝?圖片變小是通過(guò)某種算法把某塊區(qū)域的像素點(diǎn)變成一個(gè)像素點(diǎn)(比如縮小4倍的話相鄰4塊紅色的像素點(diǎn)會(huì)變成1塊紅色的像素點(diǎn))换淆,那圖片尺寸變大就是根據(jù)某種算法去補(bǔ)充某些像素點(diǎn),這些像素點(diǎn)的顏色會(huì)根據(jù)相鄰兩個(gè)像素點(diǎn)的顏色去計(jì)算几颜。
那么這種方法就是有損的产舞,為什么?你縮小時(shí)丟失了某些像素你能拿回來(lái)嗎菠剩,不能易猫。你變大時(shí)添加的顏色像素是固定的嗎,不是。不信你可以把一張圖片的高寬縮小個(gè)幾十倍褪迟,然后拿到新圖片后再把它的高寬擴(kuò)大相同的倍數(shù)及皂,看看效果。這是一個(gè)我縮小10倍再放大10倍的效果攘已。
注意,我說(shuō)這個(gè)還要提醒一件事怜跑,那就是如果你用固定的imageview來(lái)放圖片样勃,然后每次對(duì)圖片的操作都是從Imageview中獲取的話,不要再次改變圖片的大小了性芬,不然會(huì)這樣峡眶,打碼的特效。
(2) 質(zhì)量壓縮
為什么要后面說(shuō)質(zhì)量壓縮植锉,因?yàn)橘|(zhì)量壓縮的原理我不懂辫樱,我找資料找不到,我只能說(shuō)說(shuō)我自己對(duì)質(zhì)量壓縮的看法俊庇。
這里的解釋不一定正確狮暑,如果有大屌知道原理請(qǐng)指教
這里的解釋不一定正確鸡挠,如果有大屌知道原理請(qǐng)指教
這里的解釋不一定正確,如果有大屌知道原理請(qǐng)指教
重要的事說(shuō)三遍搬男。我覺(jué)得質(zhì)量壓縮的原理主要出在顏色上拣展,看看我之前質(zhì)量壓縮的圖。
可以看出壓縮后的顏色比沒(méi)壓縮的時(shí)候少了很多缔逛,我有兩種猜想瞎惫。
第一種猜想,壓縮存儲(chǔ)時(shí)改變了圖片的格式译株,使每個(gè)像素點(diǎn)的字節(jié)變少瓜喇,比如把ARGB_8888變成ARGB_4444,然后拿出來(lái)的時(shí)候再變回ARGB_8888格式歉糜。
第二種猜想乘寒,我們都知道壓縮圖片最簡(jiǎn)單的原理是,假如你圖片的分辨率是1920 * 1080匪补,第一行的顏色相同伞辛,如果一個(gè)一個(gè)像素點(diǎn)存儲(chǔ)的話,第一行就占1920 * 色彩模式的每個(gè)像素點(diǎn)的字節(jié) 這么多的字節(jié)夯缺,而通過(guò)某種算法蚤氏,他們都是一樣的,所以第一行只占 一個(gè)素點(diǎn)的字節(jié) 踊兜。那質(zhì)量壓縮就是把一連塊顏色相差不大的像素點(diǎn)變成同一種顏色竿滨,這樣就能減少了存儲(chǔ)空間,但是展示圖片時(shí)顏色就變了捏境。
當(dāng)然這只是我的猜測(cè)于游,但是質(zhì)量壓縮我敢保證100%對(duì)顏色進(jìn)行了某些操作。
4.總結(jié)
感覺(jué)講的內(nèi)容也挺多了垫言,一些操作算法贰剥,無(wú)損壓縮,封裝之類的操作我打算等整理好之后再發(fā)筷频。
其實(shí)看了這篇你至少可以知道簡(jiǎn)單的壓縮要怎么去做蚌成,壓縮并不難,難的是你要怎么去壓縮不僅能讓文件變小凛捏,而且還能保持圖像的清晰度担忧。我這也展示了濫用有損壓縮的后果,但是某種情況下我覺(jué)得是可以直接使用有損壓縮的葵袭,比如保存頭像涵妥,就可以直接用尺寸壓縮,不管你選的頭像圖片有多大坡锡,反正最后我只要你的縮略圖蓬网,而且由大變小從視覺(jué)上你也看不出什么。