Обрезать изображение без OutOfMemory – Android

Я хочу обрезать изображение без исключения OutOfMemory .
Это означает, что у меня есть x, y, ширина и высота обрезанного изображения и вы хотите обрезать исходное изображение, не принося его в память.
Да, я знаю, что BitmapRegionDecoder – хорошая идея, но, возможно, обрезанное изображение было бы слишком большим для переноса его в память.

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

EDIT: я хочу сохранить обрезанное изображение, а не просто показывать его в ImageView Я хочу сохранить его в новом файле без потери размеров.

Это пример
Введите описание изображения здесь

В этой ситуации обрезанное разрешение изображения составляет 20000×20000, а код ниже не работает. Причина OOM:

 BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options); mImageView.setImageBitmap(bitmap); 

Использование inSampleSize для уменьшения исходного размера изображения является хорошим, но результат i save больше не 20000×20000.

Как я могу обрезать 25000×25000 и сохранить часть изображения 20000×20000 в файле?

Проще говоря, для этого требуется много низкоуровневого программирования и оптимизации.

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

Также BitmapRegionDecoder, как предложено в ответах, не будет работать хорошо. Он уверен, что предотвращает загрузку всего растрового изображения в ОЗУ, но как насчет обрезанного изображения? После обрезки изображения он дает вам гигантский растровый рисунок, который, несмотря ни на что, дает вам OOM.

Поскольку ваша проблема, как вы описали, нуждается в Bitmaps для написания или чтения с диска, так как они записываются или читаются из памяти; Что называется BufferedBitmap (или так), который эффективно обрабатывает требуемую память, сохраняя небольшие фрагменты растрового изображения на диске и используя их позже, тем самым избегая OOM.

Любое другое решение, которое хочет решить проблему с масштабированием, выполняет только половину работы. Зачем? Потому что само обрезанное изображение может быть слишком большим для памяти (как вы сказали).

Тем не менее, решение проблемы путем масштабирования не так уж плохо, если вы не заботитесь о качестве обрезанного изображения по сравнению с тем качеством, которое видел пользователь, когда она обрезала его. Это то, что делают Google Фото, это просто снижает качество обрезанного изображения, очень просто!

Я не видел классов BufferedBitmap (но если они есть, это было бы потрясающе). Они, несомненно, будут полезны для решения подобных проблем.

Вы можете проверить приложение Telegram для обмена сообщениями, которое поставляется с реализацией объектов обрезки изображений с открытым исходным кодом; Вы правильно понимаете, он обрабатывает все подобные неприятные работы с добрым старым C … Следовательно, мы можем заключить, что хорошее глобальное решение (или лучше сказано, ОДИН ИЗ НЕСКОЛЬКИХ ПРИМЕНИМЫХ РЕШЕНИЙ), по-видимому, является низкоуровневым программированием для обработки диска И память.

Я знаю, что мой ответ не дал никакого решения для копирования-вставки для вашей проблемы, но, по крайней мере, я надеюсь, что он дал вам некоторые идеи моему другу.

Вы проверили BitmapRegionDecoder? Он будет извлекать прямоугольник из исходного изображения.

 BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options); mImageView.setImageBitmap(bitmap); 

http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

Вы можете решить это с помощью BitmapFactory . Чтобы определить исходный размер растрового изображения, не помещая его в память, выполните следующие действия:

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(..., options); int originalImageWith = options.outWidth; int originalImageHeight = options.outHeight; 

Теперь вы можете использовать options.inSampleSize

Если установлено значение> 1, запрашивает декодер для подвыборки исходного изображения, возвращая меньшее изображение для сохранения памяти. Размер выборки – это количество пикселей в любом измерении, которое соответствует одному пикселю в декодированном растровом изображении. Например, inSampleSize == 4 возвращает изображение, равное 1/4 ширины / высоты оригинала, и 1/16 количество пикселей. Любое значение <= 1 обрабатывается так же, как 1. Примечание: декодер использует конечное значение в зависимости от полномочий 2, любое другое значение округляется до ближайшей мощности 2.

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

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = sampleSize; Bitmap bitmap = BitmapFactory.decodeResource(..., options); 

BitmapRegionDecoder – это хороший способ обрезать большие или большие изображения, но он доступен из API 10 и выше.

Существует класс BitmapRegionDecoder, который может вам помочь, но он доступен из API 10 и выше.

Если вы не можете использовать его:

Многие форматы изображений сжаты и поэтому требуют некоторой загрузки в память.

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

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

Конечно, поскольку Android имеет открытый исходный код, вы можете попробовать использовать BitmapRegionDecoder и использовать его для любого устройства.

Ссылка: обрезать изображение без загрузки в память

Или вы можете найти другой способ ниже, который может вам помочь:

Использование растрового изображения / холста и NDK