オプションが設定されている場合にBitmapFactory.decodeStreamがnullを返す


90

で問題が発生していBitmapFactory.decodeStream(inputStream)ます。オプションなしで使用すると、画像が返されます。しかし、それをオプションと一緒に使用すると、.decodeStream(inputStream, null, options)ビットマップが返されません。

私がやろうとしていることは、ビットマップを実際にロードしてメモリを節約する前に、ビットマップをダウンサンプリングすることです。私はいくつかの良いガイドを読みましたが、どれもを使用していません.decodeStream

ワークスジャストファイン

URL url = new URL(sUrl);
HttpURLConnection connection  = (HttpURLConnection) url.openConnection();

InputStream is = connection.getInputStream();
Bitmap img = BitmapFactory.decodeStream(is, null, options);

動かない

InputStream is = connection.getInputStream();
Bitmap img = BitmapFactory.decodeStream(is, null, options);

InputStream is = connection.getInputStream();

Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

BitmapFactory.decodeStream(is, null, options);

Boolean scaleByHeight = Math.abs(options.outHeight - TARGET_HEIGHT) >= Math.abs(options.outWidth - TARGET_WIDTH);

if (options.outHeight * options.outWidth * 2 >= 200*100*2){
    // Load, scaling to smallest power of 2 that'll get it <= desired dimensions
    double sampleSize = scaleByHeight
    ? options.outHeight / TARGET_HEIGHT
    : options.outWidth / TARGET_WIDTH;
    options.inSampleSize =
        (int)Math.pow(2d, Math.floor(
        Math.log(sampleSize)/Math.log(2d)));
}

// Do the actual decoding
options.inJustDecodeBounds = false;
Bitmap img = BitmapFactory.decodeStream(is, null, options);

1
System.out.println( "Samplesize:" ...)ステートメントからの出力は何ですか?options.inSampleSizeが許容値であることを示していますか?
スティーブヘイリー

はい、毎回許容値を返します。
Robert Foss

デバッグされているため、ステートメントを削除しました。
Robert Foss

1
解決策を投稿していただきありがとうございます。やるべきことがもう1つあります。この質問は、回答を「承認済み」としてマークしていないため、「未解決の質問」リストには引き続き表示されます。答えの横にあるチェックマークアイコンをクリックして、これを行うことができます。解決策を見つけるのに役立つと感じた場合はSamuhの回答を受け入れるか、独自の回答を投稿して受け入れることができます。(通常は解答を解答に入れますが、質問を編集してそれをすでに組み込んでいるので、質問を参照するだけで済みます。)
Steve Haley

新しいユーザーがコミュニティに統合するのを手伝ってくれてありがとう:)
Robert Foss

回答:


114

問題は、いったんHttpUrlConnectionからのInputStreamを使用して画像メタデータをフェッチすると、同じInputStreamを巻き戻して再び使用することができないことでした。

したがって、画像の実際のサンプリング用に新しいInputStreamを作成する必要があります。

  Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;

  BitmapFactory.decodeStream(is, null, options);

  Boolean scaleByHeight = Math.abs(options.outHeight - TARGET_HEIGHT) >= Math.abs(options.outWidth - TARGET_WIDTH);

  if(options.outHeight * options.outWidth * 2 >= 200*200*2){
         // Load, scaling to smallest power of 2 that'll get it <= desired dimensions
        double sampleSize = scaleByHeight
              ? options.outHeight / TARGET_HEIGHT
              : options.outWidth / TARGET_WIDTH;
        options.inSampleSize = 
              (int)Math.pow(2d, Math.floor(
              Math.log(sampleSize)/Math.log(2d)));
     }

        // Do the actual decoding
        options.inJustDecodeBounds = false;

        is.close();
        is = getHTTPConnectionInputStream(sUrl);
        Bitmap img = BitmapFactory.decodeStream(is, null, options);
        is.close();

17
これは、画像を2回ダウンロードする必要があることを意味しますか?サイズを取得するために、ピクセルデータを取得するために一度ですか?
user123321 2012

1
@Robertは、おそらくこの特定の動作を説明して、他のユーザーがそれについて明確なアイデアを得られるようにする必要があります
Muhammad Babar

1
簡単な説明に感謝して、なぜ私は同じ入力ストリームでそれがうまくいかないのか疑問に思いました
kabuto178

1
再作成する必要はありません。リセットするだけで目的は解決します。私の答えを見る
Shashank Tomar 14

5
Androidのビットマップクラスは最低だと言わざるを得ません。使用するのがとても面倒でイライラします。
ネオンワージ2016

30

InputStreamをBufferedInputStreamでラップしてみてください。

InputStream is = new BufferedInputStream(conn.getInputStream());
is.mark(is.available());
// Do the bound decoding
// inJustDecodeBounds =true
is.reset();  
// Do the actual decoding

2
それはいつもあなたのために働きましたか?何らかの理由で、このメソッドを使用した非常に特殊なケースではnullが発生します。私はそれについてここに投稿しました: stackoverflow.com/questions/17774442/…–
Android開発者

1
それはうまくいったので私はそれを賛成しましたが、is.available()ドキュメントには、ストリームが空であるかどうかを確認するためだけに使用するべきであり、これは信頼できないのでサイズを計算するためではないという警告が付属しています。
Abhishek Chauhan 14年

1
反対投票されましたが、問題の入力ストリーム接続はHTTP接続であり、reset()は機能しません...
Johnny Wu

3

コードの残りの部分は私には正しいように見えるので(もちろん、inputstreamがnullではないと想定しているため)、「calculate-scale-factor」ロジックに問題があると思います。

このルーチンからすべてのサイズ計算ロジックをメソッド(factoryScaleFactor()などと呼びます)に分解し、最初にそのメソッドを個別にテストできるとよいでしょう。

何かのようなもの:

// Get the stream 
InputStream is = mUrl.openStream();

// get the Image bounds
BitmapFactory.Options options=new BitmapFactory.Options(); 
options.inJustDecodeBounds = true;

bitmap = BitmapFactory.decodeStream(is,null,options);

//get actual width x height of the image and calculate the scale factor
options.inSampleSize = getScaleFactor(options.outWidth,options.outHeight,
                view.getWidth(),view.getHeight());

options.inJustDecodeBounds = false;
bitmap=BitmapFactory.decodeStream(mUrl.openStream(),null,options);

そしてgetScaleFactor(...)を個別にテストします。

まだ完了していない場合は、コード全体をtry..catch {}ブロックで囲むことも役立ちます。


答えてくれてありがとう!'options.inSampleSize = 2'のような最終的なint値を設定してみました。しかし、同じ問題が発生します。Logcatは、デコードしようとしたすべての画像について、「SkImageDecoder :: Factory returned null」を読み取ります。try / catchブロック内でコードを実行しても、何もスローされないので役に立ちません。ただし、BitmapFactory.decodeStreamは、imgを作成できない場合はnullを返します。これは、sampleSizeを使用しようとするとできません。
Robert Foss

これは奇妙です。リソースにバンドルされているビットマップのサイズを変更してみてください。リソースファイルを開いて、デコードしてみてください。それができる場合は、リモートストリームに何らかの問題があり、デコードが失敗している可能性があります。
Samuh

BitmapFactory.decodeResource(this.getResources()、R.drawable.icon、options)== null)は、再サンプリングで正常に動作します。options.inJustDecodeBounds = trueを指定した最初のBitmapFactory.decodeStreamは機能し、オプションを正常に返します。ただし、options.inJustDecodeBounds = falseを指定した次のBitmapFactory.decodeStreamは毎回失敗します。
Robert Foss

私はこれが私を超えていると思います...同様のコードを使用していて、それが私にとってはうまく機能するので、ここで何がうまくいかないのか知りたいです。
Samuh

4
OK。解決しました。問題はhttp接続にあります。HttpUrlConnectionによって提供された入力ストリームから一度読み取ると、再度読み取ることはできず、2番目のdecodeStream()を実行するために再接続する必要があります。
Robert Foss

2

InputStreamをバイト配列に変換し、decodeByteArray()を使用できます。例えば、

public static Bitmap decodeSampledBitmapFromStream(InputStream inputStream, int reqWidth, int reqHeight) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try {
        int n;
        byte[] buffer = new byte[1024];
        while ((n = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, n);
        }
        return decodeSampledBitmapFromByteArray(outputStream.toByteArray(), reqWidth, reqHeight);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

public static Bitmap decodeSampledBitmapFromByteArray(byte[] data, int reqWidth, int reqHeight) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(data, 0, data.length, options);
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}

private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int
        reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
        int halfWidth = width / 2;
        int halfHeight = height / 2;
        while (halfWidth / inSampleSize >= reqWidth && halfHeight / inSampleSize >= reqHeight) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.