Расшифровка растровых изображений в Android с нужным размером - PullRequest
60 голосов
/ 15 апреля 2010

Я декодирую растровые изображения с 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);
}

Ответы [ 10 ]

44 голосов
/ 29 июля 2010

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

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

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

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

15 голосов
/ 17 апреля 2010

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

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

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

7 голосов
/ 03 июня 2012

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

// 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); 
1 голос
/ 02 августа 2016

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

Для точной ширины прохода 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;
}
1 голос
/ 25 января 2014
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))

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

1 голос
/ 18 апреля 2010

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

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

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

0 голосов
/ 04 апреля 2017

Мне удалось приблизительно получить «правильный размер» путем изменения масштаба файла декодированного изображения до 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);
}                

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

0 голосов
/ 25 декабря 2016

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

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
0 голосов
/ 22 апреля 2010

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

0 голосов
/ 21 апреля 2010

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

...