我在一个文件中有一个大的位图(比如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,但它不是这样工作的。
承认到目前为止另一个很好的答案,我所见过的最好的代码是在拍照工具的文档中。
请参阅“解码缩放图像”一节。
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);
}
这是“Mojo Risin”和“Ofir”解决方案的“结合”。这将给你一个按比例调整图像的最大宽度和最大高度的边界。
它只读取元数据以获得原始大小(选项。
它使用粗略的调整大小来节省内存(itmap.createScaledBitmap)
它使用了基于之前创建的粗糙Bitamp精确调整大小的图像。
对我来说,它在500万像素以下的图像上表现良好。
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);
}
上面的代码更简洁一些。InputStreams最终关闭了包装,以确保它们也被关闭:
*请注意
输入:InputStream is, int w, int h
输出:位图
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");
}
在阅读了这些答案和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);
}