python获取原图GPS位置信息,轻松得到你的活动轨迹
一、图像exif信息
介绍
exif(exchangeable image file format,可交换图像文件格式)是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据,如拍摄时间、图像分辨率、感光值、gps坐标等。
exif最初由日本电子工业发展协会在1996年制定,版本为1.0。1998年,升级到2.1,增加了对音频文件的支持。2002年3月,发表了2.2版。
exif可以附加于jpeg、tiff、riff等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。
exif信息是可以被任意编辑的,因此只有参考的功能。exif信息以0xffe1作为开头标记,后两个字节表示exif信息的长度。所以exif信息最大为64 kb,而内部采用tiff格式。
查看exif信息
windows文件属性
- 注 :本文处理图片针对原图
windows7以上操作系统具备对exif的原生支持,windows系统下,可以通过鼠标右键点击图片打开菜单,点击属性并切换到 详细信息 标签下,即可直接获取图片的exif信息。
在线查看器
- 图虫exif查看器
- 改图宝
- 我爱斗图 (一个表情包网站:joy:)
exifread 库
exifread模块为python读取图片exif信息的库。
exifread模块的下载地址: https://pypi.python.org/pypi/...
安装exifread库
pip install exifread
主要使用process_file函数进行解析,传入图片文件对象,返回一个包含图片信息的字典。其中,exif中gps格式为dms格式,即:d(degree,度)、m(minute,分)、s(second,秒),因此要进行转换才能得到常见的double类型的经纬度值。下面就用python + exifread读取图片的详细信息。
import exifread with open('img_20190618_163339.jpg', 'rb') as f: exif_dict = exifread.process_file(f) print('拍摄时间:', exif_dict['exif datetimeoriginal']) print('照相机制造商:', exif_dict['image make']) print('照相机型号:', exif_dict['image model']) print('照片尺寸:', exif_dict['exif exifimagewidth'], exif_dict['exif exifimagelength']) # 经度 lon_ref = exif_dict["gps gpslongituderef"].printable lon = exif_dict["gps gpslongitude"].printable[1:-1].replace(" ", "").replace("/", ",").split(",") lon = float(lon[0]) + float(lon[1]) / 60 + float(lon[2]) / float(lon[3]) / 3600 if lon_ref != "e": lon = lon * (-1) # 纬度 lat_ref = exif_dict["gps gpslatituderef"].printable lat = exif_dict["gps gpslatitude"].printable[1:-1].replace(" ", "").replace("/", ",").split(",") lat = float(lat[0]) + float(lat[1]) / 60 + float(lat[2]) / float(lat[3]) / 3600 if lat_ref != "n": lat = lat * (-1) print('照片的经纬度:', (lat, lon)) for key in exif_dict: print("%s: %s" % (key, exif_dict[key]))
输出:
拍摄时间: 2019:06:18 16:33:40 照相机制造商: huawei 照相机型号: hry-al00ta 照片尺寸: 3968 2976 照片的经纬度: (13.787098884444445, 100.62936401361111) image imagewidth: 3968 image imagelength: 2976 image bitspersample: [8, 8, 8] image make: huawei image model: hry-al00ta image orientation: 0 image xresolution: 72 image yresolution: 72 image resolutionunit: pixels/inch image software: hry-al00ta 9.0.1.130(c00e130r4p1) image datetime: 2019:06:18 16:33:40 image ycbcrpositioning: centered image exifoffset: 290 gps gpsversionid: [2, 2, 0, 0] gps gpslatituderef: n gps gpslatitude: [13, 47, 847249/62500] gps gpslongituderef: e gps gpslongitude: [100, 37, 45710449/1000000] ............................省略
二、经纬度转地址
要想将图片中的经纬度信息转换为详细地址,同样也有很多方法,比如在线查询、地图api或者利用python的地理位置信息库: geopy 。
在线查询
- gps查询网址1
- gps查询网址2
- 地球在线
没错,这张图片是我在泰国拍的:grinning:
地图api
这里以百度地图api为例,需要去官网注册创建应用获取ak码。
百度地图:
这里以我之前拍的重庆彩车照进行试验,从输出结果上看算是非常精准定位了。
import json import requests import exifread with open('img_20191019_164726.jpg', 'rb') as f: exif_dict = exifread.process_file(f) # 经度 lon_ref = exif_dict["gps gpslongituderef"].printable lon = exif_dict["gps gpslongitude"].printable[1:-1].replace(" ", "").replace("/", ",").split(",") lon = float(lon[0]) + float(lon[1]) / 60 + float(lon[2]) / float(lon[3]) / 3600 if lon_ref != "e": lon = lon * (-1) # 纬度 lat_ref = exif_dict["gps gpslatituderef"].printable lat = exif_dict["gps gpslatitude"].printable[1:-1].replace(" ", "").replace("/", ",").split(",") lat = float(lat[0]) + float(lat[1]) / 60 + float(lat[2]) / float(lat[3]) / 3600 if lat_ref != "n": lat = lat * (-1) print('照片的经纬度:', (lat, lon)) # 调用百度地图api转换经纬度为详细地址 secret_key = 'masvginlnytgim4uulcaelucekgnafxj' # 百度地图api 需要注册创建应用 baidu_map_api = 'http://api.map.baidu.com/reverse_geocoding/v3/?ak={}&output=json&coordtype=wgs84ll&location={},{}'.format(secret_key, lat, lon) content = requests.get(baidu_map_api).text gps_address = json.loads(content) # 结构化的地址 formatted_address = gps_address["result"]["formatted_address"] # 国家(若需访问*poi,需申请逆地理编码*poi服务权限) country = gps_address["result"]["addresscomponent"]["country"] # 省 province = gps_address["result"]["addresscomponent"]["province"] # 市 city = gps_address["result"]["addresscomponent"]["city"] # 区 district = gps_address["result"]["addresscomponent"]["district"] # 语义化地址描述 sematic_description = gps_address["result"]["sematic_description"] print(formatted_address) print(gps_address["result"]["business"])
输出
照片的经纬度: (29.564165115277778, 106.54840087888888) 重庆市渝中区学田湾正街2号11楼 大礼堂,上清寺,大溪沟
geopy库
geopy使python开发人员能够使用第三方地理编码程序和其他数据源(包括谷歌地图,必应地图,nominatim等),轻松定位全球各地的地址、城市、国家和地标的坐标。
安装
pip install geopy
通过geopy进行经纬度查询
import exifread from geopy.geocoders import nominatim with open('img_20190618_163339.jpg', 'rb') as f: exif_dict = exifread.process_file(f) # 经度 lon_ref = exif_dict["gps gpslongituderef"].printable lon = exif_dict["gps gpslongitude"].printable[1:-1].replace(" ", "").replace("/", ",").split(",") lon = float(lon[0]) + float(lon[1]) / 60 + float(lon[2]) / float(lon[3]) / 3600 if lon_ref != "e": lon = lon * (-1) # 纬度 lat_ref = exif_dict["gps gpslatituderef"].printable lat = exif_dict["gps gpslatitude"].printable[1:-1].replace(" ", "").replace("/", ",").split(",") lat = float(lat[0]) + float(lat[1]) / 60 + float(lat[2]) / float(lat[3]) / 3600 if lat_ref != "n": lat = lat * (-1) print('照片的经纬度:', (lat, lon)) reverse_value = str(lat) + ', ' + str(lon) geolocator = nominatim() location = geolocator.reverse(reverse_value) print('照片的经纬度信息:') print((location.latitude, location.longitude)) print('照片的地址信息:') print(location.address)
输出
照片的经纬度: (13.787098884444445, 100.62936401361111) 照片的经纬度信息: (13.787094791472175, 100.6293647961708) 照片的地址信息: กรุงเทพมหานคร, เขตวังทองหลาง, 10310, ประเทศไทย
三、搞事情
一张图片可以暴露你什么时间在什么地方,甚至体现了你当时在做什么,想想有多可怕,不信可以看看这个:
不过对于个人来说,图片exif信息可以让我们更好地管理自己拍摄的图片库。比如说:
按时间归类
可以通过exif时间信息,将图片归类到不同时间命名的文件夹下。
import os import exifread import shutil imgs_path = 'e:\泰国游' for img in os.listdir(imgs_path): img_path = os.path.join(imgs_path, img) img_file = open(img_path, 'rb') exif_dict = exifread.process_file(img_file) date = exif_dict['exif datetimeoriginal'] date = date.values.replace(':', '_') year_month_day = date[:10] target_path = os.path.join('e:\旅游', year_month_day) if not os.path.exists(target_path): os.mkdir(target_path) shutil.copy(img_path, target_path)
旅游地图
下面以本人2019年下半年的手机图片数据进行统计分析,并结合pyecharts和mapbox进行展示。
- pyecharts可视化
pyecharts:一个大神创建的*,将python与echarts结合的强大的数据可视化工具。注意版本不同,有些接口调用是有区别的。
pip install pyecharts pip install echarts-countries-pypkg pip install pyecharts-jupyter-installer==0.0.3
统计和转换地址的代码,输出为csv文件:
import os import json import requests import pandas as pd import exifread from geopy.geocoders import nominatim secret_key = '###################' def convert_dms2dd(coord_arr): arr = str(coord_arr).replace('[', '').replace(']', '').split(', ') d = float(arr[0]) m = float(arr[1]) s = float(arr[2].split('/')[0]) / float(arr[2].split('/')[1]) dd = float(d) + (float(m) / 60) + (float(s) / 3600) return dd def get_img_infor_tup(photo): img_file = open(photo, 'rb') image_map = exifread.process_file(img_file) img_dict = {} img_dict['image name'] = os.path.basename(photo) try: img_dict['width'] = image_map['image imagewidth'].printable img_dict['length'] = image_map['image imagelength'].printable # 图片的经度 img_longitude = convert_dms2dd(image_map["gps gpslongitude"].printable) if image_map["gps gpslongituderef"].printable != "e": img_longitude = img_longitude * (-1) img_dict['longitude'] = img_longitude # 图片的纬度 img_latitude = convert_dms2dd(image_map["gps gpslatitude"].printable) if image_map["gps gpslatituderef"].printable != "n": img_latitude = img_latitude * (-1) img_dict['latitude'] = img_latitude altitude = image_map['gps gpsaltitude'].printable if '/' in altitude: altitude = float(altitude.split('/')[0]) / float(altitude.split('/')[1]) img_dict['altitude'] = altitude # 照片拍摄时间 img_dict['date'] = image_map["exif datetimeoriginal"].printable img_file.close() # 返回经纬度元组 return img_dict except exception as e: img_file.close() print('error:图片中不包含gps信息') return none def get_detail_infor_by_baidu(lat, lon): baidu_map_api = 'http://api.map.baidu.com/reverse_geocoding/v3/?ak={0}&output=json&coordtype=wgs84ll&location={1},' \ '{2}'.format(secret_key, lat, lon) content = requests.get(baidu_map_api).text gps_address = json.loads(content) return gps_address["result"] def img_data_statistic(imgs_path): info_list = [] for file_name in os.listdir(imgs_path): img_path = os.path.join(imgs_path, file_name) info_dict = get_img_infor_tup(img_path) if info_dict is not none: gps_address_dict = get_detail_infor_by_baidu(info_dict['latitude'], info_dict['longitude']) # 省 info_dict['province'] = gps_address_dict["addresscomponent"]["province"] # 市 info_dict['city'] = gps_address_dict["addresscomponent"]["city"] # 区 info_dict['district'] = gps_address_dict["addresscomponent"]["district"] info_dict['formatted_address'] = gps_address_dict["formatted_address"] info_list.append(info_dict) # break img_df = pd.dataframe(info_list) img_df.to_csv('imginfo.csv', index=false, encoding='utf-8') if __name__ == '__main__': imgs_path = 'e:/photo' img_data_statistic(imgs_path)
可视化,主要是通过pyecharts进行绘图。
import pandas as pd from pyecharts.charts import bar df = pd.read_csv('imginfo.csv',sep=',') data = df["province"].value_counts() bar = ( bar() .add_xaxis(data.index.tolist()) .add_yaxis("图片数量", data.values.tolist()) .set_global_opts(title_opts=opts.titleopts(title="各省分布情况", subtitle='19年下半年去了哪儿'), xaxis_opts=opts.axisopts(name="省份",axislabel_opts={"rotate":45})) ) bar.render_notebook()
同理,各个城市的统计图。
以及,在地图上进行可视化。
import json import pandas as pd from pyecharts import options as opts from pyecharts.charts import geo from pyecharts.globals import currentconfig, notebooktype currentconfig.notebook_type = notebooktype.jupyter_notebook def is_chinese(word): for ch in word: if '\u4e00' <= ch <= '\u9fff': return true return false df = pd.read_csv('imginfo.csv',sep=',') data = df["city"].value_counts() # 找出国外地址,geo添加自定义点 thailand_address = [] json_data= {} for city in data.index: if not is_chinese(city): json_data[city] = df[['longitude','latitude']][df["city"] == city].mean().values.tolist() thailand_address.append(city) json_str = json.dump(json_data, open('thailand.json', 'w', encoding='utf-8'), ensure_ascii=false,indent=4) # 链式调用 c = ( geo() .add_schema(maptype="world") .add_coordinate_json(json_file='thailand.json') .add("去过的城市", [list(z) for z in zip(data.index.tolist(), data.values.tolist())], symbol_size = 30, large_threshold = 2000, symbol="pin") .set_series_opts(label_opts=opts.labelopts(is_show=false)) .set_global_opts(title_opts=opts.titleopts(title="2019下半年你去过哪儿")) ) c.render_notebook()
国内地图展示
- mapbox可视化
可以直接将包含经度和维度的统计数据(csv格式)拖入到网站:
综上,要想信息不被泄露
- 传图的时候不要用原图,可以先压缩或者p图再上传
- 在相机的设置里,将地理位置关掉
- 直接将gps的权限关掉