Extracting EXIF specific data from arcpy.GetImageEXIFProperties

1848
8
Jump to solution
08-23-2022 12:25 PM
RudyJamet
Emerging Contributor

Part of my job is to analyze images in Arcgis Pro taken by a drone. I used the EXIF properties function from ArcPy to retrieve data from a DJI drone. Someone already helped me to get the properties for several images at once, and not one at a time (see https://stackoverflow.com/questions/73224313/how-to-use-arcpy-getimageexifproperties-for-more-than-o...).

I have been trying, unsuccessfully, to extract the target longitude/latitude data and the gimbal yaw measurement for all images from the result of the previous function:

                - the result comes as a dictionary, but the actual data are in a dictionary within a list within the general dictionary. Here is an example with the first image:

{'1_DJI_20220622210442_0015_T.JPG': [-95.78288447222222, 40.61787008333334, 284.564, {'width': 640, 'height': 512 … }]}

                - the keys, associated with the actual data, have a colon in the name, and the values (coordinates and degrees) are float numbers: 'XMP:drone-dji:LRFTargetLon': '-95.7840195', 'XMP:drone-dji:LRFTargetLat': '40.6177254', 'XMP:drone-dji:GimbalYawDegree': '-99.40'

So, what would be the specific Python module and/or function/command to do the extraction?

 Also, one goal is to create a shapefile containing fields (the keys as field names), attributes (the values as data), and records (all images are individualized). I found a series of video tutorials precisely on that topic in Youtube, but feel free to express your ideas, I could use your help.

The other goal is to create a tool in Arcgis Pro that will make the extraction and shp file creation process automatic. I have sometimes hundreds of images to process at once. If you think it is possible to create such a tool, please let me know as well.

0 Kudos
1 Solution

Accepted Solutions
AlfredBaldenweck
MVP Regular Contributor

Hmm, I'm not sure why you're having weird results.

biglist=[]
smaLList1= [1,2,3,4,5,6]
smaLList2 = [8,9,10,11]
biglist.append(smaLList1)
print(biglist)
#[[1, 2, 3, 4, 5, 6]]
biglist.append(smaLList2)
print(biglist)
#[[1, 2, 3, 4, 5, 6], [8, 9, 10, 11]]

You definitely want .append(); extending will turn it all into one list.

biglist=[]
smaLList1= [1,2,3,4,5,6]
smaLList2 = [8,9,10,11]
biglist.extend(smaLList1)
print(biglist)
#[1, 2, 3, 4, 5, 6]
biglist.extend(smaLList2)
print(biglist)
#[1, 2, 3, 4, 5, 6, 8, 9, 10, 11]

 

Anyway, I'd try something like this: 

photoPropList = [] # Make sure to have the big list outside the loop
                   # So it doesn't get overwritten each time.
for name in exif_properties:
    target_data = exif_properties[name][3]
    picID = name
    targlong = target_data['XMP:drone-dji:LRFTargetLon']
    tarlat = target_data['XMP:drone-dji:LRFTargetLat']
    tardist = target_data['XMP:drone-dji:LRFTargetDistance']
    gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']

    LRF_data = [picID, targlong, tarlat, tardist, gimbalyaw]
    print(LRF_data)
    photoPropList.append(LRF_data)

 

View solution in original post

0 Kudos
8 Replies
AlfredBaldenweck
MVP Regular Contributor

I get confused by nested dictionaries/lists all the time lol.

In this case, your first dictionary key is the photo name, with a value of a list of information.

Long is the first item in the list (index 0), Latitude is second, angle third, and then that dictionary is the fourth (index 3)

If you know the exact information you want, you can search in this dictionary with the exact key, e.g. tagdict['width'] will return 640.

 

testdict= {'1_DJI_20220622210442_0015_T.JPG': [-95.78288447222222, 40.61787008333334, 284.564, {'width': 640, 'height': 512}]}
for i in testdict:
    print(i) #1_DJI_20220622210442_0015_T.JPG
    print(testdict[i])#[-95.78288447222222, 40.61787008333334, 284.564, {'width': 640, 'height': 512}]
    print(testdict[i][3])#{'width': 640, 'height': 512}
    print(testdict[i][3]['width'])# 640
    '''OR'''
    lontude = testdict[i][0]#-95.78288447222222
    lat = testdict[i][1]#40.61787008333334
    angle = testdict[i][2]#284.564
    picwidth = testdict[i][3]['width'] #640
    picheight = testdict[i][3]['height'] #512

'''feel free to make each successlist or dictionary a separate variable 
so you don't have to keep typing them out '''
    tagdict= testdict[i][3] #{'width': 640, 'height': 512}
    lat = tagdict['width'] # 640
    '''etc'''

 

 

In regards to the second part, you should be able to use Geotagged Photos to Points . It won't have all the exif info, but it will at least have Lat/Long/Angle by default , and then you can actually calculate the field with the correct data (actually, an update cursor would be better, I think?).

Create a dictionary for photo and information you want, then iterate through each record, looking in the dictionary for the name to update the empty gimbal row.

Writing this without testing it but it's something along these lines:

 

Gimbaldict = {Photoname: gimbal data}
#(Add the field if you haven't already)
with arcpy.da.UpdateCursor(photoFC, ['Name', 'Gimbal']) as cursor:
for row in cursor:
    #row[0] is the Name Field
    # row[1] is the Gimbal field that you're populating
    row[1]= Gimbaldict[row[0]] # Search Gimbal dict for the name
    cursor.updateRow(row)

 

 

Hope this helps!

0 Kudos
RudyJamet
Emerging Contributor

Thank you Alfred for your input. Your solution to the first part works very well. I just need to work on the second part now.

0 Kudos
AlfredBaldenweck
MVP Regular Contributor

Glad that helps!

Not sure entirely what you want, but to expand on my earlier post:

That tool will give you this sort of output:

AlfredBaldenweck_1-1661471038914.png

Add a field for the Yaw, then update it for each record based on your dictionary.

testdict= {'1_DJI_20220622210442_0015_T.JPG': [-95.78288447222222,
                                               40.61787008333334,
                                               284.564,
                                               {'width': 640, 'height': 512}]}
'''For reference'''
# for i in testdict:
    # lontude = testdict[i][0]#-95.78288447222222
    # lat = testdict[i][1]#40.61787008333334
    # angle = testdict[i][2]#284.564
    # picwidth = testdict[i][3]['width'] #640
    # picheight = testdict[i][3]['height'] #512
    # picYaw= testdict[i][3]['Yaw']

'''This is the real work'''
photoFolder= r"C:/users/Documents" # Where the photos live.
pictureFC= r"example.gdb\picturesFC" # Feature class you're making.

'''Create feature class for all photos in a folder.'''
#This will search all subfolders, so beware
arcpy.management.GeoTaggedPhotosToPoints(Input_Folder, pictureFC)

#Add field.
arcpy.management.AddField(pictureFC, 'Yaw', 'Short')

#Populate the fields for each record in the Feature Class.
with arcpy.da.UpdateCursor(photoFC, ['Name', 'Yaw']) as cursor:
for row in cursor:
    # row[0] is the Name Field
    # row[1] is the Yaw field that you're populating
    row[1]= testdict[row[0]][3]['Yaw'] # Search your initial EXIF dict for the name
    cursor.updateRow(row)

Hope this points you in the right direction.

 

0 Kudos
RudyJamet
Emerging Contributor

I will probably use part of the script, if not all, that you came up with.

Right now, I am trying to generate a list that contains nested lists, that I can use to create the shapefile. I cannot find the right way to do it though.

So, here is the script I used (based off your input):

for name in exif_properties:
target_data = exif_properties[name][3]
picID = name
targlong = target_data['XMP:drone-dji:LRFTargetLon']
tarlat = target_data['XMP:drone-dji:LRFTargetLat']
tardist = target_data['XMP:drone-dji:LRFTargetDistance']
gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']

LRF_data = [picID, targlong, tarlat, tardist, gimbalyaw]
print(LRF_data)

and the result (part of it) is:

['DJI_20220813135928_0001_T.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70']
['DJI_20220813135929_0001_W.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70']
['DJI_20220813135929_0001_Z.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70']
['DJI_20220813140019_0002_T.JPG', '169.2991333', '-44.9379387', '121.663', '+223.30']
['DJI_20220813140020_0002_W.JPG', '169.2991180', '-44.9379463', '120.955', '+223.30']
['DJI_20220813140020_0002_Z.JPG', '169.2991180', '-44.9379501', '121.311', '+223.30']

All the data I need for the shapefile are returned into a list, but for each picture there is a seperate list. What I want is one big list having all the lists in it. Like: [['DJI_20220813135928_0001_T.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813135929_0001_W.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ... ]

I tried the methods 'append' and 'extend' and none of them returned that specific list. They actually returned weird results.

Would you have any idea? Thanks again Alfred.

0 Kudos
AlfredBaldenweck
MVP Regular Contributor

Hmm, I'm not sure why you're having weird results.

biglist=[]
smaLList1= [1,2,3,4,5,6]
smaLList2 = [8,9,10,11]
biglist.append(smaLList1)
print(biglist)
#[[1, 2, 3, 4, 5, 6]]
biglist.append(smaLList2)
print(biglist)
#[[1, 2, 3, 4, 5, 6], [8, 9, 10, 11]]

You definitely want .append(); extending will turn it all into one list.

biglist=[]
smaLList1= [1,2,3,4,5,6]
smaLList2 = [8,9,10,11]
biglist.extend(smaLList1)
print(biglist)
#[1, 2, 3, 4, 5, 6]
biglist.extend(smaLList2)
print(biglist)
#[1, 2, 3, 4, 5, 6, 8, 9, 10, 11]

 

Anyway, I'd try something like this: 

photoPropList = [] # Make sure to have the big list outside the loop
                   # So it doesn't get overwritten each time.
for name in exif_properties:
    target_data = exif_properties[name][3]
    picID = name
    targlong = target_data['XMP:drone-dji:LRFTargetLon']
    tarlat = target_data['XMP:drone-dji:LRFTargetLat']
    tardist = target_data['XMP:drone-dji:LRFTargetDistance']
    gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']

    LRF_data = [picID, targlong, tarlat, tardist, gimbalyaw]
    print(LRF_data)
    photoPropList.append(LRF_data)

 

0 Kudos
RudyJamet
Emerging Contributor

Well, by 'weird' I meant unexpected.

Here is the entire script that I tried with 'append' (similar to the last script you wrote):

from pathlib import Path

IMAGE_FOLDER = Path(r"\\xxx\InspectionServicesData\DRONE_INSPECTION_SERVICE\01_Projects\xxx\2_FIELD-WORK\DATA-DUMP\xxx\DJI_202208131356_022")
exif_properties = {}

for image in IMAGE_FOLDER.glob("*.jpg"):
exif_properties[image.name] = arcpy.GetImageEXIFProperties(image)

shp_data = []

for name in exif_properties:
target_data = exif_properties[name][3]
picID = name
targlong = target_data['XMP:drone-dji:LRFTargetLon']
tarlat = target_data['XMP:drone-dji:LRFTargetLat']
tardist = target_data['XMP:drone-dji:LRFTargetDistance']
gimbalyaw = target_data['XMP:drone-dji:GimbalYawDegree']

LRF_data = [picID, targlong, tarlat, tardist, gimbalyaw]
shp_data.append(LRF_data)
print(shp_data)

 And here is part of the result:

[['DJI_20220813135928_0001_T.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70']]
[['DJI_20220813135928_0001_T.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813135929_0001_W.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70']]
[['DJI_20220813135928_0001_T.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813135929_0001_W.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813135929_0001_Z.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70']]
[['DJI_20220813135928_0001_T.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813135929_0001_W.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813135929_0001_Z.JPG', '169.2997437', '-44.9373970', '245.464', '+224.70'], ['DJI_20220813140019_0002_T.JPG', '169.2991333', '-44.9379387', '121.663', '+223.30']]

Basically, what is does is something like this:

[[pic1, a, b, c, d]]

[[pic1, a, b, c, d], [pic2, a, b, c, d]]

[[pic1, a, b, c, d], [pic2, a, b, c, d], [pic3, a, b, c, d]] 

...

[[pic1, a, b, c, d], [pic2, a, b, c, d], [pic3, a, b, c, d], ... [pic183, a, b, c, d]]

It adds one picture's list at a time within the big list, and it generates several big lists until it reaches the last picture's list. In fact, I get THE big list (with the list of pic183) that I want at the bottom of the result.

I just don't know how to get this last big list at once, or to extract it from the entire result.

0 Kudos
AlfredBaldenweck
MVP Regular Contributor

Sorry in case I’m missing something, but I think what you’re describing is normal and it just looks strange because you’re printing shp_data over and over.

Move the print statement outside the for loop and you should only get the last result.

0 Kudos
RudyJamet
Emerging Contributor

Oh wow! That shows you how little I know about Python...

Anyway, I just did what you advised and it worked like a charm. So, again, thank you very much Alfred.

0 Kudos