ศูนย์ครอบตัด 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 (source, x, y, width, height)

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() 
     );
}

1
แก้ไขคำตอบเพื่อให้บิตแมปปลายทางที่แท้จริงเป็นรูปสี่เหลี่ยมจัตุรัส
Joram van den Boezem

1
@ Lumis: ทำไมคุณย้อนกลับการแก้ไข 3 ดูเหมือนว่าถูกต้อง เวอร์ชันปัจจุบันของคุณสร้างจุดเริ่มต้นที่ถูกต้อง แต่รวมส่วนที่เหลือของด้านที่ยาวเกินไป เช่นให้100x1000ภาพคุณกลับ100x550ภาพ
Guvante

ขอขอบคุณคุณมีสิทธิรูปแบบคือ Bitmap.createBitmap (ที่มา, x, y, ความกว้างความสูง)
Lumis

11
ดูคำตอบของฉันเกี่ยวกับการใช้วิธี ThumbnailUtils.extractThumbnail () ในตัว ทำไมต้องคิดค้นล้อใหม่ stackoverflow.com/a/17733530/1103584
DiscDev

1
วิธีนี้จะสั้นกว่าการสร้างแคนวาสและจากนั้นก็วาดภาพวาดได้ มันยังประมวลผลน้อยกว่าโซลูชัน ThumbnailUtils (ซึ่งขนาดของกลุ่มตัวอย่างคำนวณเพื่อหาวิธีการปรับสเกล)
yincrash

319

แม้ว่าคำตอบข้างต้นส่วนใหญ่จะเป็นวิธีการทำสิ่งนี้ แต่ก็มีวิธีการในการทำสิ่งนี้อยู่แล้วและเป็นรหัส 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 เอกสารประกอบ

แยกบิตแมปแบบสาธารณะคงที่ Thumbnail (แหล่งบิตแมปความกว้าง int ความสูง int)

เพิ่มในระดับ API 8 สร้างบิตแมปกึ่งกลางของขนาดที่ต้องการ

พารามิเตอร์แหล่งต้นฉบับบิตแมปความกว้างของแหล่งความสูงของเป้าหมายความสูงของเป้าหมายความสูงของเป้าหมาย

บางครั้งฉันได้รับข้อผิดพลาดหน่วยความจำไม่เพียงพอเมื่อใช้คำตอบที่ยอมรับและการใช้ ThumbnailUtils จะแก้ไขปัญหาเหล่านั้นให้ฉันได้ นอกจากนี้ยังสะอาดและนำกลับมาใช้ใหม่ได้มากขึ้น


6
+1 ฉันคิดว่าคุณจะต้องปรับปรุงโค้ดนี้และแทนที่จะใช้ 400px ให้ส่งผ่านขนาดที่สั้นที่สุดของ bmp เพื่อเป็นทางเลือกแทนโพสต์ต้นฉบับด้านบน แต่ขอขอบคุณสำหรับการนำสิ่งนี้มาสู่ความสนใจของเราดูเหมือนว่าเป็นฟังก์ชันที่มีประโยชน์มาก น่าเสียดายที่ฉันไม่เคยเห็นมาก่อน ...
Lumis

ขวาฉันเพียงแค่ hardcoded 400 เพื่อให้ตัวอย่างที่เป็นรูปธรรม ... รายละเอียดจะขึ้นอยู่กับ implementor :) ความ
DiscDev

4
@DiscDev - นี่จะต้องเป็นหนึ่งในคำตอบที่มีประโยชน์ที่สุดในเว็บไซต์นี้ทั้งหมด อย่างจริงจัง. มันแปลกกับคำถาม Android - คุณสามารถค้นหาสองชั่วโมงก่อนที่จะหาคำตอบที่ชัดเจนง่าย ๆ ไม่ทราบว่าจะขอบคุณมากพอ เงินรางวัลระหว่างทาง!
Fattie

2
คุณยังสามารถใช้ฟังก์ชั่นที่แตกต่างกันเล็กน้อยเพื่อรีไซเคิลบิตแมปเก่า: = ThumbnailUtils.extractThumbnail (บิตแมปความกว้างความสูง ThumbnailUtils.OPTIONS_RECYCLE_INPUT)
นักพัฒนา Android

1
นี่คือคำตอบที่ทรงพลังที่สุดที่ฉันเคยเห็นสำหรับคำถามเกี่ยวกับ Android ฉันได้เห็นวิธีการแก้ปัญหาที่แตกต่างกัน 20 วิธีในการปรับขนาด / ครอบตัด / ลดระดับ / ปรับขนาดและอื่น ๆ บน Bitmap บน Android คำตอบทั้งหมดนั้นแตกต่างกัน และอันนี้ใช้เวลาเพียง 1 บรรทัดและทำงานได้ตามที่คาดไว้ ขอบคุณ.
Alex Sorokoletov

12

คุณมีการพิจารณาการทำเช่นนี้จากlayout.xml? คุณสามารถตั้งค่าสำหรับคุณScaleTypeไปและกำหนดขนาดของภาพในภายในImageViewandroid:scaleType="centerCrop"ImageViewlayout.xml


ฉันลองความคิดนี้โดยมีข้อผิดพลาด OpenGLRenderer ต่อไปนี้: "บิตแมปที่ใหญ่เกินไปที่จะอัปโหลดลงในพื้นผิว (2432x4320, สูงสุด = 4096x4096)" ดังนั้นฉันเดาว่าไม่สามารถประมวลผลความสูง 4320 ได้
GregM

1
แน่นอนว่านี่เป็นคำตอบที่ถูกต้องและตอบคำถามที่สมบูรณ์แบบ! การปรับคุณภาพ / ขนาดของภาพให้เหมาะสมสำหรับภาพขนาดใหญ่ ... ก็เป็นคำถามที่แตกต่าง!
ichalos

@ichalos คุณอาจพบสิ่งที่คุณกำลังมองหา แต่นี่ไม่ได้ตอบคำถามเดิม คำถามเดิมเกี่ยวกับการเรนเดอร์ด้วยตนเอง .. จริง ๆ แล้วฉันไม่แน่ใจว่าใครที่พยายามครอบตัดรูปภาพครั้งแรกจะทำการเรนเดอร์ด้วยตนเองบนผ้าใบ แต่
ไง

@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
Stan

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);
}

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);
            }

นี่คือการแก้ไขปัญหา WillSteel ในกรณีนี้ tempBitmap เป็นเพียงสำเนาของบิตแมปดั้งเดิม (ไม่เปลี่ยนแปลง) หรือตัวมันเอง
Yman

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;
}

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.