Handsum: An LQIP Image File Format
Handsum: An LQIP Image File Format
Nigel Tao Handsum: An LQIP Image File Format Nigel Tao Handsum:一种 LQIP 图像文件格式
Low Quality Image Placeholders (LQIPs) are very small (both in terms of file size and pixel dimensions) images that load very quickly, providing immediate visual feedback while full-resolution images load slowly in the background. 低质量图像占位符(LQIP)是指那些(在文件大小和像素尺寸上)都非常小的图像,它们加载速度极快,能在全分辨率图像在后台缓慢加载时提供即时的视觉反馈。
They are small-in-file-size enough that it’s feasible to base64-encode and inline them into a web page, minimizing network round-trips and improving page load times. 它们的文件体积足够小,以至于可以将其进行 base64 编码并内嵌到网页中,从而减少网络往返次数并缩短页面加载时间。
Existing LQIP techniques include the Blurhash and Thumbhash custom image file formats, as well as just using low-quality JPEG or WebP and very small pixel dimensions. 现有的 LQIP 技术包括 Blurhash 和 Thumbhash 自定义图像文件格式,以及直接使用低质量的 JPEG 或 WebP 并配合极小的像素尺寸。
This blog post introduces Handsum, another LQIP custom image file format. Handsum files are also a fixed size-in-bytes: 48 bytes at the lowest quality setting and 147 bytes at the highest. 本篇博文介绍了 Handsum,这是另一种 LQIP 自定义图像文件格式。Handsum 文件具有固定的字节大小:最低质量设置下为 48 字节,最高质量设置下为 147 字节。
For a desktop, native or command-line app, if you’re storing your thumbnail images in a database, this lets you use fixed-size columns, which can be faster and simpler to iterate over. 对于桌面应用、原生应用或命令行程序,如果你将缩略图存储在数据库中,这种格式允许你使用固定长度的列,这在遍历时会更快、更简单。
Handsum is based on the Discrete Cosine Transform (DCT), also used by the Blurhash algorithm, the Thumbhash algorithm and JPEG itself. Handsum 基于离散余弦变换(DCT),这也是 Blurhash 算法、Thumbhash 算法以及 JPEG 本身所使用的技术。
Understanding how the relatively simple Handsum format works should help you if you ever wanted to understand how the relatively complicated JPEG format works (the JPEG specification is a 186-page PDF file; Handsum is simpler). 理解相对简单的 Handsum 格式的工作原理,将有助于你日后理解相对复杂的 JPEG 格式是如何工作的(JPEG 规范是一个 186 页的 PDF 文件;而 Handsum 则简单得多)。
Web Page Example
网页示例
Here’s an annotated screenshot of a Handsum web page demo. It uses a Handsum decoder written in C, compiled to Wasm. 这是一张带有注释的 Handsum 网页演示截图。它使用了一个用 C 语言编写并编译为 Wasm 的 Handsum 解码器。
Comparison
对比
Here’s Handsum compared to PNG, Thumbhash (which is pretty similar to Blurhash), WebP, ETC2 and JPEG. You might want to open this image in its own tab, zoom in and pan around. There’s a lot going on here. Let’s break it down. 这是 Handsum 与 PNG、Thumbhash(与 Blurhash 非常相似)、WebP、ETC2 和 JPEG 的对比。你可能需要在新标签页中打开这张图片,放大并平移查看。这里包含的信息量很大,让我们逐一拆解。
Each row concerns a famous image, as seen on Wikipedia: earthrise, la-grande-jatte, lincoln, mona-lisa, parliament, pearl-earring, starry-night, tsunami, van-eyck, water-lillies. 每一行对应维基百科上的一张著名图片:地出(earthrise)、大碗岛的星期天下午(la-grande-jatte)、林肯(lincoln)、蒙娜丽莎(mona-lisa)、议会大厦(parliament)、戴珍珠耳环的少女(pearl-earring)、星夜(starry-night)、神奈川冲浪里(tsunami)、凡·艾克(van-eyck)、睡莲(water-lillies)。
The first column, “Original”, is the image itself, scaled to fit inside a 32×32 pixel bounding box (while preserving the aspect ratio). For example, the 500×745 pixel Mona Lisa image is resized to 21×32. 第一列“Original”是原始图像本身,缩放以适应 32×32 像素的边界框(同时保持纵横比)。例如,500×745 像素的《蒙娜丽莎》被调整为 21×32 像素。
Every other column is that original image after round tripping: encoding in some lossy fashion, then decoding. The numbers (like 1368 or 615) above each cell is the encoded size-in-bytes. More bytes are clearly correlated with more quality. 其他每一列都是原始图像经过“往返”处理后的结果:即以某种有损方式编码,然后再解码。每个单元格上方的数字(如 1368 或 615)是编码后的字节大小。显然,字节数越多,质量越高。
The question is how small (in terms of size-in-bytes) can you get while keeping acceptable quality. 问题在于,在保持可接受质量的同时,你能达到的最小字节数是多少。
The second column, “PNG 16”, simply scales the image down to fit inside a 16×16 pixel bounding box, encoding it losslessly as a PNG. (Yeah, we could possibly get a slightly smaller size-in-bytes if we pngcrushed it, or used WebP lossless, but it’s probably not that big a difference). 第二列“PNG 16”只是将图像缩小以适应 16×16 像素的边界框,并以无损 PNG 格式编码。(是的,如果我们使用 pngcrush 或 WebP 无损压缩,可能会获得稍小的字节数,但差异可能并不大)。
Decoding produces a within-16×16 image (e.g. a 11×16 Mona Lisa) which we then upsample (with a bi-linear filter) back to 32 pixels wide or high. 解码后会生成一张 16×16 以内的图像(例如 11×16 的《蒙娜丽莎》),然后我们使用双线性滤镜将其上采样回 32 像素宽或高。
The third column uses the Thumbhash LQIP codec. This produces extremely small encodings, weighing only 24–27 bytes. 第三列使用 Thumbhash LQIP 编解码器。它产生的编码极小,仅占用 24–27 字节。
The next four columns use Handsum, at each of its four quality settings. The “16” in “Hsum 16 q=1” means that, like “PNG 16”, the codec per se produces something that fits inside a 16×16 bounding box, so we upsample to 32 to get an image that’s better compared with the other cells. 接下来的四列使用 Handsum,分别对应其四种质量设置。“Hsum 16 q=1”中的“16”意味着,像“PNG 16”一样,编解码器本身生成的是适应 16×16 边界框的内容,因此我们将其上采样到 32 像素,以便与其他单元格进行更好的对比。
The four columns after that use WebP lossy, based on VP8, at quality 0, 25 and 75. The “WebP 16” columns encode (and thus decode) within-16×16 (and, again, we upsample). The “WebP 32” column encodes within-32×32 image (and no upsampling is needed on decode). 随后的四列使用基于 VP8 的 WebP 有损压缩,质量分别为 0、25 和 75。“WebP 16”列在 16×16 范围内编码(并解码,同样需要上采样)。“WebP 32”列在 32×32 范围内编码(解码时无需上采样)。
The next two columns use ETC2 (wrapped in a PKM / PACKMAN container), the Ericsson Texture Compression format, mandatory in OpenGL ES 3.0 (Appendix C.1 of the linked spec) and similar to other texture formats like BCn / DXTn / S3TC. 接下来的两列使用 ETC2(封装在 PKM / PACKMAN 容器中),这是 OpenGL ES 3.0 中强制要求的 Ericsson 纹理压缩格式(参见链接规范的附录 C.1),类似于 BCn / DXTn / S3TC 等其他纹理格式。
ETC2 is designed to be decoded on GPUs (with high parallelization), but you can decode on CPUs too, like any other image format. ETC2 is an interesting and under-appreciated image format, but diving into that is out of scope of this blog post. ETC2 设计用于在 GPU 上解码(具有高并行性),但你也可以像其他任何图像格式一样在 CPU 上解码。ETC2 是一种有趣且被低估的图像格式,但深入探讨它超出了本篇博文的范围。
Again, “16” means encoding the lower-resolution source image and upsampling after decode, “32” means encoding the higher-resolution source image. 同样,“16”表示对低分辨率源图像进行编码并在解码后上采样,“32”表示对高分辨率源图像进行编码。
The last two columns are JPEG, at quality 75 (which is roughly comparable to, but not exactly the same as, WebP’s “quality = 75”). Again, “16” vs “32” means lower-res-and-upsample vs higher-res. 最后两列是质量为 75 的 JPEG(这与 WebP 的“quality = 75”大致相当,但不完全相同)。同样,“16”与“32”分别代表“低分辨率+上采样”与“高分辨率”。
At Handsum’s q=1 (lowest quality), it’s roughly comparable to a bigger (higher file size), better (better visual quality) Thumbhash. Bigger is in relative terms. The absolute difference is barely 24 bytes. 在 Handsum 的 q=1(最低质量)下,它大致相当于一个更大(文件体积更大)、更好(视觉质量更好)的 Thumbhash。这里的“更大”是相对而言的,绝对差异仅为 24 字节。
By “better visual quality” I mean that, if you told me to guess the famous image based on the thumbnail, then for Mona Lisa or Girl with a Pearl Earring, I’d say that that’s plausible for Handsum (q=1) but implausible for Thumbhash. Still better than the modern potato art that WebP 16 q=0 produces, though. 我所说的“更好的视觉质量”是指,如果你让我根据缩略图猜出那张著名图片,对于《蒙娜丽莎》或《戴珍珠耳环的少女》,我会说 Handsum (q=1) 是可辨认的,而 Thumbhash 则很难辨认。不过,它仍然比 WebP 16 q=0 产生的现代“土豆画质”要好。
At Handsum’s q=4 (highest quality), it’s roughly comparable to WebP Lossy (VP8) and ETC2, for a given size-in-bytes budget. 在 Handsum 的 q=4(最高质量)下,在给定的字节预算内,它大致与 WebP 有损压缩 (VP8) 和 ETC2 相当。
One difference, though, is that at a given quality setting (and at c=3, see below), every Handsum image file is the same size-in-bytes: q=1 files are always 48 bytes, q=2 files are 75, q=3 files are 123 and q=4 files are 147. 不过,一个区别在于,在给定的质量设置下(且 c=3 时,见下文),每个 Handsum 图像文件的字节大小都是固定的:q=1 文件始终为 48 字节,q=2 为 75 字节,q=3 为 123 字节,q=4 为 147 字节。
If you wanted to make an embedded app for displaying a catalog of artworks (oil paintings, audio records, etc.), you could budget 256 bytes per catalog entry, for example. 如果你想制作一个用于显示艺术品目录(油画、唱片等)的嵌入式应用,你可以为每个目录条目预留 256 字节的预算。
After assigning 50 bytes each to the artist name and the artwork name (ellipsizing if necessary) and a few more bytes to other metadata (time stamp, track length, etc.), that could leave you 150 bytes for the thumbnail. 在为艺术家姓名和艺术品名称各分配 50 字节(必要时使用省略号),并为其他元数据(时间戳、曲目长度等)分配少量字节后,你还可以剩下 150 字节用于缩略图。
If you were using WebP for encoding the 16×16 thumbnails, q=75 will fit at or under 150 bytes most of the time, but not all of the time. WebP encoders also typically have a quality setting but not a byte-budget setting, so generating a small-enough (in terms of size-in-bytes) WebP takes a little dance. 如果你使用 WebP 对 16×16 缩略图进行编码,q=75 大多数情况下能控制在 150 字节以内,但并非总是如此。WebP 编码器通常只有质量设置,而没有字节预算设置,因此要生成足够小(按字节计算)的 WebP 文件需要一番折腾。
In contrast, with Handsum q=4, you’ll get 147 bytes every time, guaranteed. 相比之下,使用 Handsum q=4,你每次都能保证得到 147 字节的结果。