图片压缩简介:
图片的压缩:(分辨率压缩/像素压缩,质量压缩)
分辨率压缩个人理解:
利用 bitmap = BitmapFactory.decodeFile(path, newOpts.inSampleSize像素比率); 得到显示的bitmap,而且还可以进行输出的质量压缩,即利用
bitmap.compress(有损JPEG或无损压缩png,quality,);
质量压缩有两种:
有损压缩:将色相和色纯度相近的色素合并,使图片信息减少,达到降质压缩效果,仅仅通过减少重复达到缩小体积目的(如:JPEG 格式);
无损压缩:将相同的色素只保留一次,是对文件的数据存储方式进行优化,解压后仍能还原以前的像素数的压缩(如:PNG 格式);
也就是说,bitmap.compress()参数采用PNG格式的,就是默认使用的是无损压缩方式,同时透明度效果有效,而若采用JPEG格式的,默认使用的是有损压缩,无透明效果;
额外部分:
打印机使用 DPI 来确定图片的每一个像素和实际的一个长度单位(英寸)之间的大小比例(比如300-72 DPI)。
图片像素密度:单位英寸内像素点的个数;
图片分辨率:屏幕中横纵向像素值的乘机;
图片尺寸越大则像素密度越小,而分辨率其实还是没变的;
代码示例:
实例结果:
D/file1 图片未压缩大小: 31961088
D/file2:图片像素压缩后图片大小: 124848 文件经过图片解析,使得内存占用加大
D/file2:图片像素压缩后文件大小: 40185 图片文件在硬盘中存储大小,该文件大小大于其输出流大小
D/file3:图片质量压缩后图片大小: 124848 质量压缩后只是图片文件存储大小变小,而用于显示的分辨率未变
D/file3:图片质量压缩后文件大小: 23242
注意:保存图片的文件大小一定小于Bitmap大小,因为Bitmap是经过文件解析后显示的数字图片数据,而图片文件的输出流大小也一定会小于文件;
总结:
决定图片显示时占内存大小的决定因素,就是图片的分辨率,所以在图片显示时,可采用分辨率压缩(bitmap = BitmapFactory.decodeFile(path, newOpts.inSampleSize像素比率)),使图片显示时少占用空间;
若减小图片存储占用内存,可采用分辨率压缩和质量压缩(bitmap.compress(有损JPEG或无损压缩png,quality,))相结合方式实现;
图片的压缩:(分辨率压缩/像素压缩,质量压缩)
分辨率压缩个人理解:
利用 bitmap = BitmapFactory.decodeFile(path, newOpts.inSampleSize像素比率); 得到显示的bitmap,而且还可以进行输出的质量压缩,即利用
bitmap.compress(有损JPEG或无损压缩png,quality,);
质量压缩有两种:
有损压缩:将色相和色纯度相近的色素合并,使图片信息减少,达到降质压缩效果,仅仅通过减少重复达到缩小体积目的(如:JPEG 格式);
无损压缩:将相同的色素只保留一次,是对文件的数据存储方式进行优化,解压后仍能还原以前的像素数的压缩(如:PNG 格式);
也就是说,bitmap.compress()参数采用PNG格式的,就是默认使用的是无损压缩方式,同时透明度效果有效,而若采用JPEG格式的,默认使用的是有损压缩,无透明效果;
额外部分:
打印机使用 DPI 来确定图片的每一个像素和实际的一个长度单位(英寸)之间的大小比例(比如300-72 DPI)。
图片像素密度:单位英寸内像素点的个数;
图片分辨率:屏幕中横纵向像素值的乘机;
图片尺寸越大则像素密度越小,而分辨率其实还是没变的;
代码示例:
public class HeadPhotoUpload implements View.OnClickListener{
public Dialog picDialog;
public Activity activity;
public static final int RESULT_OK = -1;
protected static final int CHOOSE_PICTURE = 99;//选择相册中的图片作为头像
protected static final int TAKE_PICTURE = 100;//选择相册中的图片作为头像
private Bitmap bitmap;
File file;
private File file1;
private File file2;
private static HeadPhotoUpload instance = new HeadPhotoUpload();
public int targetH,targetW ;
private ImageView imageView3;
private HeadPhotoUpload() {}
public void setHW(int targetH ,int targetW){
this.targetH = targetH;
this.targetW = targetW;
}
public static HeadPhotoUpload getInstance() {
return instance;
}
//打开相册或相机
public void showChoosePicDialog(Activity activity) {
this.activity = activity;
//AlertDialog用的是v24的,若用v7的则无分割线显示,需要其他设置
View dialogContentView = LayoutInflater.from(activity).inflate(R.layout.userinfo_headphoto, null, false);
TextView takePhoto = (TextView) dialogContentView.findViewById(R.id.take_head_photo_tv);
TextView selectPhoto = (TextView) dialogContentView.findViewById(R.id.select_head_photo_tv);
TextView cancelTv = (TextView) dialogContentView.findViewById(R.id.cancel_dialog_tv);
takePhoto.setOnClickListener(this);
selectPhoto.setOnClickListener(this);
cancelTv.setOnClickListener(this);
picDialog = new Dialog(activity, R.style.AlertDialogStyle);
Window dialogWindow = picDialog.getWindow();
picDialog.setContentView(dialogContentView);
picDialog.setCancelable(true);
dialogWindow.setWindowAnimations(R.style.DialogBottom);
dialogWindow.setGravity(Gravity.BOTTOM);
picDialog.show();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.select_head_photo_tv: // 选择本地照片
localAlbumChoose();
break;
case R.id.take_head_photo_tv: // 拍照
cameraChoose();
break;
case R.id.cancel_dialog_tv: //取消头像dialog
break;
}
picDialog.dismiss();
}
private void cameraChoose() {
// 执行拍照前,判断SD卡是否存在
String SDState = Environment.getExternalStorageState();
if (!SDState.equals(Environment.MEDIA_MOUNTED)) {
ToastUtil.showContinuousToast("内存卡不存在");
return;
}
//动态权限检测及申请,未设置权限请求结果监听
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, 20);
return;
}
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Uri tempUri = Uri.fromFile(file); //指明路径能获得高清图片
// openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri);
activity.startActivityForResult(openCameraIntent, TAKE_PICTURE);
}
private void localAlbumChoose() {
Intent openAlbumIntent = new Intent(Intent.ACTION_GET_CONTENT);
openAlbumIntent.setType("image/*");
activity.startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);
}
public void onActivityResult(int requestCode, int resultCode, Intent data, ImageView toIv1,ImageView toIv2,ImageView toIv3) {
imageView3 = toIv3;
if (resultCode == RESULT_OK) { // 如果返回码是可以用的
switch (requestCode) {
case TAKE_PICTURE: //从相机中获取图片
Bundle bundle = data.getExtras();
if (bundle != null) {
bitmap = (Bitmap) bundle.get("data");
toIv1.setImageBitmap(bitmap);
toMakeSmallIV(bitmap,toIv2);
}
break;
case CHOOSE_PICTURE: //从相册中获取图片
ContentResolver resolver = activity.getContentResolver();
Uri originalUri = data.getData(); //相册中获取图片地址
if (originalUri != null) {
try {
bitmap = MediaStore.Images.Media.getBitmap(resolver, originalUri);
toIv1.setImageBitmap(bitmap);
toMakeSmallIV(bitmap,toIv2);
} catch (IOException e) {
e.printStackTrace();
}
}
break;
}
}
}
private void toMakeSmallIV(Bitmap image, ImageView toIv2) { //像素压缩(显示)
Log.d("file1 图片未压缩大小",String.valueOf(getBitmapSize(image)));
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
image.compress(Bitmap.CompressFormat.PNG, 100, baos);
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = true;
//(无损像素压缩模式)
BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toString().length(), newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;// be=1表示不缩放
if (h > targetH || w > targetW) {
final int heightRatio = Math.round((float) h / targetH);
final int widthRatio = Math.round((float) w / targetW);
be = heightRatio < widthRatio ? heightRatio : widthRatio;
}
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
image = BitmapFactory.decodeStream(isBm, null, newOpts);
// 压缩好比例大小后还可以再进行质量压缩
toIv2.setImageBitmap(image);
if (file1 == null) {//本地临时存储图片地址
file1 = new File(Environment.getExternalStorageDirectory(), "smalIV_1.jpg");
}
saveBitmap(image, file1);
int bitmapSize = getBitmapSize(image);
Log.d("file2:图片像素压缩后大小", String.valueOf(bitmapSize));
Log.d("file2:图片像素压缩后文件大小", String.valueOf(file1.length()));
compressImage(image);
}
public Bitmap compressImage(Bitmap image) { //图片质量压缩(输出)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 有损质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 100;
while (baos.toByteArray().length / 1024 > 50) { // 循环判断如果压缩后图片是否大于50kb,大于继续压缩
baos.reset();// 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//有损质量压缩
options -= 10;// 每次都减少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
image = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
if (file2 == null) {//本地临时存储图片地址
file2 = new File(Environment.getExternalStorageDirectory(), "smalIV_2.jpg");
}
saveBitmap(image, file2);
int bitmapSize = getBitmapSize(image);
Log.d("file3:图片质量压缩后大小",String.valueOf(bitmapSize));
Log.d("file3:图片质量压缩后文件大小",String.valueOf(file2.length()));
imageView3.setImageBitmap(image);
return image;
}
public static void saveBitmap(Bitmap bitmap, File file) {
FileOutputStream fos = null;
try {
File parentFile = file.getParentFile();
if(!parentFile.exists()){
parentFile.mkdirs();
}
fos = new FileOutputStream(file,false);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public File getFileMe() {
File ivFile;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
ivFile = new File(Environment.getExternalStorageDirectory(), "DCIM/camera");
else ivFile = Environment.getDataDirectory();
if (!ivFile.exists() || !ivFile.isDirectory()) ivFile.mkdirs();
return new File(ivFile, "out.jpg");
}
/**
* 得到bitmap的大小
*/
public static int getBitmapSize(Bitmap bitmap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //API 19
return bitmap.getAllocationByteCount();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {//API 12
return bitmap.getByteCount();
}
// 在低版本中用一行的字节x高度
return bitmap.getRowBytes() * bitmap.getHeight(); //earlier version
}
}
public byte[] getBody() throws AuthFailureError {
Map
params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
//即JsonRequest中的getBody()方法变成这样:
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
实例结果:
D/file1 图片未压缩大小: 31961088
D/file2:图片像素压缩后图片大小: 124848 文件经过图片解析,使得内存占用加大
D/file2:图片像素压缩后文件大小: 40185 图片文件在硬盘中存储大小,该文件大小大于其输出流大小
D/file3:图片质量压缩后图片大小: 124848 质量压缩后只是图片文件存储大小变小,而用于显示的分辨率未变
D/file3:图片质量压缩后文件大小: 23242
注意:保存图片的文件大小一定小于Bitmap大小,因为Bitmap是经过文件解析后显示的数字图片数据,而图片文件的输出流大小也一定会小于文件;
总结:
决定图片显示时占内存大小的决定因素,就是图片的分辨率,所以在图片显示时,可采用分辨率压缩(bitmap = BitmapFactory.decodeFile(path, newOpts.inSampleSize像素比率)),使图片显示时少占用空间;
若减小图片存储占用内存,可采用分辨率压缩和质量压缩(bitmap.compress(有损JPEG或无损压缩png,quality,))相结合方式实现;