图片压缩

参考框架:

个人实现(kotlin语言)

https://github.com/yline/as_x_modlestudy/blob/master/System/AppOther/Manager/src/main/java/com/manager/compress/CompressManager.kt


    前言

    图片可以划分为:内存中的图片大小(bitmap)和 图片文件大小。bitmap大小是从文件中读取时,加载到内存中,若过大,则容易导致OOM问题。而图片文件大小,在上传到服务器时,一般也是需要限定图片大小的。


公式基础

1,Bitmap大小 = 【宽 * 高 * 单个像素点大小(色深)】

1)ARGB_8888单个像素点大小 为 4byte; RGB_565单个像素点大小 为 2byte


2,图片文件大小 约等于 【宽 * 高 * (位深/8) * (1 - 压缩算法效率)】
1)bmp位图,储存时,无压缩
2)jpg、webp、png格式文件大小,小于bmp位图的文件大小,则是由图像压缩算法造成
3) 位深大小,一般为:8位、16位、24位

    需要探索两个问题,1,bitmap压缩和图片文件压缩   2,色深和位深的异同

Android Bitmap压缩

1)改变 宽、高;加载时,指定option.inSampleSize,加载时,对文件进行采样加载
val option = BitmapFactory.Options()
option.inSampleSize = scale
return BitmapFactory.decodeFile(sourcePath, option)

2)改变 单个像素点大小,指定option.inPreferredConfig,加载时,确定将要加载的单个像素点大小

val option = BitmapFactory.Options()
option.inPreferredConfig = Bitmap.Config.RGB_565
return BitmapFactory.decodeFile(sourcePath, option)

Android 图片文件压缩

1)改变 宽、高;加载或生成bitmap时,指定新的宽、高
2)改变 位深;【很遗憾,经测试,这无法通过代码改变】
3)改变 压缩算法效率
    * 改变 图片格式,不同图片格式对应的压缩效率不同,但一般而言,效率越高、耗时越长
    * 改变 储存时清晰度,即Android中常用的,保存图片的quality(这是通过降低图片的局部清晰度,实现的有损压缩)


压缩文件实现

1)采用的压缩策略有  ①改变宽高(计算采样率) ②改变图片格式 ③改变储存时清晰度

val scale = computeScale(sourcePath) // 计算采样率
val sourceBitmap = loadBitmap(sourcePath, scale) // 加载出 bitmap,第一次压缩
saveBitmap(sourceBitmap, Bitmap.CompressFormat.JPEG, quality, resultFile) // 保存bitmap,第二次压缩

2)动态计算采样率,参考Luban

    /**
     * 依据图片宽高,确定图片采样率
     * @param sourcePath 输入的文件
     */
    fun computeScale(sourcePath: String): Int {
        val option = BitmapFactory.Options()
        option.inJustDecodeBounds = true
        option.inSampleSize = 1
        BitmapFactory.decodeFile(sourcePath, option)
        // 原始宽高、进行奇偶处理
        val sourceWidth = if (option.outWidth % 2 == 1) option.outWidth + 1 else option.outWidth
        val sourceHeight = if (option.outHeight % 2 == 1) option.outHeight + 1 else option.outHeight
        val longSide = Math.max(sourceWidth, sourceHeight)
        val shortSide = Math.min(sourceWidth, sourceHeight)
        val scale = shortSide.toFloat() / longSide
        if (scale <= 1 && scale > 0.5625) {
            if (longSide < 1664) {
                return 1
            } else if (longSide < 4990) {
                return 2
            } else if (longSide > 4990 && longSide < 10240) {
                return 4
            } else {
                return if (longSide / 1280 == 0) 1 else longSide / 1280
            }
        } else if (scale <= 0.5625 && scale > 0.5) {
            return if (longSide / 1280 == 0) 1 else longSide / 1280
        } else {
            return Math.ceil(longSide / (1280.0 / scale)).toInt()
        }
    }
3) 加载出图片,因为是采样率可能不为1,因此,加载出来的bitmap宽高,其实和文件宽高已经不一样了;也就起到了修改图片宽高的作用
    fun loadBitmap(sourcePath: String, scale: Int): Bitmap {
        val option = BitmapFactory.Options()
        option.inJustDecodeBounds = false
        option.inSampleSize = scale
        return BitmapFactory.decodeFile(sourcePath, option)
    }
4)bitmap本身是没有格式的,确定输出的格式,即改变了
    /**
     *  保存图片,参数未做校验
     *  @param bitmap 准备保存的图片
     *  @param format 图片的格式
     *  @param quality 质量压缩的参数【quality(1-100),100表示不压缩】
     *  @param resultFile 输出到的文件夹
     */
    fun saveBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int, resultFile: File): Boolean {
        // 保存图片,并进行压缩
        var isSuccess = false
        var fileOutputStream: FileOutputStream? = null
        try {
            fileOutputStream = FileOutputStream(resultFile)
            isSuccess = bitmap.compress(format, quality, fileOutputStream)
        } catch (ex: IOException) {
            ex.printStackTrace()
        } finally {
            if (null != fileOutputStream) {
                fileOutputStream.flush()
                fileOutputStream.close()
            }
        }
        return isSuccess
    }

色深和位深,有什么异同

百度百科的结果:
位深:计算机储存每个像素所需要的大小
色深:计算机显示出影像时,每个像素所需要的大小
因此,可以看到,他们描述的内容相同,都是单个像素。但是状态不一样,位深是储存,而色深是显示

测试方案:
Step 1,从本地加载图片文件到内存,为loadBitmap
Step 2,分别按照ARGB_8888和RGB_565格式,生成bitmap8888和bitmap565,并记录内存大小
Step 3,分别将bitmap8888和bitmap565,储存为bmp格式,分别为文件:file8888和file565
说明:
1) 储存为bmp格式,以防不同格式,对压缩算法产生影响(实际上的确会产生影响)
2)若file8888和bitmap8888大小相同,而file565和bitmap565大小相同,则说明,位深等同于色深
不然,位深不等同于色深。

测试结果
文件原始文件
(宽 * 高 大小)
file8888  
(宽 * 高 大小)
bitmap8888
(大小)
file565
(宽 * 高 大小)
bitmap565
(大小)
A3120*4160 3597k1560*2080 9506k12675k1560*2080 9506k6337k
B4912*3264 2414k2456*1632 1174215657k2456*1632 117427828k
C314*9675 1747k413*9675 11715k15608k413*9675 11715k7804k
结果分析【每组数据都满足,因此按照A分析】
已知:ARGB_8888 = 4, RGB = 2
1,1560 * 2080 * 4 / 1024 = 12675,符合公式: Bitmap大小 = 【宽 * 高 * 单个像素点大小(色深)】
2,file8888 != bitmap8888, file565 != bitmap565,说明位深不等同于色深
3,file8888/3 = bitmap8888/4, file565/3 = bitmap565/3
可以分析出,位深都为24位,与生成bitmap的色深格式无关。而我们实际加载的图片是相同的,因此可以理解成"位深是图片产生时携带的参数"

测试结论:
位深:是计算机储存像素的单位大小,由PhotoShop等软件生成图片时,就确定了通道个数,确定了大小
色深:是计算机显示像素的单位大小,可以在加载图片时,选择加载格式确定大小
位深 != 色深

本文总结:

1,公式

1)内存:Bitmap大小 = 【宽 * 高 * 单个像素点大小(色深)】

2)文件:图片大小 约等于 【宽 * 高 * (位深/8) * (1 - 压缩算法效率)】

2,位深和色深不同,位深在产生原始图时就已经确定了。可以判断出,图片文件的大小只取决于宽、高和压缩算法效率

3,实现了将图片进行压缩,链接为:CompressManager.kt 









  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值