使用一般固定 header 创建 JPEG 缩略图

Create JPEG thumb image with general fixed header

我想为 Facebook's preview photo 这样的照片创建预览缩略图。我的计划:

最后我想出了基于Q42.ImagePreview的解决方案。

我将这些部分拆分为固定的 header:

动态部分为:

但它只适用于我的一台设备,不适用于其他设备。

那么如何生成可以在 Android 和 iOS 设备上使用的 固定、通用和标准 JPEG header

谢谢。


更多详情:

生成缩小数据流:

在正常工作的设备中,thumbData的尺寸比其他不工作的设备长。不同之处在于 Huffman Table(s)、SOS 和图像数据部分,请参见: Diff check between 2 image photos

恐怕您不能使用每个平台的 built-in 方法来做到这一点。 问题出在压缩阶段。

JPEG 压缩中有许多变量,包括扫描的类型和细分、样本、DHT 选择和 DQT 选择。 如果您使用的编码器中的任何一个不同,您将获得不同的输出。这是野兽的本性。

例如:Define Huffman Table (DHT) 定义如何压缩 "image data"(在 SoS 段之后)。而你使用固定的霍夫曼表只用于解码,这就是导致问题的原因。


因此您可能有一些选项可供选择:

  • 在缩小到最大尺寸 30px 后发送完整质量图像(不压缩)作为预览缩略图。
  • 编写您自己的压缩算法或使用 cross-platform 库。
  • 将整个原始图像上传到您的服务器以进行处理并将 "minified data" 发送回 Android/iOS。

Telegram也有预览图,他们的做法和你差不多。但是他们将整个原始图像(以字节数组形式)传输到服务器,创建一个缩略图,删除"fixed header"并发送回接收者 "minified data"。

当在移动设备上接收时,他们将 "minified data" 解码为位图,方法是附加 "fixed header" (Bitmaps.java#L111) & update image size in SoF segment. See ImageLoader.java#L750.

第 1 点:

"If I don't split the fixed header & send the max 30x30 image with quality 64 (using bitmap.compress() too), it still works fine both platform (and the size is only 1-2 Kb).

But what I want is even smaller, that why I need to split DQT & DHT as fixed header"

  • 制作 30x30 图像(位图)
  • 将位图压缩成 JPEG
  • 删除 DQT 和 DHT(对于更小的字节是可选步骤,但删除可能会导致问题)
  • 使用 Deflate 算法压缩剩余的 JPEG 数据(基本上是在发送前压缩数据)

第 2 点:

这两张图像是 30x30,使用完全相同的霍夫曼和量化表。

图片1
https://www.dropbox.com/s/qzptp9mmrhxxsq3/30x30_thumb_01.jpg?dl=1

image2
https://www.dropbox.com/s/yrvsybb564mw2vv/30x30_thumb_02.jpg?dl=1

检查它们在 iPhone 和 Android 上是否适合您显示。如果是,请在您自己的 JPEG 上尝试下面进一步提供的这些 DQT 和 DHT table。

表:(总大小= 570 字节)...

Define Quantization table (total size= 138 bytes) : 有两个,每个都以 FF DB:

字节开头

FF DB 00 43 00 08 06 06 07 06 05 08 07 07 07 09 09 08 0A 0C 14 0D 0C 0B 0B 0C 19 12 13 0F 14 1D 1A 1F 1E 1D 1A 1C 1C 20 24 2E 27 20 22 2C 23 1C 1C 28 37 29 2C 30 31 34 34 34 1F 27 39 3D 38 32 3C 2E 33 34 32

FF DB 00 43 01 09 09 09 0C 0B 0C 18 0D 0D 18 32 21 1C 21 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32

Define Huffman table (total size= 432 bytes) : 有四个,每个都以 FF C4:

字节开头

FF C4 00 1F 00 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B

FF C4 00 B5 10 00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D 01 02 03 00 04 11 05 12 21 31 41 06 13 51 61 07 22 71 14 32 81 91 A1 08 23 42 B1 C1 15 52 D1 F0 24 33 62 72 82 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA

FF C4 00 1F 01 00 03 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B

FF C4 00 B5 11 00 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77 00 01 02 03 11 04 05 21 31 06 12 41 51 07 61 71 13 22 32 81 08 14 42 91 A1 B1 C1 09 23 33 52 F0 15 62 72 D1 0A 16 24 34 E1 25 F1 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 F7 F8 F9 FA

第 3 点:

"So how to generate a fixed, general & standard JPEG header that can use on both Android & iOS devices?"

试试这个:

(1) 这些起始字节对于大于 30x30 的图像是相同的:

FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60 00 60 00 00 FF DB 00 43 00 08 06 06 07 06 05 08 07 07 07 09 09 08 0A 0C 14 0D 0C 0B 0B 0C 19 12 13 0F 14 1D 1A 1F 1E 1D 1A 1C 1C 20 24 2E 27 20 22 2C 23 1C 1C 28 37 29 2C 30 31 34 34 34 1F 27 39 3D 38 32 3C 2E 33 34 32 FF DB 00 43 01 09 09 09 0C 0B 0C 18 0D 0D 18 32 21 1C 21 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 FF C0 00 11 08 00 1E 00 1E 03 01 22 00 02 11 01 03 11 01 FF C4 00 1F 00 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B FF C4 00 B5 10 00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D 01 02 03 00 04 11 05 12 21 31 41 06 13 51 61 07 22 71 14 32 81 91 A1 08 23 42 B1 C1 15 52 D1 F0 24 33 62 72 82 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FF C4 00 1F 01 00 03 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B FF C4 00 B5 11 00 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77 00 01 02 03 11 04 05 21 31 06 12 41 51 07 61 71 13 22 32 81 08 14 42 91 A1 B1 C1 09 23 33 52 F0 15 62 72 D1 0A 16 24 34 E1 25 F1 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 F7 F8 F9 FA

(2) 在上面 header 的最后四个字节 F7 F8 F9 FA 之后是具有 14 个字节的扫描开始标记 (FF DA):

FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00

(3) 现在将您的 JPEG 扫描数据添加到结尾 FF D9 字节。

基本上在您的 .compress() 输出的 JPEG 中,删除 FF D8 向上 to FF DA + 12 more following bytes 的所有字节。这样你就删除了 header 和 DHT/DQT table。发送这个较小的数据,在接收端,您的应用程序只是将步骤 (1) 和步骤 (2) 中的 header 字节放入某个数组,然后还在 header.[=36= 之后添加接收到的字节]

现在尝试加载 re-fixed JPEG。
(您的数组应该是完整的 JPEG 数据,字节从 FF D8 开始,以 FF D9 结束)。