python獲取原圖GPS位置信息,輕鬆獲得你的活動軌跡

1、圖像EXIF信息

介紹

EXIF(Exchangeable image file format,可交換圖像文件格式)是專門爲數碼相機的照片設定的,能夠記錄數碼照片的屬性信息和拍攝數據,如拍攝時間、圖像分辨率、感光值、GPS座標等。html

Exif最初由日本電子工業發展協會在1996年制定,版本爲1.0。1998年,升級到2.1,增長了對音頻文件的支持。2002年3月,發表了2.2版。python

Exif能夠附加於JPEG、TIFF、RIFF等文件之中,爲其增長有關數碼相機拍攝信息的內容和索引圖或圖像處理軟件的版本信息。git

Exif信息是能夠被任意編輯的,所以只有參考的功能。Exif信息以0xFFE1做爲開頭標記,後兩個字節表示Exif信息的長度。因此Exif信息最大爲64 kb,而內部採用TIFF格式。shell

參考自百度百科

查看EXIF信息

windows文件屬性

  • :本文處理圖片針對原圖

Windows7以上操做系統具有對Exif的原生支持,Windows系統下,能夠經過鼠標右鍵點擊圖片打開菜單,點擊屬性並切換到詳細信息標籤下,便可直接獲取圖片的EXIF信息。json

在線查看器

  • 圖蟲EXIF查看器

https://exif.tuchong.com/windows

  • 改圖寶

https://www.gaitubao.com/exifapi

  • 我愛鬥圖(一個表情包網站😂)

https://www.52doutu.cn/tools/...微信

exifread 庫

exifread模塊爲python讀取圖片EXIF信息的庫。
exifread模塊的下載地址:https://pypi.python.org/pypi/...
安裝exifread庫app

pip install exifread

主要使用process_file函數進行解析,傳入圖片文件對象,返回一個包含圖片信息的字典。其中,exif中GPS格式爲DMS格式,即:D(degree,度)、M(minute,分)、S(second,秒),所以要進行轉換才能獲得常見的double類型的經緯度值。下面就用python + exifread讀取圖片的詳細信息。echarts

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]
............................省略

2、經緯度轉地址

要想將圖片中的經緯度信息轉換爲詳細地址,一樣也有不少方法,好比在線查詢、地圖API或者利用python的地理位置信息庫:geopy

在線查詢

  • GPS查詢網址1

http://www.gpsspg.com/maps.htm

上文圖片經緯度結果

  • GPS查詢網址2

http://www.gzhatu.com/dingwei...

  • 地球在線

https://www.earthol.com/

沒錯,這張圖片是我在泰國拍的😀

地圖API

這裏以百度地圖API爲例,須要去官網註冊建立應用獲取AK碼。
百度地圖:http://lbsyun.baidu.com
這裏以我以前拍的重慶彩車照進行試驗,從輸出結果上看算是很是精準定位了。

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, ประเทศไทย

3、搞事情

一張圖片能夠暴露你什麼時間在什麼地方,甚至體現了你當時在作什麼,想一想有多可怕,不信能夠看看這個:

不過對於我的來講,圖片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格式)拖入到網站:
https://www.mapbox.cn/labs/mb...

mapbox展現結果

綜上,要想信息不被泄露

  • 傳圖的時候不要用原圖,能夠先壓縮或者P圖再上傳
  • 在相機的設置裏,將地理位置關掉
  • 直接將GPS的權限關掉

參考:

python第三方庫之exifread庫使用

【華爲雲技術分享】Python解析照片EXIF信息,獲取座標位置

Python-根據照片信息獲取用戶詳細信息(微信發原圖或泄露位置信息)

基於Python獲取照片的GPS位置信息

Python獲取照片的位置信息並進行可視化

【原創】某少兒不宜網站圖片拍攝位置分析 (python批量讀取圖片GPS位置信息)

相關文章
相關標籤/搜索