Изменение размера большого растрового файла для масштабирования выходного файла на Android

У меня есть большой битмап (скажем, 3888x2592) в файле. Теперь я хочу изменить размер растрового изображения до 800x533 и сохранить его в другом файле. Я обычно масштабировал растровое изображение, вызывая метод Bitmap.createBitmap, но для первого аргумента он должен иметь исходный битмап, который я не могу предоставить, поскольку загрузка исходного изображения в объект Bitmap будет, конечно, превышать память (см. здесь, например).

Я также не могу прочитать растровое изображение с, например, BitmapFactory.decodeFile(file, options), предоставляя BitmapFactory.Options.inSampleSize, потому что я хочу изменить его размер до точной ширины и высоты. Использование inSampleSize приведет к изменению размера растрового изображения до 972x648 (если я использую inSampleSize=4) или 778x518 (если я использую inSampleSize=5, который даже не имеет значения 2).

Я также хотел бы избежать чтения изображения с использованием inSampleSize с, например, 972x648 на первом шаге, а затем изменить его размер до 800x533 на втором шаге, потому что качество будет плохим по сравнению с прямым изменением размера оригинала изображение.

Подводя итог моему вопросу: Есть ли способ прочитать большой файл изображения с 10 или более мегабайтами и сохранить его в новом файле изображения, измененном до определенной ширины и высоты, без получения исключения OutOfMemory?

Я также пробовал BitmapFactory.decodeFile(file, options) и устанавливал значения Options.outHeight и Options.outWidth вручную до 800 и 533, но это не работает.

Ответ 1

Нет.. Мне бы очень хотелось, чтобы кто-то меня исправил, но я принял подход load/resize, который вы пробовали в качестве компромисса.

Вот шаги для просмотра:

  • Рассчитайте максимально возможный inSampleSize, который все еще дает изображение, большее, чем ваша цель.
  • Загрузите изображение с помощью BitmapFactory.decodeFile(file, options), передав в качестве параметра параметрSampleSize.
  • Измените размер до требуемых размеров с помощью Bitmap.createScaledBitmap().

Ответ 2

Джастин отвечает на перевод кода (отлично работает для меня):

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

Ответ 3

Это "Mojo Risin" и "Ofir solutions" вместе ". Это даст вам пропорционально измененное изображение с границами максимальной ширины и максимальной высоты.

  • Он только считывает метаданные, чтобы получить исходный размер (options.inJustDecodeBounds)
  • Он использует грубое изменение размера для сохранения памяти (itmap.createScaledBitmap)
  • Он использует точно измененное изображение на основе грубого бита, созданного ранее.

Для меня он отлично работает на 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);
}

Ответ 4

Почему бы не использовать API?

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

Ответ 5

Подтверждая еще один отличный ответ, лучший код, который я видел еще для этого, содержится в документации для инструмента для съемки фотографий.

См. раздел "Декодирование масштабированного изображения".

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

Ответ 6

Прочитав эти ответы и документацию по 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);
}

Ответ 7

Когда у меня есть большие растровые изображения, и я хочу их декодировать, я использую следующие

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

Ответ 8

Это может быть полезно для кого-то другого, рассматривающего этот вопрос. Я переписал код 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 очень эффективен для сокращения накладных расходов при работе с большими растровыми изображениями.

Ответ 9

Я не знаю, подходит ли мое решение, но я достиг загрузки растрового изображения с желаемым масштабированием с помощью опций 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;

Ответ 10

Принимая во внимание, что вы хотите изменить размер до нужного размера и хотите сохранить как можно больше качества, я думаю, вы должны попробовать это.

  • Узнайте размер измененного изображения с вызовом BitmapFactory.decodeFile и предоставив checkSizeOptions.inJustDecodeBounds
  • Рассчитайте максимальный максимум, который можно использовать в файлеSampleSize, который вы можете использовать на своем устройстве, чтобы не превышать память. bitmapSizeInBytes = 2 * width * height; Как правило, для вашей картинки вSampleSize = 2 будет хорошо, так как вам понадобится только 2 * 1944x1296) = 4.8Mbб, которые должны быть в памяти
  • Использовать BitmapFactory.decodeFile с inSampleSize для загрузки растрового изображения
  • Масштабируйте растровое изображение в точном размере.

Мотивация: масштабирование с несколькими шагами может дать вам изображение более высокого качества, однако нет гарантии, что он будет работать лучше, чем использование high inSampleSize. На самом деле, я думаю, что вы также можете использовать inSampleSize, например 5 (не pow of 2), чтобы иметь прямое масштабирование за одну операцию. Или просто используйте 4, а затем вы можете просто использовать это изображение в пользовательском интерфейсе. если вы отправляете его на сервер, - вы можете сделать масштабирование до точного размера на стороне сервера, что позволит вам использовать расширенные методы масштабирования.

Примечания: если битмап, загруженный на этапе 3, по крайней мере в 4 раза больше (так что ширина 4 * targetWidth < width), вы, вероятно, можете использовать несколько размеров для достижения лучшего качества. по крайней мере, что работает в общей java, в android у вас нет возможности указать интерполяцию, используемую для масштабирования http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html

Ответ 11

Я использовал такой код:

  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.

Я проследил его, он все равно бросает OutOfMemoryError в строку "BitmapFactory.decodeStream(is, null, options);", если оригинальное растровое изображение слишком велико...

Ответ 12

Выше код сделал немного чище. InputStreams, наконец, закрывают упаковку, чтобы они также закрылись:

* Примечание Вход: InputStream is, 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");

    }

Ответ 13

Чтобы масштабировать изображение "правильным" способом, не пропуская ни одного пикселя, вам нужно будет подключиться к декодеру изображения, чтобы выполнить последовательную выборку по строке. Android (и библиотека Skia, которая лежит в основе этого) не предоставляет таких крючков, поэтому вам придется сворачивать самостоятельно. Предполагая, что вы говорите jpeg-изображениями, лучше всего использовать libjpeg напрямую, в C.

Учитывая связанные с этим сложности, использование двухступенчатой ​​подвыборки-затем-масштабирования, вероятно, лучше всего подходит для приложений типа просмотра изображений.

Ответ 14

Вот статья, которая использует другой подход к изменению размера. Он попытается загрузить максимально возможное растровое изображение в память на основе доступной памяти в процессе и затем выполнить преобразования.

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

Ответ 15

Если вы абсолютно хотите сделать одно изменение размера, вы, вероятно, можете загрузить всю растровую карту, если android: largeHeap = true, но, как вы видите, это действительно не рекомендуется.

Из документов: андроид: largeHeap Должны ли быть созданы ваши прикладные процессы с большой кучей Dalvik. Это относится ко всем процессам, созданным для приложения. Он применяется только к первому приложению, загружаемому в процесс; если вы используете общий идентификатор пользователя, чтобы разрешить нескольким приложениям использовать процесс, все они должны использовать эту опцию последовательно или у них будут непредсказуемые результаты. Большинство приложений не должны этого нуждаться и вместо этого должны сосредоточиться на сокращении общего использования памяти для повышения производительности. Включение этого также не гарантирует фиксированного увеличения доступной памяти, поскольку некоторые устройства ограничены их общей доступной памятью.

Ответ 17

Это сработало для меня. Функция получает путь к файлу на SD-карте и возвращает битмап в максимальном отображаемом размере. Код из Ofir с некоторыми изменениями, такими как файл изображения на sd, вместо 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;
    }

}

Ответ 18

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

Ответ 19

Я использую 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)
}

Ответ 20

Вот код, который я использую, который не имеет проблем с декодированием больших изображений в памяти на Android. Я смог декодировать изображения размером более 20 МБ, пока мои входные параметры составляют около 1024х1024. Вы можете сохранить возвращенное растровое изображение в другой файл. Ниже этот метод - еще один метод, который я также использую для масштабирования изображений в новом растровом изображении. Не стесняйтесь использовать этот код, как хотите.

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

Ответ 21

Измените размер растрового изображения, используя следующий код

    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