Python工具箱系列(五十二)

西安衍舆航天發表於2024-04-28

haod使用EXIF資訊對相片進行定位

開啟華為手機的相簿,你會發現已經自動進行人臉識別,相片的歸類與聚合等工作,甚至於還可以進行一步根據場景來搜尋。當然這些肯定是在使用者同意的前提下,對圖片進行了操作與計算。事實上,當拿出手機進行拍照的這一刻,很多資訊已經記錄在案,這就是EXIF資訊。Exif的全稱是“Exchangeable image file format”,翻譯過來就是可交換影像檔案格式的意思。Exif能附帶很多圖片生成的資訊,例如使用數位相機拍攝圖片的話,那麼相機型號、光圈、快門等資訊都會被Exif記錄下來。目前絕大部分圖片格式都支援Exif,只有JPEG2000、GIF等少數格式不相容Exif。而Exif也不僅僅能用於圖片檔案,它也可以用於音訊。如下圖所示,在windows 11下顯示一張圖片的相關資訊。

Python工具箱系列(五十二)

這裡我們需要注意的是,並非所有的照片都能夠進行解析,必須是攜帶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檔案中,開啟後的效果如下所示。

Python工具箱系列(五十二)

從圖中可以看出照片是在金地廣場,確實如此。

相關文章