在Android上将大型位图文件的大小调整为缩放输出文件

Man*_*uel 214 android resize image bitmap

我在一个文件中有一个大位图(比如3888x2592).现在,我想将该位图的大小调整为800x533并将其保存到另一个文件中.我通常会通过调用Bitmap.createBitmap方法来缩放位图,但它需要一个源位图作为第一个参数,我无法提供,因为将原始图像加载到Bitmap对象当然会超出内存(例如,请参见此处).

我也无法读取位图,例如,BitmapFactory.decodeFile(file, options)提供a BitmapFactory.Options.inSampleSize,因为我想将其调整为精确的宽度和高度.使用inSampleSize会将位图的大小调整为972x648(如果我使用inSampleSize=4)或778x518(如果我使用inSampleSize=5,它甚至不是2的幂).

我还想避免使用inSampleSize在第一步中使用例如972x648读取图像,然后在第二步中将其大小调整为800x533,因为与直接调整原始图像大小相比,质量会很差.

总结我的问题:是否有办法读取10MP或更高的大图像文件并将其保存到新的图像文件,调整大小到特定的新宽度和高度,而不会得到OutOfMemory异常?

我也尝试BitmapFactory.decodeFile(file, options)手动将Options.outHeight和Options.outWidth值设置为800和533,但它不起作用.

Jus*_*tin 144

不, 我喜欢有人纠正我,但我接受了你尝试作为妥协的加载/调整大小的方法.

以下是任何人浏览的步骤:

  1. 计算inSampleSize仍然产生大于目标的图像的最大可能值.
  2. 使用BitmapFactory.decodeFile(file, options)传入inSampleSize作为选项加载图像.
  3. 使用调整大小到所需的尺寸Bitmap.createScaledBitmap().

  • 据我所知,但不要让您阻止对此进行进一步的研究。 (2认同)

小智 96

贾斯汀回答翻译成代码(对我来说很完美):

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 当你使用像"b"这样的变量时很难阅读,但不是很好的答案. (15认同)
  • 请不要调用```System.gc()``` (2认同)

小智 43

这是'Mojo Risin'和'Ofir'的解决方案"合并".这将为您提供按比例调整大小的图像,其中包含最大宽度和最大高度的边界.

  1. 它只读取元数据以获得原始大小(options.inJustDecodeBounds)
  2. 它使用了一个rought resize来节省内存(itmap.createScaledBitmap)
  3. 它使用基于之前创建的粗略Bitamp的精确调整大小的图像.

对我而言,它在下面的5兆像素图像上表现良好.

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);
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 22

到目前为止,承认另一个优秀的答案,我已经看到的最好的代码是在照片拍摄工具的文档中.

请参阅标题为"解码缩放图像"的部分.

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

它提出的解决方案是像这里的其他解决方案那样调整大小的解决方案,但它非常整洁.

为方便起见,我将下面的代码复制为随时可用的功能.

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);
}
Run Code Online (Sandbox Code Playgroud)


Bos*_*one 21

为什么不使用API​​?

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

  • 因为它不会解决我的问题.这是:"......它需要一个源位图作为第一个参数,我无法提供,因为将原始图像加载到Bitmap对象当然会超出内存." 所以,我也不能将Bitmap传递给.createScaledBitmap方法,因为我仍然需要先将一个大图像加载到一个Bitmap对象中. (20认同)
  • 对.我重新阅读了你的问题,基本上(如果我理解的话)它可以归结为"我可以将图像调整到精确的尺寸,而无需将原始文件加载到内存中吗?" 如果是这样 - 我对图像处理的复杂性了解得不够,但有些东西告诉我1.它不能从API中获得,2.它不会是1-liner.我会将此标记为最喜欢的 - 看看你(或其他人)是否会解决这个问题会很有趣. (2认同)

pen*_*Dev 19

在阅读这些答案和Android文档后,这里是调整位图大小而不将其加载到内存中的代码:

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);
}
Run Code Online (Sandbox Code Playgroud)


Moj*_*sin 6

当我有大位图,我想解码它们调整大小时我使用以下

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);
Run Code Online (Sandbox Code Playgroud)

  • 你做什么'不工作'有什么不对? (2认同)
  • 你忘了包含`inJustDecodeBounds` :) (2认同)

Mus*_*key 5

这对于查看此问题的其他人可能很有用.我重写了Justin的代码,以允许该方法接收所需的目标大小对象.这在使用Canvas时非常有效.所有的功劳都应归功于JUSTIN的优秀初始代码.

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

Justin的代码非常有效地减少了使用大型Bitmaps的开销.