分享

从照片中提取GPS信息并创建Shapefile

 晴耕雨读天 2024-05-24 发布于广西

本文主要写技术验证:读取普通手机图片信息,提取经纬度信息。

在地理信息系统中,获取精确的地理位置数据对于各种分析至关重要。随着数码摄影的普及,越来越多的照片包含了EXIF元数据,其中就包含了拍摄地点的GPS信息。只要你的手机(无人机)开启了GPS定位,那么拍摄的图片就会包含GPS信息。

本文将介绍如何使用Python编程语言从照片中提取这些GPS信息,并将其转换为Shapefile文件,以便在GIS软件中进行进一步的分析和处理。

一、准备工作

在开始之前,需要确保已经安装了以下Python库:

  • PIL(或Pillow):用于处理图像文件。

  • pyshp:用于读写Shapefile文件。

可以使用pip来安装这些库:

pip install pillow pyshp

二、提取信息

首先,我们需要编写一个函数来从照片中提取EXIF元数据中的GPS信息。这个函数将打开照片文件,解析EXIF标签,并提取出经度和纬度信息。

from PIL import Image 
def exif(img):
   '''
  从图片中返回EXIF元数据
  '''
   exif_data = {}

   try:
       i = Image.open(img)  # 使用PIL库打开图片
       tags = i._getexif()  # 获取图片的EXIF标签

       for tag, value in tags.items():
           decoded = TAGS.get(tag, tag)  # 尝试从预定义的TAGS字典中获取标签的中文描述,否则使用标签ID
           exif_data[decoded] = value  # 将标签及其值存储到exif_data字典中

   except:
       pass  # 捕获所有异常并忽略,这通常不是一个好的做法,应该明确指定要捕获的异常

   return exif_data

三、GPS信息清洗

由于EXIF中的GPS信息是以度、分、秒(DMS)的格式存储的,并且可能包含方向信息(东、西、南、北),我们需要编写一个函数dms2dd()来将这些信息转换为十进制度(DD)的格式。

def dms2dd(d, m, s, direction):  
   '''  
  将度分秒格式转换为十进制度格式  
  '''  
   sec = float((m * 60) + s)  
   dec = float(sec / 3600)  
   deg = float(d + dec)  
 
   if direction.upper() == 'W' or direction.upper() == 'S':  
       deg = deg * -1  
 
   return float(deg)  

接下来,我们需要编写一个函数gps()来解析EXIF元数据中的GPS信息,并返回经度和纬度值。这个函数将首先检查是否存在GPSInfo标签,然后提取度、分、秒和方向信息,并使用dms2dd()函数进行转换。

 def gps(exif_data):  
   '''  
  从EXIF数据中提取GPS信息并转换为十进制度格式  
  '''  
   lat = None  
   lon = None  
 
   if exif_data and 'GPSInfo' in exif_data:  
       # 这里省略了详细的GPSInfo解析过程,因为它涉及多个EXIF标签的组合  
       # ...  
       # 假设我们已经从exif_data中提取了经纬度信息(度、分、秒和方向)  
       # 这里只是模拟赋值  
       lat_d, lat_m, lat_s, lat_dir = (30, 15, 30, 'N')  
       lon_d, lon_m, lon_s, lon_dir = (120, 20, 45, 'E')  
 
       lat = dms2dd(lat_d, lat_m, lat_s, lat_dir)  
       lon = dms2dd(lon_d, lon_m, lon_s, lon_dir)  
 
   return lat, lon

四、将GPS信息转换为Shapefile格式

一旦我们从照片中提取了GPS信息,就可以将其转换为Shapefile文件,这是一种常用于地理信息系统中的矢量数据格式。我们将使用Python的pyshp库来创建Shapefile文件。

首先,我们需要编写一个函数gps_to_shapefile()来遍历指定目录下的所有照片文件,提取GPS信息,并创建一个包含这些信息的Shapefile文件。

  photos = {}
  photo_dir = '.\photos'

  # 查找指定目录下的所有JPG照片
  files = glob.glob(os.path.join(photo_dir, '*.jpg'))

  # 从文件中提取GPS元数据
  for f in files:
      e = exif(f)
      lat, lon = gps(e)
      photos[f] = [lon, lat] # 注意:这里通常经度在前,纬度在后,但此处按照您的代码保持原样

  # 构建一个包含照片文件名作为属性的点shapefile
  with shapefile.Writer('photos1', shapefile.POINT) as w:
      w.field('NAME', 'C', 80) # 创建一个名为NAME的字符型字段,最大长度为80

      for f, coords in photos.items():
          w.point(*coords) # 使用经度和纬度(注意顺序)创建一个点要素
          w.record(f) # 为点要素添加文件名属性

五、完整代码

import glob
import os
try:
   import Image
   import ImageDraw
except:
   from PIL import Image
   from PIL.ExifTags import TAGS
import shapefile


def exif(img):
   '''
  从图片中返回EXIF元数据
  '''
   exif_data = {}

   try:
       i = Image.open(img)  # 使用PIL库打开图片
       tags = i._getexif()  # 获取图片的EXIF标签

       for tag, value in tags.items():
           decoded = TAGS.get(tag, tag)  # 尝试从预定义的TAGS字典中获取标签的中文描述,否则使用标签ID
           exif_data[decoded] = value  # 将标签及其值存储到exif_data字典中

   except:
       pass  # 捕获所有异常并忽略,这通常不是一个好的做法,应该明确指定要捕获的异常

   return exif_data


def dms2dd(d, m, s, i):
   '''
  将度/分/秒转换为十进制度
  '''
   sec = float((m * 60) + s)  # 将分和秒转换为秒
   dec = float(sec / 3600)  # 将秒转换为小数度
   deg = float(d + dec)  # 将度和小数度相加

   if i.upper() == 'W':  # 如果方向是西
       deg = deg * -1  # 将度数变为负数

   elif i.upper() == 'S':  # 如果方向是南
       deg = deg * -1  # 将度数变为负数

   return float(deg)


def gps(exif):
   '''
  从EXIF元数据中提取GPS信息
  '''
   lat = None  # 纬度
   lon = None  # 经度

   if exif.get('GPSInfo'):  # 如果EXIF中包含GPS信息
       # 纬度
       coords = exif['GPSInfo']
       i = coords[1]  # 纬度方向(N/S)
       d = coords[2][0]  # 纬度度数
       m = coords[2][1]  # 纬度分钟
       s = coords[2][2]  # 纬度秒
       lat = dms2dd(d, m, s, i)  # 将纬度转换为十进制度

       # 经度
       i = coords[3]  # 经度方向(E/W)
       d = coords[4][0]  # 经度度数
       m = coords[4][1]  # 经度分钟
       s = coords[4][2]  # 经度秒
       lon = dms2dd(d, m, s, i)  # 将经度转换为十进制度

   return lat, lon


if __name__ == '__main__':
   # 存储照片文件名和GPS坐标的字典
   photos = {}
   photo_dir = '.\photos'

   # 查找指定目录下的所有JPG照片
   files = glob.glob(os.path.join(photo_dir, '*.jpg'))

   # 从文件中提取GPS元数据
   for f in files:
       e = exif(f)
       lat, lon = gps(e)
       photos[f] = [lon, lat]  # 注意:这里通常经度在前,纬度在后,但此处按照您的代码保持原样

   # 构建一个包含照片文件名作为属性的点shapefile
   with shapefile.Writer('photos1', shapefile.POINT) as w:
       w.field('NAME', 'C', 80)  # 创建一个名为NAME的字符型字段,最大长度为80

       for f, coords in photos.items():
           w.point(*coords)  # 使用经度和纬度(注意顺序)创建一个点要素
           w.record(f)  # 为点要素添加文件名属性

结果在arcgis打开,如下:

图片

现在只是读取普通的图片,获取它的GPS位置信息。下一步目的是:

1、对无人机照片校正。

2、把图片格式转为KMZ格式,放到谷歌地球上显示。

今天先到这里,再会。

图片

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多