تبدیل عکس های دارای برچسب جغرافیایی به KML PhotoOverlays

Mano Marks، تیم Google Geo APIs
ژانویه 2009

هدف، واقعگرایانه

این آموزش به شما می آموزد که چگونه از عکس های دارای برچسب جغرافیایی برای ایجاد KML PhotoOverlays استفاده کنید. در حالی که کد نمونه در پایتون نوشته شده است، بسیاری از کتابخانه های مشابه در زبان های برنامه نویسی دیگر وجود دارد، بنابراین ترجمه این کد به زبان دیگر مشکلی نیست. کد موجود در این مقاله بر یک کتابخانه منبع باز پایتون، EXIF.py متکی است.

معرفی

دوربین های دیجیتال چیزهای بسیار شگفت انگیزی هستند. بسیاری از کاربران متوجه این موضوع نیستند، اما آنها چیزی بیش از گرفتن عکس و فیلم انجام می دهند. آنها همچنین آن فیلم ها و عکس ها را با متادیتا در مورد دوربین و تنظیمات آن برچسب گذاری می کنند. در چند سال گذشته، مردم راه‌هایی برای اضافه کردن داده‌های جغرافیایی به آن اطلاعات پیدا کرده‌اند، که یا توسط سازندگان دوربین، مانند برخی از دوربین‌های Ricoh و Nikon، یا از طریق دستگاه‌هایی مانند GPS Logger و EyeFi Explore تعبیه شده است. تلفن های دوربین دار مانند آیفون و تلفن هایی که از سیستم عامل اندروید استفاده می کنند، مانند T-Mobile's G1 ، این داده ها را به طور خودکار جاسازی می کنند. برخی از سایت‌های آپلود عکس، مانند Panoramio ، Picasa Web Albums و Flickr ، داده‌های GPS را به‌طور خودکار تجزیه می‌کنند و از آن برای برچسب‌گذاری جغرافیایی یک عکس استفاده می‌کنند. سپس می توانید آن داده ها را در فیدها برگردانید. اما کجای آن سرگرم کننده است؟ این مقاله چگونگی دستیابی به آن داده ها را بررسی می کند.

هدرهای Exif

رایج ترین راه برای جاسازی داده ها در یک فایل تصویری استفاده از فرمت فایل تصویری تبادلی یا EXIF ​​است. داده ها به صورت باینری در سربرگ های EXIF ​​به صورت استاندارد ذخیره می شوند. اگر مشخصات هدرهای EXIF ​​را می دانید، می توانید خودتان آنها را تجزیه کنید. خوشبختانه، شخصی قبلاً کار سختی را انجام داده و یک ماژول پایتون برای شما نوشته است. کتابخانه منبع باز EXIF.py یک ابزار عالی برای خواندن سرصفحه های فایل های JPEG است.

کد

کد نمونه این مقاله در این فایل است: exif2kml.py . اگر می‌خواهید مستقیماً به استفاده از آن رد شوید، آن ماژول و همچنین EXIF.py را دانلود کنید و آنها را در همان فهرست قرار دهید. python exif2kml.py foo.jpg foo.jpg را با مسیر عکس دارای برچسب جغرافیایی جایگزین foo.jpg کنید. فایلی به نام test.kml تولید می کند.

تجزیه سرصفحه های Exif

EXIF.py یک رابط آسان برای بیرون کشیدن هدرهای Exif فراهم می کند. به سادگی تابع process_file() را اجرا کنید و هدرها را به عنوان یک شی dict برمی گرداند.

def GetHeaders(the_file):
  """Handles getting the Exif headers and returns them as a dict.

  Args:
    the_file: A file object

  Returns:
    a dict mapping keys corresponding to the Exif headers of a file.
  """

  data = EXIF.process_file(the_file, 'UNDEF', False, False, False)
  return data

هنگامی که هدرهای Exif را دارید، باید مختصات GPS را استخراج کنید. EXIF.py اینها را به‌عنوان اشیاء Ratio ، اشیایی برای ذخیره‌سازی صورت‌دهنده و مخرج مقادیر در نظر می‌گیرد. این یک نسبت دقیق را به جای تکیه بر یک عدد ممیز شناور تنظیم می کند. اما، KML انتظار اعداد اعشاری، نه نسبت ها را دارد. بنابراین شما هر یک از مختصات را استخراج می کنید و صورت و مخرج را به یک عدد ممیز شناور برای درجه اعشار تبدیل می کنید:

def DmsToDecimal(degree_num, degree_den, minute_num, minute_den,
                 second_num, second_den):
  """Converts the Degree/Minute/Second formatted GPS data to decimal degrees.

  Args:
    degree_num: The numerator of the degree object.
    degree_den: The denominator of the degree object.
    minute_num: The numerator of the minute object.
    minute_den: The denominator of the minute object.
    second_num: The numerator of the second object.
    second_den: The denominator of the second object.

  Returns:
    A deciminal degree.
  """

  degree = float(degree_num)/float(degree_den)
  minute = float(minute_num)/float(minute_den)/60
  second = float(second_num)/float(second_den)/3600
  return degree + minute + second


def GetGps(data):
  """Parses out the GPS coordinates from the file.

  Args:
    data: A dict object representing the Exif headers of the photo.

  Returns:
    A tuple representing the latitude, longitude, and altitude of the photo.
  """

  lat_dms = data['GPS GPSLatitude'].values
  long_dms = data['GPS GPSLongitude'].values
  latitude = DmsToDecimal(lat_dms[0].num, lat_dms[0].den,
                          lat_dms[1].num, lat_dms[1].den,
                          lat_dms[2].num, lat_dms[2].den)
  longitude = DmsToDecimal(long_dms[0].num, long_dms[0].den,
                           long_dms[1].num, long_dms[1].den,
                           long_dms[2].num, long_dms[2].den)
  if data['GPS GPSLatitudeRef'].printable == 'S': latitude *= -1
  if data['GPS GPSLongitudeRef'].printable == 'W': longitude *= -1
  altitude = None

  try:
    alt = data['GPS GPSAltitude'].values[0]
    altitude = alt.num/alt.den
    if data['GPS GPSAltitudeRef'] == 1: altitude *= -1

  except KeyError:
    altitude = 0

  return latitude, longitude, altitude

هنگامی که مختصات را دارید، ایجاد یک PhotoOverlay ساده برای هر عکس آسان است:

def CreatePhotoOverlay(kml_doc, file_name, the_file, file_iterator):
  """Creates a PhotoOverlay element in the kml_doc element.

  Args:
    kml_doc: An XML document object.
    file_name: The name of the file.
    the_file: The file object.
    file_iterator: The file iterator, used to create the id.

  Returns:
    An XML element representing the PhotoOverlay.
  """

  photo_id = 'photo%s' % file_iterator
  data = GetHeaders(the_file)
  coords = GetGps(data)

  po = kml_doc.createElement('PhotoOverlay')
  po.setAttribute('id', photo_id)
  name = kml_doc.createElement('name')
  name.appendChild(kml_doc.createTextNode(file_name))
  description = kml_doc.createElement('description')
  description.appendChild(kml_doc.createCDATASection('<a href="#%s">'
                                                     'Click here to fly into '
                                                     'photo</a>' % photo_id))
  po.appendChild(name)
  po.appendChild(description)

  icon = kml_doc.createElement('icon')
  href = kml_doc.createElement('href')
  href.appendChild(kml_doc.createTextNode(file_name))

  camera = kml_doc.createElement('Camera')
  longitude = kml_doc.createElement('longitude')
  latitude = kml_doc.createElement('latitude')
  altitude = kml_doc.createElement('altitude')
  tilt = kml_doc.createElement('tilt')

  # Determines the proportions of the image and uses them to set FOV.
  width = float(data['EXIF ExifImageWidth'].printable)
  length = float(data['EXIF ExifImageLength'].printable)
  lf = str(width/length * -20.0)
  rf = str(width/length * 20.0)

  longitude.appendChild(kml_doc.createTextNode(str(coords[1])))
  latitude.appendChild(kml_doc.createTextNode(str(coords[0])))
  altitude.appendChild(kml_doc.createTextNode('10'))
  tilt.appendChild(kml_doc.createTextNode('90'))
  camera.appendChild(longitude)
  camera.appendChild(latitude)
  camera.appendChild(altitude)
  camera.appendChild(tilt)

  icon.appendChild(href)

  viewvolume = kml_doc.createElement('ViewVolume')
  leftfov = kml_doc.createElement('leftFov')
  rightfov = kml_doc.createElement('rightFov')
  bottomfov = kml_doc.createElement('bottomFov')
  topfov = kml_doc.createElement('topFov')
  near = kml_doc.createElement('near')
  leftfov.appendChild(kml_doc.createTextNode(lf))
  rightfov.appendChild(kml_doc.createTextNode(rf))
  bottomfov.appendChild(kml_doc.createTextNode('-20'))
  topfov.appendChild(kml_doc.createTextNode('20'))
  near.appendChild(kml_doc.createTextNode('10'))
  viewvolume.appendChild(leftfov)
  viewvolume.appendChild(rightfov)
  viewvolume.appendChild(bottomfov)
  viewvolume.appendChild(topfov)
  viewvolume.appendChild(near)

  po.appendChild(camera)
  po.appendChild(icon)
  po.appendChild(viewvolume)
  point = kml_doc.createElement('point')
  coordinates = kml_doc.createElement('coordinates')
  coordinates.appendChild(kml_doc.createTextNode('%s,%s,%s' %(coords[1],
                                                              coords[0],
                                                              coords[2])))
  point.appendChild(coordinates)

  po.appendChild(point)

  document = kml_doc.getElementsByTagName('Document')[0]
  document.appendChild(po)

می‌بینید که ما فقط از روش‌های استاندارد W3C DOM استفاده می‌کنیم، زیرا آن‌هایی هستند که در اکثر زبان‌های برنامه‌نویسی موجود هستند. برای اینکه ببینید چگونه همه چیز با هم هماهنگ می شود، کد را از اینجا دانلود کنید.

این نمونه از قدرت کامل PhotoOverlays که به شما امکان می‌دهد کاوش‌های عمیق عکس‌های با وضوح بالا ایجاد کنید. اما، نشان می دهد که چگونه می توان یک عکس را به سبک بیلبورد در Google Earth آویزان کرد. در اینجا نمونه ای از یک فایل KML ایجاد شده با استفاده از این کد آمده است:

<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <PhotoOverlay id="photo0">
      <name>
        1228258523134.jpg
      </name>
      <description>
<![CDATA[<a href="#photo0">Click here to fly into photo</a>]]>      </description>
      <Camera>
      	<longitude>
          -122.3902159196034
        </longitude>
        <latitude>
           37.78961266330473
        </latitude>
        <altitude>
          10
        </altitude>
        <tilt>
          90
        </tilt>
      </Camera>
      <Icon>
        <href>
          1228258523134.jpg
        </href>
      </Icon>
      <ViewVolume>
        <leftFov>
          -26.6666666667
        </leftFov>
        <rightFov>
          26.6666666667
        </rightFov>
        <bottomFov>
          -20
        </bottomFov>
        <topFov>
          20
        </topFov>
        <near>
          10
        </near>
      </ViewVolume>
      <Point>
        <coordinates>
          -122.3902159196034,37.78961266330473,0
        </coordinates>
      </Point>
    </PhotoOverlay>
  </Document>
</kml>

و این چیزی است که در Google Earth به نظر می رسد:


هشدارها

برچسب گذاری جغرافیایی عکس ها هنوز در مراحل اولیه است.

در اینجا تعدادی از موارد وجود دارد که باید از آنها آگاه بود:

  • دستگاه‌های GPS همیشه 100% دقیق نیستند، به‌ویژه دستگاه‌هایی که با دوربین عرضه می‌شوند، بنابراین باید موقعیت عکس‌های خود را بررسی کنید.
  • بسیاری از دستگاه‌ها ارتفاع را ردیابی نمی‌کنند، در عوض آن را روی 0 تنظیم می‌کنند. اگر ارتفاع برای شما مهم است، باید راه دیگری برای گرفتن آن داده‌ها پیدا کنید.
  • موقعیت GPS موقعیت دوربین است نه موضوع عکس. به همین دلیل است که این نمونه عنصر دوربین را در موقعیت GPS قرار می دهد و عکس واقعی را از آن موقعیت دور می کند.
  • Exif اطلاعاتی در مورد جهتی که دوربین شما به سمت آن نشان می دهد نمی گیرد، بنابراین ممکن است به این دلیل نیاز به تنظیم PhotoOverlays خود داشته باشید. خبر خوب این است که برخی از دستگاه‌ها، مانند تلفن‌های ساخته شده بر روی سیستم عامل اندروید، به شما امکان می‌دهند تا داده‌هایی مانند جهت قطب‌نما و شیب را مستقیماً ضبط کنید، نه در هدرهای Exif.

تمام آنچه گفته شد، این هنوز یک راه قدرتمند برای تجسم عکس های شما است. امیدواریم در آینده نزدیک شاهد برچسب گذاری جغرافیایی بیشتر و دقیق تری از عکس ها باشیم.

از اینجا کجا برویم

اکنون که شروع به استفاده از هدرهای EXIF ​​کرده‌اید، می‌توانید مشخصات EXIF ​​را بررسی کنید. داده‌های زیادی در آنجا ذخیره می‌شوند، و ممکن است علاقه داشته باشید که آن‌ها را بگیرید و در یک بالن توضیحات قرار دهید. همچنین ممکن است با استفاده از ImagePyramids ، PhotoOverlays های غنی تر ایجاد کنید. مقاله راهنمای توسعه دهندگان در مورد PhotoOverlays نمای کلی خوبی از استفاده از آنها دارد.