Android에서 큰 비트 맵 파일의 크기를 조정 된 출력 파일로 조정


218

파일에 큰 비트 맵 (예 : 3888x2592)이 있습니다. 이제 비트 맵의 ​​크기를 800x533으로 조정하여 다른 파일에 저장하려고합니다. 일반적으로 Bitmap.createBitmap메서드 를 호출하여 비트 맵의 ​​크기를 조정 하지만 첫 번째 인수로 소스 비트 맵이 필요합니다. 원본 이미지를 Bitmap 객체에로드하면 물론 메모리를 초과하기 때문에 제공 할 수 없습니다 ( 여기 참조) . 예 : ).

또한, 함께 비트 맵을 읽어 예를 들어, 수 BitmapFactory.decodeFile(file, options)하는 제공 BitmapFactory.Options.inSampleSize내가 정확한 폭과 높이로 크기를 조정할 때문이다. 를 사용 inSampleSize하면 비트 맵의 ​​크기를 972x648 (사용하는 경우 inSampleSize=4) 또는 778x518 (사용하는 경우)inSampleSize=5 하는 2의 거듭 제곱이 아님)으로 조정됩니다.

또한 첫 번째 단계에서 972x648과 같은 inSampleSize를 사용하여 이미지를 읽은 다음 두 번째 단계에서 정확히 800x533으로 크기를 조정하는 것을 피하고 싶습니다. 원래 이미지의 직접 크기 조정에 비해 품질이 좋지 않기 때문입니다.

내 질문을 요약하면 : 10MP 이상의 큰 이미지 파일을 읽고 새로운 이미지 파일에 저장하고 OutOfMemory 예외를 발생시키지 않고 특정 새로운 너비와 높이로 크기를 조정하는 방법이 있습니까?

또한 BitmapFactory.decodeFile(file, options)Options.outHeight 및 Options.outWidth 값을 수동으로 800 및 533으로 설정 하려고 시도했지만 작동하지 않습니다.


아니요, outHeight 및 outWidth는 디코드 메소드의 출력 매개 변수입니다. 즉, 나는 당신과 같은 문제가 있으며, 2 단계 접근 방식에 크게 만족하지 않습니다.
rds

종종 감사합니다. 한 줄의 코드 만 사용할 수 있습니다 .. stackoverflow.com/a/17733530/294884
Fattie

독자 여러분, 이것이 절대적으로 중요한 QA입니다 !!! stackoverflow.com/a/24135522/294884
Fattie

1
Pls는이 질문은 이제 5 살이며 전체 솔루션은 .. stackoverflow.com/a/24135522/294884 Cheers!
Fattie

2
이 주제에 대한 공식 문서가 있습니다 : developer.android.com/training/displaying-bitmaps/…
Vince

답변:


146

아니요. 다른 사람이 나를 고치길 원하지만 타협으로 시도한로드 / 크기 조정 방법을 수락했습니다.

누구나 탐색하는 단계는 다음과 같습니다.

  1. 가능한 최대 계산 inSampleSize여전히 대상보다 큰 이미지를 얻을 .
  2. 을 사용하여 이미지를로드하고 BitmapFactory.decodeFile(file, options)inSampleSize를 옵션으로 전달하십시오.
  3. 을 사용하여 원하는 치수로 크기를 조정하십시오 Bitmap.createScaledBitmap().

나는 그것을 피하려고 노력했다. 따라서 한 번에 큰 이미지의 크기를 직접 조정할 수있는 방법이 없습니까?
Manuel

2
내 지식은 아니지만 더 이상 이것을 탐구하지 못하게하십시오.
Justin

좋아, 지금까지 내가 받아 들인 대답을 위해 이것을 취할 것입니다. 다른 방법을 찾으면 알려 드리겠습니다.
Manuel

답변에 PSIXO가 언급했듯이 inSampleSize를 사용한 후에도 여전히 문제가 있으면 android : largeHeap을 사용할 수도 있습니다 .
user276648

비트 맵 변수가 비었습니다
Prasad

99

저스틴 답변은 코드로 번역되었습니다 (완벽하게 작동합니다).

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();



    int scale = 1;
    while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", 
       orig-height: " + options.outHeight);

    Bitmap resultBitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        options = new BitmapFactory.Options();
        options.inSampleSize = scale;
        resultBitmap = BitmapFactory.decodeStream(in, null, options);

        // resize to desired dimensions
        int height = resultBitmap.getHeight();
        int width = resultBitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, 
           (int) y, true);
        resultBitmap.recycle();
        resultBitmap = scaledBitmap;

        System.gc();
    } else {
        resultBitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + 
       resultBitmap.getHeight());
    return resultBitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

15
"b"와 같은 변수를 사용할 때 읽기가 어렵지만 덜 좋은 대답은 아닙니다.
Oliver Dixon

@Ofir : getImageUri (경로); 이 방법으로 무엇을 전달해야합니까?
Biginner

1
(w h) /Math.pow (scale, 2) 대신 (w h) >> scale 을 사용하는 것이 더 효율적 입니다.
david.perez

2
전화하지 마세요 System.gc()제발
gw0

감사합니다 @Ofir 그러나이 변환은 이미지의 방향을 보존하지 않습니다 : - /
JoeCoolman

43

이것이 바로 'Mojo Risin'과 'Ofir'의 솔루션이 결합 된 것입니다. 그러면 최대 너비와 최대 높이의 경계가있는 비례 적으로 크기가 조정 된 이미지가 제공됩니다.

  1. 메타 데이터를 읽어서 원래 크기 (options.inJustDecodeBounds)를 가져옵니다.
  2. 대략적인 크기 조정을 사용하여 메모리를 절약합니다 (itmap.createScaledBitmap)
  3. 이전에 생성 된 대략적인 비트 앰프를 기반으로 정확하게 크기가 조정 된 이미지를 사용합니다.

나를 위해 아래의 5 MegaPixel 이미지에서 잘 수행되었습니다.

try
{
    int inWidth = 0;
    int inHeight = 0;

    InputStream in = new FileInputStream(pathOfInputImage);

    // decode image size (decode metadata only, not the whole image)
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();
    in = null;

    // save width and height
    inWidth = options.outWidth;
    inHeight = options.outHeight;

    // decode full image pre-resized
    in = new FileInputStream(pathOfInputImage);
    options = new BitmapFactory.Options();
    // calc rought re-size (this is no exact resize)
    options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
    // decode full image
    Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

    // calc exact destination size
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
    RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

    // resize bitmap
    Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    // save image
    try
    {
        FileOutputStream out = new FileOutputStream(pathOfOutputImage);
        resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    }
    catch (Exception e)
    {
        Log.e("Image", e.getMessage(), e);
    }
}
catch (IOException e)
{
    Log.e("Image", e.getMessage(), e);
}

23

왜 API를 사용하지 않습니까?

int h = 48; // height in pixels
int w = 48; // width in pixels    
Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);

21
내 문제를 해결하지 못하기 때문입니다. "... 첫 번째 인수로 소스 비트 맵이 필요합니다. 원래 이미지를 Bitmap 객체에로드하면 메모리를 초과하기 때문에 제공 할 수 없습니다." 따라서 큰 이미지를 먼저 Bitmap 객체에로드해야하기 때문에 Bitmap을 .createScaledBitmap 메서드에도 전달할 수 없습니다.
마누엘

2
권리. 귀하의 질문을 다시 읽고 기본적으로 (올바르게 이해하면) "원본 파일을 메모리에로드하지 않고 이미지를 정확한 크기로 조정할 수 있습니까?" 그렇다면-이미지 처리의 복잡성에 대해 충분히 알지 못하지만 대답은 1. API에서 사용할 수 없으며 1-라이너가 아니라는 것을 말해줍니다. 나는 이것을 좋아하는 것으로 표시 할 것입니다-당신 (또는 다른 사람)이 이것을 해결할 수 있는지 보는 것이 흥미로울 것입니다.
Bostone

내가 uri를 얻고 비트 맵으로 변환하기 때문에 저에게 효과적이었습니다.
Hamza

22

지금까지 다른 훌륭한 답변을 인정했지만, 아직 본 최고의 코드는 사진 촬영 도구의 설명서에 있습니다.

"축소 된 이미지 디코딩"섹션을 참조하십시오.

http://developer.android.com/training/camera/photobasics.html

그것이 제안하는 솔루션은 여기에있는 다른 크기와 같은 크기 조정 및 규모 조정 솔루션이지만 매우 깔끔합니다.

편의를 위해 바로 사용할 수있는 기능으로 아래 코드를 복사했습니다.

private void setPic(String imagePath, ImageView destination) {
    int targetW = destination.getWidth();
    int targetH = destination.getHeight();
    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    destination.setImageBitmap(bitmap);
}

1
먼저 결과를 바닥에 올릴 정수를 고안하고 있습니다. 두 번째로 코드는 targetW 또는 targetH가 0으로 충돌합니다 (아마도 이해가되지는 않지만). 세 번째 inSampleSize는 2의 거듭 제곱이어야합니다.
cybergen

내가 틀리지 마 이렇게하면 이미지가 명확하게로드되지만 바닥이 들여 쓰기되면 보이지 않습니다. 그리고 이미지가 예상대로 조정되지 않기 때문에 이것은 정답이 아닙니다. 이미지보기가 이미지 크기의 절반 이하가 될 때까지 아무런 작업도 수행하지 않습니다. 그런 다음 이미지보기가 이미지 크기의 1/4이 될 때까지 아무 일도 일어나지 않습니다. 그리고 2의 힘으로 계속!
cybergen

18

이 답변과 Android 설명서를 읽은 후 비트 맵을 메모리에로드하지 않고 크기를 조정하는 코드는 다음과 같습니다.

public Bitmap getResizedBitmap(int targetW, int targetH,  String imagePath) {

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    //inJustDecodeBounds = true <-- will not load the bitmap into memory
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    return(bitmap);
}

bmOptions.inPurgeable = true; 더 이상 사용되지 않습니다.
Ravit

6

큰 비트 맵이 있고 크기를 해독하고 싶을 때 다음을 사용합니다.

BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);

1
inSampleSize는 Integer이므로 원하는 픽셀 너비와 높이를 거의 얻지 못할 수 있습니다. 때로는 가까이 갈 수도 있지만 소수점에 따라 멀리 떨어질 수도 있습니다.
Manuel

아침, 나는 당신의 코드를 시도했지만 (이 스레드의 위의 게시물) 작동하지 않는 것 같습니다. 어디에서 잘못 했습니까? 모든 제안을 환영합니다 :-)
RRTW

5

다른 사람이이 질문을보고있는 경우 유용 할 수 있습니다. 메소드가 필요한 대상 크기 객체를 수신 할 수 있도록 Justin의 코드를 다시 작성했습니다. Canvas를 사용할 때 매우 효과적입니다. 모든 훌륭한 크레딧은 JUSTIN에게 보내야합니다.

    private Bitmap getBitmap(int path, Canvas canvas) {

        Resources resource = null;
        try {
            final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
            resource = getResources();

            // Decode image size
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(resource, path, options);

            int scale = 1;
            while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
                  IMAGE_MAX_SIZE) {
               scale++;
            }
            Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

            Bitmap pic = null;
            if (scale > 1) {
                scale--;
                // scale to max possible inSampleSize that still yields an image
                // larger than target
                options = new BitmapFactory.Options();
                options.inSampleSize = scale;
                pic = BitmapFactory.decodeResource(resource, path, options);

                // resize to desired dimensions
                int height = canvas.getHeight();
                int width = canvas.getWidth();
                Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

                double y = Math.sqrt(IMAGE_MAX_SIZE
                        / (((double) width) / height));
                double x = (y / height) * width;

                Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
                pic.recycle();
                pic = scaledBitmap;

                System.gc();
            } else {
                pic = BitmapFactory.decodeResource(resource, path);
            }

            Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
            return pic;
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(),e);
            return null;
        }
    }

Justin의 코드는 큰 비트 맵 작업의 오버 헤드를 줄이는 데 매우 효과적입니다.


4

솔루션이 모범 사례인지는 모르겠지만 inDensityand inTargetDensity옵션 을 사용하여 원하는 스케일링으로 비트 맵을로드했습니다 . inDensity이다0 이 방법은 로딩이 아닌 자원 이미지 그래서, 드로어 블 리소스를로드하지 않을 경우 처음.

변수 imageUri, maxImageSideLength그리고 context내 방식의 매개 변수입니다. 명확성을 위해 AsyncTask 줄 바꿈없이 메소드 구현 만 게시했습니다.

            ContentResolver resolver = context.getContentResolver();
            InputStream is;
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Options opts = new Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, opts);

            // scale the image
            float maxSideLength = maxImageSideLength;
            float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
            // do not upscale!
            if (scaleFactor < 1) {
                opts.inDensity = 10000;
                opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
            }
            opts.inJustDecodeBounds = false;

            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }

            return bitmap;

2
아주 좋아요! Bitmap.createScaledBitmap 대신 inDensity를 사용하면 많은 메모리 힙이 절약되었습니다. inSamplesize와 더 잘 결합됩니다.
Ostkontentitan

2

정확한 크기로 크기를 조정하고 필요한만큼 품질을 유지하려는 것을 고려할 때이 방법을 사용해야한다고 생각합니다.

  1. BitmapFactory.decodeFile을 호출하고 checkSizeOptions.inJustDecodeBounds를 제공하여 크기가 조정 된 이미지의 크기를 찾으십시오.
  2. 기기에서 메모리를 초과하지 않도록 사용할 수 있는 최대 SampleSample 크기를 계산하십시오 . bitmapSizeInBytes = 2 * 폭 * 높이; 일반적으로 그림의 경우 SampleSize = 2는 2 * 1944x1296) = 4.8Mbб 만 필요하므로 괜찮습니다. 메모리에 피트해야합니다.
  3. inSampleSize와 함께 BitmapFactory.decodeFile을 사용하여 비트 맵을로드하십시오.
  4. 비트 맵을 정확한 크기로 조정하십시오.

동기 부여 : 여러 단계로 스케일링하면 더 높은 품질의 사진을 얻을 수 있지만 높은 inSampleSize를 사용하는 것보다 더 잘 작동한다는 보장은 없습니다. 실제로, 한 번의 작업으로 직접 스케일링하기 위해 5와 같은 inSampleSize를 사용할 수 있다고 생각합니다. 또는 4를 사용하면 UI에서 해당 이미지를 사용할 수 있습니다. 서버로 전송하는 경우-서버 측에서 정확한 크기로 스케일링 할 수있는 것보다 고급 스케일링 기술을 사용할 수 있습니다.

참고 : 3 단계에서로드 된 비트 맵이 최소 4 배 더 크면 (따라서 4 * targetWidth <width) 더 나은 품질을 얻기 위해 여러 크기 조정을 사용할 수 있습니다. 적어도 그것은 일반 자바에서 작동합니다. 안드로이드에서는 http://today.java.net/pub/a/today/2007/04/03/perils-of- 스케일링에 사용되는 보간을 지정할 수있는 옵션이 없습니다 image-getscaledinstance.html


2

나는 다음과 같은 코드를 사용했다 :

  String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg";
  BitmapFactory.Options options=new BitmapFactory.Options();
  InputStream is=new FileInputStream(filePath);
  BitmapFactory.decodeStream(is, null, options);
  is.close();
  is=new FileInputStream(filePath);
  // here w and h are the desired width and height
  options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired...
  // bmp is the resized bitmap
  Bitmap bmp=BitmapFactory.decodeStream(is, null, options);
  is.close();
  Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());

원래 이미지는 1230 x 1230이고 비트 맵에 330 x 330
이라고 시도했습니다. 2590 x 3849를 시도하면 OutOfMemoryError가 발생합니다.

원본 비트 맵이 너무 큰 경우 "BitmapFactory.decodeStream (is, null, options);"줄에 OutOfMemoryError가 계속 발생합니다.


2

위의 코드는 조금 더 깔끔했습니다. InputStream은 마지막으로 포장을 닫아서 닫히도록합니다.

* 참고
입력 : InputStream은 int w, int h
출력 : 비트 맵

    try
    {

        final int inWidth;
        final int inHeight;

        final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");

        {

            final FileOutputStream tempOut = new FileOutputStream(tempFile);

            StreamUtil.copyTo(is, tempOut);

            tempOut.close();

        }



        {

            final InputStream in = new FileInputStream(tempFile);
            final BitmapFactory.Options options = new BitmapFactory.Options();

            try {

                // decode image size (decode metadata only, not the whole image)
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            // save width and height
            inWidth = options.outWidth;
            inHeight = options.outHeight;

        }

        final Bitmap roughBitmap;

        {

            // decode full image pre-resized
            final InputStream in = new FileInputStream(tempFile);

            try {

                final BitmapFactory.Options options = new BitmapFactory.Options();
                // calc rought re-size (this is no exact resize)
                options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                // decode full image
                roughBitmap = BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            tempFile.delete();

        }

        float[] values = new float[9];

        {

            // calc exact destination size
            Matrix m = new Matrix();
            RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
            RectF outRect = new RectF(0, 0, w, h);
            m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
            m.getValues(values);

        }

        // resize bitmap
        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

        return resizedBitmap;

    }
    catch (IOException e) {

        logger.error("Error:" , e);
        throw new ResourceException("could not create bitmap");

    }

1

픽셀을 건너 뛰지 않고 이미지를 "올바른"방식으로 스케일링하려면 다운 샘플링을 행 단위로 수행하기 위해 이미지 디코더에 연결해야합니다. 안드로이드 (그리고 그 기초가되는 Skia 라이브러리)는 그러한 후크를 제공하지 않으므로 직접 롤백해야합니다. jpeg 이미지를 말하고 있다고 가정하면 가장 좋은 방법은 C에서 libjpeg를 직접 사용하는 것입니다.

복잡성이 수반되는 경우, 2 단계 서브 샘플을 사용한 다음 리 스케일을 사용하는 것이 이미지 미리보기 유형 앱에 가장 적합합니다.



1

절대적으로 한 단계 크기 조정을 원한다면 android : largeHeap = true 인 경우 전체 비트 맵을로드 할 수 있지만 실제로는 이것이 바람직하지 않습니다.

docs : android : largeHeap 애플리케이션 프로세스가 큰 Dalvik 힙으로 작성되어야하는지 여부입니다. 이는 응용 프로그램을 위해 생성 된 모든 프로세스에 적용됩니다. 프로세스에로드 된 첫 번째 애플리케이션에만 적용됩니다. 여러 사용자가 프로세스를 사용할 수 있도록 공유 사용자 ID를 사용하는 경우 모두이 옵션을 일관되게 사용해야합니다. 그렇지 않으면 예상치 못한 결과가 발생합니다. 대부분의 앱은이를 필요로하지 않으며 성능 향상을 위해 전체 메모리 사용량을 줄이는 데 집중해야합니다. 이 기능을 활성화해도 사용 가능한 메모리의 고정 된 증가를 보장 할 수는 없습니다. 일부 장치에는 사용 가능한 총 메모리가 제한되어 있기 때문입니다.



0

이것은 나를 위해 일했습니다. 이 함수는 sd 카드의 파일 경로를 가져 와서 표시 가능한 최대 크기의 비트 맵을 반환합니다. sd의 이미지 파일과 같은 일부 변경 사항이있는 Ofir의 코드이며 Ressource 대신 witdth와 heigth가 Display Object에서 가져옵니다.

private Bitmap makeBitmap(String path) {

    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        //resource = getResources();

        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        int scale = 1;
        while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
                IMAGE_MAX_SIZE) {
            scale++;
        }
        Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

        Bitmap pic = null;
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            pic = BitmapFactory.decodeFile(path, options);

            // resize to desired dimensions

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.y;
            int height = size.x;

            //int height = imageView.getHeight();
            //int width = imageView.getWidth();
            Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

            double y = Math.sqrt(IMAGE_MAX_SIZE
                    / (((double) width) / height));
            double x = (y / height) * width;

            Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
            pic.recycle();
            pic = scaledBitmap;

            System.gc();
        } else {
            pic = BitmapFactory.decodeFile(path);
        }

        Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
        return pic;

    } catch (Exception e) {
        Log.e("TAG", e.getMessage(),e);
        return null;
    }

}

0

다음은 Android의 메모리에서 큰 이미지를 디코딩하는 데 아무런 문제가없는 사용 코드입니다. 입력 매개 변수가 약 1024x1024 인 한 20MB보다 큰 이미지를 디코딩 할 수있었습니다. 반환 된 비트 맵을 다른 파일에 저장할 수 있습니다. 이 방법 아래에는 이미지를 새로운 비트 맵으로 스케일링하는 데 사용하는 또 다른 방법이 있습니다. 원하는대로이 코드를 사용하십시오.

/*****************************************************************************
 * public decode - decode the image into a Bitmap
 * 
 * @param xyDimension
 *            - The max XY Dimension before the image is scaled down - XY =
 *            1080x1080 and Image = 2000x2000 image will be scaled down to a
 *            value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image - a value of "null" if there is an issue decoding
 *         image dimension
 * 
 * @throws FileNotFoundException
 *             - If the image has been removed while this operation is
 *             taking place
 */
public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException
{
    // The Bitmap to return given a Uri to a file
    Bitmap bitmap = null;
    File file = null;
    FileInputStream fis = null;
    InputStream in = null;

    // Try to decode the Uri
    try
    {
        // Initialize scale to no real scaling factor
        double scale = 1;

        // Get FileInputStream to get a FileDescriptor
        file = new File( this.imageUri.getPath() );

        fis = new FileInputStream( file );
        FileDescriptor fd = fis.getFD();

        // Get a BitmapFactory Options object
        BitmapFactory.Options o = new BitmapFactory.Options();

        // Decode only the image size
        o.inJustDecodeBounds = true;
        o.inPreferredConfig = bitmapConfig;

        // Decode to get Width & Height of image only
        BitmapFactory.decodeFileDescriptor( fd, null, o );
        BitmapFactory.decodeStream( null );

        if( o.outHeight > xyDimension || o.outWidth > xyDimension )
        {
            // Change the scale if the image is larger then desired image
            // max size
            scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) );
        }

        // Decode with inSampleSize scale will either be 1 or calculated value
        o.inJustDecodeBounds = false;
        o.inSampleSize = (int) scale;

        // Decode the Uri for real with the inSampleSize
        in = new BufferedInputStream( fis );
        bitmap = BitmapFactory.decodeStream( in, null, o );
    }
    catch( OutOfMemoryError e )
    {
        Log.e( DEBUG_TAG, "decode : OutOfMemoryError" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "decode : NullPointerException" );
        e.printStackTrace();
    }
    catch( RuntimeException e )
    {
        Log.e( DEBUG_TAG, "decode : RuntimeException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "decode : FileNotFoundException" );
        e.printStackTrace();
    }
    catch( IOException e )
    {
        Log.e( DEBUG_TAG, "decode : IOException" );
        e.printStackTrace();
    }

    // Save memory
    file = null;
    fis = null;
    in = null;

    return bitmap;

} // decode

참고 : 위의 createScaledBitmap 호출 디코드 메소드를 제외하고 메소드는 서로 관련이 없습니다. 참고 너비와 높이는 원본 이미지와 다를 수 있습니다.

/*****************************************************************************
 * public createScaledBitmap - Creates a new bitmap, scaled from an existing
 * bitmap.
 * 
 * @param dstWidth
 *            - Scale the width to this dimension
 * @param dstHeight
 *            - Scale the height to this dimension
 * @param xyDimension
 *            - The max XY Dimension before the original image is scaled
 *            down - XY = 1080x1080 and Image = 2000x2000 image will be
 *            scaled down to a value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image scaled - a value of "null" if there is an issue
 * 
 */
public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig )
{
    Bitmap scaledBitmap = null;

    try
    {
        Bitmap bitmap = this.decode( xyDimension, bitmapConfig );

        // Create an empty Bitmap which will contain the new scaled bitmap
        // This scaled bitmap should be the size we want to scale the
        // original bitmap too
        scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig );

        float ratioX = dstWidth / (float) bitmap.getWidth();
        float ratioY = dstHeight / (float) bitmap.getHeight();
        float middleX = dstWidth / 2.0f;
        float middleY = dstHeight / 2.0f;

        // Used to for scaling the image
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale( ratioX, ratioY, middleX, middleY );

        // Used to do the work of scaling
        Canvas canvas = new Canvas( scaledBitmap );
        canvas.setMatrix( scaleMatrix );
        canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) );
    }
    catch( IllegalArgumentException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" );
        e.printStackTrace();
    }

    return scaledBitmap;
} // End createScaledBitmap

스케일의 전력 계산은 여기에서 단순히 잘못되었습니다. android doco 페이지에서 계산을 사용하십시오.
Fattie

0
 Bitmap yourBitmap;
 Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

또는:

 resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);

0

Integer.numberOfLeadingZeros최고의 샘플 크기, 더 나은 성능을 계산하는 데 사용 합니다.

코 틀린의 전체 코드 :

@Throws(IOException::class)
fun File.decodeBitmap(options: BitmapFactory.Options): Bitmap? {
    return inputStream().use {
        BitmapFactory.decodeStream(it, null, options)
    }
}

@Throws(IOException::class)
fun File.decodeBitmapAtLeast(
        @androidx.annotation.IntRange(from = 1) width: Int,
        @androidx.annotation.IntRange(from = 1) height: Int
): Bitmap? {
    val options = BitmapFactory.Options()

    options.inJustDecodeBounds = true
    decodeBitmap(options)

    val ow = options.outWidth
    val oh = options.outHeight

    if (ow == -1 || oh == -1) return null

    val w = ow / width
    val h = oh / height

    if (w > 1 && h > 1) {
        val p = 31 - maxOf(Integer.numberOfLeadingZeros(w), Integer.numberOfLeadingZeros(h))
        options.inSampleSize = 1 shl maxOf(0, p)
    }
    options.inJustDecodeBounds = false
    return decodeBitmap(options)
}

-2

다음 코드를 사용하여 비트 맵 크기를 조정하십시오

    public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;        
    BitmapFactory.decodeFile(file.getPath(), options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(file.getPath(), options);
   }

    private static int calculateInSampleSize(
    BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     }

     return inSampleSize;
   }    

다음 팁 / 트릭에서도 마찬가지입니다.

http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory

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