Android端调整图片至目标文件大小的方案及逻辑缺陷问询
核心缺陷与盲区汇总
第一,公式推导存在数学错误
你推导缩放系数的时候混淆了像素缩放比例和文件大小比例的关系:
目标像素总数 = (原宽度 × 缩放系数s) × (原高度 × 缩放系数s)= 原像素总数 × s²
如果你假设文件大小和像素总数成正比,那么正确的缩放系数应该是s = sqrt(targetFilesize / originFilesize),而非直接取文件大小的比值。你当前的计算方式会直接把目标像素数压到预期的平方级,这是实际文件大小远低于目标值的核心原因之一。
比如你测试用例里目标文件大小是原大小的0.479倍,正确的缩放系数应该是√0.479≈0.692,你用了0.479的缩放系数,实际像素数只有原大小的0.479²≈0.23倍,远低于需求所需的像素数,白白浪费了画质。第二,代码存在逻辑错误
你已经通过createScaledBitmap生成了缩放后的scaledBitMap对象,但后续调用compress方法时用的还是未缩放的原bitmap对象,相当于白做了分辨率缩放操作,这也会导致最终输出结果和预期不符。如果是贴代码时的笔误还好,要是线上实际代码就是这么写的,会直接导致输出的分辨率和计算值完全不一致。第三,核心假设不成立
你认为同内容、同编码格式的图片压缩率恒定,这个假设在实际场景中几乎无法成立:- JPEG压缩率和图片的高频信息(纹理、细节、噪点)强相关,分辨率缩小后图片的高频信息会被采样过滤,压缩效率会明显高于原图,相同像素数的压缩后大小会更小
- 原图的JPEG压缩质量和你输出时用的100质量没有任何关联,两者的压缩率本来就不一致
- 图片的EXIF信息、缩略图、ICC色彩配置文件等元数据也会占用文件大小,这部分不会随分辨率缩放线性变化
第四,缺少兜底兼容逻辑
当前逻辑没有处理边界场景:如果按照计算值缩放后文件大小还是超过目标值(比如图片内容特别复杂,压缩率远低于预期),没有降级调整的逻辑,直接输出就会不符合要求;另外如果目标大小比原文件大,当前逻辑会无意义拉伸图片,反而导致文件大小变大。
优化建议
可以在现有逻辑基础上做少量调整即可覆盖大多数场景:
- 修正缩放系数计算逻辑,加上开平方操作
- 修正代码中压缩对象的错误,使用缩放后的bitmap执行压缩
- 增加二次调整逻辑:如果第一次压缩后的文件大小超过目标值,就按照实际大小和目标值的比值再次降低分辨率/调整压缩质量,循环1-2次基本就能命中目标大小,误差不会超过10%
- 如果不需要最高画质,可以把压缩质量从100降到85-90,肉眼几乎看不出差异,文件大小能小很多
内容的提问来源于stack exchange,提问作者chuan

