使用常规的固定标头创建JPEG缩略图图像

时间:2019-05-21 10:57:36

标签: java android image image-processing jpeg

我想为Facebook's preview photo之类的照片创建预览图。我的计划:

  • 发件人:从原始照片生成缩放的拇指(最大尺寸为30px),剥离所有固定的标题以发送。
  • 接收器:从“最小化”字节数组中,添加固定的标头(客户端代码中的硬编码)。然后将其转换为Bitmap进行显示。

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

我将这些部分拆分为固定标题:

  • 图像开始(0xFFD8
  • App0(以0xFFE0开头)
  • 定义量化表
  • 定义霍夫曼表

动态部分为:

  • 帧开始(以0xFFC0开头):因为它包含宽度/高度字节。
  • 开始扫描(以0xFFDA开头)。
  • 压缩的图像数据。
  • 图像结尾(0xFFD9

但是它只能在我的一部设备上使用,而不能在其他设备上使用。

那么如何生成可在Android和iOS设备上使用的固定的常规和标准JPEG标头

谢谢。


更多细节:

生成最小数据流:

  • 使用BitmapFactoryMatrix

    从原始图像(最大尺寸为30px,保持宽高比)创建缩放的位图。
  • 使用64压缩质量为Bitmap#compress()的缩放位图,并存储在byte[] thumbData中。

  • thumbData到末尾的0xFFDA子数组。 (SOS,图像数据和EOI)并存储在byte[] body中。

  • 使用4个字节表示宽度和高度为body,然后转换为Base64字符串并发送。

在运行正常的设备中,thumbData的大小比其他无法运行的设备长。而霍夫曼表,SOS和图像数据部分的不同之处在于: Diff check between 2 image photos

2 个答案:

答案 0 :(得分:3)

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

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

例如:定义霍夫曼表(DHT)定义如何压缩“图像数据”(在SoS段之后)。而且您使用固定的霍夫曼表仅用于解码,这就是导致问题的原因。


因此,您可能有一些选择:

  • 按比例缩小到最大尺寸30px后,发送完整质量的图像(不压缩)作为预览缩略图。
  • 编写您自己的压缩算法或使用跨平台库。
  • 将整个原始图像上传到您的服务器,以处理“最小数据”并将其发送回Android / iOS。

电报也有预览照片,其处理方式与您相似。但是他们将整个原始图像(以字节数组形式)传输到服务器,创建缩略图,去除“固定标头” ,然后将“最小数据”发送回接收方。

当在移动设备上接收时,他们通过将“最小化数据”附加到“固定标头”(Bitmaps.java#L111)并将其解码为位图,并在SoF段中更新图像大小。参见ImageLoader.java#L750

答案 1 :(得分:1)

要点1:

  

“如果我不拆分固定标头,并发送最大30x30的图像,   质量64(也使用bitmap.compress()),但两者仍然可以正常工作   平台(大小仅为1-2 Kb)。

  但是我想要的是even smaller,这就是为什么我需要将DQT和DHT拆分为固定标头

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

要点2:

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

image1
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表。

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

定义量化表(总大小= 138字节):有两个,每个以字节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

定义霍夫曼表(总大小= 432字节):有四个,每个以字节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:

  

“那么如何生成可在Android和iOS设备上使用的固定的,通用的和标准的JPEG标头?”

尝试一下:

(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)在上方标头的最后四个字节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 D8to FF DA + 12 more following bytes删除所有字节。这样,您就删除了标题和DHT / DQT表。发送这些较小的数据,然后在接收方,您的应用程序会将步骤(1)和步骤(2)的标头字节放入某个数组中,然后在标头后添加收到的字节。

现在尝试加载重新固定的JPEG。
(您的数组应该是完整的JPEG数据,字节以FF D8开头,以FF D9结尾)。