サーバーからデバイスに画像が送信される画像集約型のソーシャルアプリを作成しています。デバイスの画面解像度が小さい場合、デバイスのビットマップのサイズを変更して、目的の表示サイズに合わせる必要があります。
問題は、createScaledBitmapを使用すると、サムネイル画像の大群のサイズを変更した後、多くのメモリ不足エラーが発生することです。
Androidでビットマップのサイズを変更する最もメモリ効率の良い方法は何ですか?
サーバーからデバイスに画像が送信される画像集約型のソーシャルアプリを作成しています。デバイスの画面解像度が小さい場合、デバイスのビットマップのサイズを変更して、目的の表示サイズに合わせる必要があります。
問題は、createScaledBitmapを使用すると、サムネイル画像の大群のサイズを変更した後、多くのメモリ不足エラーが発生することです。
Androidでビットマップのサイズを変更する最もメモリ効率の良い方法は何ですか?
回答:
この回答は 、inSampleSizeを使用して縮小されたビットマップバージョンをロードする方法を説明する大きなビットマップの効率的なロードから要約されています。
特に、プリスケーリングビットマップは、さまざまな方法の詳細、それらを組み合わせる方法、および最もメモリ効率のよい方法を説明します。
Androidでビットマップのサイズを変更する主な方法は3つあり、メモリプロパティが異なります。
このAPIは既存のビットマップを取り込み、選択した正確な寸法で新しいビットマップを作成します。
プラス面では、探しているイメージサイズを正確に取得できます(外観に関係なく)。しかし、欠点は、このAPIが機能するために既存のビットマップを必要とすることです。つまり、新しい小さなバージョンを作成する前に、画像を読み込んでデコードし、ビットマップを作成する必要があります。これは、正確な寸法を取得するという点では理想的ですが、追加のメモリオーバーヘッドという点では恐ろしいものです。このように、これはメモリを意識する傾向があるほとんどのアプリ開発者にとって一種の取引ブレーカーです
BitmapFactory.Options
inSampleSize
一時的なビットマップにデコードする必要を回避するために、デコード中にイメージのサイズを変更するというプロパティがあります。ここで使用されるこの整数値は、1 / xの縮小サイズでイメージをロードします。たとえば、inSampleSize
2 に設定するとサイズの半分の画像が返され、4に設定するとサイズの4分の1の画像が返されます。基本的に、画像サイズは常に元のサイズよりも2の累乗で小さくなります。
メモリの観点から見ると、使用inSampleSize
は非常に高速な操作です。実際には、イメージのX番目のピクセルのみを結果のビットマップにデコードします。inSampleSize
ただし、2つの主な問題があります。
正確な解像度は得られません。ビットマップのサイズを2の累乗で減らすだけです。
最高品質のサイズ変更は生成されません。ほとんどのサイズ変更フィルターは、ピクセルのブロックを読み取り、それらを重み付けして問題のサイズ変更されたピクセルを生成することにより、見栄えの良い画像を生成します。inSampleSize
数ピクセルごとに読み取るだけでこれをすべて回避します。結果は非常にパフォーマンスが高く、メモリは少なくなりますが、品質が低下します。
画像をいくつかのpow2サイズだけ縮小することを処理していて、フィルタリングが問題ではない場合、メモリ効率(またはパフォーマンス効率)よりも優れた方法を見つけることができませんinSampleSize
。
inScaled、inDensity、inTargetDensityフラグ
2の累乗に等しくない次元に画像を拡大縮小する必要がある場合はinScaled
、inDensity
とのinTargetDensity
フラグが必要になりますBitmapOptions
。inScaled
フラグが設定されている場合、システムはを値で除算してビットマップに適用するスケーリング値を導出inTargetDensity
しinDensity
ます。
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);
この方法を使用すると、画像のサイズが変更され、「サイズ変更フィルター」も適用されます。つまり、サイズ変更の手順で追加の計算が考慮されるため、最終結果はより良く見えます。ただし、注意が必要です。この追加のフィルターステップは余分な処理時間を要し、大きな画像をすばやく追加して、サイズ変更が遅くなり、フィルター自体に追加のメモリが割り当てられる可能性があります。
余分なフィルタリングのオーバーヘッドのため、通常、この手法を目的のサイズより大幅に大きい画像に適用することはお勧めできません。
魔法の組み合わせ
メモリとパフォーマンスの観点から、これらのオプションを組み合わせて最良の結果を得ることができます。(設定inSampleSize
、inScaled
、inDensity
およびinTargetDensity
フラグ)
inSampleSize
最初に画像に適用され、ターゲットサイズよりも大きい2のべき乗の大きさになります。次に、inDensity
&inTargetDensity
を使用して結果を希望する正確なサイズにスケーリングし、フィルター操作を適用して画像をクリーンアップします。
これらの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
このフラグを使用して最初にサイズをデコードし、次にターゲット解像度にスケーリングするための適切な値を計算できます。
destination width
または略してdstWidthです