Змініть розмір великого растрового файла до масштабованого вихідного файлу на Android


218

У мене є великий растровий файл (скажімо, 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, але це не працює таким чином.


Ні, outHeight та outWidth - це параметри з методу декодування. Попри це, у мене те саме питання, що і у вас, і я не дуже задоволений підходом у два кроки ніколи.
rds

часто, дякую добру, можна використовувати один рядок коду .. stackoverflow.com/a/17733530/294884
Fattie

Читачі, зверніть увагу на цю абсолютно критичну якість! stackoverflow.com/a/24135522/294884
Fattie

1
Будь ласка, зауважте, що цьому питанню зараз 5 років, і повне рішення .. stackoverflow.com/a/24135522/294884 Привіт!
Fattie

2
Зараз є офіційна документація на цю тему: developer.android.com/training/displaying-bitmaps/…
Vince

Відповіді:


146

Ні. Я хотів би, щоб хтось мене виправив, але я прийняв підхід "навантаження / зміна", який ви намагалися, як компроміс.

Ось такі кроки для кожного, хто переглядає:

  1. Обчисліть максимальний можливий показник, inSampleSizeякий досі дає зображення, яке перевищує вашу ціль.
  2. Завантажте зображення, використовуючи BitmapFactory.decodeFile(file, options), передаючи inSampleSize як опцію.
  3. Змініть розмір до потрібних розмірів, використовуючи Bitmap.createScaledBitmap().

Я намагався цього уникнути. Тож немає способу безпосередньо змінити розмір великого зображення лише за один крок?
Мануель

2
Наскільки мені не відомо, але не дозволяйте цьому заважати вам далі вивчати це.
Джастін

Добре, я візьму це за свою прийняту відповідь досі. Якщо я дізнаюся якісь інші методи, я повідомлю вас про це.
Мануель

Як згадується у відповіді PSIXO, ви також можете використовувати android: largeHeap, якщо після використання inSampleSize у вас все ще виникають проблеми.
користувач276648

змінна растрова карта ставала порожньою
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", але не менш добре відповідь.
Олівер Діксон

@Ofir: getImageUri (шлях); що мені потрібно пройти в цьому методі?
Biginner

1
Замість (w h) /Math.pow (шкала, 2) ефективніше використовувати (w h) >> шкала.
david.perez

2
Не дзвоніть, System.gc()будь ласка
gw0

Дякую @Ofir, але ця трансформація не зберігає орієнтацію зображення: - /
JoeCoolman

43

Це "комбіновані рішення" Моджо Разіна та "Офіра". Це дасть вам пропорційно змінене зображення з межами максимальної ширини та максимальної висоти.

  1. Він читає лише метадані, щоб отримати оригінальний розмір (options.inJustDecodeBounds)
  2. Він використовує грубі розміри для збереження пам'яті (itmap.createScaledBitmap)
  3. Він використовує точно розмір зображення на основі грубого Bitamp, створеного раніше.

Для мене це чудово спрацьовує на 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 методу .createScaledBitmap, тому що мені все-таки потрібно спочатку завантажити велике зображення в об’єкт Bitmap.
Мануель

2
Правильно. Я перечитував ваше запитання і в основному (якщо я правильно це розумію) він зменшується до "чи можу я змінити розмір зображення до точних розмірів, не завантажуючи оригінальний файл у пам'ять?" Якщо так - я не знаю достатньо тонкощів обробки зображень, щоб відповісти на це, але щось підказує мені, що 1. це не доступно в API, 2. це не буде 1-лайнер. Я відзначу це як улюблене - було б цікаво подивитися, чи вирішите ви (чи хтось інший) це.
Бостон

це працювало для мене, тому що я отримую урі і перетворююсь на растрові карти, тому масштабування їх легко для мене 1+ для найпростішого.
Хамза

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 (хоча це не має особливого сенсу). Третім в SampleSize повинна бути потужність 2.
кіберген

Не зрозумій мене неправильно. Це напевно завантажить зображення, але якщо настил вкладених кольорів буде прокладений, це виглядає не так. І це також точно не є правильною відповіддю, оскільки зображення не буде масштабуватися так, як очікувалося. Це нічого не зробить, поки перегляд зображення не буде наполовину менше зображення. Тоді нічого не відбувається, поки перегляд зображення не набере 1/4 розміру зображення. І так далі з двома повноваженнями!
кіберген

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; застаріло.
Равіт

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 є цілим числом, ви дуже рідко отримуєте точну ширину та висоту пікселів, яку ви хочете отримати. Ви можете іноді наблизитися, але ви також можете бути далеко від цього, залежно від десяткових знаків.
Мануель

Вранці я спробував ваш код (повідомлення вище в цій темі), але, здається, не працює, де я помилився? Будь-які пропозиції вітаються :-)
RRTW

5

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

Код Джастіна ДУЖЕ ефективний для зменшення витрат на роботу з великими растровими картами.


4

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

2
Дуже хороша! Використання inDensity замість Bitmap.createScaledBitmap врятувало мені багато пам’яті. Ще краще поєднується з inSamplesize.
Ostkontentitan

2

Враховуючи те, що ви хочете змінити розмір до точного розміру і хочете зберегти якомога більше якості, я думаю, ви повинні спробувати це.

  1. Дізнайтеся розмір зміненого зображення з викликом BitmapFactory.decodeFile та наданням checkSizeOptions.inJustDecodeBounds
  2. Обчисліть максимальний можливий inSampleSize, який ви можете використовувати на своєму пристрої, щоб не перевищувати обсяг пам’яті. bitmapSizeInBytes = 2 * ширина * висота; Як правило, для вашої картинки inSampleSize = 2 було б добре, оскільки вам знадобиться лише 2 * 1944x1296) = 4,8 Мбіт, що має стопи в пам'яті
  3. Використовуйте BitmapFactory.decodeFile з inSampleSize, щоб завантажити Bitmap
  4. Масштабуйте растрову карту до точного розміру.

Мотивація: багатоетапне масштабування може дати вам більш високу якість зображення, однак немає гарантії, що воно буде працювати краще, ніж використання високої inSampleSize. Насправді, я думаю, що ви також можете використовувати inSampleSize на зразок 5 (а не 2 р.) Для прямого масштабування за одну операцію. Або просто скористайтеся 4 і тоді ви можете просто використовувати це зображення в інтерфейсі користувача. якщо ви надсилаєте його на сервер - тоді ви можете зробити масштабування до точного розміру на стороні сервера, що дозволяє використовувати передові методи масштабування.

Примітки: якщо Bitmap, завантажений на кроці-3, принаймні в 4 рази більший (значить, 4 * targetWidth <ширина), ви, ймовірно, можете використовувати кілька змін, щоб досягти кращої якості. принаймні, це працює в загальній Java, в android у вас немає можливості вказувати інтерполяцію, яка використовується для масштабування http://today.java.net/pub/a/today/2007/04/03/perils-of- image-getcaledinstan.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.

Я простежив це, він все ще викидає OutOfMemoryError на рядок "BitmapFactory.decodeStream (є null, параметри);", якщо оригінальний растровий файл занадто великий ...


2

Вище код зробив трохи чистішим. Нарешті, InputStreams завершить обгортку, щоб переконатися, що вони також закриті:

* Примітка
Input: InputStream є, int w, int h
Вихід: Bitmap

    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

Щоб масштабувати зображення «правильним» способом, не пропускаючи жодних пікселів, вам доведеться підключити декодер зображення, щоб виконувати рядок вибірки за рядком. Android (і бібліотека Skia, що лежить в основі) не має таких гачків, тому вам доведеться скочувати свій власний. Якщо припустити, що ви говорите на зображеннях jpeg, найкращим варіантом буде використання libjpeg безпосередньо, в C.

З огляду на складнощі, використання двоетапної підпробовки, а потім масштабування, мабуть, найкраще для додатків типу перегляду зображень.



1

Якщо ви абсолютно хочете зробити один крок зміни розміру, ви, ймовірно, можете завантажити всю растрову карту, якщо android: largeHeap = true, але, як ви бачите, це не дуже доцільно.

Від docs: android: largeHeap Чи потрібно створювати процеси у вашій програмі за допомогою великої купи Dalvik. Це стосується всіх процесів, створених для програми. Це стосується лише першої програми, завантаженої в процес; якщо ви використовуєте спільний ідентифікатор користувача, щоб дозволити декільком програмам використовувати процес, всі вони повинні використовувати цю опцію послідовно, або вони матимуть непередбачувані результати. У більшості додатків цього не потрібно, а натомість слід зосередитись на зменшенні загального використання пам’яті для підвищення продуктивності. Увімкнення цього параметра також не гарантує фіксованого збільшення доступної пам’яті, оскільки деякі пристрої обмежені загальною доступною пам’яттю.



0

Це працювало для мене. Функція отримує шлях до файлу на sd-картці та повертає Bitmap в максимально відображеному розмірі. Код від Ofir з деякими змінами, такими як файл зображення на sd, замість Ressource, а witdth і heigth отримують від Object Display.

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. Мені вдалося розшифрувати зображення розміром більше 20 Мб до тих пір, поки мої вхідні параметри становлять приблизно 1024x1024. Ви можете зберегти повернений растровий файл в інший файл. Нижче цього методу - ще один метод, який я також використовую для масштабування зображень до нової растрової карти. Сміливо використовуйте цей код за своїм бажанням.

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

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

Повний код у kotlin:

@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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.