飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • AngularJS教程
  • UEditor使用文档
  • ThinkPHP5.0教程

【图像处理】Golang获取JPG图像的宽高-

时间:2022-05-19  作者:voipman  

一、背景

有些业务需要判断图片的宽高,来做一些图片相关缩放,旋转等基础操作。

但是图片缩放,旋转,拼接等操作需要将图片从 JPG 格式转成 RGBA 格式操作,操作完毕后,再转回 JPG 图片。

那如何不做 RGBA 的转换就能得到 JPG 图片的宽和高呢?

如下通过 JPG 文件的分析,并编写一个简单的代码,从 JPG 文件中获取宽度和高度。

二、JPG 图片信息分析

分析一张 JPG 图片时,关键的信息如下,部分来自维基百科:

Common JPEG markers
简写 字节标识 负载信息 说明 详细介绍
SOI 0xFF, 0xD8 none JPG 开始标识  
SOF0 0xFF, 0xC0 variable size 开始帧 (baseline DCT) Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components, and component subsampling (e.g., 4:2:0).
SOF1 0xFF, 0xC1 variable size 开始帧 (extended sequential DCT) Indicates that this is a extended sequential DCT-based JPEG, and specifies the width, height, number of components, and component subsampling (e.g., 4:2:0).
SOF2 0xFF, 0xC2 variable size 开始帧 (progressive DCT) Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, and component subsampling (e.g., 4:2:0).
DHT 0xFF, 0xC4 variable size 哈夫曼编码定义表 Specifies one or more Huffman tables.
DQT 0xFF, 0xDB variable size Define Quantization Table(s) Specifies one or more quantization tables.
DRI 0xFF, 0xDD 4 bytes Define Restart Interval Specifies the interval between RSTn markers, in Minimum Coded Units (MCUs). This marker is followed by two bytes indicating the fixed size so it can be treated like any other variable size segment.
SOS 0xFF, 0xDA variable size Start Of Scan Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan. Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it will contain, and is immediately followed by entropy-coded data.
RSTn 0xFF, 0xDn (n=0..7) none Restart Inserted every r macroblocks, where r is the restart interval set by a DRI marker. Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
APPn 0xFF, 0xEn variable size Application-specific For example, an Exif JPEG file uses an APP1 marker to store metadata, laid out in a structure based closely on TIFF.
COM 0xFF, 0xFE variable size 图片注释信息 Contains a text comment.
EOI 0xFF, 0xD9 none 图片结束  

三、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

 祝玩的开心~

标签:go编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。