图片压缩2
# 图片压缩
# 背景
成像系统两大核心: 镜头+感光元器件
底大一级压死人
小米11发布会上:
一般全画幅单反/微单相机,厂家设置的有效像素为2400万像素 cmos面积:864mm2 每平方毫米上2.78个像素
asp-c 有效像素为2400万像素 cmos面积 332.3mm2 每平方毫米上7.23个像素
华为mate40 pro 1/1.28英寸 5000万像素,常用1000w像素 cmos面积192.11mm2 每平方毫米26个/5.2个像素
如果按全画幅单反的解析度,华为mate40 pro 应该配500万像素,而不是5000万.
小米10 pro 1/1.33英寸 (15.28mmx11.46mm) 一亿像素 cmos面积175.09mm2 每平方毫米上57个像素
(1英寸约等于25.4mm)
从数值上看,手机1000w像素拍出来的放到最大,效果应该和asp-c 2400万像素放到最大的效果差不多
# but: 细节见功夫
放大到100%, 手机拍照的效果比相机差太多了.不是一个档次
# 压缩算法/图片格式
gif:动图格式,兼容性好,画面质量较差.
png: 可带透明通道.文件偏大.无损压缩.
jpeg:无与伦比的兼容性,非常不错的压缩比. 有损压缩.
webp: 谷歌主推,没有流行起来
Guetzli: 谷歌出的玩具算法,以更高的压缩比压成jpg格式,但极度耗内存和cpu,基本不具有工业价值.
hevc/heif: h.265对应的图片压缩算法,有版权问题. 腹死胎中,只有苹果玩了几个版本.
avif: h.266对应的图片压缩算法,全开源. 压缩比超高,不惧反复压缩.各巨头火热开发中,很有前景.
# 质量压缩
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
quality: 取值0-100.
一般来说,手机/相机拍照的质量为96,98.
其实肉眼看,80和100并无明显差别.
用于存硬盘收藏的图片,推荐80-90
用于聊天,晒朋友圈之类的上传,推荐70
用于跑算法模型的上传,具体看算法的要求. 可以使用80-90的webp
![image-20210322174334485](/Users/hss/Library/Application Support/typora-user-images/image-20210322174334485.png)
# 图片的缩与放
向下采样 向上插值
相关算法:
单线性/最近邻: 速度最快,但可能失真,有明显锯齿
双线性
双三次/双立方
Lanczos
其中,Android只实现了单线性和双线性.
且直接读流的向下采样时,只支持单线性采样.
# 缩小: 向下采样:
# 放大: 向上插值
# Android里的单线性/双线性操作:
# 从流中解码bitmap:
BitmapFactory.Options options = new BitmapFactory.Options();
//或者 inDensity 搭配 inTargetDensity 使用,算法和 inSampleSize 一样
options.inSampleSize = 2;//向下采样,仅支持单线性采样
Bitmap compress = BitmapFactory.decodeFile("/sdcard/test.png", options);
2
3
4
# bitmap的scale操作:
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Bitmap compress = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, true);//最后一个参数 true代表使用双线性,false:使用单线性
2
或者使用matrix操作:
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
bm = Bitmap.createBitmap(bitmap, 0, 0, bit.getWidth(), bit.getHeight(), matrix, true);
//最后一个参数 true代表使用双线性,false:使用单线性
2
3
4
5
# bitmap在view上的绘制:
当把一个bitmap对象绘制到比bitmap尺寸大的view上时,默认使用单线性插值.此时锯齿明显
如果要使用双线性,重写view的onPreDraw(canvas)方法,给canvas加上抗锯齿即可:
canvas.setDrawFilter(new PaintFlagsDrawFilter(0,Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
下面是一朵花放大四倍的对比:
更多效果见:QQ音乐团队分享:Android中的图片压缩技术详解(下篇) (opens new window)
在Android上,图片缩放推荐使用双线性
# jpeg压缩算法的改进
JPEG压缩算法的过程有:
色彩模型变换(bitmap的RGB转YUV),离散余弦变换、量化、Z字形编码、游程编码、Huffman编码
Android上开启霍夫曼压缩 : 7.0开始已经自动开启.
自适应的量化表: 不使用jpeg算法内置的两张量化表,而针对特定类型图片使用调教好的量化表
# 图像质量评价
参考
无参考评价图像质量(主观评价客观化) (opens new window)
指标/维度:
此处不关注图片本身质量,只关注jpg压缩过程中量化表所得压缩质量
# 1 单图质量评价
# 量化因子相关的质量
可用于避免重复压缩
已知图像和量化表,反向估算量化因子,得到图片质量的估计值.
可使用库: https://github.com/sephiroth74/Android-Exif-Extended 里的
int jpeg_quality = exif.getQualityGuess()
或者使用如何获取JPEG图片质量和预测压缩图片大小 (opens new window)中单独的算法
用c语言从JPEG(文件后缀为.jpg)中提取jpeg压缩的亮度和色度量化矩阵 (opens new window)
两个算法的测试结果如下:
实际压缩质量50以上,可准确判断质量,50以下,已无法准确判断.
但是,这个准确也是取决于jpg压缩的实际算法,如果使用的是libjpeg,反推出的量化因子就准确.
如果使用的是mozjpeg,那么反推出的质量也会不准确:
比如,使用squoosh里的mozjpeg压缩一张图到75的质量,计算出的质量因子为:
# 工业上可用的两个质量评价指标
亮度: 计算平均亮度或者ROI的平均亮度, 绝对值可用.
模糊程度(高斯模糊): opencv的拉普拉斯算子计算可得.可用于相对比较.
# 压缩前后质量比对评价
# Butteraugli
Butteraugli 项目是一个图片差异比较库,用于测试图片的心理视觉误差阈值,即查看者开始注意到图片质量下降的点。换句话说,此项目试图量化您的压缩图片的失真程度。
# SSIM (Structural SIMilarity) 结构相似性
PNSR一般用于视频质量快速评价 图像质量评价指标之 PSNR 和 SSIM (opens new window)
# exif
https://zh.wikipedia.org/wiki/Exif
不仅仅是jpg图片可以有exif,webp也可以有
可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机 (opens new window)的照片设定的文件格式 (opens new window),可以记录数码照片的属性信息和拍摄数据。
Exif最初由日本电子工业发展协会 (opens new window)在1996年制定,版本为1.0。1998年,升级到2.1版,增加了对音频文件的支持。2002年3月,发表了2.2版。
Exif可以附加于JPEG (opens new window)、TIFF (opens new window)、RIFF (opens new window)等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。
Windows 7 (opens new window)操作系统具备对Exif的原生支持,通过鼠标右键点击图片打开菜单,点击属性并切换到详细信息标签下即可直接查看Exif信息。
Exif信息是可以被任意编辑的,因此只有参考的功能。
Exif信息以0xFFE1作为开头标记,后两个字节表示Exif信息的长度。所以Exif信息最大为64 kB,而内部采用TIFF格式。
# 读写工具
Android: androidx.exifinterface.media.ExifInterface 或者Android-Exif-Extended (opens new window)
win/Mac: 命令行工具-可跨平台,跨语言使用: exiftool (opens new window)
题外话: 命令行工具的跨平台,跨语言使用
Mac/windows/linux java,js,Python,c,c++
Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec("cmd.exe /c dir d:\\"); //exec(String command, String[] envp, File dir) InputStream inputStream = process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"gb2312")); String line = null; while((line = br.readLine()) != null) { System.out.println(line); } //表现形式上,命令行工具接受的string[]参数,与java的main方法入口类似: 在命令行: java -Xms128m -Xmx256m -jar xxx.jar public static void main(string[] args){ //args: 可以拿到执行java命令时传入的各种参数,对这些参数做出一些处理. } //用java包装命令行工具的方法: 将丰富的参数包装成builder模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
python里读写exif而不改变图片数据区如何实现?
思路: 先把整个文件拷过去,然后把exif读到内存,内存修改exif,再把修改后的exif写入到拷贝的文件中,这样就不会改变图片质量了.
不要用PIL,有坑,会改变图片质量
import piexif
from shutil import copyfile
import exifread
original = "IMG_20200628_111551.jpg"
target = "IMG_20200628_111551-3.jpg"
copyfile(original, target)
exif_dict = piexif.load(original)
exif_dict["0th"][piexif.ImageIFD.Copyright] = "lalalla----"
piexif.insert(piexif.dump(exif_dict), target)
# 打印
f = open(target, 'rb')
tags = exifread.process_file(f)
print(tags.__len__())
for tag in tags.keys():
print(" {}: {}".format(tag, tags[tag]))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 压缩策略
# 压缩比的计算
参考flutter_image_compress (opens new window)
// Using dart as an example, the actual implementation is Kotlin or OC.
import 'dart:math' as math;
void main() {
var scale = calcScale(
srcWidth: 4000,
srcHeight: 2000,
minWidth: 1920,
minHeight: 1080,
);
print("scale = $scale"); // scale = 1.8518518518518519
print("target width = ${4000 / scale}, height = ${2000 / scale}"); // target width = 2160.0, height = 1080.0
}
double calcScale({
double srcWidth,
double srcHeight,
double minWidth,
double minHeight,
}) {
var scaleW = srcWidth / minWidth;
var scaleH = srcHeight / minHeight;
var scale = math.max(1.0, math.min(scaleW, scaleH));
return scale;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 代码库
thumbnailator (opens new window)
# 一些应用
app开发中,上传图片
typora写blog时,上传博客图片
https://github.com/JuZiSang/picgo-plugin-compress
# 扩展阅读
QQ音乐团队分享:Android中的图片压缩技术详解(上篇) (opens new window)