在 macOS 中,如何自动压缩截屏图片大小

错误的误会

我是这样在 Markdown 中插入截屏图片的:截屏到剪贴板后,直接在 Typora 中粘贴,iPic 将自动将图片上传到阿里云图床(对象存储)。我一直使用 AlfredClipboard History 功能查看截图大小,发现图片占内存都很大,全屏截图有 10 多 MB,就想着一定要找到一种方法来缩小截屏大小,不然我的阿里云图床费用得嗖嗖涨。

image

昨天,我将截图不保存到剪贴板,而直接保存到本地,发现图片根本没有 10 多 MB,就几百 KB。想着我这个高分辨率超高清 Retain 全屏截图 🐶 ,不能只有几百 KB 呀 ,一定是我电脑显示有问题。

image

最后我打开阿里云对象存储在线检查了一下,发现真的只有几百 KB,开始心里默默对 Alfred ***。遂在 Alfred 论坛发讨论帖,网友回复说,对于剪贴板图片来说,其数据格式是 TIFF 数据格式,不是图片本身的内存大小,所以 Clipboard History 数据显示较大。喔霍,原来是个错误的误会呀。

虽然几百 KB 相对 10 多 MB 是小多了,现在我这小博客无人问津,图床费用每月也就几毛几分钱,图片大点也无所谓。但为了长远考虑,图片当然越小越好。图片小了也可以提升访问速度。

正好搜到一篇关于压缩图片的优质教程,还包含视频教程,简单的方式就能实现自动压缩图片,那还等什么,得赶快用起来。强烈推荐看原文,很通俗易懂。

图片压缩时机

图片压缩有三个时间点:1、截屏时由截屏软件压缩;2、上传到图床时由上传软件压缩;3、上传到图床后由图床压缩。

下面要介绍的这种方式可以类似看作是第 1 条。

对于第 2 条,如果你使用 iPic,iPic 有提供可选的有损压缩的功能,先压缩再上传,我大概测试了一下,一般能压缩 60% 多吧(具体视情况而定)。但如果在 Typora 中复制,不支持压缩,曲线救国的方式是先使用快捷键上传,再在 Typora 中粘贴返回的 URL。个人觉得体验有分裂,没有截屏时就压缩方便,所以才用第 1 种实现。

对于第 3 条,七牛云对象存储提供自动图片压缩功能,不过是收费功能。

使用 pngquant 压缩

pngquant

pngquant 是一个可以几乎不损害图片质量,通过减少像素大小来压缩 PNG 图片的命令行程序(库),属于有损压缩。

# 使用 HomeBrew 安装 pngquant
brew install pngquant

# 将 32 位的 RGBA PNG 数字转换为 8 位(或更小)的 RGBA 调色板
# pngquant [颜色数量] [参数] input.png
# --skip-if-larger 仅保存转化后比原文件小的文件
# --strip 去除可选的元数据
# --ext=.png 设置压缩后图片名和原图片名一样
# --force 覆盖原文件
# 示例:压缩 example.png,压缩后图片仍叫 example.png
pngquant 256 --skip-if-larger --strip --ext=.png --force example.png

当 pngqunt 压缩后,还可以通过 zopfli 进一步无损压缩,但耗时较长,压缩比例也不高,我就没做这步操作。zopfli 的原理使用 DEFLATE 算法来实现无损压缩。

ImageAlpha 是一个集成 pngquant 的 macOS 客户端,可以图形化调整图片显示颜色数量。

压缩效果

PNG-32 (465KB) 256 colors (117KB)
Xnip2020-08-02_20-55-07

压缩效果:74.8%

压缩原理

macOS 默认截屏生成图片的格式为 PNG,更准确的说,是 PNG-32,即每个像素点占 32 位,每个像素点由 4 部分构成,分别是红色(Red)、绿色(Green)、蓝色(Blue)和透明度(Alpha (Transparency))(简写 RGBA)。所以每种颜色有 2^8(256)个级别,所以图片可以显示 1600 万种颜色(256  ×  256  ×  256, 2^24)。

截屏一般对颜色要求没那么高,显示 1600 万种颜色与 256 种颜色差别不大,截屏图片基于调色板,可以利用工具将图片显示颜色更改为 256 种,来实现压缩图片的功能。此时单位像素由 32 位变为 8 位(4 个字节变为 1 个字节),所以大约能压缩 75% 的容量。

注意:除了 256,还可以选择 128、64、24、16、8、4 等级别,原作者测试 64 跟 256 差不多。这里我选择了 256。

自动压缩工作流设置

我们可以将 pngqunt 命令作为一个脚本,交给工作流来自动执行。这是一个自动压缩的工作流,设置完成后,我们使用截图工具(如: Xnip)将截图保存到指定文件夹时,将会触发工作流,完成自动压缩。

工作流可以选择 Alfred 工作流(Workflow)、系统自带的 Automator 和专业工作流软件 Hazel(试用结束后软件收费)。Alfred 工作流需要关键字或快捷键触发,不符合我们自动压缩的 feelstyle,pass。下面分别演示如何使用 Automator 和 Hazel。

Automator 设置工作流:

  1. 选择 Folder Action(文件夹操作),选择指定文件夹,如「截图」
  2. 添加 Run Shell Script(运行 Shell 脚本),设置传递输入为「作为自变量」,当「截图」文件夹中新增图片时就会执行脚本

  • 脚本内容:
for f in "$@"
do
# 压缩
/opt/homebrew/bin/pngquant 256 --skip-if-larger --strip --ext=.png --force "$f"

# 获取压缩后文件大小
compressed_file_size=$(stat -f%z "$f")

# 复制到剪贴板
osascript -e "set the clipboard to (read (POSIX file \"$(perl -e "print glob('$f')")\") as {«class PNGf»})"

# 大于 500KB,发送通知
if [ "$compressed_file_size" -gt 512000 ]; then
osascript -e 'display notification "压缩后的图片仍然大于 500KB" with title "压缩文件过大"'
fi
done

# 发送通知
osascript -e 'display notification "图片已压缩并复制到剪贴板" with title "图片已压缩并复制到剪贴板"'

PS:使用 Automator,可能需在 Security & Privay(安全与隐私)-> Accessibility(辅助功能)里面开启权限。

Hazel 设置工作流:

1、添加指定文件夹,设置触发条件,条件为对文件夹中最近一分钟新增的 PNG 文件执行脚本

2、设置脚本,脚本为压缩图片,并复制压缩后的图片到剪贴板

  • 脚本内容:
pngquant 256 --skip-if-larger --strip --ext=.png --force "$1"
osascript -e "set the clipboard to (read (POSIX file \"$(perl -e "print glob('$1')")\") as {«class PNGf»})"

3、通知压缩完成

ps:Hazel 官方 UI 真心好看。/usr/local/bin/pngquanpnguant 作用一样。

像素 / 分辨率 与 PPI

上面一直说像素,所以深入了解一下。

像素(Pixel,简写 px)分为物理像素和图片像素。

物理像素是物理设备上显示颜色的物理点。物理设备如电脑、手机、电视等。

  • MacBook Pro 2017 13.3-inch(后面简称 MacBook Pro)为例,屏幕尺寸是 13.3 英寸,即屏幕斜对角长为 13.3 英寸(1 英寸 = 2.54 cm)。(斜对角长约 34.0 cm,长约 28.8 cm,宽约 18.0 cm),像素个数为 2560 × 1600,所以每个像素点的面积约为 0.01125 cm × 0.01125 cm。
  • iPhone XR,屏幕尺寸为 6.06-inch。(斜对角长约 15.4 cm,长约 14 cm,宽约 6.5 cm),像素个数为:1792 × 828,所以每个像素点的面积约为 0.00785cm × 0.00785 cm。

我们可以看出,MacBook Rro 和 iPhone XR 屏幕的像素点为正方形。

图片像素是图片中最小的单位。同样大小的照片,包含的像素可能不同。小米 10 有一个颗 1 亿像素的摄像头,如果全输出,拍出来的照片,包含像素个数为 12032 × 9024 ,乘积结果为 1.08 亿。iPhone XR 是 1200 万像素,照片像素为 3024  ×  4032。摄像头一般说多少多少像素,对于屏幕,我们一般称分辨率。

分辨率,也叫显示分辨率(Display Resolution),分辨率指像素的总和,比如我们说 MacBook Pro 屏幕的分辨率是 2560 × 1600 px。分辨率和屏幕大小尺寸没有关系,MacBook Pro 的分辨率是 2560 × 1600 px,一台 75 英寸的 小米电视 5 的分辨率也才 3840 × 2160 px。小米电视 5 自称是 4K,其实只是接近 4K,4K 的标准是横向像素达到 4000。衡量屏幕显示的是像素密度。

像素密度 ,英语为 Pixel Density,也叫 Pixels Per Inch(PPI),即单位英寸像素的多少(个数)。计算公式为 ppi = 像素/英寸。

  • 以 MacBook Pro 2017 13.3-inch 为例,ppi = √(2560^2 × 1600^2) / 13.3 ≈ 227,即斜对角每英寸有 227 个像素
  • 以 iPhone XR 为例,ppi = √(1792^2 × 828^2) / 6.06 ≈ 326
  • iPhone 11 PRO 为例,ppi = √(2436^ × 1125^) / 5.8 ≈ 458

对于屏幕来说,ppi 越高,显示就越清晰,就越不容易看见颗粒感。

像素的内存大小

上面那张截图占内存太小,找张占内存大一点的分析。

未压缩前 压缩后
  • 1KB = 1000 Bytes(字节)(十进制换算);1KB = 1024 Bytes(二进制换算)

这张全屏壁纸截图未压缩前大小为 2880 × 1800,所以有 5,184,000 个像素点,图片内存为 8.7 MB(8,738,825 bytes),有 69,910,600 位(bit),每个像素点约占 13.5 位。

压缩后分辨率不变,内存为 2.8 MB,为 23,488,102 位,每个像素点约占 4.3 位。

可以看出,截屏图片的像素没有达到默认的 32 位,我认为是图片颜色没有特别丰富,所以用不了 32 位表示。测试截颜色单一的屏幕,所需要的内存更低。个人认为这跟 UTF-8 类似,像素点的大小为可变,不常见的颜色更占内存。

值得注意的是,虽然 MacBook Pro 的屏幕分辨率是 2560 × 1600 px,但系统默认做了一定缩放,缩放为了 2880  ×  1800 px,而屏幕显示为 1440  ×  900 pt(point,1pt = 2px),即将 4 个像素压缩为 1 个像素显示,来实现更好的显示效果,这被称为 Retain 技术。所以在 macOS 中,浏览器前端样式 1 px 对应 1 pt。

PNG vs JPEG(JPG)

PNGPortable Network Graphics,便携式网络图形。是一种支持无损压缩位图图形格式。PNG 更适合保存截屏。

无损压缩一方面指压缩后对图片质量没有损失,另一方面指压缩后还可以还原(如压缩包解压)。PNG 图片可以无损压缩,但也可以有损压缩,就像前面的 pngquant 一样。

JPEGJoint Photographic Experts Group,联合图像专家小组。一种用于有损压缩的图片格式。更适合保存照片。

有损压缩的原理一般是变换数据编码方法,变换编码方法为用不精确逼近和部分数据丢弃来表示内容。有损压缩后不能还原。

  • JPG:JPG 是 JPEG 图片的一种文件后缀,可以认为两者没有区别。ps:一个号称可以无损压缩 JPEG 图片的网站:picdiect

结语

关于图片大小的错觉,来自一次我公众号不能显示文章图片的经历,那张图片是我用 iPhone 拍摄的图片,有几 MB,这造成了我认为苹果设备图片会很大的错觉,所以认为 Clipboard History 显示截屏图片大小就是正确的。我还通过将图片上传到阿里云后,在线打开链接,复制图片到剪贴板,再通过 Clipboard History 来查看图片大小来验证图片是不是真的这么大,这种错误的方式进一步加深了我认为图片确实很大的错觉。汗颜。

友情提示:使用阿里云对象储存作为图床时,建议设置防盗链,避免别人使用你图片链接造成你图床费用大幅增加的问题。

这篇文章实现自动压缩截屏用时不多,但为了正确表述出「像素与分辨率」却用时不少,而关于为什么 PNG 适合保存截图,JPEG 适合保存照片,也还没彻底搞懂,后面有机会再深入研究一下。

最后,如果你有更好的方法实现截屏压缩,请一定留言告诉我。

延伸阅读

deppwang wechat

评论默认使用 ,你也可以切换到 来留言。