옵션이 설정되면 null을 반환하는 BitmapFactory.decodeStream


90

나는 문제가있다 BitmapFactory.decodeStream(inputStream) 있습니다. 옵션없이 사용하면 이미지가 반환됩니다. 그러나 옵션과 함께 사용하면 .decodeStream(inputStream, null, options)Bitmap을 반환하지 않습니다.

내가하려는 것은 메모리를 절약하기 위해 실제로로드하기 전에 비트 맵을 다운 샘플링하는 것입니다. 나는 좋은 가이드를 읽었지만 .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가 허용 가능한 값임을 표시하고 있습니까?
Steve Haley

예, 매번 허용되는 값을 반환합니다.
Robert Foss

디버그 중이므로 문을 제거했습니다.
Robert Foss

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
이미지를 두 번 다운로드해야 함을 의미합니까? 한 번은 크기를 얻고 한 번은 픽셀 데이터를 얻습니까?
user123321

1
다른 사용자가 해당에 대한 명확한 아이디어를 얻을 수 있도록 당신은 아마이 특정 행동을 설명한다 @Robert
무하마드 바바

1
궁금 해서요 왜 것 같은 inputstream의 자신의 간단한 설명에 감사하지 작업
kabuto178

1
다시 만들 필요가 없으며 재설정 만하면 목적이 해결됩니다. 내 대답을 참조하십시오
Shashank 망치고

5
Android의 Bitmap 클래스가별로라고 말해야합니다. 사용하기에 너무 혼란스럽고 답답합니다.
네온 Warge

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/…
안드로이드 개발자

1
그것은 작동했기 때문에 그것을 upvoted했지만 is.available () doc은 이것이 신뢰할 수 없기 때문에 크기를 계산하지 않고 스트림이 비어 있는지 확인하는 데에만 사용해야한다는 경고와 함께 제공됩니다.
Abhishek Chauhan

1
다운 투표를하지만, 문제의 inputstream의 연결은하지 않습니다 작업 있습니다 .... HTTP 연결 및 리셋 ()입니다
조니 우

3

문제는 "calculate-scale-factor"로직에 있다고 생각합니다. 나머지 코드가 나에게 맞아 보이기 때문입니다 (물론 inputstream이 null이 아니라고 가정).

이 루틴의 모든 크기 계산 논리를 하나의 메서드 (CalculateScaleFactor () 등으로 호출)로 빼내고 먼저 해당 메서드를 독립적으로 테스트 할 수 있다면 더 좋을 것입니다.

다음과 같은 것 :

// 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가 null을 반환했습니다'를 읽습니다. try / catch 블록 내에서 코드를 실행하면 아무것도 던지지 않으므로 도움이되지 않습니다. 그러나 BitmapFactory.decodeStream은 img를 만들 수 없으면 null을 반환하며 sampleSize를 사용하려고 할 때 사용할 수 없습니다.
Robert Foss

이건 이상해. 리소스에 번들로 포함 된 일부 비트 맵의 ​​크기를 조정할 수 있습니까? 리소스 파일을 열고 디코딩을 시도하는 것과 같습니다. 그렇게 할 수 있다면 디코딩이 실패하는 원격 스트림에 문제가있을 수 있습니다.
Samuh 2010 년

BitmapFactory.decodeResource (this.getResources (), R.drawable.icon, options) == null) 리샘플링으로 잘 작동합니다. options.inJustDecodeBounds = true 인 첫 번째 BitmapFactory.decodeStream이 작동하고 옵션을 제대로 반환합니다. 그러나 options.inJustDecodeBounds = false 인 다음 BitmapFactory.decodeStream은 매번 실패합니다.
Robert Foss

나는 이것이 나를 넘어서는 것이 두렵다. 나는 비슷한 코드를 사용하고 있고 그것은 나를 위해 잘 작동하기 때문에 여기서 무엇이 잘못 될 수 있는지 알고 싶을 것이다.
Samuh 2010 년

4
확인. 나는 그것을 해결했다. 문제는 http- 연결에 있습니다. HttpUrlConnection에서 제공하는 입력 스트림에서 한 번 읽은 경우 다시 읽을 수 없으며 두 번째 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.