你就不想知道,他/她/ta/他们的这些照片,是在哪里拍的么~

你就不想知道,他/她/ta/他们的这些照片,是在哪里拍的么~
Page content

相信我们能在网络上浏览到各种各样的图片,或是在我们的手机里也存着非常多的照片。有没有在那么一刻,你渴望知道,这些图像的拍摄地,在哪儿。或是你想了解,某个人的曾经去了哪儿。那么今天,就给大家介绍一下,如何用工具解析图像的exif信息,画出所谓的人轨。

0x00 EXIF信息

可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。例如 GPS定位,设备型号,拍摄时间等等。

当前我们依据EXIF 2.3版所定义的标准来截取tag

更多tags可参考 https://www.exiv2.org/tags.html

0x01 获取GPS信息

那么例如我们要通过编写脚本方法来获取GPS信息,我们就可以这样


from PIL import Image
from fractions import Fraction

def format(datas: list)-> float:
    """
        经纬度格式转换 度分秒转小数
    """
    return float(
        Fraction(*datas[0])
        + Fraction(Fraction(*datas[1]),60)
        + Fraction(Fraction(*datas[2]),3600)
    )

def get_exif_datas(file,name=''):
    """
        获取图像经纬度
    """
    try:
        gps_key = 34853 # GPS键名
        gps_data = Image.open(filename)._getexif().get(gps_key, None)
        if gps_data:
            return (
                (1 if gps_data[3] == "E" else -1) * format(gps_data[4]), # 经度
                (1 if gps_data[1] == "N" else -1) * format(gps_data[2])  # 维度
            )
    except Exception as e:
        pass

因为不能用自己的相册做测试,所以我们在github搜寻了一个较为特殊的图集,数量1000+,大概就是些女装吧=_=

由于数据比较大,大概1G以上,所以我们选择在Colab上做测试

通过以上命令!git clone https://github.com/komeiji-satori/Dress,大概也就几秒就已经把数据存到了您的google云盘中。挺快。

经过上述操作的测试 我们获得了大约47张图片的GPS标记。

0x02 获取其他辅助信息

当然,这部分信息在实际应用中似乎不足以做些啥。于是我们打算加入

  • path 图像路径
  • date 拍摄日期
  • alt 照片高度信息
  • soft 所用编辑软件
  • model 所用设备版本
  • make 所用设备型号
  • address 大概位置信息

以上字段除了address,其他都可以直接从exif之中获取。

0x03 获取大概地址

我们通过http://restapi.amap.com/v3/geocode/regeo?key={}&s=rsv3&location={},{}地址作为API来截取大概地址信息,前提是得注册并获取一个Key。

0x04 最终数据

最终我们获取到的单个数据是这样的。


{
    "path": "images/16.jpg",
    "date": "2019-06-20 16:31:38",
    "gps": [
        36.3425,
        119.75333333333333
    ],
    "alt": [
        0.0,
        "海平面"
    ],
    "soft": "Adobe Photoshop CC 2018 (Windows)",
    "model": "SM-N9500",
    "make": "samsung",
    "address": "山东省潍坊市高密市高密市朝阳街道潍坊市高密中等专业学校青岛科技大学高密校区"
}

0x05 可视化处理

在获取了这些数据之后,我们使用强大的地理标记库folium来做可视化处理。以30,120作为中心。


def make_popup(item):
    """
        生成一个较为合理的信息面板
    """
    add_normal = lambda data, dicts: f"<p>{data[1]}: {dicts[data[0]]}</p>" if data[0] in dicts else "" # 判断信息是否需要添加
    html = ""
    cols = (("address", "地址"), ("make", "设备"), ("model", "型号"), ("soft", "编辑软件")) # 基础信息键值
    if "path" in item:
        html += '<center><p> <a href="{}">{}</a ></p></center>'.format(
            item["path"], item["path"]
        )
    if "date" in item:
        html += f"<center><p>{item['date']}</p></center>"
    if "path" in item:
        html += "<img src='{}' height='240' width='240' />".format(item["path"])

    html += "".join([add_normal(_, item) for _ in cols])
    if "alt" in item and item["alt"][0] > 0.0:
        html += "<p>高度: {1} {0}米</p>".format(*item["alt"])
    return html

通过上面的方法我们可以得到一个信息较为直观的信息面板。


map = folium.Map([30, 120], world_copy_jump=False, detect_retina=True, no_wrap=True, zoom_start=5) # 预设地图
locations = [] # 定位点
popups = [] # 标点提示
for item in data:
    locations.append(item["gps"])
    popups.append(folium.Popup(make_popup(item), parse_html=False, max_width="100%")) # 载入标点
plugins.MarkerCluster(locations, popups=popups).add_to(map) # 生成标点
map.fit_bounds(map.get_bounds()) # 自动转到大概位置
map.save("res.html") # 生成html

最终我们通过下面这个动图来更好的展现结果。

可以看到,各位女装大佬的位置已经被mark到了地图上。

在此之后我将这次的代码包装成了一个可用的脚本,毕竟它的用处可不止这样~ 关注微信公众号进击的Robot,回复相册定位,即可获得源代码。更多精彩,等你解锁。