Python工具箱系列(五十二)
haod使用EXIF信息对相片进行定位
打开华为手机的图库,你会发现已经自动进行人脸识别,相片的归类与聚合等工作,甚至于还可以进行一步根据场景来搜索。当然这些肯定是在用户同意的前提下,对图片进行了操作与计算。事实上,当拿出手机进行拍照的这一刻,很多信息已经记录在案,这就是EXIF信息。Exif的全称是“Exchangeable image file format”,翻译过来就是可交换图像文件格式的意思。Exif能附带很多图片生成的信息,例如使用数码相机拍摄图片的话,那么相机型号、光圈、快门等信息都会被Exif记录下来。目前绝大部分图片格式都支持Exif,只有JPEG2000、GIF等少数格式不兼容Exif。而Exif也不仅仅能用于图片文件,它也可以用于音频。如下图所示,在windows 11下显示一张图片的相关信息。
这里我们需要注意的是,并非所有的照片都能够进行解析,必须是携带exif信息的原始图片。如果中间进行了压缩或者P图,那么就无法识别了。当然像一些社交平台也会专门针对exif进行处理,比如微信,你发在朋友圈的图片会自动压缩,所以是不会暴露信息的。下面的python代码示范了如何获得exif信息。
import exifread
def exifinfo(fullfilename: str) -> dict:
"""
获得图片的EXIF信息
Args:
fullfilename (str): 图片文件名
Returns:
dict: EXIF信息组成的字典
"""
f = open(fullfilename, 'rb')
tags = exifread.process_file(f)
for tag in tags.keys():
if tag not in ('JPEGThumbnail', 'TIFFThumbnail', 'Filename', 'EXIF MakerNote'):
print("Key: %s, value %s" % (tag, tags[tag]))
def exifgps(fullfilename: str)->list:
"""
获得图片的EXIF信息中的GPS位置信息
Args:
fullfilename (str): 图片文件名
Returns:
list: 包括经度、纬度与创建时间的列表
"""
f = open(fullfilename, 'rb')
tags = exifread.process_file(f)
key = 'EXIF DateTimeOriginal'
if key in tags.keys():
eDate=tags[key].printable
key = 'GPS GPSLongitude'
if key in tags.keys():
eLon=tags[key].printable
eLat=tags['GPS GPSLatitude'].printable
lon=eLon[1:-1].replace(' ','').replace('/',',').split(',')
lon=float(lon[0])+float(lon[1])/60+float(lon[2])/float(lon[3])/3600
lat=eLat[1:-1].replace(' ','').replace('/',',').split(',')
lat=float(lat[0])+float(lat[1])/60+float(lat[2])/float(lat[3])/3600
return [lon,lat,eDate] #经度,纬度,拍摄时间
targetfile = r'd:\test\IMG_20220813_121501.jpg'
exifinfo(targetfile)
print(exifgps(targetfile))
上述代码中:
exifinfo中跳过了很难看懂的key,例如"JPEGThumbnail",否则显示一大堆数字。
exifgps中,不断的进行判断,是因为图片不一定存在exif,也不一定存在gps的定位信息。许多软件都具备破坏exif的能力。
接下来的任务就是通过坐标来在地图上打点,我们使用python folium库来完成。folium底层使用leaflet.js库,因此数据可视化的呈现是网页。之所以使用folium,而不是使用网上常见的pyechart的主要原因就是不想受制于大厂的专有技术。使用folium也可以在后台灵活对接多个第三方的地图引擎。下述代码完整演示了这个过程。
import exifread
import folium
def exifinfo(fullfilename: str) -> dict:
"""
获得图片的EXIF信息
Args:
fullfilename (str): 图片文件名
Returns:
dict: EXIF信息组成的字典
"""
f = open(fullfilename, 'rb')
tags = exifread.process_file(f)
for tag in tags.keys():
if tag not in ('JPEGThumbnail', 'TIFFThumbnail', 'Filename', 'EXIF MakerNote'):
print("Key: %s, value %s" % (tag, tags[tag]))
def exifgps(fullfilename: str)->list:
"""
获得图片的EXIF信息中的GPS位置信息
Args:
fullfilename (str): 图片文件名
Returns:
list: 包括经度、纬度与创建时间的列表
"""
f = open(fullfilename, 'rb')
tags = exifread.process_file(f)
key = 'EXIF DateTimeOriginal'
if key in tags.keys():
eDate=tags[key].printable
key = 'GPS GPSLongitude'
if key in tags.keys():
eLon=tags[key].printable
eLat=tags['GPS GPSLatitude'].printable
lon=eLon[1:-1].replace(' ','').replace('/',',').split(',')
lon=float(lon[0])+float(lon[1])/60+float(lon[2])/float(lon[3])/3600
lat=eLat[1:-1].replace(' ','').replace('/',',').split(',')
lat=float(lat[0])+float(lat[1])/60+float(lat[2])/float(lat[3])/3600
return [lon,lat,eDate] #经度,纬度,拍摄时间
targetfile = r'd:\test\IMG_20220813_121501.jpg'
exifinfo(targetfile)
# 获得图片的经纬度
lon,lat,_= exifgps(targetfile)
# 注意location是(纬度,经度)组合
m = folium.Map(location=[lat,lon],width=600,height=600,zoom_start=12)
folium.Marker(location=[lat,lon],popup='Here!',icon=folium.Icon(icon='cloud')).add_to(m)
m.save('d:\dev\map.html')
folium能够在jupyter中直接展示,这段代码运行后将结果保存到html文件中,打开后的效果如下所示。
从图中可以看出照片是在金地广场,确实如此。