Thay đổi kích thước tệp bitmap lớn thành tệp đầu ra được thu nhỏ trên Android


218

Tôi có một bitmap lớn (giả sử 3888x2592) trong một tệp. Bây giờ, tôi muốn thay đổi kích thước bitmap đó thành 800x533 và lưu nó vào một tệp khác. Tôi thường sẽ chia tỷ lệ bitmap bằng Bitmap.createBitmapphương thức gọi nhưng nó cần một bitmap nguồn làm đối số đầu tiên, mà tôi không thể cung cấp vì việc tải hình ảnh gốc vào một đối tượng Bitmap dĩ nhiên sẽ vượt quá bộ nhớ ( ví dụ ở đây ).

Tôi cũng không thể đọc bitmap với, ví dụ BitmapFactory.decodeFile(file, options), cung cấp một BitmapFactory.Options.inSampleSize, vì tôi muốn thay đổi kích thước của nó thành chiều rộng và chiều cao chính xác. Việc sử dụng inSampleSizesẽ thay đổi kích thước bitmap thành 972x648 (nếu tôi sử dụng inSampleSize=4) hoặc 778x518 (nếu tôi sử dụng inSampleSize=5, thậm chí không phải là sức mạnh của 2).

Tôi cũng muốn tránh đọc hình ảnh bằng inSampleSize, ví dụ, 972x648 trong bước đầu tiên và sau đó thay đổi kích thước của nó thành chính xác 800x533 trong bước thứ hai, vì chất lượng sẽ kém so với thay đổi kích thước trực tiếp của hình ảnh gốc.

Để tóm tắt câu hỏi của tôi: Có cách nào để đọc tệp hình ảnh lớn có 10MP trở lên và lưu nó vào tệp hình ảnh mới, thay đổi kích thước thành chiều rộng và chiều cao mới cụ thể mà không cần ngoại lệ OutOfMemory không?

Tôi cũng đã thử BitmapFactory.decodeFile(file, options)và đặt các giá trị Options.outHeight và Options.outWidth theo cách thủ công thành 800 và 533, nhưng nó không hoạt động theo cách đó.


Không, outHeight và outWidth là các tham số out từ phương thức giải mã. Điều đó đang được nói, tôi có cùng một vấn đề với bạn, và tôi không hài lòng với cách tiếp cận 2 bước bao giờ.
rds

thông thường, cảm ơn chúa, bạn có thể sử dụng một dòng mã .. stackoverflow.com/a/17733530/294884
Fattie

Độc giả, xin lưu ý QA hoàn toàn quan trọng này !!! stackoverflow.com/a/24135522/294884
Fattie

1
Xin lưu ý rằng câu hỏi này hiện đã được 5 tuổi và giải pháp đầy đủ là .. stackoverflow.com/a/24135522/294884 Chúc mừng!
Fattie

2
Hiện tại đã có tài liệu chính thức về chủ đề đó: developer.android.com/training/displaying-bitmaps/
mẹo

Câu trả lời:


146

Không. Tôi yêu một người sửa lỗi cho tôi, nhưng tôi chấp nhận cách tiếp cận tải / thay đổi kích thước mà bạn đã thử như một sự thỏa hiệp.

Dưới đây là các bước cho bất kỳ ai duyệt:

  1. Tính toán tối đa có thể inSampleSizemà vẫn mang lại một hình ảnh lớn hơn mục tiêu của bạn.
  2. Tải hình ảnh bằng cách sử dụng BitmapFactory.decodeFile(file, options), chuyển inSampleSize dưới dạng tùy chọn.
  3. Thay đổi kích thước theo kích thước mong muốn bằng cách sử dụng Bitmap.createScaledBitmap().

Tôi đã cố gắng tránh điều đó. Vì vậy, không có cách nào để thay đổi kích thước trực tiếp một hình ảnh lớn chỉ trong một bước?
Manuel

2
Không phải tôi biết, nhưng đừng để điều đó ngăn bạn khám phá điều này hơn nữa.
Justin

Được rồi, tôi sẽ lấy điều này cho câu trả lời được chấp nhận của tôi cho đến nay. Nếu tôi tìm ra bất kỳ phương pháp nào khác, tôi sẽ cho bạn biết.
Manuel

Như PSIXO đã đề cập trong câu trả lời, bạn cũng có thể muốn sử dụng android: LargeHeap nếu bạn vẫn gặp sự cố sau khi sử dụng inSampleSize.
dùng276648

biến bitmap đang trống rỗng
Prasad

99

Justin trả lời dịch sang mã (hoạt động hoàn hảo cho tôi):

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();



    int scale = 1;
    while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", 
       orig-height: " + options.outHeight);

    Bitmap resultBitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        options = new BitmapFactory.Options();
        options.inSampleSize = scale;
        resultBitmap = BitmapFactory.decodeStream(in, null, options);

        // resize to desired dimensions
        int height = resultBitmap.getHeight();
        int width = resultBitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, 
           (int) y, true);
        resultBitmap.recycle();
        resultBitmap = scaledBitmap;

        System.gc();
    } else {
        resultBitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + 
       resultBitmap.getHeight());
    return resultBitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

15
Làm cho nó khó đọc khi bạn sử dụng các biến như "b" nhưng câu trả lời tốt không phải là ít.
Oliver Dixon

@Ofir: getImageUri (đường dẫn); những gì tôi phải vượt qua trong phương pháp này?
Biginner

1
Thay vì (w h) /Math.pow (scale, 2), sử dụng hiệu quả hơn (w h) >> scale.
david.perez

2
Đừng gọi System.gc()xin vui lòng
gw0

Cảm ơn @Ofir nhưng sự chuyển đổi này không bảo tồn hướng hình ảnh: - /
JoeCoolman

43

Đây là "giải pháp của Mojo Risin và" Ofir "kết hợp". Điều này sẽ cung cấp cho bạn một hình ảnh thay đổi kích thước tương ứng với các ranh giới của chiều rộng tối đa và chiều cao tối đa.

  1. Nó chỉ đọc dữ liệu meta để có được kích thước ban đầu (tùy chọn.inJustDecodeBound)
  2. Nó sử dụng một kích thước thay đổi kích thước để lưu bộ nhớ (itmap.createScaledBitmap)
  3. Nó sử dụng một hình ảnh được thay đổi kích thước chính xác dựa trên Bitamp thô được tạo trước đó.

Đối với tôi, nó đã hoạt động tốt trên 5 hình ảnh MegaPixel bên dưới.

try
{
    int inWidth = 0;
    int inHeight = 0;

    InputStream in = new FileInputStream(pathOfInputImage);

    // decode image size (decode metadata only, not the whole image)
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, options);
    in.close();
    in = null;

    // save width and height
    inWidth = options.outWidth;
    inHeight = options.outHeight;

    // decode full image pre-resized
    in = new FileInputStream(pathOfInputImage);
    options = new BitmapFactory.Options();
    // calc rought re-size (this is no exact resize)
    options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight);
    // decode full image
    Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

    // calc exact destination size
    Matrix m = new Matrix();
    RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
    RectF outRect = new RectF(0, 0, dstWidth, dstHeight);
    m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
    float[] values = new float[9];
    m.getValues(values);

    // resize bitmap
    Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    // save image
    try
    {
        FileOutputStream out = new FileOutputStream(pathOfOutputImage);
        resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    }
    catch (Exception e)
    {
        Log.e("Image", e.getMessage(), e);
    }
}
catch (IOException e)
{
    Log.e("Image", e.getMessage(), e);
}

23

Tại sao không sử dụng API?

int h = 48; // height in pixels
int w = 48; // width in pixels    
Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);

21
Bởi vì nó sẽ không giải quyết vấn đề của tôi. Đó là: "... nó cần một bitmap nguồn làm đối số đầu tiên, mà tôi không thể cung cấp vì tải hình ảnh gốc vào một đối tượng Bitmap tất nhiên sẽ vượt quá bộ nhớ." Vì vậy, tôi cũng không thể truyền Bitmap cho phương thức .createScaledBitmap, vì trước tiên tôi vẫn cần phải tải một hình ảnh lớn vào một đối tượng Bitmap.
Manuel

2
Đúng. Tôi đọc lại câu hỏi của bạn và về cơ bản (nếu tôi hiểu đúng), nó tự hào là "tôi có thể thay đổi kích thước hình ảnh thành kích thước chính xác mà không cần tải tệp gốc vào bộ nhớ không?" Nếu vậy - tôi không biết đủ về sự phức tạp của xử lý hình ảnh để trả lời nhưng có điều gì đó cho tôi biết rằng 1. nó không có sẵn từ API, 2. nó sẽ không phải là 1-liner. Tôi sẽ đánh dấu điều này là yêu thích - sẽ rất thú vị để xem nếu bạn (hoặc ai đó) sẽ giải quyết điều này.
Bostone

nó đã làm việc cho tôi vì tôi đang nhận được uri và chuyển đổi sang bitmap nên việc nhân rộng chúng là dễ dàng đối với tôi 1+ cho đơn giản nhất.
Hamza

22

Thừa nhận câu trả lời xuất sắc khác cho đến nay, mã tốt nhất tôi từng thấy cho điều này là trong tài liệu cho công cụ chụp ảnh.

Xem phần có tên "Giải mã hình ảnh thu nhỏ".

http://developer.android.com/training/camera/photobasics.html

Giải pháp mà nó đề xuất là một giải pháp thay đổi kích thước rồi thay đổi tỷ lệ như các giải pháp khác ở đây, nhưng nó khá gọn gàng.

Tôi đã sao chép mã dưới đây là một chức năng sẵn sàng để thuận tiện.

private void setPic(String imagePath, ImageView destination) {
    int targetW = destination.getWidth();
    int targetH = destination.getHeight();
    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    destination.setImageBitmap(bitmap);
}

1
Trước tiên, bạn đang tìm hiểu các số nguyên sẽ đưa ra kết quả. Thứ hai mã gặp sự cố với targetW hoặc targetH là 0 (mặc dù điều này không có nhiều ý nghĩa mà tôi biết). InSampleSize thứ ba sẽ là một sức mạnh của 2.
cybergen

Đừng hiểu lầm tôi. Điều này sẽ xác định tải một hình ảnh nhưng nếu đặt các ints thì có vẻ như vậy. Và đây cũng chắc chắn không phải là câu trả lời đúng vì hình ảnh sẽ không được thu nhỏ như mong đợi. Nó sẽ không làm gì cho đến khi chế độ xem hình ảnh bằng một nửa kích thước của hình ảnh hoặc nhỏ hơn. Sau đó, không có gì xảy ra cho đến khi xem hình ảnh là 1/4 kích thước của hình ảnh. Và như vậy với sức mạnh của hai!
cybergen

18

Sau khi đọc những câu trả lời và tài liệu android ở đây, mã để thay đổi kích thước bitmap mà không cần tải nó vào bộ nhớ:

public Bitmap getResizedBitmap(int targetW, int targetH,  String imagePath) {

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    //inJustDecodeBounds = true <-- will not load the bitmap into memory
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
    return(bitmap);
}

Xin lưu ý rằng bmOptions.inPurgizable = true; bị phản đối
Ravit

6

Khi tôi có bitmap lớn và tôi muốn giải mã chúng thay đổi kích thước, tôi sử dụng như sau

BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);

1
Vì inSampleSize là một Integer, nên bạn sẽ hiếm khi nhận được chiều rộng và chiều cao pixel chính xác mà bạn muốn nhận. Đôi khi bạn có thể đến gần, nhưng bạn cũng có thể ở rất xa, tùy thuộc vào số thập phân.
Manuel

Buổi sáng, tôi đã thử mã của bạn (bài viết ở trên trong chủ đề này), nhưng dường như không hoạt động, tôi đã làm sai ở đâu? Mọi đề xuất đều được chào đón :-)
RRTW

5

Điều này có thể hữu ích cho người khác nhìn vào câu hỏi này. Tôi viết lại mã của Justin để cho phép phương thức nhận đối tượng kích thước đích yêu cầu. Điều này hoạt động rất tốt khi sử dụng Canvas. Tất cả tín dụng nên đến JUSTIN cho mã ban đầu tuyệt vời của anh ấy.

    private Bitmap getBitmap(int path, Canvas canvas) {

        Resources resource = null;
        try {
            final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
            resource = getResources();

            // Decode image size
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(resource, path, options);

            int scale = 1;
            while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > 
                  IMAGE_MAX_SIZE) {
               scale++;
            }
            Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

            Bitmap pic = null;
            if (scale > 1) {
                scale--;
                // scale to max possible inSampleSize that still yields an image
                // larger than target
                options = new BitmapFactory.Options();
                options.inSampleSize = scale;
                pic = BitmapFactory.decodeResource(resource, path, options);

                // resize to desired dimensions
                int height = canvas.getHeight();
                int width = canvas.getWidth();
                Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

                double y = Math.sqrt(IMAGE_MAX_SIZE
                        / (((double) width) / height));
                double x = (y / height) * width;

                Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
                pic.recycle();
                pic = scaledBitmap;

                System.gc();
            } else {
                pic = BitmapFactory.decodeResource(resource, path);
            }

            Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
            return pic;
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(),e);
            return null;
        }
    }

Mã của Justin rất hiệu quả trong việc giảm chi phí làm việc với Bitmap lớn.


4

Tôi không biết liệu giải pháp của mình có phải là cách thực hành tốt nhất hay không, nhưng tôi đã đạt được việc tải một bitmap với tỷ lệ mong muốn của mình bằng cách sử dụng các tùy chọn inDensityinTargetDensity. inDensity0ban đầu khi không tải tài nguyên drawable, vì vậy phương pháp này là cho tải hình ảnh phi tài nguyên.

Các biến imageUri, maxImageSideLengthcontextlà các thông số của phương pháp của tôi. Tôi chỉ đăng bài thực hiện phương thức mà không có gói AsyncTask cho rõ ràng.

            ContentResolver resolver = context.getContentResolver();
            InputStream is;
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Options opts = new Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, opts);

            // scale the image
            float maxSideLength = maxImageSideLength;
            float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
            // do not upscale!
            if (scaleFactor < 1) {
                opts.inDensity = 10000;
                opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
            }
            opts.inJustDecodeBounds = false;

            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }
            try {
                is = resolver.openInputStream(imageUri);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Image not found.", e);
                return null;
            }
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }

            return bitmap;

2
Rất đẹp! Sử dụng inD mật độ thay vì Bitmap.createScaledBitmap đã tiết kiệm cho tôi rất nhiều bộ nhớ. Thậm chí tốt hơn kết hợp với inSamplesize.
Ostkontentitan

2

Có tính đến việc bạn muốn thay đổi kích thước thành kích thước chính xác và muốn giữ chất lượng nhiều như tôi cần, tôi nghĩ bạn nên thử điều này.

  1. Tìm hiểu kích thước của hình ảnh đã thay đổi kích thước với lệnh gọi của BitmapFactory.decodeFile và cung cấp checkSizeOptions.inJustDecodeBound
  2. Tính toán tối đa có thể inSampleSize bạn có thể sử dụng trên thiết bị của mình để không vượt quá bộ nhớ. bitmapSizeInBytes = 2 * width * height; Nói chung, đối với ảnh của bạn, inSampleSize = 2 sẽ ổn vì bạn sẽ chỉ cần 2 * 1944x1296) = 4,8Mbб nên đặt trong bộ nhớ
  3. Sử dụng BitmapFactory.decodeFile với inSampleSize để tải Bitmap
  4. Quy mô bitmap đến kích thước chính xác.

Động lực: nhân rộng nhiều bước có thể cung cấp cho bạn hình ảnh chất lượng cao hơn, tuy nhiên không có gì đảm bảo rằng nó sẽ hoạt động tốt hơn so với sử dụng inSampleSize cao. Trên thực tế, tôi nghĩ rằng bạn cũng có thể sử dụng inSampleSize như 5 (không phải pow của 2) để có tỷ lệ trực tiếp trong một thao tác. Hoặc chỉ sử dụng 4 và sau đó bạn chỉ có thể sử dụng hình ảnh đó trong UI. nếu bạn gửi nó đến máy chủ - hơn bạn có thể mở rộng theo kích thước chính xác ở phía máy chủ cho phép bạn sử dụng các kỹ thuật mở rộng nâng cao.

Lưu ý: nếu Bitmap được tải trong bước 3 lớn hơn ít nhất 4 lần (vì vậy, 4 * targetWidth <width), bạn có thể sử dụng một số thay đổi kích thước để đạt được chất lượng tốt hơn. ít nhất là hoạt động trong java chung, trong Android, bạn không có tùy chọn chỉ định nội suy được sử dụng để chia tỷ lệ http://today.java.net/pub/a/today/2007/04/03/perils-of- hình ảnh-gotcaledinstance.html


2

Tôi đã sử dụng mã như thế này:

  String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg";
  BitmapFactory.Options options=new BitmapFactory.Options();
  InputStream is=new FileInputStream(filePath);
  BitmapFactory.decodeStream(is, null, options);
  is.close();
  is=new FileInputStream(filePath);
  // here w and h are the desired width and height
  options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired...
  // bmp is the resized bitmap
  Bitmap bmp=BitmapFactory.decodeStream(is, null, options);
  is.close();
  Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());

Tôi đã thử hình ảnh gốc là 1230 x 1230 và nhận được bitmap là 330 x 330.
Và nếu thử 2590 x 3849, tôi sẽ nhận được OutOfMemoryError.

Tôi đã theo dõi nó, nó vẫn ném OutOfMemoryError trên dòng "BitmapFactory.decodeStream (là, null, tùy chọn);", nếu bitmap gốc quá lớn ...


2

Trên mã làm cho sạch hơn một chút. InputStreams cuối cùng cũng đóng gói để đảm bảo chúng cũng được đóng lại:

* Lưu ý
Nhập: InputStream là, int w, int h
Đầu ra: Bitmap

    try
    {

        final int inWidth;
        final int inHeight;

        final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");

        {

            final FileOutputStream tempOut = new FileOutputStream(tempFile);

            StreamUtil.copyTo(is, tempOut);

            tempOut.close();

        }



        {

            final InputStream in = new FileInputStream(tempFile);
            final BitmapFactory.Options options = new BitmapFactory.Options();

            try {

                // decode image size (decode metadata only, not the whole image)
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            // save width and height
            inWidth = options.outWidth;
            inHeight = options.outHeight;

        }

        final Bitmap roughBitmap;

        {

            // decode full image pre-resized
            final InputStream in = new FileInputStream(tempFile);

            try {

                final BitmapFactory.Options options = new BitmapFactory.Options();
                // calc rought re-size (this is no exact resize)
                options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                // decode full image
                roughBitmap = BitmapFactory.decodeStream(in, null, options);

            }
            finally {
                in.close();
            }

            tempFile.delete();

        }

        float[] values = new float[9];

        {

            // calc exact destination size
            Matrix m = new Matrix();
            RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
            RectF outRect = new RectF(0, 0, w, h);
            m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
            m.getValues(values);

        }

        // resize bitmap
        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

        return resizedBitmap;

    }
    catch (IOException e) {

        logger.error("Error:" , e);
        throw new ResourceException("could not create bitmap");

    }

1

Để chia tỷ lệ hình ảnh theo cách "chính xác", không bỏ qua bất kỳ pixel nào, bạn phải nối vào bộ giải mã hình ảnh để thực hiện hàng lấy mẫu xuống theo từng hàng. Android (và thư viện Skia làm cơ sở cho nó) không cung cấp các móc nối như vậy, vì vậy bạn phải tự cuộn. Giả sử bạn đang nói về hình ảnh jpeg, cách tốt nhất của bạn là sử dụng libjpeg trực tiếp, trong C.

Với sự phức tạp liên quan, sử dụng mẫu phụ hai bước-sau đó bán lại có lẽ là tốt nhất cho các ứng dụng loại xem trước hình ảnh.



1

Nếu bạn hoàn toàn muốn thực hiện thay đổi kích thước một bước, bạn có thể tải toàn bộ bitmap nếu android: LargeHeap = true nhưng như bạn thấy điều này không thực sự được khuyến khích.

Từ docs: android: LargeHeap Liệu các quy trình của ứng dụng của bạn có nên được tạo ra với một đống Dalvik lớn hay không. Điều này áp dụng cho tất cả các quy trình được tạo cho ứng dụng. Nó chỉ áp dụng cho ứng dụng đầu tiên được tải vào một quy trình; nếu bạn đang sử dụng ID người dùng chung để cho phép nhiều ứng dụng sử dụng một quy trình, tất cả họ phải sử dụng tùy chọn này một cách nhất quán nếu không họ sẽ có kết quả không thể đoán trước. Hầu hết các ứng dụng không cần điều này và thay vào đó nên tập trung vào việc giảm mức sử dụng bộ nhớ chung để cải thiện hiệu suất. Kích hoạt tính năng này cũng không đảm bảo tăng cố định bộ nhớ khả dụng, vì một số thiết bị bị hạn chế bởi tổng bộ nhớ khả dụng.



0

Điều này làm việc cho tôi. Hàm nhận được đường dẫn đến một tệp trên thẻ sd và trả về Bitmap ở kích thước hiển thị tối đa. Mã được lấy từ Ofir với một số thay đổi như tệp hình ảnh trên sd thay vì Ressource và witdth và heigth được lấy từ Đối tượng hiển thị.

private Bitmap makeBitmap(String path) {

    try {
        final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
        //resource = getResources();

        // Decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        int scale = 1;
        while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
                IMAGE_MAX_SIZE) {
            scale++;
        }
        Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);

        Bitmap pic = null;
        if (scale > 1) {
            scale--;
            // scale to max possible inSampleSize that still yields an image
            // larger than target
            options = new BitmapFactory.Options();
            options.inSampleSize = scale;
            pic = BitmapFactory.decodeFile(path, options);

            // resize to desired dimensions

            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.y;
            int height = size.x;

            //int height = imageView.getHeight();
            //int width = imageView.getWidth();
            Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height);

            double y = Math.sqrt(IMAGE_MAX_SIZE
                    / (((double) width) / height));
            double x = (y / height) * width;

            Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true);
            pic.recycle();
            pic = scaledBitmap;

            System.gc();
        } else {
            pic = BitmapFactory.decodeFile(path);
        }

        Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
        return pic;

    } catch (Exception e) {
        Log.e("TAG", e.getMessage(),e);
        return null;
    }

}

0

Đây là mã tôi sử dụng không có vấn đề gì khi giải mã hình ảnh lớn trong bộ nhớ trên Android. Tôi đã có thể giải mã hình ảnh lớn hơn 20MB miễn là các tham số đầu vào của tôi vào khoảng 1024x1024. Bạn có thể lưu bitmap được trả về vào một tệp khác. Bên dưới phương thức này là một phương pháp khác mà tôi cũng sử dụng để chia tỷ lệ hình ảnh thành một bitmap mới. Hãy sử dụng mã này như bạn muốn.

/*****************************************************************************
 * public decode - decode the image into a Bitmap
 * 
 * @param xyDimension
 *            - The max XY Dimension before the image is scaled down - XY =
 *            1080x1080 and Image = 2000x2000 image will be scaled down to a
 *            value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image - a value of "null" if there is an issue decoding
 *         image dimension
 * 
 * @throws FileNotFoundException
 *             - If the image has been removed while this operation is
 *             taking place
 */
public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException
{
    // The Bitmap to return given a Uri to a file
    Bitmap bitmap = null;
    File file = null;
    FileInputStream fis = null;
    InputStream in = null;

    // Try to decode the Uri
    try
    {
        // Initialize scale to no real scaling factor
        double scale = 1;

        // Get FileInputStream to get a FileDescriptor
        file = new File( this.imageUri.getPath() );

        fis = new FileInputStream( file );
        FileDescriptor fd = fis.getFD();

        // Get a BitmapFactory Options object
        BitmapFactory.Options o = new BitmapFactory.Options();

        // Decode only the image size
        o.inJustDecodeBounds = true;
        o.inPreferredConfig = bitmapConfig;

        // Decode to get Width & Height of image only
        BitmapFactory.decodeFileDescriptor( fd, null, o );
        BitmapFactory.decodeStream( null );

        if( o.outHeight > xyDimension || o.outWidth > xyDimension )
        {
            // Change the scale if the image is larger then desired image
            // max size
            scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) );
        }

        // Decode with inSampleSize scale will either be 1 or calculated value
        o.inJustDecodeBounds = false;
        o.inSampleSize = (int) scale;

        // Decode the Uri for real with the inSampleSize
        in = new BufferedInputStream( fis );
        bitmap = BitmapFactory.decodeStream( in, null, o );
    }
    catch( OutOfMemoryError e )
    {
        Log.e( DEBUG_TAG, "decode : OutOfMemoryError" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "decode : NullPointerException" );
        e.printStackTrace();
    }
    catch( RuntimeException e )
    {
        Log.e( DEBUG_TAG, "decode : RuntimeException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "decode : FileNotFoundException" );
        e.printStackTrace();
    }
    catch( IOException e )
    {
        Log.e( DEBUG_TAG, "decode : IOException" );
        e.printStackTrace();
    }

    // Save memory
    file = null;
    fis = null;
    in = null;

    return bitmap;

} // decode

LƯU Ý: Các phương thức không liên quan gì đến nhau ngoại trừ phương thức giải mã cuộc gọi createdScaledBitmap ở trên. Lưu ý chiều rộng và chiều cao có thể thay đổi từ hình ảnh gốc.

/*****************************************************************************
 * public createScaledBitmap - Creates a new bitmap, scaled from an existing
 * bitmap.
 * 
 * @param dstWidth
 *            - Scale the width to this dimension
 * @param dstHeight
 *            - Scale the height to this dimension
 * @param xyDimension
 *            - The max XY Dimension before the original image is scaled
 *            down - XY = 1080x1080 and Image = 2000x2000 image will be
 *            scaled down to a value equal or less then set value.
 * @param bitmapConfig
 *            - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444,
 *            Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 )
 * 
 * @return Bitmap - Image scaled - a value of "null" if there is an issue
 * 
 */
public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig )
{
    Bitmap scaledBitmap = null;

    try
    {
        Bitmap bitmap = this.decode( xyDimension, bitmapConfig );

        // Create an empty Bitmap which will contain the new scaled bitmap
        // This scaled bitmap should be the size we want to scale the
        // original bitmap too
        scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig );

        float ratioX = dstWidth / (float) bitmap.getWidth();
        float ratioY = dstHeight / (float) bitmap.getHeight();
        float middleX = dstWidth / 2.0f;
        float middleY = dstHeight / 2.0f;

        // Used to for scaling the image
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale( ratioX, ratioY, middleX, middleY );

        // Used to do the work of scaling
        Canvas canvas = new Canvas( scaledBitmap );
        canvas.setMatrix( scaleMatrix );
        canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) );
    }
    catch( IllegalArgumentException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" );
        e.printStackTrace();
    }
    catch( NullPointerException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" );
        e.printStackTrace();
    }
    catch( FileNotFoundException e )
    {
        Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" );
        e.printStackTrace();
    }

    return scaledBitmap;
} // End createScaledBitmap

việc tính toán công suất cho thang đo đơn giản là sai ở đây; chỉ cần sử dụng tính toán trên trang tài liệu android.
Fattie

0
 Bitmap yourBitmap;
 Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);

hoặc là:

 resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);

0

Tôi sử dụng Integer.numberOfLeadingZerosđể tính kích thước mẫu tốt nhất, hiệu suất tốt hơn.

Mã đầy đủ trong kotlin:

@Throws(IOException::class)
fun File.decodeBitmap(options: BitmapFactory.Options): Bitmap? {
    return inputStream().use {
        BitmapFactory.decodeStream(it, null, options)
    }
}

@Throws(IOException::class)
fun File.decodeBitmapAtLeast(
        @androidx.annotation.IntRange(from = 1) width: Int,
        @androidx.annotation.IntRange(from = 1) height: Int
): Bitmap? {
    val options = BitmapFactory.Options()

    options.inJustDecodeBounds = true
    decodeBitmap(options)

    val ow = options.outWidth
    val oh = options.outHeight

    if (ow == -1 || oh == -1) return null

    val w = ow / width
    val h = oh / height

    if (w > 1 && h > 1) {
        val p = 31 - maxOf(Integer.numberOfLeadingZeros(w), Integer.numberOfLeadingZeros(h))
        options.inSampleSize = 1 shl maxOf(0, p)
    }
    options.inJustDecodeBounds = false
    return decodeBitmap(options)
}

-2

Thay đổi kích thước bitmap bằng mã sau

    public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;        
    BitmapFactory.decodeFile(file.getPath(), options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(file.getPath(), options);
   }

    private static int calculateInSampleSize(
    BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     }

     return inSampleSize;
   }    

Điều tương tự cũng được giải thích trong mẹo / mẹo sau

http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.