Android: bitmap.getByteCount () в API меньше 12

Когда я искал, как определить размер изображения перед сохранением его на SD-карту, я обнаружил следующее:

bitmap.getByteCount();

но этот метод добавлен в API 12, и я использую API 10. Итак, я снова обнаружил следующее:

getByteCount () - это просто удобный метод, который делает именно то, что вы поместили в блок else. Другими словами, если вы просто переписываете getSizeInBytes, чтобы всегда возвращать «bitmap.getRowBytes () * bitmap.getHeight ()»

здесь:

Какого черта Bitmap getByteCount ()?

Итак, вычислив это bitmap.getRowBytes() * bitmap.getHeight(), я получил значение 120000 (117 KB).

где размер изображения на SD-карте равен 1.6 KB.

Что мне не хватает? или делаешь не так?

Спасибо


person Archie.bpgc    schedule 25.09.2012    source источник
comment
Как отмечено в комментарии ниже, мне интересно, связано ли это с размером (несжатого) растрового изображения в памяти по сравнению с размером (сжатый PNG / JPG) на диске. Таким образом, поскольку это, как я думаю, из примера LruCache, 117 КБ может быть более безопасной ставкой.   -  person pjco    schedule 30.03.2013
comment
Я добавил более исчерпывающий ответ ниже, вы правильно его рассчитываете   -  person pjco    schedule 30.03.2013


Ответы (7)


Вы все делаете правильно!

Быстрый способ точно узнать, верны ли значения, - это зарегистрировать это следующим образом:

int numBytesByRow = bitmap.getRowBytes() * bitmap.getHeight();
int numBytesByCount = bitmap.getByteCount();
Log.v( TAG, "numBytesByRow=" + numBytesByRow ); 
Log.v( TAG, "numBytesByCount=" + numBytesByCount ); 

Это дает результат:

03-29 17:31:10.493: V/ImageCache(19704): numBytesByRow=270000
03-29 17:31:10.493: V/ImageCache(19704): numBytesByCount=270000

Таким образом, оба вычисляют одно и то же число, которое, как я подозреваю, является размером растрового изображения в памяти. Это отличается от JPG или PNG. на диске, так как он полностью несжатый.


Для получения дополнительной информации мы можем обратиться к AOSP и источнику в примере проекта. Этот файл используется в примере проекта BitmapFun в документации для разработчиков Android Кеширование растровых изображений

AOSP ImageCache.java

/**
 * Get the size in bytes of a bitmap in a BitmapDrawable.
 * @param value
 * @return size in bytes
 */
@TargetApi(12)
public static int getBitmapSize(BitmapDrawable value) {
    Bitmap bitmap = value.getBitmap();

    if (APIUtil.hasHoneycombMR1()) {
        return bitmap.getByteCount();
    }
    // Pre HC-MR1
    return bitmap.getRowBytes() * bitmap.getHeight();
}

Как видите, они используют ту же технику.

bitmap.getRowBytes() * bitmap.getHeight();

Использованная литература:

person pjco    schedule 29.03.2013
comment
+1 Большое спасибо за ваше драгоценное время за такой Хороший ответ :) - person swiftBoy; 30.09.2013
comment
для разных устройств он дает разный размер растрового изображения ... по какой-либо причине? - person Kalpesh Lakhani; 21.06.2014
comment
@KalpeshLakhani разные устройства используют разные цветовые режимы и, таким образом, занимают разный объем памяти для полного несжатого изображения. В Android SDK перечислены четыре режима: ALPHA_8, ARGB_4444, ARGB_8888 и RGB_565. См. Bitmap.Config. - person pjco; 25.06.2014

На данный момент я использую это:

ByteArrayOutputStream bao = new ByteArrayOutputStream();
my_bitmap.compress(Bitmap.CompressFormat.PNG, 100, bao);
byte[] ba = bao.toByteArray();
int size = ba.length;

чтобы получить общее количество байтов в качестве размера. Потому что значение, которое я здесь получаю, идеально соответствует размеру (в байтах) изображения на SD-карте.

person Archie.bpgc    schedule 26.09.2012
comment
потому что размер на диске не совпадает с размером в памяти. Если у вас есть объект Bitmap, он представляет полные пиксельные данные изображения, чтобы вы могли отобразить его на экране. Когда вы сохраняете файл как информацию на диске, эта информация может быть сжата с помощью алгоритма сжатия, такого как JPG или PNG. - person Rich; 22.02.2013
comment
Не делай этого! Это работает, но для LruMemoryCache - плохая идея (откуда и исходит вопрос). Это загружает все данные в память во второй раз. - person pjco; 30.03.2013
comment
@pjco Из всех перечисленных здесь методов это единственный, который может точно описать размер файла, который мне нужен для индикатора сетевого трафика. Где data - это растровое изображение: data.getByteCount(), data.getRowBytes() * data.getHeight() и data.getAllocationByteCount() возвращают гораздо более высокие значения, чем длина массива байтов. - person Kyle Falconer; 02.06.2014
comment
@netinept, я бы подумал, может быть, посмотреть на File () или FileDescriptor (), чтобы получить размер на диске (я забыл о макушке, которая будет работать), было бы лучше, не так ли? Этот вопрос был задан в отношении LruMemoryCache, который относится к размеру памяти. Ваш вариант использования звучит немного иначе. Я думаю, это зависит от того, где вы берете растровое изображение и что вы с ним делаете. Если это сработает для вас, хотя это хорошо, спасибо за комментарий :) - person pjco; 04.06.2014

Ничего не пропало! Ваш код-сниппет показывает точную реализацию из Android-Source:

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/graphics/Bitmap.java#Bitmap.getByteCount%28%29

Я думаю, что разница в размере - это результат сжатия изображений (jpg и т. Д.).

person darkNeuron    schedule 25.09.2012
comment
но я не сжимаю его. Если я использую формат JPEG, я устанавливаю качество на 100. Кроме того, я также пробовал использовать PNG (без потерь), где он всегда дает исходное качество независимо от того, какое значение я установил. - person Archie.bpgc; 26.09.2012
comment
Даже если качество установлено на 100, оно все еще сжимается (stackoverflow.com/questions/7982409/). Метод getByteCount не имеет ничего общего с форматами изображений, он показывает, сколько памяти занимает изображение в оперативной памяти. - person junkdog; 28.01.2013

Вот альтернативный способ:

public static int getBitmapByteCount(Bitmap bitmap) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1)
        return bitmap.getRowBytes() * bitmap.getHeight();
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
        return bitmap.getByteCount();
    return bitmap.getAllocationByteCount();
}

Заявление из IDE для getAllocationByteCount():

Это может быть больше, чем результат getByteCount (), если растровое изображение повторно используется для декодирования других растровых изображений меньшего размера или путем ручной реконфигурации. См. Reconfigure (int, int, Bitmap.Config), setWidth (int), setHeight (int), setConfig (Bitmap.Config) и BitmapFactory.Options.inBitmap. Если растровое изображение не изменено таким образом, это значение будет таким же, как и значение, возвращаемое getByteCount ().

person Enzokie    schedule 15.09.2015

Можете ли вы попробовать этот код?

int pixels = bitmap.getHeight() * bitmap.getWidth();
int bytesPerPixel = 0;
switch(bitmap.getConfig()) {
case ARGB_8888:
bytesPerPixel = 4;
break;
case RGB_565:
bytesPerPixel = 2; 
 break;
case ARGB_4444:
bytesPerPixel = 2; 
 break;
case ALPHA_8 :
 bytesPerPixel = 1; 
 break;
}
 int byteCount = pixels / bytesPerPixel;
person steevoo    schedule 26.09.2012
comment
Сначала я подумал, что могу упустить какой-то другой фактор. но дело в том, что я получаю значение, используя формулу bitmap.getHeight () * bitmap.getWidth (); составляет 117 КБ (это 120000, если вы называете это пикселями), тогда как размер изображения на SD-карте составляет всего 1,6 КБ. поэтому, если мне нужно умножить на bytesPerPixel, я получу еще большее значение :( - person Archie.bpgc; 26.09.2012

изображение на SD-карте имеет другой размер, потому что оно сжато. на устройстве это будет зависеть от ширины / высоты

person Rafael Sanches    schedule 28.02.2013

Почему бы вам не попробовать разделить его на 1024? Чтобы получить КБ вместо байтов.

person NemesisDoom    schedule 13.08.2013