2010-04-14 33 views
59

我使用BitmapFactory.decodeFile解碼來自SD卡的位圖。有時位圖比應用程序需要的或堆允許的大,因此我使用BitmapFactory.Options.inSampleSize來請求二次抽樣(較小)的位圖。使用合適的尺寸解碼Android中的位圖

問題是,該平臺沒有強制執行inSampleSize的確切值,並且有時我最終得到的位圖太小或者對於可用內存來說仍然太大。

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize

注:解碼器將努力實現這一 要求,但由此產生的位圖 可能有 正是已要求不同的尺寸。 另外,2的冪次常常是 更快/更容易讓解碼器以 的榮譽。

我應該如何解碼SD卡上的位圖以獲得我需要的確切大小的位圖,同時儘可能少地使用內存來解碼它?

編輯:

電流源代碼:

BitmapFactory.Options bounds = new BitmapFactory.Options(); 
this.bounds.inJustDecodeBounds = true; 
BitmapFactory.decodeFile(filePath, bounds); 
if (bounds.outWidth == -1) { // TODO: Error } 
int width = bounds.outWidth; 
int height = bounds.outHeight; 
boolean withinBounds = width <= maxWidth && height <= maxHeight; 
if (!withinBounds) { 
    int newWidth = calculateNewWidth(int width, int height); 
    float sampleSizeF = (float) width/(float) newWidth; 
    int sampleSize = Math.round(sampleSizeF); 
    BitmapFactory.Options resample = new BitmapFactory.Options(); 
    resample.inSampleSize = sampleSize; 
    bitmap = BitmapFactory.decodeFile(filePath, resample); 
} 
+4

你只使用2的冪數?我完全可以理解爲什麼像這樣會喜歡這樣的事情,但我不確定自己曾經見過這樣的API參考 - 「我們會盡力給你你所要求的,但它可能完全是其他的東西」。 – MusiGenesis 2010-04-14 23:35:07

+0

您也可能想要設置位圖的outWidth和outHeight屬性,以便控制大小。 – MusiGenesis 2010-04-14 23:37:58

+0

我沒有使用2的權力。兩個權力太有限制,所產生的位圖太小或太大。也許我應該使用另一個API? – hpique 2010-04-14 23:40:54

回答

44

您現在處於正確的軌道上,但是您一次試圖做兩件事:讀取文件並將其縮放到合適的大小。

第一步是將文件讀取到略大於所需的位圖,使用BitmapFactory.Options.inSampleSize以確保在所有需要的位置都是較小的縮略圖或屏幕時,不會消耗讀取較大位圖的過多內存分辨率圖像。

第二步是調用Bitmap.createScaledBitmap()創建一個新的位圖,以達到您需要的確切分辨率。

確保在臨時位圖後清理以回收其內存。 (要麼讓變量超出範圍並讓GC處理它,要麼在其上調用.recycle()如果您正在加載大量圖像並且在內存上運行緊張。)

+3

小心,我們首先需要找到高寬比,否則最終的圖像會被拉長。 – usman 2014-03-07 10:11:56

15

您可能需要使用inJustDecodeBounds。將其設置爲TRUE並按原樣加載文件。

圖像不會被加載到內存中。但outheightoutwidth的屬性BitmapFactory.Options將包含指定圖像的實際大小參數。計算你想要多少次樣本。即1/2或1/4或1/8等,並相應地將2/4/8等分配給樣本尺寸中的

現在設置inJustDecodeBoundsFALSE和呼叫BitmapFactory.decodeFile()如上計算加載的確切大小的圖像。

+0

這就是我現在正在做的。問題是找不到inSampleSize的正確值。問題是inSampleSize並不總是被平臺所尊敬,所以我可能會得到比所需要的更小或更大的位圖。 – hpique 2010-04-17 17:09:48

+0

inSampleSize應該返回一個子採樣圖像。即確切的尺寸將取決於正在加載的原始圖像U.它相對於原始圖像將是半/四分之一/八分之一等。 U可以加載最接近你需要的圖像子樣本,並在顯示時適當地拉伸它。 你在旁邊做什麼來顯示圖像?也許我可以幫助那裏。嗯...... – TheCodeArtist 2010-04-18 05:40:45

+0

這正是我現在正在做的事情,並且因爲inSampleSize並不總是令人滿意,所以結果並不總是最優的。如果可能,我正在尋找替代方案。 – hpique 2010-04-18 15:09:23

1

由於API已經聲明樣本大小可能會或可能不會被兌現。 所以我想你使用BitmapFactory時運氣不好。

但出路將使用您自己的位圖閱讀器。但我建議你堅持使用BitmapFactory,因爲它的測試和標準化程度相當高。

我會盡量不要太擔心一點額外的內存消耗,但試着找出爲什麼它不尊重值。可悲的是我不知道這個:(

+0

也許有另一種使用BitmapFactory的替代方法,但方式不同。是否有可能加載塊的位圖,重新調整他們的內存,然後加入他們?當然會更慢,但它會提供確切的結果。 – hpique 2010-04-18 15:24:29

+0

你想優化什麼,空間或分辨率? – the100rabh 2010-04-18 18:34:13

+0

分辨率和堆內存是主要問題。獲得所需的位圖應儘可能少地使用內存(當然,除了位圖)。 – hpique 2010-04-18 22:47:28

0

因爲我使用inSampleSize我再也不面對內存問題,所以inSampleSize對我來說工作非常穩定,我建議你仔細檢查問題。儘管如此,它應該減少,不應該有更多的「內存不足」,我也建議在這裏發佈你的代碼片段,也許這裏有什麼問題。

0

正如the100rabh所說,如果你使用BitmapFactory你沒有太多的選擇。但是,位圖解碼非常簡單,根據要使用的縮放算法,通常非常簡單,可以在不需要將大部分原始位圖讀入內存的情況下進行即時縮放(通常,您可以只需要將原始位圖的1-2行所需的內存加倍)。

6

首先,您需要對圖像到最接近的採樣值,以便位圖變得高效,這是您完美描述的。一旦完成,您可以將其擴展至適合您的屏幕。

// This function taking care of sampling 
backImage =decodeSampledBitmapFromResource(getResources(),R.drawable.back, width, height); 

// This will scale it to your screen width and height. you need to pass screen width and height. 
backImage = Bitmap.createScaledBitmap(backImage, width, height, false); 
1
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE)) 

您可以直接設置根據您的需要選擇THUMBSIZE,但我不知道對下級實施細則,使輸出圖像質量和性能,但我通常喜歡它,當我需要顯示縮略圖。

+0

這可以釋放你的位圖ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath),THUMBSIZE,THUMBSIZE),ThumbnailUtils.OPTIONS_RECYCLE_INPUT) – Arst 2016-08-07 12:23:33

1

對於那些正在尋找資源的確切大小圖像。

對於精確寬度傳遞reqHight = 0和對於確切高度通過reqWidth = 0

public static Bitmap decodeBitmapResource(Context context, int resId, int reqWidth, int reqHeight) 
{ 
    final BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 
    BitmapFactory.decodeResource(context.getResources(), resId, options); 
    float scale = 1; 
    if (reqWidth != 0 && reqHeight == 0) 
    { 
     options.inSampleSize = (int) Math.ceil(options.outWidth/(float) reqWidth); 
     scale = (float) options.outWidth/(float) reqWidth; 
    } 
    else if (reqHeight != 0 && reqWidth == 0) 
    { 
     options.inSampleSize = (int) Math.ceil(options.outHeight/(float) reqHeight); 
     scale = (float) options.outHeight/(float) reqHeight; 
    } 
    else if (reqHeight != 0 && reqWidth != 0) 
    { 
     float x = (float) options.outWidth/(float) reqWidth; 
     float y = (float) options.outHeight/(float) reqHeight; 
     scale = x > y ? x : y; 
    } 
    reqWidth = (int) ((float) options.outWidth/scale); 
    reqHeight = (int) ((float) options.outHeight/scale); 
    options.inJustDecodeBounds = false; 
    Bitmap tmp = BitmapFactory.decodeResource(context.getResources(), resId, options); 
    Bitmap out = Bitmap.createScaledBitmap(tmp, reqWidth, reqHeight, false); 
    tmp.recycle(); 
    tmp = null; 
    return out; 
} 
0

當inScaled標誌被設置(默認爲TRUE),如果強度氣體和inTargetDensity不爲0,則位圖將在加載時縮放以匹配inTargetDensity,而不是依靠圖形系統每次將其繪製到Canvas時縮放它。所以只需將inScaled設置爲false即可。

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inScaled = false; 
0

我能夠通過將解碼圖像文件重新縮放到其位圖大小的70%來近似得到「正確的大小」。

Bitmap myBitmap = BitmapFactory.decodeFile(path); 
if(myBitmap != null) 
{ 
    //reduce to 70% size; bitmaps produce larger than actual image size 
    Bitmap rescaledMyBitmap = Bitmap.createScaledBitmap(
                myBitmap, 
                myBitmap.getWidth()/10*7, 
                myBitmap.getHeight()/10*7, 
                false); 

    image.setImageBitmap(rescaledMyBitmap); 
}     

我希望這可以幫助你以某種方式!和平!