JPG 图像中经常会保存相机记录的图像拍摄位置的 GPS 信息,本文记录 Python 获取、保存图像拍摄位置信息的方法。
简介
EXIF(Exchangeable Image File Format)是一种用于存储图像拍摄相关信息的标准,这些信息包括相机设置、拍摄时间、地点等。EXIF信息通常嵌入在JPEG、TIFF和RAW图像文件中,以便于软件和设备(如数字相机、手机、扫描仪)能够读取和使用这些信息。
在EXIF信息中,GPS信息是一个重要的组成部分,它可以提供关于图像拍摄位置的详细数据。以下是GPS信息可能包含的内容:
GPS版本信息:表示EXIF中GPS信息的版本号。
GPS国界:标识图像拍摄位置所在的国家或地区。
GPS定位日期和时间:记录图像拍摄时的日期和时间。
GPS卫星信息:显示在图像拍摄时,哪些卫星参与了GPS定位。
GPS经度:表示图像拍摄位置的经度,通常以度、分、秒的形式表示。
GPS纬度:表示图像拍摄位置的纬度,同样以度、分、秒的形式表示。
GPS高度:以米为单位,表示图像拍摄位置相对于海平面的高度。
GPS速度:表示图像拍摄时的移动速度,通常以千米/小时为单位。
通过这些GPS信息,我们可以了解到图像的拍摄位置、时间和拍摄时的运动状态。这些信息对于图片的归档、分享和地理信息系统(GIS)分析等方面非常有用。
Python 获取路线
可以在 Python 中很容易地获取到图像的 exif 信息,并从中提取 gps 信息,本质上都是从文件中读取 exif 信息字段,将其解析成我们可读的 gps 信息。
这里介绍三种实现方法:
Pillow 路线
piexif
exifread
经度、纬度概念
经度 Longitude , 本初子午线 位置 为 0 度经线 , 相当于水平 x 轴 的坐标 , 经度的取值范围 -180 度 ~ +180 度 ;
纬度 Latitude , 相当于 垂直 y 轴 的坐标 , 纬度的取值范围 -90 度 ~ + 90 度 ;
西经 和 南纬 是负数 ;
Pillow
安装
使用
上源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from PIL import Image, ExifTagsdef pillow_get_exif_data (image_path ): image = Image.open (image_path) # 获取图片的exif信息 exif_data = image._getexif() if exif_data is not None : # 使用自定义的函数转换exif标签 exif_data = { ExifTags.TAGS[k]: v for k, v in exif_data.items() if k in ExifTags.TAGS } return exif_data
if __name__ == '__main__' : image_path = 'demo.jpg' exif_data = pillow_get_exif_data(image_path) print (exif_data['GPSInfo' ])
输出:
1 {0: b'\x02\x03\x00\x00' , 1: 'N' , 2: (31.0 , 5.0 , 37.6518 ) , 3: 'E' , 4: (121.0 , 14.0 , 54.4151 ) , 5: b'\x00' , 6: 93.606 , 7: (2.0 , 32.0 , 54.0 ) , 9: 'A' , 18: 'WGS-84\x00' , 29: '2024:05:04' }
含义:
1 —— 南北
2 —— 纬度(度分秒)
3 —— 东西
4 —— 经度(度分秒)
6 —— 高度(米)
piexif
安装
使用
上源码:
1 2 3 4 5 6 7 8 9 10 import piexifdef piexif_get_exif_data (image_path ): exif_dict = piexif.load(image_path) return exif_dict
if __name__ == '__main__' : image_path = 'demo.jpg' exif_data = piexif_get_exif_data(image_path) print (exif_data['GPS' ])
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 0 :(2, 3, 0, 0) 1 : b'N' 2 :((31, 1) , (5, 1) , (376518, 10000) )3 : b'E' 4 :((121, 1) , (14, 1) , (544151, 10000) )5 :0 6 :(93606, 1000) 7 :((2, 1) , (32, 1) , (54, 1) )9 : b'A' 18 : b'WGS-84\x00'
含义:
其中的 tuple 需要第一个数除以第二个数
1 —— 南北
2 —— 纬度(度分秒)
3 —— 东西
4 —— 经度(度分秒)
6 —— 高度(米)
exifread
安装
使用
上源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 import exifreaddef exifread_get_exif_data (image_path ): f = open (image_path,'rb' ) contents = exifread.process_file(f) f.close() return contents
if __name__ == '__main__' : image_path = 'demo.jpg'
contents = exifread_get_exif_data(image_path) print (contents["GPS GPSLongitude" ], contents['GPS GPSLongitudeRef' ], contents["GPS GPSLatitude" ], contents['GPS GPSLatitudeRef' ], contents["GPS GPSAltitude" ])
输出:
1 [121 , 14 , 544151/10000 ] E [31 , 5 , 188259/5000 ] N 46803/500
pyexiv2
安装
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pyexiv2 import Image as xmpImagefrom pyexiv2 import ImageDatadef phase_all_exif_info (image_path ): img = xmpImage(image_path) total_info = dict ()
xmp_data = img.read_xmp() total_info.update(xmp_data)
exif_info = img.read_exif() total_info.update(exif_info)
return total_info
性能评估
连续运行一千次,统计时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 from PIL import Image, ExifTagsimport mtutils as mtimport numpy as npimport piexifimport exifreadimport timedef piexif_get_exif_data (image_path ): exif_dict = piexif.load(image_path) return exif_dict
def pillow_get_exif_data (image_path ): image = Image.open (image_path) # 获取图片的exif信息 exif_data = image._getexif() if exif_data is not None : # 使用自定义的函数转换exif标签 exif_data = { ExifTags.TAGS[k]: v for k, v in exif_data.items() if k in ExifTags.TAGS } return exif_data
def exifread_get_exif_data (image_path ): f = open (image_path,'rb' ) contents = exifread.process_file(f) f.close() return contents
if __name__ == '__main__' : image_path = 'demo.JPG'
times = 1000
start = time.time() for _ in range (times): exif_data = pillow_get_exif_data(image_path) end = time.time() print (f'pillow_get_exif_data: {end - start} ' )
start = time.time() for _ in range (times): exif_data2 = piexif_get_exif_data(image_path) end = time.time() print (f'piexif_get_exif_data: {end - start} ' )
start = time.time() for _ in range (times): exifread_get_exif_data(image_path) end = time.time() print (f'exifread_get_exif_data: {end - start} ' )
pass
输出:
1 2 3 pillow_get_exif_data : 0 .94569993019104 piexif_get_exif_data : 0 .17222881317138672 exifread_get_exif_data : 0 .8518986701965332
结论 :piexif 又快又好用。
写入exif信息
pyexiv2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pyexiv2 import Image as xmpImagefrom pyexiv2 import ImageDatadef modify_xmp_and_exif (image_path, xmp_changes=None , exif_changes=None ): """ 只修改 XMP 元数据,不操作像素数据 """ try : # 1. 打开图片读取元数据(仅加载元数据,不加载完整像素) with xmpImage(image_path) as img: # 如果提供了 EXIF 修改,应用它 if exif_changes: img.modify_exif(exif_changes)
# 如果提供了 XMP 修改,应用它 if xmp_changes: img.modify_xmp(xmp_changes)
# 3. 获取修改后的完整元数据(包括 EXIF、XMP) exif = img.read_exif() xmp = img.read_xmp()
# 4. 用原始图片创建 ImageData 对象(承载像素和原始元数据) with open (image_path, 'rb' ) as f: img_data = ImageData(f.read())
# 5. 将修改后的元数据写入 ImageData(仅更新元数据区) img_data.modify_exif(exif) img_data.modify_xmp(xmp)
except FileNotFoundError: print (f"错误:找不到文件 '{image_path} '" ) except Exception as e: print (f"处理图片时发生错误: {e} " )
Pillow
当需要用Python 保存图像并附带 exif 信息时,可以用到 Pillow 库了,exif信息这里两种方式:
pillow 自带信息
1 2 3 4 5 from PIL import Image image_path = 'demo.jpg' pil_img = Image.open (image_path) pil_img.save('target.jpg' , exif=pil_img.info['exif' ])
piexif 信息压缩成字符串送入 pil 写图像中
1 2 3 4 5 6 7 8 9 10 from PIL import Imageimport piexifimage_path = 'demo.jpg' pil_img = Image.open (image_path)
exif_dict = piexif.load(img.info['exif' ]) exif_bytes = piexif.dump(exif_dict)
pil_img.save('target.jpg' , exif=exif_bytes)
参考资料
文章链接:
https://www.zywvvd.com/notes/coding/python/get-jpg-img-gps-info/get-jpg-img-gps-info/