Androidでビットマップのサイズを変更する最もメモリ効率の良い方法?


115

サーバーからデバイスに画像が送信される画像集約型のソーシャルアプリを作成しています。デバイスの画面解像度が小さい場合、デバイスのビットマップのサイズを変更して、目的の表示サイズに合わせる必要があります。

問題は、createScaledBitmapを使用すると、サムネイル画像の大群のサイズを変更した後、多くのメモリ不足エラーが発生することです。

Androidでビットマップのサイズを変更する最もメモリ効率の良い方法は何ですか?


7
サーバーが正しいサイズを送信できないため、顧客のRAMと帯域幅を節約できますか?
ジェームズ

2
これは、私がサーバーリソースを所有していて、計算コンポーネントが利用できる場合にのみ有効です。また、すべてのケースで、まだ表示されていないアスペクト比の画像の正確なサイズを予測できます。したがって、サードパーティのCDN(私と同じように)からリソースコンテンツを読み込んでいる場合は機能しません:(
Colt McAnlis

回答:


168

この回答は 、inSampleSizeを使用して縮小されたビットマップバージョンをロードする方法を説明する大きなビットマップの効率的なロードから要約されています。

特に、プリスケーリングビットマップは、さまざまな方法の詳細、それらを組み合わせる方法、および最もメモリ効率のよい方法を説明します。

Androidでビットマップのサイズを変更する主な方法は3つあり、メモリプロパティが異なります。

createScaledBitmap API

このAPIは既存のビットマップを取り込み、選択した正確な寸法で新しいビットマップを作成します。

プラス面では、探しているイメージサイズを正確に取得できます(外観に関係なく)。しかし、欠点は、このAPIが機能するために既存のビットマップを必要とすることです。つまり、新しい小さなバージョンを作成する前に、画像を読み込んでデコードし、ビットマップを作成する必要があります。これは、正確な寸法を取得するという点では理想的ですが、追加のメモリオーバーヘッドという点では恐ろしいものです。このように、これはメモリを意識する傾向があるほとんどのアプリ開発者にとって一種の取引ブレーカーです

inSampleSizeフラグ

BitmapFactory.OptionsinSampleSize一時的なビットマップにデコードする必要を回避するために、デコード中にイメージのサイズを変更するというプロパティがあります。ここで使用されるこの整数値は、1 / xの縮小サイズでイメージをロードします。たとえば、inSampleSize2 に設定するとサイズの半分の画像が返され、4に設定するとサイズの4分の1の画像が返されます。基本的に、画像サイズは常に元のサイズよりも2の累乗で小さくなります。

メモリの観点から見ると、使用inSampleSizeは非常に高速な操作です。実際には、イメージのX番目のピクセルのみを結果のビットマップにデコードします。inSampleSizeただし、2つの主な問題があります。

  • 正確な解像度は得られません。ビットマップのサイズを2の累乗で減らすだけです。

  • 最高品質のサイズ変更は生成されません。ほとんどのサイズ変更フィルターは、ピクセルのブロックを読み取り、それらを重み付けして問題のサイズ変更されたピクセルを生成することにより、見栄えの良い画像を生成します。inSampleSize数ピクセルごとに読み取るだけでこれをすべて回避します。結果は非常にパフォーマンスが高く、メモリは少なくなりますが、品質が低下します。

画像をいくつかのpow2サイズだけ縮小することを処理していて、フィルタリングが問題ではない場合、メモリ効率(またはパフォーマンス効率)よりも優れた方法を見つけることができませんinSampleSize

inScaled、inDensity、inTargetDensityフラグ

2の累乗に等しくない次元に画像を拡大縮小する必要がある場合はinScaledinDensityとのinTargetDensityフラグが必要になりますBitmapOptionsinScaledフラグが設定されている場合、システムはを値で除算してビットマップに適用するスケーリング値を導出inTargetDensityinDensityます。

mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
      mImageIDs, mBitmapOptions);

この方法を使用すると、画像のサイズが変更され、「サイズ変更フィルター」も適用されます。つまり、サイズ変更の手順で追加の計算が考慮されるため、最終結果はより良く見えます。ただし、注意が必要です。この追加のフィルターステップは余分な処理時間を要し、大きな画像をすばやく追加して、サイズ変更が遅くなり、フィルター自体に追加のメモリが割り当てられる可能性があります。

余分なフィルタリングのオーバーヘッドのため、通常、この手法を目的のサイズより大幅に大きい画像に適用することはお勧めできません。

魔法の組み合わせ

メモリとパフォーマンスの観点から、これらのオプションを組み合わせて最良の結果を得ることができます。(設定inSampleSizeinScaledinDensityおよびinTargetDensityフラグ)

inSampleSize最初に画像に適用され、ターゲットサイズよりも大きい2のべき乗の大きさになります。次に、inDensityinTargetDensityを使用して結果を希望する正確なサイズにスケーリングし、フィルター操作を適用して画像をクリーンアップします。

これらのinSampleSizeステップを組み合わせると、結果として密度ベースのステップでサイズ変更フィルターを適用するために必要なピクセル数が減るため、操作がはるかに高速になります。

mBitmapOptions.inScaled = true;
mBitmapOptions.inSampleSize = 4;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth * mBitmapOptions.inSampleSize;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);

あなたが特定の寸法に画像をフィットする必要がしている場合、いくつかのよりよいフィルタリング、そしてこの技術は、正しいサイズを得るために最善の橋ですが、高速で、低メモリフットプリントの操作で行います。

画像の寸法を取得する

画像全体をデコードせずに画像サイズを取得するビットマップのサイズを変更するには、入力サイズを知る必要があります。inJustDecodeBoundsフラグを使用すると、実際にピクセルデータをデコードする必要がない画像のサイズを取得できます。

// Decode just the boundaries
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;


//now go resize the image to the size you want

このフラグを使用して最初にサイズをデコードし、次にターゲット解像度にスケーリングするための適切な値を計算できます。


1
あなたが私たちにdstWidthが何であるかを伝えることができればそれは素晴らしいことでしたか
k0sh 2015

@ k0sh dstWIdthは、IEへの移動先のImageViewの幅、destination widthまたは略してdstWidthです
tyczj

@tyczj答えてくれてありがとう、私はそれが何であるか知っています、しかしそれを知らない人もいます、そして実際にこの質問に答えたコルト以来、おそらく彼はそれを説明して人々が混乱しないようにすることができます。
k0sh 2015

素敵な投稿...以前はinScaled、inDensity、inTargetDensityフラグについて知らなかった...
maveroid

私はAndroidパフォーマンスパターンシリーズを見てきましたが、たくさんのことを学びました!
2015

13

この答えと同じように素晴らしく(かつ正確)、それも非常に複雑です。車輪を再発明するのではなく、GlidePicassoUILIon、またはこの複雑でエラーが発生しやすいロジックを実装する他の多くのライブラリを検討してください。

コルト自身も、プレスケーリングビットマップパフォーマンスパターンビデオでグライドとピカソをご覧になることをお勧めしています。

ライブラリを使用することで、Coltの回答で述べられているあらゆる効率を得ることができますが、Androidのすべてのバージョンで一貫して機能する非常にシンプルなAPIを使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.