Android-центр растровых изображений Android

150

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

int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
    value = bitmap.getHeight();
} else {
    value = bitmap.getWidth();
}

Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);

Затем я масштабирую его до битмапа 144 x 144, используя это:

Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);

Проблема в том, что он обрезает верхний левый угол оригинального растрового изображения. У кого-нибудь есть код для обрезки центра растрового изображения?

Морис
источник

Ответы:

354

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

Это может быть достигнуто с помощью: Bitmap.createBitmap (источник, x, y, ширина, высота)

if (srcBmp.getWidth() >= srcBmp.getHeight()){

  dstBmp = Bitmap.createBitmap(
     srcBmp, 
     srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
     0,
     srcBmp.getHeight(), 
     srcBmp.getHeight()
     );

}else{

  dstBmp = Bitmap.createBitmap(
     srcBmp,
     0, 
     srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
     srcBmp.getWidth(),
     srcBmp.getWidth() 
     );
}
Lumis
источник
1
Отредактировал ответ так, чтобы фактическое изображение назначения было квадратом.
Йорам ван ден Бозем
1
@Lumis: Почему вы откатили ревизию 3? Это оказалось правильным. Ваша текущая версия создает правильную отправную точку, но затем включает в себя остаток слишком длинной стороны. Например, учитывая 100x1000изображение, вы получаете 100x550изображение обратно .
Гуванте
Спасибо, вы правы, формат Bitmap.createBitmap (источник, x, y, ширина, высота)
Lumis
11
Смотрите мой ответ об использовании встроенного метода ThumbnailUtils.extractThumbnail (). Зачем изобретать велосипед ??? stackoverflow.com/a/17733530/1103584
DiscDev
1
это решение короче, чем создание холста, а затем рисование на нем рисунка. он также выполняет меньше операций, чем решение ThumbnailUtils (которое вычисляет размер выборки, чтобы выяснить, как масштабировать).
Инкраш
320

Хотя большинство из приведенных выше ответов обеспечивают способ сделать это, уже есть встроенный способ сделать это, и это 1 строка кода ( ThumbnailUtils.extractThumbnail())

int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);

...

//I added this method because people keep asking how 
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
    //use the smallest dimension of the image to crop to
    return Math.min(bitmap.getWidth(), bitmap.getHeight());
}

Если вы хотите, чтобы растровый объект был переработан, вы можете передать параметры, которые делают это так:

bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);

От: ThumbnailUtils Документация

общедоступная статическая растровая карта extractThumbnail (источник растрового изображения, int width, int height)

Добавлено на уровне API 8 Создает центрированное растровое изображение нужного размера.

Параметры исходный исходный растровое изображение ширина источника целевая ширина высота целевая высота

Иногда я получал ошибки памяти при использовании принятого ответа, и использование ThumbnailUtils решало эти проблемы для меня. Плюс, это намного чище и более многоразово.

DiscDev
источник
6
+1 Я думаю, вам нужно улучшить этот код и вместо 400px передать самый короткий размер bmp, чтобы обеспечить альтернативу исходному посту выше. Но спасибо, что обратили на это наше внимание, это кажется очень полезной функцией. Жаль, что я не видел это раньше ...
Lumis
Правильно, я просто жестко запрограммировал 400, чтобы привести конкретный пример ... детали до разработчика :)
DiscDev
4
@DiscDev - это должен быть буквально один из самых полезных ответов на всем этом сайте. Шутки в сторону. Это странно с вопросами об Android - вы можете часто искать в течение двух часов, прежде чем найдете простой очевидный ответ. Не знаю, как тебя отблагодарить. Баунти в пути!
Толстяк
2
Вы могли бы даже использовать немного другую функцию, чтобы также утилизировать старую битовую карту: = ThumbnailUtils.extractThumbnail (bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT)
разработчик Android
1
Это самый мощный ответ, который я видел до сих пор на вопрос Android. Я видел около 20 различных решений, как масштабировать / обрезать / уменьшать масштаб / изменять размер и т. Д. Bitmap на Android. Все ответы разные. И этот просто занимает 1 строку и работает точно так, как ожидалось. Спасибо.
Алексей Сороколетов
12

Рассматривали ли вы сделать это из layout.xml? Вы можете установить для вашего ImageViewна ScaleType к android:scaleType="centerCrop"и установить размеры изображения в ImageViewвнутри layout.xml.

Овидиу Латку
источник
Я попробовал эту идею со следующей ошибкой OpenGLRenderer: «Растровое изображение слишком велико для загрузки в текстуру (2432x4320, max = 4096x4096)» Итак, я предполагаю, что высота 4320 не может быть обработана.
ГрегМ
1
Конечно это правильный ответ и отвечает на вопрос просто отлично! Оптимизация качества изображения / размера для больших изображений ... Ну, это другой вопрос!
ichalos
@ichalos Возможно, вы нашли то, что искали, но это не отвечает на первоначальный вопрос. Первоначальный вопрос был о ручном рендеринге на холсте ... на самом деле, я не уверен, как чья-то первая попытка обрезать фотографию будет ручной рендерингом на холсте, но, эй, хорошо, что вы нашли решение здесь :)
milosmns
@milosmns Возможно, ты прав. Возможно, именно так пользователь попытался подойти к проблеме ручного кадрирования к центру. Все зависит от точных потребностей первоначального пользователя.
ichalos
9

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

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);

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

Для более подробной информации вы можете обратиться к этому блогу

Хитеш Патель
источник
1
E / AndroidRuntime (30010): вызвано: java.lang.IllegalArgumentException: x + width должно быть <= bitmap.width (), и это из-за 4-кратного 100px
Стан
9

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

Это более полная комбинация других решений.

final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();

float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);

Bitmap croppedBitmap;
if (landscape){
    int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
    int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
mschmoock
источник
8

Наверное, самое простое решение на данный момент:

public static Bitmap cropCenter(Bitmap bmp) {
    int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
    return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}

импорт:

import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
Кирилл Кулаков
источник
3

Чтобы исправить решение @willsteel:

if (landscape){
                int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
            } else {
                int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
            }
Yman
источник
это исправление к решению WillSteel. В этом случае tempBitmap - это просто копия исходного (неизмененного) Bitmap или самого себя.
Иман
1
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();
    if (w == size && h == size) return bitmap;
    // scale the image so that the shorter side equals to the target;
    // the longer side will be center-cropped.
    float scale = (float) size / Math.min(w,  h);
    Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
    int width = Math.round(scale * bitmap.getWidth());
    int height = Math.round(scale * bitmap.getHeight());
    Canvas canvas = new Canvas(target);
    canvas.translate((size - width) / 2f, (size - height) / 2f);
    canvas.scale(scale, scale);
    Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
    canvas.drawBitmap(bitmap, 0, 0, paint);
    if (recycle) bitmap.recycle();
    return target;
}

private static Bitmap.Config getConfig(Bitmap bitmap) {
    Bitmap.Config config = bitmap.getConfig();
    if (config == null) {
        config = Bitmap.Config.ARGB_8888;
    }
    return config;
}
kakopappa
источник
1
public Bitmap getResizedBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    int narrowSize = Math.min(width, height);
    int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
    width  = (width  == narrowSize) ? 0 : differ;
    height = (width == 0) ? differ : 0;

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
    bm.recycle();
    return resizedBitmap;
}
Ваге Гарибян
источник