이미지가 서버에서 장치로 전송되는 이미지 집약적 인 소셜 앱을 구축하고 있습니다. 장치의 화면 해상도가 더 작 으면 원하는 디스플레이 크기와 일치하도록 장치에서 비트 맵의 크기를 조정해야합니다.
문제는 createScaledBitmap 을 사용 하면 썸네일 이미지의 크기를 조정 한 후 많은 메모리 부족 오류가 발생한다는 것입니다.
Android에서 비트 맵 크기를 조정하는 가장 메모리 효율적인 방법은 무엇입니까?
이미지가 서버에서 장치로 전송되는 이미지 집약적 인 소셜 앱을 구축하고 있습니다. 장치의 화면 해상도가 더 작 으면 원하는 디스플레이 크기와 일치하도록 장치에서 비트 맵의 크기를 조정해야합니다.
문제는 createScaledBitmap 을 사용 하면 썸네일 이미지의 크기를 조정 한 후 많은 메모리 부족 오류가 발생한다는 것입니다.
Android에서 비트 맵 크기를 조정하는 가장 메모리 효율적인 방법은 무엇입니까?
답변:
이 답변은 축소 된 비트 맵 버전을로드하기 위해 inSampleSize를 사용하는 방법을 설명하는 Load large bitmaps Efficiently 에서 요약됩니다 .
특히 사전 크기 조정 비트 맵 은 다양한 방법의 세부 사항, 이들을 결합하는 방법 및 가장 메모리 효율적인 방법을 설명합니다.
메모리 속성이 다른 Android에서 비트 맵의 크기를 조정하는 세 가지 주요 방법이 있습니다.
이 API는 기존 비트 맵을 가져와 선택한 정확한 치수로 새 비트 맵을 만듭니다.
플러스 측면에서는 (어떻게 보이는지에 관계없이) 찾고있는 이미지 크기를 정확하게 얻을 수 있습니다. 그러나 단점 은이 API가 작동하려면 기존 비트 맵 이 필요하다는 것 입니다. 더 작은 새 버전을 만들려면 이미지를로드하고 디코딩하고 비트 맵을 만들어야합니다. 이것은 정확한 치수를 얻는 측면에서 이상적이지만 추가 메모리 오버 헤드 측면에서 끔찍합니다. 따라서 이것은 메모리를 의식하는 경향이있는 대부분의 앱 개발자에게 일종의 거래 차단기입니다.
BitmapFactory.Options
inSampleSize
임시 비트 맵으로 디코딩 할 필요가 없도록 디코딩하는 동안 이미지 크기를 조정 하는 속성 이 있습니다. 여기에 사용 된이 정수 값은 1 / x 축소 된 크기로 이미지를로드합니다. 예를 들어 inSampleSize
2로 설정 하면 크기의 절반 인 이미지가 반환되고 4로 설정하면 크기의 1/4 인 이미지가 반환됩니다. 기본적으로 이미지 크기는 항상 소스 크기보다 약간 더 작습니다.
메모리 관점에서 보면 사용 inSampleSize
은 정말 빠른 작업입니다. 사실상, 이미지의 모든 X 번째 픽셀 만 결과 비트 맵으로 디코딩합니다. inSampleSize
하지만 두 가지 주요 문제가 있습니다 .
정확한 해상도를 제공하지 않습니다 . 비트 맵의 크기를 2의 제곱만큼만 줄입니다.
최상의 품질로 크기를 조정하지 못합니다 . 대부분의 크기 조정 필터는 픽셀 블록을 읽은 다음 가중치를 부여하여 문제의 크기가 조정 된 픽셀을 생성함으로써보기 좋은 이미지를 생성합니다. inSampleSize
몇 픽셀마다 읽기만하면이 모든 것을 피할 수 있습니다. 결과는 상당히 성능이 좋고 메모리가 적지 만 품질이 떨어집니다.
이미지를 pow2 크기로 축소하는 작업 만 처리하고 필터링이 문제가되지 않는 경우 .NET보다 메모리 효율적인 (또는 성능 효율적인) 방법을 찾을 수 없습니다 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로 이동합니다
이 답변은 훌륭하고 정확하지만 매우 복잡합니다. 바퀴를 다시 발명하기보다는 Glide , Picasso , UIL , Ion 또는이 복잡하고 오류가 발생하기 쉬운 논리를 구현하는 다른 라이브러리를 고려 하십시오.
Colt 자신도 Pre-scaling Bitmaps Performance Patterns Video 에서 Glide 및 Picasso를 살펴볼 것을 권장 합니다.
라이브러리를 사용하면 Colt의 답변에 언급 된 모든 효율성을 얻을 수 있지만 모든 Android 버전에서 일관되게 작동하는 훨씬 더 간단한 API를 사용할 수 있습니다.