Декодирование растровых изображений в Android с нужным размером

Я декодирую растровые изображения с SD-карты с помощью BitmapFactory.decodeFile. Иногда растровые изображения больше, чем того требует приложение или что куча позволяет, поэтому я использую BitmapFactory.Options.inSampleSize для запроса подвыборки (меньшего) растрового изображения.

Проблема в том, что платформа не обеспечивает точное значение inSampleSize, и иногда я получаю растровое изображение слишком маленькое или все еще слишком большое для доступной памяти.

Из http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize:

Примечание: декодер попытается выполнить этот запрос, но полученный битмап могут иметь разные размеры, которые именно то, что было запрошено. Кроме того, полномочия 2 часто быстрее/проще для декодера честь.

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

Edit:

Текущий исходный код:

BitmapFactory.Options bounds = new BitmapFactory.Options();
this.bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, bounds);
if (bounds.outWidth == -1) { // TODO: Error }
int width = bounds.outWidth;
int height = bounds.outHeight;
boolean withinBounds = width <= maxWidth && height <= maxHeight;
if (!withinBounds) {
    int newWidth = calculateNewWidth(int width, int height);
    float sampleSizeF = (float) width / (float) newWidth;
    int sampleSize = Math.round(sampleSizeF);
    BitmapFactory.Options resample = new BitmapFactory.Options();
    resample.inSampleSize = sampleSize;
    bitmap = BitmapFactory.decodeFile(filePath, resample);
}

Ответ 1

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

Первым шагом является чтение файла в битмап немного больше, чем вам нужно, используя BitmapFactory.Options.inSampleSize, чтобы убедиться, что вы не потребляете чрезмерную память, читающую большое растровое изображение, когда все, что вам нужно, - это меньшая эскиз или разрешение экрана изображение.

Второй шаг - вызвать Bitmap.createScaledBitmap(), чтобы создать новое растровое изображение с требуемым требуемым разрешением.

Убедитесь, что вы очистили после временного растрового изображения, чтобы восстановить его память. (Либо пусть переменная выходит за пределы области действия и позволяет GC справиться с ней, либо называть ее .recycle(), если вы загружаете большое количество изображений и плотно работаете в памяти.)

Ответ 2

Вы можете использовать inJustDecodeBounds. Установите его в TRUE и загрузите файл как есть.

Изображение не будет загружено в память. Но свойства outheight и outwidth BitmapFactory.Options будут содержать параметры фактического размера указанного изображения. Вычислите, сколько U хотите подзапрос. то есть 1/2 или 1/4 или 1/8 и т.д. и назначьте 2/4/8 и т.д., соответственно, в inSampleSize.

Теперь установите inJustDecodeBounds на FALSE и вызовите BitmapFactory.decodeFile(), чтобы загрузить изображение точного размера, рассчитанное выше.

Ответ 3

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

// This function taking care of sampling
backImage =decodeSampledBitmapFromResource(getResources(),R.drawable.back, width, height);

// This will scale it to your screen width and height. you need to pass screen width and height.
backImage = Bitmap.createScaledBitmap(backImage, width, height, false); 

Ответ 4

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

Но выход будет использовать ваш собственный растровый читатель. Но я бы посоветовал вам придерживаться BitmapFactory, поскольку он достаточно хорошо проверен и стандартизирован.

Я бы постарался и не слишком беспокоился о небольшом избыточном потреблении памяти, но попытаюсь выяснить, почему он не соблюдает значения. К сожалению, я не знаю об этом: (

Ответ 5

ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))

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

Ответ 6

Для тех, кто ищет точный размер изображения из ресурсов.

Для точной ширины pass reqHight = 0 и для точного прохода высоты reqWidth = 0

public static Bitmap decodeBitmapResource(Context context, int resId, int reqWidth, int reqHeight)
{
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(context.getResources(), resId, options);
    float scale = 1;
    if (reqWidth != 0 && reqHeight == 0)
    {
        options.inSampleSize = (int) Math.ceil(options.outWidth / (float) reqWidth);
        scale = (float) options.outWidth / (float) reqWidth;
    }
    else if (reqHeight != 0 && reqWidth == 0)
    {
        options.inSampleSize = (int) Math.ceil(options.outHeight / (float) reqHeight);
        scale = (float) options.outHeight / (float) reqHeight;
    }
    else if (reqHeight != 0 && reqWidth != 0)
    {
        float x = (float) options.outWidth / (float) reqWidth;
        float y = (float) options.outHeight / (float) reqHeight;
        scale = x > y ? x : y;
    }
    reqWidth = (int) ((float) options.outWidth / scale);
    reqHeight = (int) ((float) options.outHeight / scale);
    options.inJustDecodeBounds = false;
    Bitmap tmp = BitmapFactory.decodeResource(context.getResources(), resId, options);
    Bitmap out = Bitmap.createScaledBitmap(tmp, reqWidth, reqHeight, false);
    tmp.recycle();
    tmp = null;
    return out;
}

Ответ 7

Поскольку я использую inSampleSize, я больше не сталкиваюсь с проблемами памяти. Поэтому inSampleSize работает достаточно стабильно для меня. Я бы рекомендовал вам дважды проверить проблему. Размер может быть не таким же, как вы просили. Но это все равно должно быть уменьшено, и не должно быть больше "Недостаточно памяти". Я также рекомендую опубликовать фрагмент кода здесь, возможно, что-то не так.

Ответ 8

Как сказал 100rabh, если вы используете BitmapFactory, у вас действительно нет большого выбора. Однако растровое декодирование действительно простое, и в зависимости от алгоритма масштабирования, который вы хотите использовать, обычно довольно просто масштабировать его "на лету" без необходимости считывать большие части исходного растрового изображения в память (в общем, вы будете требуется только удвоить объем памяти, необходимый для 1-2 строк исходного растрового изображения).

Ответ 9

Когда установлен флаг inScaled (по умолчанию это TRUE), если inDensity и inTargetDensity не равны 0, растровое изображение будет масштабироваться в соответствии с inTargetDensity при загрузке, вместо того, чтобы полагаться на графическую систему, масштабируя ее каждый раз, когда она нарисована Холст. Поэтому просто установите значение inScaled в false.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;

Ответ 10

Мне удалось приблизительно получить "правильный размер", изменив размер файла декодированного изображения на 70% его размер растрового изображения.

Bitmap myBitmap = BitmapFactory.decodeFile(path);
if(myBitmap != null)
{
  //reduce to 70% size; bitmaps produce larger than actual image size
  Bitmap rescaledMyBitmap = Bitmap.createScaledBitmap(
                                                  myBitmap, 
                                                  myBitmap.getWidth()/10*7, 
                                                  myBitmap.getHeight()/10*7, 
                                                  false);

  image.setImageBitmap(rescaledMyBitmap);
}                

Надеюсь, это поможет вам как-нибудь! Мир!