5

I would like to import EXIF & metadatas of JPG images into QGIS. For each image, a vector were created by 'ImportPhotos' pluggin, it adds some EXIF into attributes fields but some importants EXIF informations are missing.

In fact, I need 3 more fields. The XMP tags I need are: GimbalYawDegree, FlightYawDegree, RelativeAltitude value are real. That's why I've created 3 new columns (attributes fields) named: GimbalYawDegree, FlightYawDegree, RelativeAltitude.

I would like to create a python script to run a batch for every feature (img imported by ImportPhotos) of a layer. The task would :

  • import the XMP datas of images stored at the path located into the attribute field named "Path"

    import PIL.ExifTags
    exif = {
        PIL.ExifTags.TAGS[k]: v
        for k, v in img._getexif().items()
        if k in PIL.ExifTags.TAGS
    }
    

This code should read the EXIF but this is my first experience in Python and I'm not able to finish the job.

there few samples of images including GimbalYawDegree, FlightYawDegree, RelativeAltitude tags https://we.tl/t-BjPuzBGMRc

10
  • 1
    This is using only python, but I think this will help you to obtain the information. stackoverflow.com/questions/4764932/… Commented Dec 15, 2019 at 12:25
  • @Jhon Galindo thanks for this answer it definitly helps as it resolves the "Reading EXIF" part of the code. Now I have to build the second part: importing these datas into as attributes for every features (image imported by ImportPhotos) in a layer. But with my null knowledge in Python, it is going to be funny xD
    – zlikotp
    Commented Dec 15, 2019 at 12:50
  • 2
    For Time field, if you change 'Widget Type' as 'Text Edit' in 'Layer Properties > Attributes Form'. Then you'll see Time value in 'form view'. Commented Dec 15, 2019 at 12:58
  • 2
    Your question is too broad. Because exif data has different types (string, date, time, integer, decimal, etc.). And all should be handled one by one. ImportPhotos plugin works in that way for fields which it adds to attribute table. Maybe a solution is proposed for only one or two exif data which is not added by ImportPhotos plugin. And then you customize the script for the others. Commented Dec 15, 2019 at 18:02
  • 1
    I've been trying for hours. I can't provide a solution without sample image includes GimbalYawDegree, FlightYawDegree, RelativeAltitude tags. Commented Dec 15, 2019 at 19:45

1 Answer 1

4

PIL Solution

Add three new fields (real type) named "fyd", "gyd", "ra" (fyd: FlightYawDegree, gyd: GimbalYawDegree, ra: RelativeAltitude)

Copy/paste the script to QGIS Python Editor (not to Console), select layer (important) and run.

from xml.etree import ElementTree as ET

layer = iface.activeLayer()
dpr = layer.dataProvider()

fyd_ind = layer.fields().indexFromName("fyd")
gyd_ind = layer.fields().indexFromName("gyd")
ra_ind  = layer.fields().indexFromName("ra")

for feature in layer.getFeatures():
    file_path = feature["Path"] 
    print(file_path)

    with open(file_path, "rb") as f:
        s = str(f.read())

    start = s.find('<x:xmpmeta')
    end   = s.find('</x:xmpmeta')
    xmp   = s[start:end+12].replace("\\n","\n")
    tree  = ET.XML(xmp)

    fyd = tree[0][0].attrib['{http://www.dji.com/drone-dji/1.0/}FlightYawDegree']
    gyd = tree[0][0].attrib['{http://www.dji.com/drone-dji/1.0/}GimbalYawDegree']
    ra  = tree[0][0].attrib['{http://www.dji.com/drone-dji/1.0/}RelativeAltitude']

    dpr.changeAttributeValues({  feature.id():{fyd_ind: fyd,
                                               gyd_ind: gyd,
                                               ra_ind:   ra}  })

#  Other DJI Tags if you need
#  {http://www.dji.com/drone-dji/1.0/}GpsLatitude'
#  {http://www.dji.com/drone-dji/1.0/}GpsLongitude'
#  {http://www.dji.com/drone-dji/1.0/}AbsoluteAltitude'
#  {http://www.dji.com/drone-dji/1.0/}GimbalRollDegree'
#  {http://www.dji.com/drone-dji/1.0/}GimbalPitchDegree'
#  {http://www.dji.com/drone-dji/1.0/}FlightRollDegree'
#  {http://www.dji.com/drone-dji/1.0/}FlightPitchDegree'
#  {http://www.dji.com/drone-dji/1.0/}CamReverse'
#  {http://www.dji.com/drone-dji/1.0/}GimbalReverse'

pyexiv2 Solution

When you run next script, you'll see all metadata includes EXIF, IPTC, XMP .

from pyexiv2 import Image

image = Image("file_path")
metadata = image.read_all() # returns all metadata (EXIF, IPTC, XMP)
print(metadata.keys()) # prints dict_keys(['EXIF', 'IPTC', 'XMP'])
print(metadata["XMP"]) # prints all XMP metadata keys and values

Then, decide tags you want to add, add new fields for them, customize next script, select layer and run the script.

from pyexiv2 import Image

layer = iface.activeLayer()
dpr = layer.dataProvider()

fyd_ind = layer.fields().indexFromName("fyd")
gyd_ind = layer.fields().indexFromName("gyd")
ra_ind  = layer.fields().indexFromName("ra")

for feature in layer.getFeatures():
    file_path = feature["Path"] 
    print(file_path)

    image = Image(file_path)
    metadata = image.read_all()
    xmp = metadata["XMP"]

    fyd = float(xmp["Xmp.drone-dji.FlightYawDegree"])
    gyd = float(xmp["Xmp.drone-dji.GimbalYawDegree"])
    ra  = float(xmp["Xmp.drone-dji.RelativeAltitude"])

    dpr.changeAttributeValues({ feature.id():{fyd_ind: fyd,
                                              gyd_ind: gyd,
                                              ra_ind:   ra} })
8
  • You should use after using Import Photos plugin. Commented Dec 15, 2019 at 22:58
  • Perfect I'm going to try but before I'm trying to read the script and my question is about image_folder. Since the path of every file is already stored (because I've used ImportPhotos), could we avoid to have to change the path manually by using the field Path?
    – zlikotp
    Commented Dec 15, 2019 at 23:19
  • You're right. I didn't notice that. My mistake. Commented Dec 15, 2019 at 23:24
  • another point is the url : dji.com/drone-dji/1.0 does this function requiert any internet connexion ?
    – zlikotp
    Commented Dec 15, 2019 at 23:25
  • 1
    No. Just Element Treemodule parses xmp data in that way. Commented Dec 15, 2019 at 23:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.