一文教你如何用Python读取图片GPS定位

本文介绍了如何使用Python读取JPG图片中的GPS定位信息。通过理解JPEG的APP1、IFD(Image File Directory)结构和Exif格式,找到并解析GPS IFD来获取数据。提供的代码示例展示了这一过程。
摘要由CSDN通过智能技术生成

起因

早上起来,看到有人问Python获取一张JPG格式图片拍摄的时候的GPS定位的代码。GPS应该说是个敏感的信息,既然有人想读取我们的信息,那么我们至少应该直到我们的敏感信息被保存在了哪里。
研究了一天,四处搜集文档,对着一张JPG格式文件的二进制代码,终于摸到了点门道。结论就是并不是所有的图片都带着GPS等信息,例如我们微信发送图片的时候,如果不发送原图,很多信息都会被抹除(抹除APP1标签,下文有介绍)。
这里顺路推荐一个Linux下查看二进制文件的一个命令行工具:hexedit,Ubuntu下使用命令sudo apt install hexedit就可以安装,虽然没有UltraEdit这种好用,但是对于查看文件也足够了。
写代码,肯定要先直到原理,因此下面需要简单介绍下JPEG这个东西。

JPG 简介

JPEG(Joint Photographic Experts Group,联合图像专家组)标准定义了一套对静态图片进行压缩的算法,用于对图像或者视频进行压缩。JPEG标准定义了四种操作,分别是顺序DCT(sequential Discrete Cosine Transform) 模式、渐进DCT(progressive DCT)模式、无损(Lossless)模式和分层(hierarchical)模式。根据不同模式会对原是图像进行多次扫描,每扫描一次就得到一帧。每一帧前面会添加有一些压缩的参数,例如量化表、霍夫曼编码表等。这套算法可以对图像数据或者视频数据进行压缩,但是却没有定义怎么将这些压缩后的数据通过一个图片格式保存。因此JFIF(JPEG File Interchange Format)就成了一个事实上的标准,它通过标签段的方式,为这些压缩数据提供了额外的信息。
另外还有一种表示JPEG图片的格式是Exif( Exchangeable image file Format),它并不是一个新的标准,而是通过组合已有标准而成的格式。对压缩数据的标准使用的是(ISO/IEC 10918-1),和JFIF的标准一样,只不过增加了一个额外表示信息的标签APP1,这个APP1的格式标准使用的是(TIFF Rev. 6.0)。因此他们俩主要区别就是附加信息的标签不同,JFIF的标签是APP0,而Exif的标签是APP1。而例如拍摄图片的所用的相继型号等信息,就是储存在APP1标签里面。

我们的主要目的是获取图片属性信息,因此我们主要介绍Exif的格式。

Exif图片的主要结构如图所示,被特定的标签被分成了一段一段的数据。所谓标签,就是2个有特定数值的字节,它们对应的名字和数值如图所示。这些标签其实就是一组特定的数值,每个标签占两个字节,其中灰色的是必须有的结构,白色的根据情况有可能没有。

1584186733395.jpeg

Exif格式中可能存在的标签以及每个标签对应的值和含义如下:
image.png

APP1

因为我们关注的重点是APP1标签,所以我们看一下APP1的结构,其他标签等如DQT等示关于压缩数据的,如果需要处理图片内容的可以详细看,这里就忽略了。APP1有两种形式,一种是带缩略图信息的,另一种是不带缩略图信息的,分别如下面图和图所示:
Basic structure of jpg.jpeg

Structure with thumbnail.jpeg

我们已经直到APP1标签的值示0xFFE1;而Length记录了APP1段的长度,长度不能超过64KB,因为Length占用2个字节记录长度,因此最大子能表示64KB;Exif标示符占6个字节,内容是0x45 0x78 0x69 0x66 0x00 0x00,也就是Exif四个字符外加两个空字符;而TIFF头如下图所示:

TIFF Header.jpeg

TIFF头之后就是IFD和IFD的值。0th IFD主要存储主图的信息,1st IFD可能存储着缩略图的信息。

IFD(Image File Directory) 结构

JPEG图片的信息存储分为两部分:IFD和IFD Value。IFD是一个线索,通过这个线索可以找到IFD Value。举个栗子,IFD就是租房中介,IFD Value是房子。中介手上会有所有房子的信息,你找到了中介,就能直到所有房子的数量、户型、地址。
IFD由三部分组成:

  1. 第一部分:占据2个字节,这两个字节记录一共有多少条信息;
  2. 根据第一部分记录的信息的数量,每条信占用12个字节,着十二个字节又可以分为四个部分:
    1) Tag:2个字节表示这条信息的类型,比如是记录长、宽还是拍摄时间等信息;
    2) Type:2个字节表示这些记录存储为二进制的信息怎么翻译,比如是翻译成整数、小数还是字符串等;
    3)Count:4个字节表示这个记录的值有多少个,不一定示多少个字节。例如当值的类型示无符号整型的时候,因为一个无符号整型数值占用两字节,Count = 1就表示两个字节;
    4) Offset:4个字节表示找到这条记录的偏移地址,以TIFF头为基准。如果记录的数使用着四个字节就装的下,那么Offset记录的就不是地址而是实际的值,如果所用到的字节小于4,那么从最左边的字节开始使用,也就是Offset的低位开始。例如存储一个SHORT类型1的时候,大端格式中着四个字节的内容就是0x0001 0000,而小端格式就是0x0000 0000 0000 0001
  3. 第三部分占用4个细节,记录的是下一个IFD的偏移地址,如果没有下一个IFD了,这四个字节的值就是0x00000000。

IFD的结构如下:
IFD结构

IFD中Type的含义如下:
image.png

0th IFD中Tag的值和其对应意思如下:
1584175240316.jpeg

可以看到,上面的Tag表格中并为包含GPS信息,因为GPS自己有一个属于自己的IFD,在0th IFD中只有一个指向GPS IFD的指针:
image.png
GPS IFD.jpeg

有了上面的铺垫,我们就可以开始编程了。

Coding

下面是我根据我的理解写的代码,额外用到了一个numpy库,平常工作中用的比较多,更重要的是它能够将图片按照我的想法读取进内存。
代码的思路是是这样:

  1. 确定这是一张JPG图片:通过SOI标签确定,这个标签在文件的最开头,内容是0xFFD8;
  2. 找到APP1标签,理论上APP1标签是要紧跟着SOI标签的,但是我在查看图片二进制内容的时候发现并不是所有的照片都这样,因此使用搜索APP1的方法;
  3. 找到0th IFD,按照上文中的解释来找;
  4. 把找到的标签的、TIFF头的地址都记录下来
  5. 获取所有的IFD;
  6. 看找到的IFD有没有GPS IFD的指针
  7. 通过GPS IFD的指针找到GPS IFD;
  8. 读取GPS信息

下面是代码实现,另外源代码在本人Github地址为:https://github.com/zmychou/jpg-info-extractor
时间很晚了,就先草草收场了。

import numpy as np

IMAGE = []

markers = {
   
    'APP1': [0xFF, 0xE1],
    'SOI': [0xFF, 0xD8]
}



class APP1(object):
    attributes_name = {
   
        271: 'Manufacturer',
        272: 'Model',
        306: 'Last Modify',
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值