我在一个文件中有一个大的位图(比如3888x2592)。现在,我想将位图大小调整为800x533,并将其保存到另一个文件中。
我通常通过调用bitmap来缩放位图。createBitmap方法,但它需要一个源位图作为第一个参数,我不能提供,因为加载原始图像到位图对象当然会超过内存(例如,见这里)。
我也不能读取位图,例如,BitmapFactory.decodeFile(文件,选项),提供BitmapFactory.Options。inSampleSize,因为我想把它调整到精确的宽度和高度。使用inSampleSize将位图大小调整为972x648(如果我使用inSampleSize=4)或778x518(如果我使用inSampleSize=5,这甚至不是2的幂)。
我还希望避免在第一步中使用inSampleSize读取图像,例如,在第一步中使用972x648,然后在第二步中将其大小调整为800x533,因为与直接调整原始图像的大小相比,质量会较差。
总结一下我的问题:
是否有一种方法来读取一个10MP或更多的大图像文件,并将其保存到一个新的图像文件,调整到一个特定的新宽度和高度,而不得到OutOfMemory异常?
我还尝试了BitmapFactory.decodeFile(文件,选项)和设置选项。out theight and Options。outidth值手动为800和533,但它不是这样工作的。
我使用了这样的代码:
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());
我试过原始图像是1230 x 1230,得到的位图是330 x 330。
如果尝试2590 x 3849,我将得到OutOfMemoryError。
我跟踪了它,它仍然抛出OutOfMemoryError在行"BitmapFactory.decodeStream(is, null, options);",如果原始位图太大…
我使用Integer。numberOfLeadingZeros计算最佳样本量,性能更好。
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)
}
Bitmap yourBitmap;
Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);
or:
resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);
考虑到你想要调整大小到精确的大小,并想要保持尽可能多的质量,我认为你应该试试这个。
通过调用BitmapFactory.decodeFile并提供checkSizeOptions.inJustDecodeBounds来查找调整后图像的大小
计算您可以在设备上使用的不超过内存的inSampleSize的最大值。bitmapSizeInBytes = 2*width*height;一般来说,对于你的图片inSampleSize=2就可以了,因为你只需要2*1944x1296)=4.8Mbб,这应该是在内存中
使用BitmapFactory.decodeFile和inSampleSize来加载位图
将位图缩放到精确的大小。
动机:多步缩放可以提供更高质量的图片,但不能保证它会比使用high inSampleSize更好。
实际上,我认为你也可以使用inSampleSize 5(不是pow of 2)在一个操作中直接缩放。或者用4,然后你可以在UI中使用那个图像。如果你把它发送到服务器,那么你可以在服务器端进行精确的缩放,这允许你使用高级缩放技术。
注意:如果在步骤3中加载的位图至少是4倍大(所以4*targetWidth < width),你可能会使用几次调整来获得更好的质量。
至少这在通用java中是有效的,在android中你没有指定用于缩放的插值的选项
http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html