web-dev-qa-db-ja.com

Android適切なサイズのビットマップのデコード

BitmapFactory.decodeFileを使用して、SDカードからビットマップをデコードします。ビットマップがアプリケーションが必要とするサイズまたはヒープが許可するサイズよりも大きい場合があるため、BitmapFactory.Options.inSampleSizeを使用してサブサンプリングされた(より小さい)ビットマップを要求します。

問題は、プラットフォームが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);
}
60
hpique

あなたは正しい軌道に乗っていますが、一度に2つのことをしようとしています。ファイルを読み込み、適切なサイズにスケーリングします。

最初のステップは、BitmapFactory.Options.inSampleSizeを使用して、必要なサイズよりも少し大きいビットマップにファイルを読み込むことです。これにより、必要なものが小さいサムネイルまたは画面解像度の画像である場合に大きなビットマップの読み込みで過度のメモリを消費しないようにします。

2番目のステップは、Bitmap.createScaledBitmap()を呼び出して、必要な正確な解像度で新しいビットマップを作成します。

一時ビットマップの後にクリーンアップして、メモリを再利用してください。 (変数をスコープから外してGCに処理させるか、大量の画像を読み込んでいてメモリが不足している場合は.recycle()を呼び出します。)

45
sirdodger

inJustDecodeBounds を使用することもできます。 TRUEに設定し、ファイルをそのままロードします。

画像はメモリにロードされません。ただし、BitmapFactory.Optionsoutheightおよびoutwidthプロパティには、実際の指定された画像のサイズパラメータ。サブサンプリングする量を計算します。つまり、1/2または1/4または1/8などで、inSampleSizeに応じて2/4/8などを割り当てます。

inJustDecodeBoundsFALSEに設定し、BitmapFactory.decodeFile()を呼び出してロードします上記で計算された正確なサイズの画像。

15
TheCodeArtist

最初に、ビットマップがメモリ効率を良くするために、最も近いサンプリング値に画像をサンプリングする必要があります。完了したら、画面に合わせて拡大できます。

// 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); 
7
Ravi Bhojani
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))

要件に応じてTHUMBSIZEを直接設定できますが、低レベルの実装の詳細、出力画像の品質とそのパフォーマンスについてはわかりませんが、サムネイルを表示する必要がある場合は通常それを好みます。

1
Ankur Gautam

APIには、サンプルサイズが尊重される場合とされない場合があることが既に記載されているためです。したがって、BitmapFactoryを使用しているときに不運だと思います。

しかし、解決策は、独自のビットマップリーダーを使用することです。ただし、BitmapFactoryは、十分にテストおよび標準化されているため、そのまま使用することをお勧めします。

私は試してみて、余分なメモリの消費を少し心配することはありませんが、値を尊重しない理由を見つけてみてください。悲しいことに私はそれについて考えていません:(

1
the100rabh

リソースから正確なサイズの画像を探している人向け。

正確な幅の場合は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;
}
1
Amit Padekar

InScaledフラグが設定されている場合(デフォルトではTRUE)、inDensityとinTargetDensityが0でない場合、ビットマップは、キャンバスに描画されるたびにスケーリングするグラフィックシステムに依存するのではなく、ロード時にinTargetDensityに一致するようにスケーリングされます。したがって、inScaledをfalseに設定するだけです。

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

The100rabhが言ったように、BitmapFactoryを使用する場合、あまり選択肢はありません。ただし、ビットマップのデコードは非常に簡単で、使用するスケーリングアルゴリズムによっては、元のビットマップの大部分をメモリに読み込むことなく、オンザフライでスケーリングするのが通常非常に簡単です(一般に、元のビットマップの1〜2行に必要なメモリの2倍しか必要ありません)。

0
Tal Pressman

デコードされたイメージファイルをビットマップサイズの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);
}                

これが何らかの形で役立つことを願っています!平和!

0
masarapmabuhay

InSampleSizeを使用しているため、メモリ不足の問題に直面することはありません。したがって、inSampleSizeは非常に安定して動作します。問題を再確認することをお勧めします。サイズは、要求したものとまったく同じではない場合があります。しかし、とにかく減らす必要があり、「メモリ不足」がなくなるはずです。また、ここにコードスニペットを投稿することをお勧めします。おそらく何か問題がある可能性があります。

0
Fedor