【图像处理】Golang 获取JPG图像的宽高
一、背景
有些业务需要判断图片的宽高,来做一些图片相关缩放,旋转等基础操作。
但是图片缩放,旋转,拼接等操作需要将图片从 JPG 格式转成 RGBA 格式操作,操作完毕后,再转回 JPG 图片。
那如何不做 RGBA 的转换就能得到 JPG 图片的宽和高呢?
如下通过 JPG 文件的分析,并编写一个简单的代码,从 JPG 文件中获取宽度和高度。
二、JPG 图片信息分析
分析一张 JPG 图片时,关键的信息如下,部分来自维基百科:
三、JPG开始,宽高分析
3.1 JPG格式判断方法
JPG 图片的开头是0xFF 0xD8
因此,判断 JPG 图片的魔法文件 magic 标识就是0xFF 0xD8
在 golang 中也是通过0xFF 0xD8判断图片是否为 JPG 文件,如下所示:
&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},
3.2 JPG 图片宽高获取
本文通过分析JPG 图片的开始帧SOF 获取图片的宽高。
预览一张图片获取图像的宽高基本信息。
宽:1200,高:1002
可以使用二进制方式打开文件,查看 JPG 图片的头部信息,获取 JPG 图片开始帧信息如SOF0, SOF1, SOF2。
SOF0 表示baseline DCT, 基线 DCT(离散余弦变换),开头的标识是 0xFF 0xC0
SOF1 表示extended sequential DCT,扩展序列 DCT ,开头的标识是 0xFF 0xC1
SOF2 表示progressive DCT,升级 DCT, 开头的标识是 0xFF 0xC2
如下是一个 JPG 的头部信息:
从上图中可以看到开始帧信息是 SOF0,即 绿色标记的 ffc0。
找到 SOF 后,向后偏移5个字节得到高和宽
高:03 ea,计算得到高等于 3<<8|0xea = 1002
宽:04 b0,计算得到宽等于4<<8|0xb0 = 1200
得到的宽高和预览时的宽高一致。
3.3. JPG 宽高计算原理
eg: [ff c0] 00 11 08 [03 ea] [04 b0]
| | |
| | |
-> SOF1 ->height ->width
脚本计算宽高如下:
% expr 3<<8|0xea 1002 % expr 4<<8|0xb0 1200
3.4 通过golang 实现 JPG 图片宽高的获取
知道了 JPG 获取图片宽高的原理后,使用 golang代码或者 JPG 图片的宽高如下:
/** * 入参: JPG 图片文件的二进制数据 * 出参:JPG 图片的宽和高 **/ func GetWidthHeightForJpg(imgBytes []byte) (int, int) { var offset int imgByteLen := len(imgBytes) for i := 0; i < imgByteLen-1; i++ { if imgBytes[i] != 0xff { continue } if imgBytes[i+1] == 0xC0 || imgBytes[i+1] == 0xC1 || imgBytes[i+1] == 0xC2 { offset = i break } } offset += 5 if offset >= imgByteLen { return 0, 0 } height := int(imgBytes[offset])<<8 + int(imgBytes[offset+1]) width := int(imgBytes[offset+2])<<8 + int(imgBytes[offset+3]) return width, height }
总结
通过分析 JPG 图片的 SOF 信息,就可以提取图片的宽和高,而不用将其转换成 RGBA,再获取 RGBA 的宽高,可以节约一些计算资源。
后续再分析 JPG 图片的其他信息,如下离散余弦变换和哈夫曼编码等。
Done
祝玩的开心~