在 android 中减小图像大小而不损失其质量
Posted
技术标签:
【中文标题】在 android 中减小图像大小而不损失其质量【英文标题】:Decrease image size without losing its quality in android 【发布时间】:2015-04-10 02:06:52 【问题描述】:如何在 android 中减小图像尺寸而不损失其质量。 我的项目中有一个 ImageView,我必须在其中放入更大尺寸的图像。 我是安卓新手。 请帮忙。
【问题讨论】:
我使用了裁剪。但。裁剪图像会损害其质量..裁剪后我可以将图像添加到 imageview.但它会降低图像的质量..但是当我拍摄更大的图像时..图像没有显示.. 现在我怎样才能在不损失质量的情况下减小图像大小..任何人都可以提供一些示例代码..请帮助..提前谢谢.. 这样就可以了。 -> Bitmap.createScaledBitmap(original, (int) toWidth, (int) newHeight, false); 【参考方案1】:以下代码适用于比例图像作为纵横比:
Bitmap bitmapImage = BitmapFactory.decodeFile("Your path");
int nh = (int) ( bitmapImage.getHeight() * (512.0 / bitmapImage.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(bitmapImage, 512, nh, true);
your_imageview.setImageBitmap(scaled);
压缩您的图像而不会像 whatsapp 一样损失质量
public String compressImage(String imageUri)
String filePath = getRealPathFromURI(imageUri);
Bitmap scaledBitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
// by setting this field as true, the actual bitmap pixels are not loaded in the memory. Just the bounds are loaded. If
// you try the use the bitmap here, you will get null.
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(filePath, options);
int actualHeight = options.outHeight;
int actualWidth = options.outWidth;
// max Height and width values of the compressed image is taken as 816x612
float maxHeight = 816.0f;
float maxWidth = 612.0f;
float imgRatio = actualWidth / actualHeight;
float maxRatio = maxWidth / maxHeight;
// width and height values are set maintaining the aspect ratio of the image
if (actualHeight > maxHeight || actualWidth > maxWidth)
if (imgRatio < maxRatio)
imgRatio = maxHeight / actualHeight;
actualWidth = (int) (imgRatio * actualWidth);
actualHeight = (int) maxHeight;
else if (imgRatio > maxRatio)
imgRatio = maxWidth / actualWidth;
actualHeight = (int) (imgRatio * actualHeight);
actualWidth = (int) maxWidth;
else
actualHeight = (int) maxHeight;
actualWidth = (int) maxWidth;
// setting inSampleSize value allows to load a scaled down version of the original image
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
// inJustDecodeBounds set to false to load the actual bitmap
options.inJustDecodeBounds = false;
// this options allow android to claim the bitmap memory if it runs low on memory
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[16 * 1024];
try
// load the bitmap from its path
bmp = BitmapFactory.decodeFile(filePath, options);
catch (OutOfMemoryError exception)
exception.printStackTrace();
try
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
catch (OutOfMemoryError exception)
exception.printStackTrace();
float ratioX = actualWidth / (float) options.outWidth;
float ratioY = actualHeight / (float) options.outHeight;
float middleX = actualWidth / 2.0f;
float middleY = actualHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
// check the rotation of the image and display it properly
ExifInterface exif;
try
exif = new ExifInterface(filePath);
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION, 0);
Log.d("EXIF", "Exif: " + orientation);
Matrix matrix = new Matrix();
if (orientation == 6)
matrix.postRotate(90);
Log.d("EXIF", "Exif: " + orientation);
else if (orientation == 3)
matrix.postRotate(180);
Log.d("EXIF", "Exif: " + orientation);
else if (orientation == 8)
matrix.postRotate(270);
Log.d("EXIF", "Exif: " + orientation);
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix,
true);
catch (IOException e)
e.printStackTrace();
FileOutputStream out = null;
String filename = getFilename();
try
out = new FileOutputStream(filename);
// write the compressed bitmap at the destination specified by filename.
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
catch (FileNotFoundException e)
e.printStackTrace();
return filename;
public String getFilename()
File file = new File(Environment.getExternalStorageDirectory().getPath(), "MyFolder/Images");
if (!file.exists())
file.mkdirs();
String uriSting = (file.getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg");
return uriSting;
private String getRealPathFromURI(String contentURI)
Uri contentUri = Uri.parse(contentURI);
Cursor cursor = getContentResolver().query(contentUri, null, null, null, null);
if (cursor == null)
return contentUri.getPath();
else
cursor.moveToFirst();
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(index);
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
final float totalPixels = width * height;
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap)
inSampleSize++;
return inSampleSize;
【讨论】:
@Jitty Aandyan 如果图像尺寸和尺寸过小或过大,您的代码将如何反应......以及为什么 816x612 是硬编码的,我们可以通过编程方式计算它吗 基本上上面的代码用于在保持纵横比的情况下减小图像的大小。最重要的是每个图像都应该有特定的高度和宽度,这些是由您的应用程序的要求定义的。如果您为个人资料图片使用高质量图像,您可以将最大高度最大宽度更改为 300,因此它尊重您的实施。您还可以在压缩前检查实际高度和宽度,如果它大于您的要求,您可以进行压缩。 @JittyAandyan 它对画廊图像工作得非常好。但是如果我尝试缩小相机图像,它会扭曲图像质量并产生非常小的图像。知道如何解决这个问题吗? 看起来你有错误:float imgRatio = (float) actualWidth / (float) actualHeight;
该代码适用于纵向图像(actualWidth < actualHeight)
,但对于横向图像,两个(imgRatio vs maxRatio)
比较需要反转。【参考方案2】:
为 CompressFile 图像创建类
public class CompressFile
public static File getCompressedImageFile(File file, Context mContext)
try
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
if (getFileExt(file.getName()).equals("png") || getFileExt(file.getName()).equals("PNG"))
o.inSampleSize = 6;
else
o.inSampleSize = 6;
FileInputStream inputStream = new FileInputStream(file);
BitmapFactory.decodeStream(inputStream, null, o);
inputStream.close();
// The new size we want to scale to
final int REQUIRED_SIZE = 100;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE &&
o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
inputStream = new FileInputStream(file);
Bitmap selectedBitmap = BitmapFactory.decodeStream(inputStream, null, o2);
ExifInterface ei = new ExifInterface(file.getAbsolutePath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation)
case ExifInterface.ORIENTATION_ROTATE_90:
selectedBitmap = rotateImage(selectedBitmap, 90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
selectedBitmap = rotateImage(selectedBitmap, 180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
selectedBitmap = rotateImage(selectedBitmap, 270);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
break;
inputStream.close();
// here i override the original image file
File folder = new File(Environment.getExternalStorageDirectory() + "/FolderName");
boolean success = true;
if (!folder.exists())
success = folder.mkdir();
if (success)
File newFile = new File(new File(folder.getAbsolutePath()), file.getName());
if (newFile.exists())
newFile.delete();
FileOutputStream outputStream = new FileOutputStream(newFile);
if (getFileExt(file.getName()).equals("png") || getFileExt(file.getName()).equals("PNG"))
selectedBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
else
selectedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
return newFile;
else
return null;
catch (Exception e)
return null;
public static String getFileExt(String fileName)
return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
public static Bitmap rotateImage(Bitmap source, float angle)
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
matrix, true);
【讨论】:
【参考方案3】:使用此代码压缩较小尺寸的位图。
BitmapFactory.Options Options = new BitmapFactory.Options();
Options.inSampleSize = 4;
Options.inJustDecodeBounds = false;
action_bitmap = BitmapFactory.decodeFile(Glob.savedImage, Options);
Glob.savedImage 是图片的字符串路径。
【讨论】:
【参考方案4】://我们将从已经打开文件的原始图片开始
File imgFileOrig = getPic(); //change "getPic()" for whatever you need to open the image file.
Bitmap b = BitmapFactory.decodeFile(imgFileOrig.getAbsolutePath());
// original measurements
int origWidth = b.getWidth();
int origHeight = b.getHeight();
final int destWidth = 600;//or the width you need
if(origWidth > destWidth)
// picture is wider than we want it, we calculate its target height
int destHeight = origHeight/( origWidth / destWidth ) ;
// we create an scaled bitmap so it reduces the image, not just trim it
Bitmap b2 = Bitmap.createScaledBitmap(b, destWidth, destHeight, false);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// compress to the format you want, JPEG, PNG...
// 70 is the 0-100 quality percentage
b2.compress(Bitmap.CompressFormat.JPEG,70 , outStream);
// we save the file, at least until we have made use of it
File f = new File(Environment.getExternalStorageDirectory()
+ File.separator + "test.jpg");
f.createNewFile();
//write the bytes in file
FileOutputStream fo = new FileOutputStream(f);
fo.write(outStream.toByteArray());
// remember close de FileOutput
fo.close();
【讨论】:
【参考方案5】:从图库中获取图像时,接受的答案可以正常工作,但是当尝试从文件管理器中获取图像时,它会失败。原因是 Uri 的格式不同,getRealPathFromURI(imageUri)
无法从中获取真实路径。
为了解决这个问题,除了copressImage()
和getRealPathFromURI()
之外,使用与接受的答案相同的方法,它们经过调整以支持从文件管理器和图库中抓取的两个图像:
public static String compressImage(Context context, Uri uri)
String filePath = getRealPathFromURI(context, uri);
Bitmap scaledBitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
// by setting this field as true, the actual bitmap pixels are not loaded in the memory. Just the bounds are loaded. If
// you try the use the bitmap here, you will get null.
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(filePath, options);
int actualHeight = options.outHeight;
int actualWidth = options.outWidth;
// max Height and width values of the compressed image is taken as 816x612
float maxHeight = 816.0f;
float maxWidth = 612.0f;
float imgRatio = actualWidth / actualHeight;
float maxRatio = maxWidth / maxHeight;
// width and height values are set maintaining the aspect ratio of the image
if (actualHeight > maxHeight || actualWidth > maxWidth)
if (imgRatio < maxRatio)
imgRatio = maxHeight / actualHeight;
actualWidth = (int) (imgRatio * actualWidth);
actualHeight = (int) maxHeight;
else if (imgRatio > maxRatio)
imgRatio = maxWidth / actualWidth;
actualHeight = (int) (imgRatio * actualHeight);
actualWidth = (int) maxWidth;
else
actualHeight = (int) maxHeight;
actualWidth = (int) maxWidth;
// setting inSampleSize value allows to load a scaled down version of the original image
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
// inJustDecodeBounds set to false to load the actual bitmap
options.inJustDecodeBounds = false;
// this options allow android to claim the bitmap memory if it runs low on memory
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[16 * 1024];
try
// load the bitmap from its path
bmp = BitmapFactory.decodeFile(filePath, options);
catch (OutOfMemoryError exception)
exception.printStackTrace();
try
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
catch (OutOfMemoryError exception)
exception.printStackTrace();
float ratioX = actualWidth / (float) options.outWidth;
float ratioY = actualHeight / (float) options.outHeight;
float middleX = actualWidth / 2.0f;
float middleY = actualHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
// check the rotation of the image and display it properly
ExifInterface exif;
try
exif = new ExifInterface(filePath);
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION, 0);
Log.d("EXIF", "Exif: " + orientation);
Matrix matrix = new Matrix();
if (orientation == 6)
matrix.postRotate(90);
Log.d("EXIF", "Exif: " + orientation);
else if (orientation == 3)
matrix.postRotate(180);
Log.d("EXIF", "Exif: " + orientation);
else if (orientation == 8)
matrix.postRotate(270);
Log.d("EXIF", "Exif: " + orientation);
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix,
true);
catch (IOException e)
e.printStackTrace();
FileOutputStream out = null;
String filename = getFilename();
try
out = new FileOutputStream(filename);
// write the compressed bitmap at the destination specified by filename.
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
catch (FileNotFoundException e)
e.printStackTrace();
return filename;
public static String getRealPathFromURI(Context context, Uri uri)
Uri queryUri = MediaStore.Files.getContentUri("external");
String columnData = MediaStore.Files.FileColumns.DATA;
String columnSize = MediaStore.Files.FileColumns.SIZE;
String[] projectionData = MediaStore.Files.FileColumns.DATA;
String name = null;
String size = null;
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if ((cursor != null) && (cursor.getCount() > 0))
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
cursor.moveToFirst();
name = cursor.getString(nameIndex);
size = cursor.getString(sizeIndex);
cursor.close();
String imagePath = "";
if ((name != null) && (size != null))
String selectionNS = columnData + " LIKE '%" + name + "' AND " + columnSize + "='" + size + "'";
Cursor cursorLike = context.getContentResolver().query(queryUri, projectionData, selectionNS, null, null);
if ((cursorLike != null) && (cursorLike.getCount() > 0))
cursorLike.moveToFirst();
int indexData = cursorLike.getColumnIndex(columnData);
if (cursorLike.getString(indexData) != null)
imagePath = cursorLike.getString(indexData);
cursorLike.close();
return imagePath;
【讨论】:
actualHeight & actualWidth 是 0 例外【参考方案6】:要使用 JPEG 压缩来压缩图像并像 PNG 一样保持透明度,请使用此代码。它对我有用。
Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
Canvas canvas = new Canvas(newBitmap);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bitmap, 0, 0, null);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
newBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
【讨论】:
以上是关于在 android 中减小图像大小而不损失其质量的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android 上旋转 JPEG 文件而不损失质量和增加文件大小?