Android 图片压缩技术

最近开发中有用到图片压缩技术,对此,抽空来写一篇对此的总结,方便后期的查看,如果对文中有什么疑惑,欢迎留言探讨。

Ps:最近忙项目真的是身心俱疲,好容易前天状态好一点,眼睛不知道怎么搞的,肿了,导致文章也是一拖再拖,好吧,放假就多写几篇吧,加油!

在看接下来的内容之前先了解下基础的图片知识

0.图片的基本知识

1.文件形式(存储在硬盘上的二进制文件)
获取大小(Byte):File.length()
2.流的形式(即以二进制形式存在于内存中)
获取大小(Byte):new FileInputStream(File).available()
3.Bitmap形式
获取大小(Byte):Bitmap.getByteCount()

看下图就知道3者的大小区别了


1、拍摄完的照片文件大小和读取到内存中的文件流大小是一样的,说明文件形式和流的形式对图片体积大小并没有影响。
2、当图片以Bitmap形式存在时,占用的内存就大的多了,为什么呢,首先我们需要知道Bitmap大小计算的方式
bitmap大小=图片长度(px)*图片宽度(px)*单位像素占用的字节数
单位像素所占字节数又是什么鬼,说白了就是图片的色彩模式。
在BitmapFactory.Options.inPreferredConfig这里可以找到,一共有4种, ARGB代表:A 透明度 , R 红色, G 绿色, B 蓝色


上面的bitmap在内存中的大小就可以计算了(默认色彩模式为ARGB_8888),

2368*4224*4/1024/1024=38.15625 M

由此可知看到bitmap占用内存消耗,所以用完调用Bitmap.recycle()是个好习惯,不过,要回收时一定要注意回收的时机,确保不再需要了才能去回收,否则会报错。


1.为什么要进行图片压缩

目的无非就2个,一,避免占用内存过多。二,可能要上传图片,如果图片太大,浪费流量。(有时候特殊需要需要上传原图除外)

(1)、避免内存过多的压缩方法:

归根结底,图片是要显示在界面组件上的,所以还是要用到bitmap,从上面可得出Bitmap的在内存中的大小只和图片尺寸和色彩模式有关,那么要想改变Bitmap在内存中的大小,要么改变尺寸,要么改变色彩模式。

(2)、避免上传浪费流量的压缩方法:

改变图片尺寸,改变色彩模式,改变图片质量都行。正常情况下,先改变图片尺寸和色彩模式,再改变图片质量。

2.图片压缩的方法

(1)改变图片质量的压缩方法

 /**
     * 
     * 根据bitmap压缩图片质量
     * @param bitmap 未压缩的bitmap
     * @return 压缩后的bitmap
     */
    public static Bitmap compressQuality(Bitmap bitmap){
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        int beginRate = 100;
        //第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差  ,第三个参数:保存压缩后的数据的流
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bOut);
        while(bOut.size()/1024/1024>100){  //如果压缩后大于100Kb,则提高压缩率,重新压缩
            beginRate -=10;
            bOut.reset();
            bitmap.compress(Bitmap.CompressFormat.JPEG, beginRate, bOut);
        }
        ByteArrayInputStream bInt = new ByteArrayInputStream(bOut.toByteArray());
        Bitmap newBitmap = BitmapFactory.decodeStream(bInt);
        if(newBitmap!=null){
            return newBitmap;
        }else{
            return bitmap;
        }
    }

(2) 改变图片大小的压缩方法

/**
	 * 压缩图片的尺寸
	 * 
	 * @param filePath
	 *            图片文件路径名
	 * @param savePath
	 *            要保存压缩后图片的路径名
	 * @return
	 */
	public static boolean compressSize(String filePath, String savePath) {
		OutputStream out = null;
		BitmapFactory.Options option = new BitmapFactory.Options();
		option.inJustDecodeBounds = true; // 设置为true,只读尺寸信息,不加载像素信息到内存
		Bitmap bitmap = BitmapFactory.decodeFile(filePath, option); // 此时bitmap为空
		option.inJustDecodeBounds = false;
		int bWidth = option.outWidth;
		int bHeight = option.outHeight;
		int toWidth = 400;
		int toHeight = 800;
		int be = 1; // be = 1代表不缩放
		if (bWidth / toWidth > bHeight / toHeight && bWidth > toWidth) {
			be = (int) bWidth / toWidth;
		} else if (bWidth / toWidth < bHeight && bHeight > toHeight) {
			be = (int) bHeight / toHeight;
		}
		option.inSampleSize = be; // 设置缩放比例
		Log.e("压缩大小---->", "" + be);
		bitmap = BitmapFactory.decodeFile(filePath, option);
		try {
			out = new FileOutputStream(new File(savePath));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return bitmap.compress(CompressFormat.JPEG, 100, out);
	}

一般情况下,要将两者结合使用,于是有了下面的算法,经过我的测试,此方法相比其他方法在保证了原图不失真的情况下,图片保存的尺寸是较小的。

/**
	 * 根据给定图片路径压缩图片并保存到指定路径
	 * @param imgpath
	 * @param outPath
	 */
	public static void compressImgUtil(String imgpath, String outPath) {
		File imgPath = new File(imgpath);
		long fileSize = imgPath.length();
		final long fileMaxSize = 400 * 1024;
		if (fileSize >= fileMaxSize) {
			BitmapFactory.Options options = new BitmapFactory.Options();
			options.inJustDecodeBounds = true;
			int height = options.outHeight;
			int width = options.outWidth;

			double scale = Math.sqrt((float) fileSize / fileMaxSize);
			options.outHeight = (int) (height / scale);
			options.outWidth = (int) (width / scale);
			options.inSampleSize = (int) (scale + 0.5);
			options.inJustDecodeBounds = false;

			Bitmap bitmap = BitmapFactory.decodeFile(imgpath, options);
			File fout = new File(outPath);

			FileOutputStream fos = null;
			try {
				fos = new FileOutputStream(fout);
				bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
				fos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Log.d("ssss", "" + fout.length());
			if (bitmap != null && !bitmap.isRecycled()) {
				bitmap.recycle();
				bitmap = null;
			}
		}
	}

有时,有的手机(平板)拍的照片是旋转了角度的,你得在上面合适的位置加上以下代码,将其旋转回来。

/**
	 * 获取指定路径图片的旋转角度
	 * 
	 * @param path
	 * @return 旋转的角度值
	 */
	private static int readPictureDegree(String path) {
		int degree = 0;
		try {
			ExifInterface exifInterface = new ExifInterface(path);
			int orientation = exifInterface.getAttributeInt(
					ExifInterface.TAG_ORIENTATION,
					ExifInterface.ORIENTATION_NORMAL);
			switch (orientation) {
			case ExifInterface.ORIENTATION_ROTATE_90:
				degree = 90;
				break;
			case ExifInterface.ORIENTATION_ROTATE_180:
				degree = 180;
				break;
			case ExifInterface.ORIENTATION_ROTATE_270:
				degree = 270;
				break;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return degree;
	}
然后根据旋转角度将给定的bitmap给调整过来

/**
	 * 根据角度值旋转所选的图片的bitmap
	 * 
	 * @param bitmap
	 * @param rotate
	 * @return 所选图片的bitmap
	 */
	private static Bitmap rotateBitmap(Bitmap bitmap, int rotate) {
		if (bitmap == null)
			return null;

		int w = bitmap.getWidth();
		int h = bitmap.getHeight();

		// Setting post rotate to 90
		Matrix mtx = new Matrix();
		mtx.postRotate(rotate);
		return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
	}

下面给出网上压缩bitmap的方法

public static void compressImage(String filePath, String fileName, int q)
			throws FileNotFoundException {

		Bitmap bm = getSmallBitmap(filePath);

		int degree = readPictureDegree(filePath);

		if (degree != 0) {// 旋转照片角度
			bm = rotateBitmap(bm, degree);
		}
		
		File outputFile = new File(fileName);

		FileOutputStream out = new FileOutputStream(outputFile);

		bm.compress(Bitmap.CompressFormat.PNG, q, out);

	}
其中readPictureDegree和rotateBitmap两个方法同上

getSmallBitmap方法如下

public static Bitmap getSmallBitmap(String filePath) {
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePath, options);//我不知道此处代码是什么意思,有知道的,麻烦留个言

		// Calculate inSampleSize inSampleSize为1表示宽度和高度不缩放,为2表示压缩后的宽度与高度为原来的1/2
		options.inSampleSize = calculateInSampleSize(options, 480, 800);

		// Decode bitmap with inSampleSize set
		options.inJustDecodeBounds = false;

		return BitmapFactory.decodeFile(filePath, options);
	}
但是我用上面网上的方法,效果没有我最上面写的那个压缩效果好。
好的,文章到此就结束了,有问题的可以留言,欢迎探讨!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值