Can't write geotag data

Bug #389960 reported by Michael Scheper
14
This bug affects 2 people
Affects Status Importance Assigned to Milestone
pyexiv2
Fix Released
High
Olivier Tilloy
Declined for 0.1.x by Olivier Tilloy

Bug Description

Immediately after setting a list of Rationals to Exif.GPSInfo.GPSLatitude and Exif.GPSInfo.GPSLongitude, those elements return invalid data (0/1).

Code to demonstrate this bug:

---
import pyexiv2
import sys
import sets

print '''First, we'll read in the image of some graffiti in Chile.'''
image = pyexiv2.Image('graffiti_06421.jpg')
image.readMetadata()
defined_keys = sets.Set(image.exifKeys())
gps_keys = sets.Set(['Exif.GPSInfo.GPSVersionID', 'Exif.GPSInfo.GPSLatitudeRef',
       'Exif.GPSInfo.GPSLatitude', 'Exif.GPSInfo.GPSLongitudeRef',
       'Exif.GPSInfo.GPSLongitude', 'Exif.GPSInfo.GPSLongitude',
       'Exif.GPSInfo.GPSMapDatum'])
if (defined_keys.intersection(gps_keys)):
   print '''Hmmm... looks like there's already GPS data in the image.''' +\
           ''' That kinda invalidates this demo. Oh well.'''
   sys.exit()
latitude_list = [
   pyexiv2.Rational(33,1),
   pyexiv2.Rational(2,1),
   pyexiv2.Rational(512196,10000)]
longitude_list = [
   pyexiv2.Rational(71,1),
   pyexiv2.Rational(37,1),
   pyexiv2.Rational(361920,10000)]
print u'''This photo was taken at %s\xb0 %s' %s" S, %s\xb0 %s' %s" W.''' % (
       latitude_list[0], latitude_list[1], latitude_list[2],
       longitude_list[0], longitude_list[1], longitude_list[2])
print '''So now we assign that and other info to the image...'''
image['Exif.GPSInfo.GPSVersionID'] = '2 0 0 0 '
image['Exif.GPSInfo.GPSLatitudeRef'] = 'S'
image['Exif.GPSInfo.GPSLatitude'] = latitude_list
image['Exif.GPSInfo.GPSLongitudeRef'] = 'W'
image['Exif.GPSInfo.GPSLongitude'] = longitude_list
image['Exif.GPSInfo.GPSMapDatum'] = 'WGS-84'
print '''... but the values for latitude and longitude weren't assigned''' +\
       ''' properly! They're %s and %s! Huh?!''' % (
       image['Exif.GPSInfo.GPSLatitude'],
       image['Exif.GPSInfo.GPSLongitude'])
#image.writeMetadata()
---

Output from running this code:

---
eddie:~/tmp$ python possiblebugdemo.py
First, we'll read in the image of some graffiti in Chile.

This photo was taken at 33/1° 2/1' 512196/10000" S, 71/1° 37/1' 361920/10000" W.

So now we assign that and other info to the image...

... but the values for latitude and longitude weren't assigned properly! They're 0/1 and 0/1! Huh?!
---

Sample photo attached.

Related branches

Revision history for this message
Michael Scheper (s-launchpad-michaelscheper-com) wrote :
Olivier Tilloy (osomon)
Changed in pyexiv2:
importance: Undecided → High
status: New → Confirmed
Revision history for this message
robinmills (robinmills) wrote :

I haven't had any trouble with this. I've geotagged at least 1000 photos with this script:

http://clanmills/articles/gpsexiftags/gps_py.shtml

I'm using #import surd to deal with Rational numbers and I have some one-line functions to encode the lat/long as a tuple of rational numbers. I can see you are doing something similar - however there must be a significant difference.

I've been using this on pyexiv2.0.1.2 on exiv2 0.16 and more recently with 0.1.3 and exiv2 0.18 (on Mac, Windows and Linux) and haven't noticed any problems with it.

Olivier Tilloy (osomon)
Changed in pyexiv2:
assignee: nobody → Olivier Tilloy (osomon)
status: Confirmed → In Progress
Revision history for this message
Olivier Tilloy (osomon) wrote :

Fixed in the 0.2 branch.
The following example script should work on the attached image:

import pyexiv2

metadata = pyexiv2.ImageMetadata('graffiti_06421.jpg')
metadata.read()

latitude = [
    pyexiv2.Rational(33, 1),
    pyexiv2.Rational(2, 1),
    pyexiv2.Rational(512196, 10000)]

longitude = [
    pyexiv2.Rational(71, 1),
    pyexiv2.Rational(37, 1),
    pyexiv2.Rational(361920, 10000)]

metadata['Exif.GPSInfo.GPSVersionID'] = \
    pyexiv2.ExifTag('Exif.GPSInfo.GPSVersionID', '2 0 0 0 ')
metadata['Exif.GPSInfo.GPSLatitudeRef'] = \
    pyexiv2.ExifTag('Exif.GPSInfo.GPSLatitudeRef', 'S')
metadata['Exif.GPSInfo.GPSLatitude'] = \
    pyexiv2.ExifTag('Exif.GPSInfo.GPSLatitude', latitude)
metadata['Exif.GPSInfo.GPSLongitudeRef'] = \
    pyexiv2.ExifTag('Exif.GPSInfo.GPSLongitudeRef', 'W')
metadata['Exif.GPSInfo.GPSLongitude'] = \
    pyexiv2.ExifTag('Exif.GPSInfo.GPSLongitude', longitude)
metadata['Exif.GPSInfo.GPSMapDatum'] = \
    pyexiv2.ExifTag('Exif.GPSInfo.GPSMapDatum', 'WGS-84')

metadata.write()

Changed in pyexiv2:
milestone: none → 0.2
status: In Progress → Fix Committed
Revision history for this message
Jacques-D. Piguet (jacques-piguet) wrote :

Got the same problem with:

img=pyexiv2.Image('IMG_0580.JPG')
img.readMetadata()
print img['Exif.GPSInfo.GPSLongitude']
(<pyexiv2.Rational instance at 0x1dcefc8>, <pyexiv2.Rational instance at 0x1d362d8>, <pyexiv2.Rational instance at 0x1d365f0>)
for n in img['Exif.GPSInfo.GPSLongitude']:print n

3/1
44/1
4811/100
img['Exif.GPSInfo.GPSLongitude']=[pyexiv2.Rational(47,1), pyexiv2.Rational(27,1), pyexiv2.Rational(5615, 100)]
print img['Exif.GPSInfo.GPSLongitude']
0/1

Do you see a chance to get a fix in the 1.3 (Ubuntu Karmic) branche?

BR, Jacques-D.

Image file is attached.

Revision history for this message
robinmills (robinmills) wrote :

Jacques

I didn't look at this because I thought Olivier submitted a fix last week.

I'm not sure I've understood what is being discussed. Is it pyexiv2.Rational that is wrong, all Latitude values or what?

Can you:
1) Shorten the code to the minimum
2) Explain what happens, what you expect and why it is wrong.
3) Is it only 2,1 that's wrong or all Rational Numbers or what?
4) Can you use the surd.py code to handle the rational numbers instead of pyexiv2.Rational

Olivier. Did you submit a fix to the 0.2 branch (and/or 0.1.3?)

Robin

Revision history for this message
Jacques-D. Piguet (jacques-piguet) wrote : Re: [Bug 389960] Re: Can't write geotag data

Le lundi 18 janvier 2010 à 22:23 +0000, clanmills a écrit :
> Jacques
>
> I didn't look at this because I thought Olivier submitted a fix last
> week.
>
> I'm not sure I've understood what is being discussed. Is it
> pyexiv2.Rational that is wrong, all Latitude values or what?
>
> Can you:
> 1) Shorten the code to the minimum
> 2) Explain what happens, what you expect and why it is wrong.
> 3) Is it only 2,1 that's wrong or all Rational Numbers or what?
> 4) Can you use the surd.py code to handle the rational numbers instead of pyexiv2.Rational
>
> Olivier. Did you submit a fix to the 0.2 branch (and/or 0.1.3?)
>
> Robin
>

Hi Robin,

The problem occurs by setting the value to an existing tag:

1. Create an image object and read the metadata:
import pyexiv2
img=pyexiv2.Image('IMG_0580.JPG')
img.readMetadata()

2. Check the longitude tag (as expected, the result is a tuple of 3
rationals):
print img['Exif.GPSInfo.GPSLongitude']
(<pyexiv2.Rational instance at 0xa560dac>, <pyexiv2.Rational instance at
0xa560c6c>, <pyexiv2.Rational instance at 0xa560e6c>)

3. Modify the tag:
img['Exif.GPSInfo.GPSLongitude']=(pyexiv2.Rational(3,1),pyexiv2.Rational(44,1),pyexiv2.Rational(5615,100))

4. Check the modification:
print img['Exif.GPSInfo.GPSLongitude']
0/1

5. Here is the problem: instead of 3 rationals there is only 1, and with
a dummy value. I think that the rationals from pyexiv2.Rational are
correct, but the set operation fails somewhere.

I am using only standart packages from the Ubuntu 9.10 repositories, but
I will try to use surd.py

BR, Jacques-D.

Revision history for this message
Olivier Tilloy (osomon) wrote :

If I understand correctly, Jacques confirms the original bug, which I can confirm too with pyexiv2 0.1.3.
The fix I committed last week is for the 0.2 branch and I don't plan to backport it to the 0.1 branch, all my development efforts are focused towards getting a stable 0.2 soon. Likewise, there won't be any other release of the 0.1 branch if I can avoid it.

Revision history for this message
robinmills (robinmills) wrote :

Hello Jacques

What a helpful response. Thank you very much. We're on the same page now.

I modified your script (and called it foo2.py and it operates on file foo.jpg). I copied a 'NOT geotagged file' RA.jpg to foo.jpg, added the GPSLongitude 22deg/33'/44" using surd and pyexiv2.Rational(). Exiv2 reports the result correctly.

650 /Users/rmills> cp RA.jpg foo.jpg ; python foo2.py ; exiv2 -pt foo.jpg | grep GPSLong
22d 33' 44"
Exif.GPSInfo.GPSLongitude Rational 3 22deg 33' 44"
651 /Users/rmills>

I think the issue is when you write and immediately attempt to report the metadata, the read is wrong (however the data's good!). The work around for the moment, is to close and re-open the file before attempting to read the metadata.

####
# NOT GOOD
img=pyexiv2.Image(bla..bla)
img.readMetadata()
img['Exif.GPSInfo.GPSLongitude' ] = [ .... ] ;
print img['Exif.GPSInfo.GPSLongitude'] # bad

####
# GOOD
img=pyexiv2.Image(bla..bla)
img.readMetadata()
img['Exif.GPSInfo.GPSLongitude' ] = [ .... ] ;
img.writeMetadata()

img=pyexiv2.Image(bla..bla)
img.readMetadata() ;
print img['Exif.GPSInfo.GPSLongitude']

I'm off to bed now (it's 00:56 here in California). Let's wait to see what Olivier says when he gets to work.

foo2.py

#!/usr/bin/env python
import pyexiv2
import surd

# img=pyexiv2.Image('foo.jpg')
# img.readMetadata()

# Check the longitude tag (as expected, the result is a tuple of 3 rationals):
# print img['Exif.GPSInfo.GPSLongitude']
# (<pyexiv2.Rational instance at 0xa560dac>, <pyexiv2.Rational instance at 0xa560c6c>, <pyexiv2.Rational instance at 0xa560e6c>)

# 3. Modify the tag:
# img['Exif.GPSInfo.GPSLongitude']=(pyexiv2.Rational(3,1),pyexiv2.Rational(44,1),pyexiv2.Rational(5615,100))

# 4. Check the modification:
# print img['Exif.GPSInfo.GPSLongitude']
# 0/1

##
# Ration number support
def R(f):
 """R(float) - get a Rational number for a float"""
 s = surd.surd(float(f))
 return pyexiv2.Rational(s.num,s.denom)

def d(angle):
 """d(any) - get degrees from a number :eg d(33.41) -> 33"""
 return int(angle)

def m(angle):
 """m(any) - get minutes from a number :eg d(33.41) -> 24"""
 return int( angle*60 - d(angle)* 60)

def s(angle):
 """s(any) - get seconds from a number :eg s(33.41) -> 36"""
 return int( angle*3600 - d(angle)*3600 - m(angle)*60 )

##
#
def f(r):
    return float(r.numerator)/float(r.denominator)

##
#
def ff(x):
    return f(x[0])+ (f(x[1])/60.0) + (f(x[2])/3660.0)

##
#
def fs(x):
    return str( int(f(x[0])) ) + "d " +\
            str( int(f(x[1])) ) + "' " +\
            str( int(f(x[2])) ) + '" '

# Create an image object and read the metadata:
img=pyexiv2.Image('foo.jpg')
img.readMetadata()

lon = 22.0 + 33.0/60.0 + 44.0/3600.0;
img['Exif.GPSInfo.GPSLongitude' ] = [R(d(lon)),R(m(lon)),R(s(lon))]
img.writeMetadata()
##
# here's the bandit!
# print fs(img['Exif.GPSInfo.GPSLongitude'])

img=pyexiv2.Image('foo.jpg')
img.readMetadata()
print fs(img['Exif.GPSInfo.GPSLongitude'])

Revision history for this message
Jacques-D. Piguet (jacques-piguet) wrote :
Download full text (6.3 KiB)

Hi Robin,

You were right: I just try it and it works: as soon as the assignement
is done, I wrote the data back and checked the image file by using
another application: the file was showing the correct (changed) tags.

Then the problem is not in the assignement itself, but in the metadata
buffer, which is not correctly updated after setting the value.

I will implement that as a workaround, waiting for a definitive fix in
the 0.2.X branche...

BR and thanks, Jacques-D.

Le mardi 19 janvier 2010 à 09:00 +0000, clanmills a écrit :
> Hello Jacques
>
> What a helpful response. Thank you very much. We're on the same page
> now.
>
> I modified your script (and called it foo2.py and it operates on file
> foo.jpg). I copied a 'NOT geotagged file' RA.jpg to foo.jpg, added the
> GPSLongitude 22deg/33'/44" using surd and pyexiv2.Rational(). Exiv2
> reports the result correctly.
>
> 650 /Users/rmills> cp RA.jpg foo.jpg ; python foo2.py ; exiv2 -pt foo.jpg | grep GPSLong
> 22d 33' 44"
> Exif.GPSInfo.GPSLongitude Rational 3 22deg 33' 44"
> 651 /Users/rmills>
>
> I think the issue is when you write and immediately attempt to report
> the metadata, the read is wrong (however the data's good!). The work
> around for the moment, is to close and re-open the file before
> attempting to read the metadata.
>
> ####
> # NOT GOOD
> img=pyexiv2.Image(bla..bla)
> img.readMetadata()
> img['Exif.GPSInfo.GPSLongitude' ] = [ .... ] ;
> print img['Exif.GPSInfo.GPSLongitude'] # bad
>
> ####
> # GOOD
> img=pyexiv2.Image(bla..bla)
> img.readMetadata()
> img['Exif.GPSInfo.GPSLongitude' ] = [ .... ] ;
> img.writeMetadata()
>
> img=pyexiv2.Image(bla..bla)
> img.readMetadata() ;
> print img['Exif.GPSInfo.GPSLongitude']
>
> I'm off to bed now (it's 00:56 here in California). Let's wait to see
> what Olivier says when he gets to work.
>
>
> foo2.py
>
>
> #!/usr/bin/env python
> import pyexiv2
> import surd
>
> # img=pyexiv2.Image('foo.jpg')
> # img.readMetadata()
>
> # Check the longitude tag (as expected, the result is a tuple of 3 rationals):
> # print img['Exif.GPSInfo.GPSLongitude']
> # (<pyexiv2.Rational instance at 0xa560dac>, <pyexiv2.Rational instance at 0xa560c6c>, <pyexiv2.Rational instance at 0xa560e6c>)
>
> # 3. Modify the tag:
> # img['Exif.GPSInfo.GPSLongitude']=(pyexiv2.Rational(3,1),pyexiv2.Rational(44,1),pyexiv2.Rational(5615,100))
>
> # 4. Check the modification:
> # print img['Exif.GPSInfo.GPSLongitude']
> # 0/1
>
> ##
> # Ration number support
> def R(f):
> """R(float) - get a Rational number for a float"""
> s = surd.surd(float(f))
> return pyexiv2.Rational(s.num,s.denom)
>
> def d(angle):
> """d(any) - get degrees from a number :eg d(33.41) -> 33"""
> return int(angle)
>
> def m(angle):
> """m(any) - get minutes from a number :eg d(33.41) -> 24"""
> return int( angle*60 - d(angle)* 60)
>
> def s(angle):
> """s(any) - get seconds from a number :eg s(33.41) -> 36"""
> return int( angle*3600 - d(angle)*3600 - m(angle)*60 )
>
> ##
> #
> def f(r):
> return float(r.numerator)/float(r.denominator)
>
> ##
> #
> def ff(x):
> return f(x[0])+ (f(x[1])/60.0) + (f(x[2])/3660.0)
>
> ##
> #...

Read more...

Revision history for this message
Olivier Tilloy (osomon) wrote :

As suggested by Robin, it looks like the value for the longitude is correctly written but the value in pyexiv2's internal cache is wrong.
I suspect the workaround can be simplified, re-creating the Image object shouldn't be necessary: calling readMetadata() should be enough.

Thanks for your thorough investigation Robin!

Revision history for this message
Jacques-D. Piguet (jacques-piguet) wrote :

Hello Olivier,

I just have done other tests, if it can help: it seems that as
workaround, it's required to close and re-open the file...

BR, Jacques-D.

>>> img=None
>>> img=pyexiv2.Image('0812/IMG_0580.JPG')
>>> img.readMetadata()
>>> for r in img['Exif.GPSInfo.GPSLatitude']:
... print r
...
47/1
27/1
5760/100
>>> img['Exif.GPSInfo.GPSLatitude'] = (pyexiv2.Rational(47,1),
pyexiv2.Rational(27,1), pyexiv2.Rational(5937,100))
>>> for r in img['Exif.GPSInfo.GPSLatitude']:
... print r
...
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: iteration over non-sequence
>>> print img['Exif.GPSInfo.GPSLatitude']
0/1
>>> img.readMetadata()
>>> for r in img['Exif.GPSInfo.GPSLatitude']:
... print r
...
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: iteration over non-sequence
>>> img.writeMetadata()
>>> print img['Exif.GPSInfo.GPSLatitude']
0/1
>>> img.readMetadata()
>>> print img['Exif.GPSInfo.GPSLatitude']
0/1
>>> img=None
>>> img=pyexiv2.Image('0812/IMG_0580.JPG')
>>> img.readMetadata()
>>> print img['Exif.GPSInfo.GPSLatitude']
(<pyexiv2.Rational instance at 0xa7d05ec>, <pyexiv2.Rational instance at
0xa7d066c>, <pyexiv2.Rational instance at 0xa7d006c>)
>>> for r in img['Exif.GPSInfo.GPSLatitude']:
... print r
...
47/1
27/1
5760/100
>>> img['Exif.GPSInfo.GPSLatitude'] = (pyexiv2.Rational(47,1),
pyexiv2.Rational(27,1), pyexiv2.Rational(5937,100))
>>> img.writeMetadata()
>>> img=None
>>> img=pyexiv2.Image('0812/IMG_0580.JPG')
>>> img.readMetadata()
>>> for r in img['Exif.GPSInfo.GPSLatitude']:
... print r
...
47/1
27/1
5937/100
>>>

Le mardi 19 janvier 2010 à 13:03 +0000, Olivier Tilloy a écrit :
> As suggested by Robin, it looks like the value for the longitude is correctly written but the value in pyexiv2's internal cache is wrong.
> I suspect the workaround can be simplified, re-creating the Image object shouldn't be necessary: calling readMetadata() should be enough.
>
> Thanks for your thorough investigation Robin!
>

Olivier Tilloy (osomon)
Changed in pyexiv2:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.