解析JPEG文件的GPS信息

作者:中科大鹏

1 前言  

        因为最近有个项目开发,需要通过读取JPEG图片文件的GPS位置信息,显示到相应的地图界面中。这两天,查询了相关资料,发现这方面科普性的文章挺多,但适合项目应用开发的示例代码有限。为此,自己查阅资料,梳理了解析GPS信息的相关过程,编写了部分代码,在此分享给大家。不足之处望多多包涵。

         文章只列举了很少部分的JPEG格式说明,关于JPEG文件格式详细介绍,可以参阅相关文献(文末会分享查阅的接口标准和参考文献),另外,本文只针对需要的GPS信息进行了解析,其余的很多信息直接略过,大家可以参看接口标准说明文件。

2 JPEG图像数据的基本结构

2.1原始图像数据的基本结构

        图像数据采用的存储格式,取决于需要的图像数据类型:

  •  
    •  未压缩RGB数据:   采用TIFF Rev. 6.0标准修订版的RGB全彩图像类型格式
    • 未压缩YCbCr数据: TIFF 6.0 修订版的扩展YCbCr 图像类型数据格式
    •  JPEG压缩数据:     JPEG标准的ADCT。

        就压缩数据而言,DSC应用程序所需要的属性信息记录在文件APP1信息中,写入APP1中的数据需要与TIFF兼容。图像文件使用了一种存储压缩和未压缩数据属性信息的通用方法,这样可以得到更简单的格式;同时又利用标记的可扩展性机制,实现了为附加信息添加私有标记的可能。

2.2未压缩RGB数据的基本结构

        未压缩RGB数据的按照TIFF Rev. 6.0 RGB 标准的全色彩图像要求进行记录。

        文件的通用属性信息记录在TIFF Rev. 6.0指定的Tag标签中。Exif特定的属性信息使用该标准为TIFF保留的私有Tag标签来记录。私有Tag标签指向该属性的信息集合(Exif IFD)。同样,JPEG文件的GPS信息也属于扩展属性信息,记录在TIFF Rev. 6.0中规定的标记中。

        需要注意的是,在TIFF标准中,并未规定每个IFD(Image File Directory)值的记录位置。

        JPEG文件结构主要包括:

  •            
    •    File Header,                             文件头
    •    0th IFD,                                    第0个IFD
    •    0th IFD Value,                          第0个IFD的值
    •    1st IFD,                                     第1个IFD
    •    1st IFD Value,                            第一个IFD的值
    •    1st (Thumbnail) Image Data,     缩略图
    •    0th (Primary) Image Data.          图片数据

基本文件结构如图所示:

图 1 未压缩数据文件的基本结构

        TIFF标准中定义的文件头占8字节,其中,到IFD的偏移量字段表示从TIFF头开始至第0个IFD的起始地址的偏移位置。

        同样,第0个IFD存储的IFD偏移量,表示第1个IFD(缩略图)的存储地址。当没有记录缩略图,即没有第1个IFD时,第0个IFD存储的偏移量以00000000.H结束。

        TIFF头部结构如表所示:

表 1 TIFF头部结构

名称

长度(字节)

描述

Byte Order

2

“II”(4949.H)或 “MM”(4D4D.H)

•     II 表示数字存储遵循 intel 的字节序,即小端存储

•     MM 表示数据存储遵循 Motorola 的字节序,即大端存储

不同的存储字节序的选择主要是因为不同厂商的不同的数码产品的差异引起。大部分的数码相机使用 Intel 的字节序,也有些奇葩的产品的,比如Sony 的大部分产品都是使用Intel字节序的。

重要:Byte Order直接影响到数据内容,所以在解析Exiff数据前必须检查文件的Byte align

固定值

2

固定为002A.H

到IFD的偏移量

4

第0个 IFD偏移量。如果TIFF头后面紧跟着第0个IFD,则它被写为00000008.H。(从Byte Order起算。后续的数据偏移量也从Byte Order起算)

2.3 JPEG压缩数据的基本结构

        压缩数据文件按照ISO/IEC 10918-1中规定的JPEG DCT格式进行记录,并插入应用片段(APP1)。APP1紧随着ISO标记而记录,ISO标记指示了文件的开头(见图)。同样,可以根据需要记录多个APP2,并且可以紧随APP1之后。Exif不使用APP1和APP2之外的APPn或COM段。因此,Exif读取程序设计需要跳过未知的APPn和COM数据段。

图 2 压缩数据文件的基本结构

        互操作性:APP1由APP1标记、Exif标识符代码和属性信息本身组成。APP1的大小包含所有这些元素,其长度不得超过JPEG标准规定的64KB。

        属性信息存储于TIFF结构中,包括1个文件头,最多2个IFD(0th IFD和1th IFD)。0th IFD记录与压缩图像(主图像)有关的属性信息。1th IFD可用于记录缩略图等。

        APP2由APP2标记、FPXR标识符代码和扩展记录或流数据的内容列表组成。多个APP2标记段的字符串可用于记录超过64KB的数据。

2.4 缩略图数据的基本结构

        缩略图数据使用两种现有的图像格式记录在第一个IFD中,与主图像类似。

        缩略图的大小没有限制,也不是强制性的。

        缩略图不用采用与主图像相同的数据结构。然而,当主图像记录为未被压缩的RGB数据或未压缩的YCbCr数据时,缩略图不能记录为JPEG压缩数据(见表)。

表 2 主图像和压缩图像适应性

       以压缩格式记录缩略图时,按照标准TIFF Rev. 6.0 RGB Full Color Images 或者TIFF Rev. 6.0 Extensions YCbCr Images要求将其记录在第一个IFD中。

        Exif特定的记录格式用于压缩的缩略图。此时,压缩标签值被设置为“6”,第一个IFD的标签用于指定位置和大小。图像在指定位置存储为符合JPGE标准DCT格式的数据流(从SOI到EOI)。JPEG数据流中不记录APPn标记、COM标记或重置标记,见图。为了避免重复定义,第一个IFD不用于记录指示TIFF图像的标记或记录在别处作为JPEG标记段的信息。

图 3 具有压缩缩略图的Exif文件结构

3 Tag标签

3.1 属性信息的特征

        RGB数据符合标准Rev. 6.0 RGB Full Color Images, YCbCr符合TIFF Rev. 6.0 扩展YCbCr Images标准。因此,TIFF后面紧跟的数据库应按照TIFF标准记录信息。除了TIFF标准中规定的强制性属性外,Exif标准还添加了用于DSC或其他系统的TIFF可选标签,包括用于记录DSC特定属性信息的Exif特定标签,以及用于记录位置信息的GPS标签。同样,还有一些原始规格用于记录Exif压缩缩略图的信息并不包括在标准中。

        文件记录的压缩数据与非压缩数据的区别在于:

  • 以压缩数据记录主图像时,没有指定主图像或其地址指针的Tag标签。
  • 缩略图数据以压缩形式记录时,使用Exif特定的Tag标签指定其地址和大小。
  • 无论主图像还是缩略图,与JPEG标准定义的Tag标签信息不会重复记录。
  • 与压缩相关的信息能够用Tag标签记录。

3.2 IFD结构

        Exif标准中使用的IFD共计12字节,结构如下:

                字节0-1:  标签Tag字段

                字节2-3:  类型Type字段

                字节4-7:  数量Count字段

                字节8-11: 下一个IFD偏移字段。

(1)Tag标签

        每个Tag标签分配唯一2字节数字来标识该字段,Exif 0th IFD和1st IFD的标签号都与TIFF标签号相同。

(2)类型Type

        Exif使用了以下类型:

表 3 IFD数据类型

类型编码

大小(字节)

描述

1

1

BYTE

8bit unsigned integer

2

1*n

ASCII

ASCII信息码。最后一个字节必须以空字符结尾。

3

2

SHORT

(2-byte) unsigned integer

4

4

LONG

(4-byte) unsigned long

5

8

LONG * 2

第一个数字是分子,第二个数字是分母

7

n

UNDEFINED

可以包含任意数值

9

4

SLONG

32-bit (4-byte) signed integer

10

8

SLONG * 2

第一个数字是分子,第二个数字是分母

(3)数量Count字段

        数值的数量。需要注意的是,数量不是字节的总和,而是根据类型Type值确定的个数。比如,在类型Type=03的情况下,Count=1,此时数据是2个字节。

(4)偏移量

        这个字段记录从TIFF标头开始到记录值本身位置的偏移量。如果数值是4字节,则记录的是数值本身;如果数值长度小于4字节,则该值存储在从左侧开始的4字节中,即偏移区域的低位开始。例如,在大端存储big endian(TIFF Headers Byte Order 4D4D.H)时,如果short类型值为1,则存储为00010000.H

        注意:Interoperability字段的Tag标签应按从最小号开始按顺序记录。标签值(value)记录的顺序和位置没有规定。

3.3 Exif特定的IFD

表 4 Exif特定的IFD

IFD

Tag标签

Type

Count

Default

Exif IFD

34665 (8769.H)

LONG

1

--

GPS IFD

34853 (8825.H)

LONG

1

--

Interoperability IFD

40965 (A005.H)

LONG

1

--

3.4 GPS信息

表 5 GPS信息

IFD

Tag标签

Type

Count

默认值及含义

GPSVersionID

0 (0.H)

BYTE

4

2.2.0.0

2.2.0.0 = Version 2.2

Other = reserved

GPSLatitudeRef

1 (1.H)

ASCII

2

--

'N' = North latitude

'S' = South latitude

GPSLatitude

2 (2.H)

RATIONAL

3

---

GPSLongitudeRef

3 (3.H)

ASCII

2

--

'E' = East longitude

'W' = West longitude

GPSLongitude

4(4.H)

RATIONAL

3

---

GPSAltitudeRef

5 (5.H)

BYTE

1

0

0 = Sea level

1 = (negative value)

GPSAltitude

6 (6.H)

RATIONAL

1

---

GPSTimeStamp

7 (7.H)

RATIONAL

3

---

GPSSatellites

8 (8.H)

ASCII

可变

---

GPSStatus

9 (9.H)

ASCII

2

---

'A' = in progress

'V' = Interoperability

GPSMeasureMode

10 (A.H)

ASCII

2

---

'2' = 2-D

'3' = 3-D

GPSDOP

11 (B.H)

RATIONAL

1

---

GPSSpeedRef

12 (C.H)

ASCII

2

'K'

'K' = Kilometers per hour

'M' = Miles per hour

'N' = Knots

GPSSpeed

13 (D.H)

RATIONAL

1

---

……

……

……

……

……

4 数据解析示例

4.1 示例JPEG文件头部信息数据

00000000h: FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 ; ??.JFIF.....x

00000010h: 00 78 00 00 FF E1 00 C4 45 78 69 66 00 00 49 49 ; .x..?腅xif..II

00000020h: 2A 00 08 00 00 00 02 00 31 01 02 00 10 00 00 00 ; *.......1.......

00000030h: 26 00 00 00 25 88 04 00 01 00 00 00 36 00 00 00 ; &...%?.....6...

00000040h: 00 00 00 00 4C 6F 63 61 53 70 61 63 65 56 69 65 ; ....LocaSpaceVie

00000050h: 77 65 72 00 06 00 01 00 02 00 02 00 00 00 4E 00 ; wer...........N.

00000060h: 00 00 02 00 05 00 03 00 00 00 84 00 00 00 03 00 ; ..........?....

00000070h: 02 00 02 00 00 00 45 00 00 00 04 00 05 00 03 00 ; ......E.........

00000080h: 00 00 9C 00 00 00 05 00 01 00 01 00 00 00 00 00 ; ..?............

00000090h: 00 00 06 00 05 00 01 00 00 00 B4 00 00 00 00 00 ; ..........?....

000000a0h: 00 00 1E 00 00 00 01 00 00 00 37 00 00 00 01 00 ; ..........7.....

000000b0h: 00 00 87 0F 68 0F 80 96 98 00 67 00 00 00 01 00 ; ..?h.€枠.g.....

000000c0h: 00 00 1D 00 00 00 01 00 00 00 10 A1 D8 0C 80 96 ; ...........∝.€?

000000d0h: 98 00 20 4E 00 00 10 27 00 00                   ; ? N...'..

4.2 JPEG数据层级解析

        首先把原始数据按照几个大块进行分割。

        APP数据都是按照标志(2字节)、长度(4字节)、数据正文(长度根据数据定)的结构。如表所示。

表 6 JPEG数据层级解析

信息

类型

存储

位置

Hex

描述

文件

标志

00.H.

FF D8

Start Of Image (SOI),JPEG文件开始标识符

APP0

02.H

FF E0

Application-sepcific  0 (APP0)标志

注:FF En, n是从0~F的数字,如FF E0,表示APPn标志

04.H

00 10

APP0数据帧长度,不包括标识符FF E0, 包括长度符 00 10自身

06.H

4A 46 49 46 00 01 01 01 00 78 00 78 00 00

APP0数据帧信息内容

APP1

14.H

FF E1

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JPEG(Joint Photographic Experts Group)是一种流行的图像压缩格式。在一般的情况下,编写一个完整的JPEG解析器需要相当多的代码,可能超过100行。下面我将尝试用简化的方式概括一下JPEG解析过程。 首先,JPEG图像由多个部分组成,每个部分都有特定的格式和数据。我们需要读取图像的字节数据,并解析出每个部分的信息。 其次,在JPEG图像的开始处,有一个文件头部分,其中包含一些标识符和格式相关的数据,我们需要解析这部分数据以确认图像的类型和版本。 然后,我们需要解析图像的DQT(量化表)部分,其中包含了用于压缩和解压缩图像数据的量化表。每个量化表都包含了一系列用于降低图像质量的数值。我们需要正确解析这些数据以便后续的解码过程。 接下来,我们需要处理图像的DHT(霍夫曼表)部分。霍夫曼编码是一种用于无损数据压缩的技术,JPEG使用了这种编码来进一步压缩图像数据。我们需要解析出每个霍夫曼表的结构和数据,以便在解码过程中正确地还原原始图像数据。 然后,我们会遇到SOF(帧头部分),其中包含了图像的尺寸、颜色空间和采样率等信息。我们需要解析这些数据以便正确地处理图像的每个像素。 最后,我们需要解析图像的SOS(扫描头部分),其中包含了图像的扫描信息和扫描行数据。我们需要正确解析这些数据,并根据量化表、霍夫曼表和颜色空间等信息来解码图像的每个像素值。 当然,以上只是对JPEG解析过程的简化描述,实际编写一个可靠的JPEG解析器需要更详细和复杂的代码。由于篇幅所限,这里无法给出全部代码。如果你对这个话题感兴趣,建议参考相关的JPEG解析库或教程,以便更深入地了解JPEG解析的具体实现细节。 ### 回答2: JPEG是一种广泛用于图像压缩的文件格式。虽然解析JPEG需要大量代码,但在这里我们只能提供一个简短的概述。 要解析JPEG,我们需要使用图像处理库(如OpenCV)或者自己编写代码去解码JPEG文件。下面是一个简单的代码示例,用于解析JPEG文件: 1. 导入所需库: ``` import cv2 ``` 2. 打开JPEG文件: ``` jpeg_filename = "example.jpg" jpeg_file = open(jpeg_filename, "rb") ``` 3. 读取文件内容并解码: ``` jpeg_data = jpeg_file.read() image = cv2.imdecode(np.frombuffer(jpeg_data, np.uint8), cv2.IMREAD_COLOR) ``` 4. 显示图像: ``` cv2.imshow("JPEG Image", image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 以上代码首先导入了OpenCV库,然后打开并读取JPEG文件。接下来使用`imdecode`函数解码JPEG数据,并将其存储在`image`变量中。最后,使用`imshow`函数显示图像,并使用`waitKey`和`destroyAllWindows`函数等待并关闭显示窗口。 当然,这只是一个简单的示例,实际上JPEG解析的代码会更加复杂。在真实情况下,我们还需要处理图像的压缩参数、颜色空间转换以及其他图像处理操作。如果你对JPEG解析的具体细节感兴趣,我建议你参考相关的图像处理书籍或搜索开源JPEG解析库的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值