使用一般固定 header 创建 JPEG 缩略图
Create JPEG thumb image with general fixed header
我想为 Facebook's preview photo 这样的照片创建预览缩略图。我的计划:
- 发件人: 从原始照片生成缩放缩略图(最大尺寸为 30px),去除所有固定的 header 发送。
- Receiver:从 "minified" 字节数组追加固定的 header(客户端代码中的硬编码)。然后转换成
Bitmap
显示。
最后我想出了基于Q42.ImagePreview的解决方案。
我将这些部分拆分为固定的 header:
- 图像开始(
0xFFD8
)
- App0(以
0xFFE0
开头)
- 定义量化 Table(s)
- 定义霍夫曼 Table(s)
动态部分为:
- 帧开始(以
0xFFC0
开头):因为它包含width/height字节。
- 扫描开始(从
0xFFDA
开始)。
- 压缩图像数据。
- 图像结束(
0xFFD9
)
但它只适用于我的一台设备,不适用于其他设备。
那么如何生成可以在 Android 和 iOS 设备上使用的 固定、通用和标准 JPEG header?
谢谢。
更多详情:
生成缩小数据流:
使用 BitmapFactory
& Matrix
从原始图像创建缩放位图(最大尺寸 30px,保持纵横比)
使用 Bitmap#compress()
压缩质量 64
的缩放位图并存储在 byte[] thumbData
.
Sub-array上面的thumbData
从0xFFDA
到最后。 (SOS、图像数据和 EOI)并存储在 byte[] body
.
将表示宽度和高度的4个字节添加到body
之前,转换为Base64字符串并发送。
在正常工作的设备中,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
结束)。
我想为 Facebook's preview photo 这样的照片创建预览缩略图。我的计划:
- 发件人: 从原始照片生成缩放缩略图(最大尺寸为 30px),去除所有固定的 header 发送。
- Receiver:从 "minified" 字节数组追加固定的 header(客户端代码中的硬编码)。然后转换成
Bitmap
显示。
最后我想出了基于Q42.ImagePreview的解决方案。
我将这些部分拆分为固定的 header:
- 图像开始(
0xFFD8
) - App0(以
0xFFE0
开头) - 定义量化 Table(s)
- 定义霍夫曼 Table(s)
动态部分为:
- 帧开始(以
0xFFC0
开头):因为它包含width/height字节。 - 扫描开始(从
0xFFDA
开始)。 - 压缩图像数据。
- 图像结束(
0xFFD9
)
但它只适用于我的一台设备,不适用于其他设备。
那么如何生成可以在 Android 和 iOS 设备上使用的 固定、通用和标准 JPEG header?
谢谢。
更多详情:
生成缩小数据流:
使用
BitmapFactory
&Matrix
从原始图像创建缩放位图(最大尺寸 30px,保持纵横比)
使用
Bitmap#compress()
压缩质量64
的缩放位图并存储在byte[] thumbData
.Sub-array上面的
thumbData
从0xFFDA
到最后。 (SOS、图像数据和 EOI)并存储在byte[] body
.将表示宽度和高度的4个字节添加到
body
之前,转换为Base64字符串并发送。
在正常工作的设备中,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 iseven smaller
, that why I need to split DQT & DHT as fixed header"
- 制作 30x30 图像(位图)
- 将位图压缩成 JPEG
- 删除 DQT 和 DHT(对于更小的字节是可选步骤,但删除可能会导致问题)
- 使用 Deflate 算法压缩剩余的 JPEG 数据(基本上是在发送前压缩数据)
第 2 点:
这两张图像是 30x30,使用完全相同的霍夫曼和量化表。
https://www.dropbox.com/s/qzptp9mmrhxxsq3/30x30_thumb_01.jpg?dl=1
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
结束)。