"Растрові зображення занадто великі для завантаження в текстуру"


154

Я завантажую растровий файл у ImageView і бачу цю помилку. Я вважаю, що це обмеження стосується обмеження розміру для апаратних текстур OpenGL (2048x2048). Зображення, яке мені потрібно завантажити, - це зображення із збільшенням 4000 пікселів.

Я намагався вимкнути апаратне прискорення в маніфесті, але ніякої радості.

    <application
        android:hardwareAccelerated="false"
        ....
        >

Чи можна завантажити зображення розміром більше 2048 пікселів у ImageView?


1
Для тих, хто шукає тут, не забудьте поставити своє зображення у подання прокрутки, якщо ви хочете, щоб воно могло прокручуватися. Це дозволить позбутися від помилки. Я витратив деякий час, перш ніж зрозумів, що це моя проблема.
— Джейсон Райдж

Для всіх, хто хоче відображати великі зображення, які зберігають якість зображення, зверніться до бібліотеки у відповіді @stoefln нижче. Я використав це і варто спробувати. Однозначно краще, ніж inSampleSizeпідхід.
— Махендра Лія

1
@Joe Blow поточна відповідь не працювала у вашому випадку? Якщо ні, то, будь ласка, розкажіть про проблему, з якою ви стикалися в контексті цього питання?
— Pravin Divraniya

1
Ей @ VC.One - дивно, що треба працювати над моєю людиною. Я повинен був натиснути "Нагородити наявну відповідь". Ніхто не турбує нудно натискаючи кнопку "найкращого" на цьому спливаючому вікні, тому що це нерозумно :) Це все вирішено, коли я натиснув щедроту. Ура !!
— Fattie

1
Я намагаюся завжди виплачувати щедрості (мені не подобається збирати бали). Я думаю, якщо ти натиснеш на мій профіль, тоді щедро, @ VC.One ви побачите багато справді чудових QA від SO !!!
— Fattie

Відповіді:


48

Вся візуалізація базується на OpenGL, тому ні в якому разі не можна перевищувати цю межу (GL_MAX_TEXTURE_SIZE залежить від пристрою, але мінімум - 2048x2048, тому будь-яке зображення нижче 2048x2048 підійде).

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

Або ви можете зменшити масштаб зображення перед його відображенням (див . Відповідь користувача1352407 на це запитання1352407).

А також будьте обережні, у яку папку ви вкладете зображення, Android може автоматично масштабувати зображення. Подивіться нижче на це запитання відповідь Pilot_51 .


23
Якщо це так, як програма Галерея дозволяє відображати та маніпулювати зображеннями, зробленими фотоапаратом? 2048x2048 - це лише 4-мегапіксельне зображення, і багато Android-телефонів роблять фотографії набагато більшими, ніж це, і програма Галерея, схоже, не має проблем.
— Оллі C

3
Оскільки GL_MAX_TEXTURE_SIZE залежить від пристрою.
— jptsetung

24
Це справді не має сенсу. Зараз я зіткнувся з тією ж проблемою - із зображенням 1286x835 пікселів. І: тільки на Galaxy Nexus я отримую це повідомлення про помилку і жодне зображення! Це просто здається смішним, що передовий смартфон не може відображати таке маленьке зображення! Мій HTC Hero здатний це показати! Що я можу зробити?
— Зордід

1
Дивіться відповідь Ромена тут: stackoverflow.com/questions/7428996/…
— Бен Лі

3
@OlilieC Я також хотів би знати, як це роблять програми галереї. Тож якщо хтось знає чи має приклад для показу великих зображень, це було б чудово.
— Іннова

408

Це не пряма відповідь на питання (завантаження зображень> 2048), але можливе рішення для тих, хто відчуває помилку.

У моєму випадку зображення було меншим за 2048 в обох вимірах (точніше 1280x727), і проблема була спеціально випробувана на Galaxy Nexus. Зображення було в drawableпапці, і жодна з кваліфікованих папок. Android передбачає, що малюнки без класифікатора щільності є mdpi і масштабують їх вгору або вниз для інших густин, в цьому випадку збільшують в 2 рази на xhdpi. Переміщення образу винуватця drawable-nodpiдля запобігання масштабування вирішило проблему.


3
У мене виникли проблеми з пам’яттю, намагаючись завантажити чернетки. Провели 2 години, намагаючись зрозуміти, чому це відбувається. Дякую за непряму відповідь.
— TrueCoke

16
Це правильна відповідь і її слід позначити як таку.
— wblaschko

3
Близько трьох з половиною місяців зараз я майже повною програмою працював на Android, і я зараз це зрозумів. Здається, німим зробити drawableпо суті приховану drawable-mdpiпапку. Це також пояснило, чому мої спеціальні маркери карт виглядали жахливо (вони були розширені, а потім зменшені).
— theblang

10
так, до біса! Я думаю, що 99% андроїдських програмістів вважають, що «малювати» означає «не масштабувати це».
— Метт Логан

3
Це найкорисніша відповідь, яку я коли-небудь бачив. Все, що я можу сказати, - я надсилаю щедроту! ДЯКУЮ.
— Fattie

105

Я зменшив зображення таким чином:

ImageView iv  = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);

2
дякую, createScaledBitmap був корисним для мене, щоб я міг показати занадто велику растрову карту
— Хлопчик

Вирішив моє питання .. дякую. Насправді я знімав зображення з камери і відображався у ImageView у Samsung Galaxy S4 з 4.4.2
— Мітеш Шах

вибачте, але я не можу виявити, що таке "w"
— Боббелініо

1
Вибачте, що це ctxозначає?
— Ameer Sabith

1
@AmeerSabith виглядає так, що ctx - це об'єкт контексту (наприклад, ваша запущена діяльність)
— Метт,

34

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

Я використовував цей єдиний рядок коду, щоб видалити проблему "занадто велика растрова карта ...."

Picasso.load(resourceId).fit().centerCrop().into(imageView);

Використання centerCrop () без виклику resize () призведе до IllegalStateException. Бібліотека змушує викликати розмір, коли використовується центральний обріз.
— Zsolt Boldizsár

Це має ідеальний сенс, адже обрізка означає, що ви змінюєте розмір зображення.
— Філео99

2
Швидке, легке і просте рішення. Не впевнений, чи це найкраще, але я отримав те, що хотів. Дуже дякую
— Nabin

як і раніше отримують ту саму помилку, коли використовували ціль із Пікассо, для зображення великих розмірів :(
— Нарендра Сінгх

Спробуйте використовувати версію 2.5.3-SNAPSHOT, здається, зараз правильно працювати з великими зображеннями
— Антон Малмигін

20

Додавання наступних 2 атрибутів у ( AndroidManifest.xml) працювало для мене:

android:largeHeap="true"
android:hardwareAccelerated="false"

Це вирішило мою проблему на пристрої Samsung. Спасибі
— Hitesh Bisht

16

Для мене працювала зміна файлу зображення в drawable-nodpiпапку з drawableпапки.


Це не дозволить Android намагатися автоматично масштабувати зображення на основі розмірів пристрою та щільності екрана; саме тому це рішення працює. Android намагатиметься автоматично масштабувати все, що є в drawableпапці.
— Chris Cirefice

10

Я використовував Пікассо і мав ту ж проблему. зображення було занадто великим принаймні за розміром, шириною чи висотою. нарешті я знайшов тут рішення. ви можете зменшити масштаб зображення в залежності від розміру дисплея, а також зберегти співвідношення сторін:

    public Point getDisplaySize(Display display) {
    Point size = new Point();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
        display.getSize(size);
    } else {
        int width = display.getWidth();
        int height = display.getHeight();
        size = new Point(width, height);
    }

    return size;
}

і використовувати цей метод для завантаження зображення від Picasso:

    final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
        final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
        Picasso.with(this)
                .load(urlSource)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

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

    public String reviseImageUrl(final Integer displayWidth,     final Integer displayHeight,
        final String originalImageUrl) {
    final String revisedImageUrl;

    if (displayWidth == null && displayHeight == null) {
        revisedImageUrl = originalImageUrl;
    } else {
        final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();

        if (displayWidth != null && displayWidth > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
        }

        if (displayHeight != null && displayHeight > 0) {
            uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
        }

        revisedImageUrl = uriBuilder.toString();
    }

    return revisedImageUrl;
}

    final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);

і потім:

    Picasso.with(this)
                .load(newImageUlr)
                .resize(size, size)
                .centerInside()
                .into(imageViewd);

EDIT: getDisplaySize ()

display.getWidth()/getHeight()застаріло. Замість Displayвикористання DisplayMetrics.

public Point getDisplaySize(DisplayMetrics displayMetrics) {
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        return new Point(width, height);
}

6

BitmapRegionDecoder робить трюк.

Ви можете замінити onDraw(Canvas canvas), запустити нову нитку і декодувати область, видиму користувачеві.


5

Як вказує Ларчо, починаючи з рівня 10 API, ви можете використовувати BitmapRegionDecoderдля завантаження певних регіонів із зображення, і за допомогою цього ви зможете показати велике зображення у високій роздільній здатності, виділивши в пам'яті лише потрібні регіони. Нещодавно я розробив кришку, яка забезпечує візуалізацію великих зображень із керуванням жестами. Вихідний код та зразки доступні тут .


3

Перегляд рівня

Ви можете відключити апаратне прискорення для окремого перегляду під час виконання за допомогою наступного коду:

myView.setLayerType (View.LAYER_TYPE_SOFTWARE, null);


3

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

Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);

Це краще для будь-якого розміру екрану для Android. дайте мені знати, чи працює це для вас.


2

Зменшення зображення:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);

int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
    pow += 1;
options.inSampleSize = 1 << pow; 
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);

Зображення буде зменшено на розмір reqHeight та reqWidth. Як я розумію, вSampleSize приймають лише 2 значення.


як знати reqHeight та reqWidth? Я маю на увазі, чи потрібно нам це зафіксувати до статичного значення? чи ми можемо змінити ці пристрої wrt?
— Прашант Деббадвар

2

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

Glide: https://github.com/bumptech/glide

Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);

2
Використання Glide замість Пікассо допомогло. Здається, Glide
— Johnny Five

1

Я спробував усі рішення, описані вище, один за одним, протягом багатьох годин, і жодне, здавалося, не спрацювало! Нарешті, я вирішив подивитися офіційний приклад щодо зйомки зображень за допомогою камери Android та їх відображення. Офіційний приклад ( тут ) нарешті дав мені єдиний метод, який спрацював. Нижче я представляю рішення, яке я знайшов у цьому прикладі програми:

public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {

            /* There isn't enough memory to open up more than a couple camera photos */
    /* So pre-scale the target bitmap into which the file is decoded */

    /* Get the size of the ImageView */
    int targetW = imgView.getWidth();
    int targetH = imgView.getHeight();

    /* Get the size of the image */
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    /* Figure out which way needs to be reduced less */
    int scaleFactor = 1;
    if ((targetW > 0) || (targetH > 0)) {
        scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    }

    /* Set bitmap options to scale the image decode target */
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    /* Decode the JPEG file into a Bitmap */
    Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);

    /* Associate the Bitmap to the ImageView */
    imgView.setImageBitmap(bitmap);
    imgView.setVisibility(View.VISIBLE);
}

0

ПРИМІТКА ДЛЯ ТИХ, КОМИ ХОЧУТЬ ВСТАВИТИ Зображення МАЛОГО РОЗМІРУ:

Рішення Pilot_51 (переміщення зображень у drawable-nodpiпапку) працює, але є ще одна проблема: він створює зображення занадто малий на екрані , якщо зображення не змінюється в дуже великий (наприклад , 2000 х 3800) дозвіл , щоб відповідати екрану - то це робить ваше додаток важче .

РІШЕННЯ : вставте свої файли зображень drawable-hdpi- Це працювало як принадність для мене.


1
Ви просто маскуєте свою проблему із щільністю зображення. На апараті низької щільності ваші зображення будуть виглядати більше.
— рвап

я також не думаю, що у рішення Pilot_51 є якісь проблеми, ви повинні використовувати відповідні розміри зображення відповідно до ваших потреб
— Nilabja

0

Використовуючи правильну рисувальну підпапку, це вирішило для мене. Моє рішення полягало в тому , щоб замість папки, що перетягується, помістити моє зображення в повній роздільній здатності (1920x1200) у папку Dravable-xhdpi .

Я також помістив зменшене зображення (1280x800) у папку Dravable-hdpi .

Ці дві резолюції відповідають планшетам Nexus 7 2013 та 2012, які я програмую. Я також випробував розчин на деяких інших планшетах.


0
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);
    ///*
    if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){



        uri = data.getData();

        String[] prjection ={MediaStore.Images.Media.DATA};

        Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);

        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndex(prjection[0]);

        ImagePath = cursor.getString(columnIndex);

        cursor.close();

        FixBitmap = BitmapFactory.decodeFile(ImagePath);

        ShowSelectedImage = (ImageView)findViewById(R.id.imageView);

      //  FixBitmap = new BitmapDrawable(ImagePath);
        int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
        FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);

       // ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));

        ShowSelectedImage.setImageBitmap(FixBitmap);

    }
}

Цей код - робота

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