안드로이드에서 비트 맵의 ​​크기를 조정하는 가장 메모리 효율적인 방법은 무엇입니까?


115

이미지가 서버에서 장치로 전송되는 이미지 집약적 인 소셜 앱을 구축하고 있습니다. 장치의 화면 해상도가 더 작 으면 원하는 디스플레이 크기와 일치하도록 장치에서 비트 맵의 ​​크기를 조정해야합니다.

문제는 createScaledBitmap 을 사용 하면 썸네일 이미지의 크기를 조정 한 후 많은 메모리 부족 오류가 발생한다는 것입니다.

Android에서 비트 맵 크기를 조정하는 가장 메모리 효율적인 방법은 무엇입니까?


7
서버가 올바른 크기를 보내지 않아 고객의 RAM과 대역폭을 절약 할 수 있습니까?
James

2
서버 리소스를 소유하고 컴퓨팅 구성 요소를 사용할 수있는 경우에만 유효하며 모든 경우에 아직 보지 못한 종횡비에 대한 정확한 이미지 크기를 예측할 수 있습니다. 따라서 타사 CDN에서 리소스 콘텐츠를로드하는 경우 (예 : 저처럼) 작동하지 않습니다. (
Colt McAnlis 2015 년

답변:


168

이 답변은 축소 된 비트 맵 버전을로드하기 위해 inSampleSize를 사용하는 방법을 설명하는 Load large bitmaps Efficiently 에서 요약됩니다 .

특히 사전 크기 조정 비트 맵 은 다양한 방법의 세부 사항, 이들을 결합하는 방법 및 가장 메모리 효율적인 방법을 설명합니다.

메모리 속성이 다른 Android에서 비트 맵의 ​​크기를 조정하는 세 가지 주요 방법이 있습니다.

createScaledBitmap API

이 API는 기존 비트 맵을 가져와 선택한 정확한 치수로 새 비트 맵을 만듭니다.

플러스 측면에서는 (어떻게 보이는지에 관계없이) 찾고있는 이미지 크기를 정확하게 얻을 수 있습니다. 그러나 단점 은이 API가 작동하려면 기존 비트 맵 이 필요하다는 것 입니다. 더 작은 새 버전을 만들려면 이미지를로드하고 디코딩하고 비트 맵을 만들어야합니다. 이것은 정확한 치수를 얻는 측면에서 이상적이지만 추가 메모리 오버 헤드 측면에서 끔찍합니다. 따라서 이것은 메모리를 의식하는 경향이있는 대부분의 앱 개발자에게 일종의 거래 차단기입니다.

inSampleSize 플래그

BitmapFactory.OptionsinSampleSize임시 비트 맵으로 디코딩 할 필요가 없도록 디코딩하는 동안 이미지 크기를 조정 하는 속성 이 있습니다. 여기에 사용 된이 정수 값은 1 / x 축소 된 크기로 이미지를로드합니다. 예를 들어 inSampleSize2로 설정 하면 크기의 절반 인 이미지가 반환되고 4로 설정하면 크기의 1/4 인 이미지가 반환됩니다. 기본적으로 이미지 크기는 항상 소스 크기보다 약간 더 작습니다.

메모리 관점에서 보면 사용 inSampleSize은 정말 빠른 작업입니다. 사실상, 이미지의 모든 X 번째 픽셀 만 결과 비트 맵으로 디코딩합니다. inSampleSize하지만 두 가지 주요 문제가 있습니다 .

  • 정확한 해상도를 제공하지 않습니다 . 비트 맵의 ​​크기를 2의 제곱만큼만 줄입니다.

  • 최상의 품질로 크기를 조정하지 못합니다 . 대부분의 크기 조정 필터는 픽셀 블록을 읽은 다음 가중치를 부여하여 문제의 크기가 조정 된 픽셀을 생성함으로써보기 좋은 이미지를 생성합니다. inSampleSize몇 픽셀마다 읽기만하면이 모든 것을 피할 수 있습니다. 결과는 상당히 성능이 좋고 메모리가 적지 만 품질이 떨어집니다.

이미지를 pow2 크기로 축소하는 작업 만 처리하고 필터링이 문제가되지 않는 경우 .NET보다 메모리 효율적인 (또는 성능 효율적인) 방법을 찾을 수 없습니다 inSampleSize.

inScaled, inDensity, inTargetDensity 플래그

2의 거듭 제곱과 같지 않은 차원으로 이미지의 크기를 조정해야하는 경우 inScaled, inDensityinTargetDensity플래그가 필요합니다 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, inDensityinTargetDensity플래그)

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

이 플래그를 사용하여 먼저 크기를 디코딩 한 다음 대상 해상도에 맞게 조정하기위한 적절한 값을 계산할 수 있습니다.


1
dstWidth가 무엇인지 알려 주시면 정말 좋았습니다.
k0sh

@ k0sh dstWIdth는 ImageView의 너비입니다. 즉 destination width, 짧게 dstWidth로 이동합니다
tyczj

@tyczj 답변 주셔서 감사합니다, 나는 그것이 무엇인지 알고 있지만 일부는 그것을 알지 못할 수도 있고 실제로이 질문에 대답 한 Colt 이후 아마도 사람들이 혼동하지 않도록 설명 할 수 있습니다.
k0sh

멋진 게시물 ... 이전에는 inScaled, inDensity, inTargetDensity 플래그에 대해 몰랐습니다 ...
maveroid

안드로이드 퍼포먼스 패턴 시리즈를 봤고 많이 배웠어요!
Anis

13

이 답변은 훌륭하고 정확하지만 매우 복잡합니다. 바퀴를 다시 발명하기보다는 Glide , Picasso , UIL , Ion 또는이 복잡하고 오류가 발생하기 쉬운 논리를 구현하는 다른 라이브러리를 고려 하십시오.

Colt 자신도 Pre-scaling Bitmaps Performance Patterns Video 에서 Glide 및 Picasso를 살펴볼 것을 권장 합니다.

라이브러리를 사용하면 Colt의 답변에 언급 된 모든 효율성을 얻을 수 있지만 모든 Android 버전에서 일관되게 작동하는 훨씬 더 간단한 API를 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.