programing

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

muds 2023. 10. 23. 22:10
반응형

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

파일에 큰 비트맵(예: 3888x2592)이 있습니다.비트맵의 크기를 800x533으로 조정하고 다른 파일에 저장합니다.보통 비트맵의 크기를 조정할 때는Bitmap.createBitmap메서드는 첫 번째 인수로 소스 비트맵이 필요합니다. 원래 이미지를 비트맵 개체에 로드하면 당연히 메모리를 초과하기 때문에 제공할 수 없습니다(예: 여기 참조).

예를 들어 비트맵을 읽을 수도 없습니다.BitmapFactory.decodeFile(file, options), 제공하기BitmapFactory.Options.inSampleSize, 정확한 폭과 높이로 크기를 조정하고 싶기 때문입니다.으로.inSampleSize비트맵 크기를 972x648로 조정합니다(사용하는 경우).inSampleSize=4) 또는 778x518로(사용하는 경우)inSampleSize=5, 2)의 힘도 아닌 것을 의미합니다.

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

제 질문을 요약하자면:OutOfMemory 예외 없이 10MP 이상의 대용량 이미지 파일을 읽고 특정 새 너비와 높이로 크기를 조정한 새 이미지 파일에 저장할 수 있는 방법이 있습니까?

저도 노력했습니다.BitmapFactory.decodeFile(file, options)Options.outHeight and Options.outWidth 값을 수동으로 800과 533으로 설정합니다.

아니요. 누군가 저를 고쳐줬으면 좋겠는데, 당신이 시도한 부하/크기 조정 방법을 타협안으로 받아들였습니다.

검색하는 모든 사용자를 위한 단계는 다음과 같습니다.

  1. 가능한 최대를 계산합니다.inSampleSize목표치보다 더 큰 이미지를 얻을 수 있습니다.
  2. 다음을 사용하여 이미지 로드BitmapFactory.decodeFile(file, options), SampleSize를 옵션으로 전달합니다.
  3. 원하는 치수로 크기 조정하기Bitmap.createScaledBitmap().

코드로 번역된 Justin 답변(나에게 딱 맞는 답변):

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;
}

이것은 '모조 리신'과 '오피르'의 솔루션 '결합'입니다.이렇게 하면 최대 너비 및 최대 높이 경계가 있는 비례 크기의 이미지가 제공됩니다.

  1. 원래 크기를 얻기 위해 메타 데이터만 읽습니다(options.inJustDecodeBounds).
  2. 대략적인 크기를 사용하여 메모리를 저장합니다(itmap.createScaled Bitmap).
  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);
}

API를 사용하지 않는 이유는 무엇입니까?

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

지금까지 제가 본 최고의 코드는 사진 촬영 도구에 대한 설명서입니다.

"스케일링된 이미지 디코딩" 섹션을 참조하십시오.

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);
}

이 답변과 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);
}

크기가 큰 비트맵을 가지고 있고 크기가 조정된 비트맵을 디코딩하고 싶을 때 다음을 사용합니다.

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);

이 질문을 보는 다른 사용자에게 유용할 수 있습니다.저는 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;
        }
    }

저스틴의 코드는 큰 비트맵으로 작업하는 오버헤드를 줄이는 데 매우 효과적입니다.

제 솔루션이 최선의 방법인지는 모르겠지만, 사용자가 원하는 확장 기능을 통해 비트맵을 로드하는 데 성공했습니다.inDensity그리고.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;

정확한 크기로 사이즈를 조정하고 필요한 만큼 품질을 유지하고 싶은 점을 고려하면 시도해보셔야 할 것 같습니다.

  1. BitmapFactory.decodeFile을 호출하고 checkSizeOptions.inJustDecodeBounds를 제공하여 크기가 조정된 이미지의 크기를 확인합니다.
  2. 메모리를 초과하지 않도록 장치에서 사용할 수 있는 SampleSize에서 가능한 최대 값을 계산합니다. 비트맵SizeInBytes = 2*width*height. 일반적으로 SampleSize=2의 사진의 경우 2* 1944x1296)=4만 필요하므로 괜찮습니다.메모리에 피트가 있어야 하는 8Mb б
  3. SampleSize 내의 BitmapFactory.decodeFile을 사용하여 비트맵을 로드합니다.
  4. 비트맵의 크기를 정확한 크기로 조정합니다.

동기부여: 여러 단계의 스케일링을 통해 높은 화질의 사진을 얻을 수 있지만, 높은 샘플 크기를 사용하는 것보다 더 잘 작동할 것이라는 보장은 없습니다.실제로 SampleSize에서 (2의 pow가 아닌) 5와 같이 사용하여 한 작업에서 직접 스케일링을 할 수도 있다고 생각합니다.또는 4를 사용하면 UI에서 해당 이미지를 사용할 수 있습니다. 서버로 전송하면 고급 스케일링 기술을 사용할 수 있는 서버 측에서 정확한 크기로 스케일링할 수 있습니다.

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

나는 다음과 같은 코드를 사용했습니다.

  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를 시도하면 OutOf Memory Error가 발생합니다.

추적했지만 원래 비트맵이 너무 크면 "BitmapFactory.decodeStream(즉, null, options);"에서 OutOfMemoryError를 여전히 던집니다.

위의 코드는 조금 더 깨끗해졌습니다.InputStreams는 최종적으로 닫힘을 보장하기 위한 닫힘 랩핑(close wrapping)을 수행했습니다.

*참고
Input: InputStream은 intw, intw, intw
출력: 비트맵

    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");

    }

이미지를 "올바른" 방식으로 확대하려면 픽셀을 건너뛰지 않고 이미지 디코더에 연결하여 행 단위로 다운샘플링을 수행해야 합니다.Android(및 이를 기반으로 하는 Skia 라이브러리)는 이러한 후크를 제공하지 않으므로 직접 롤(roll)해야 합니다.당신이 jpeg 이미지를 말하는 것을 가정한다면, 당신의 최선의 방법은 c에서 libjpeg를 직접 사용하는 것일 것입니다.

관련된 복잡성을 고려할 때 이미지 미리보기 유형의 앱에는 2단계 하위 샘플을 사용한 후 재스케일을 사용하는 것이 가장 적합할 것입니다.

크기 조정에 다른 방법을 사용하는 기사가 있습니다.프로세스에서 사용 가능한 메모리를 기반으로 가능한 최대 비트맵을 메모리에 로드한 후 변환을 수행합니다.

http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android/

한 단계 크기를 꼭 조정하고 싶다면 안드로이드:largeHeap = true인 경우 전체 비트맵을 로드할 수 있지만 보시다시피 권장되지 않습니다.

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

Android 개발자 웹사이트에는 이 정확한 문제에 대한 훌륭한 기사가 있습니다.대용량 비트맵을 효율적으로 로드

저는 이게 통했어요.함수는 sd 카드의 파일에 대한 경로를 가져오고 표시 가능한 최대 크기의 비트맵을 반환합니다.코드는 Ofir에서 온 것으로, sd의 이미지 파일과 같은 일부 변경 사항이 Resource와 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;
    }

}

안드로이드에서 메모리의 큰 이미지를 디코딩하는 데 문제가 없는 제가 사용하는 코드입니다.입력 파라미터가 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 calls decode 메서드를 제외하고는 서로 관계가 없습니다.노트 너비와 높이는 원래 이미지에서 변경될 수 있습니다.

/*****************************************************************************
 * 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
 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);

사용합니다.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)
}

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

    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

언급URL : https://stackoverflow.com/questions/3331527/resize-a-large-bitmap-file-to-scaled-output-file-on-android

반응형