Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnboundLocalError: local variable 'new_value' referenced before assignment #131

Open
djjudas21 opened this issue Mar 10, 2022 · 4 comments

Comments

@djjudas21
Copy link

I've got the following code:

image_metadata = {"0th": encoded_ifd, "Exif": encoded_exif, "GPS": encoded_gps}
pp.pprint(image_metadata)
exif_bytes = piexif.dump(image_metadata)
piexif.insert(exif_bytes, file)

It outputs this:

{'0th': {271: 'Canon',
         272: 'A-1',
         315: 'Jonathan Gazeley',
         33432: '© 2021 Jonathan Gazeley'},
 'Exif': {34850: 3,
          34867: 100,
          36867: '2021-08-26T22:01:00Z',
          37377: '0.00080000',
          37381: '2.0',
          37383: 2,
          37386: '50.0',
          41989: '50.0',
          42016: '1d580fb5-a40f-46e2-abb0-9ee658dcc0e3',
          42033: '12345',
          42035: 'Canon',
          42036: 'FD 50mm f/1.8',
          42037: '22222'},
 'GPS': {}}
Traceback (most recent call last):
  File "/home/jonathan/git/camerahub/tagger-cli/main.py", line 169, in <module>
    exif_bytes = piexif.dump(image_metadata)
  File "/home/jonathan/.cache/pypoetry/virtualenvs/tagger-cli-SSoXMnv6-py3.10/lib/python3.10/site-packages/piexif/_dump.py", line 68, in dump
    exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length)
  File "/home/jonathan/.cache/pypoetry/virtualenvs/tagger-cli-SSoXMnv6-py3.10/lib/python3.10/site-packages/piexif/_dump.py", line 335, in _dict_to_bytes
    length_str, value_str, four_bytes_over = _value_to_bytes(raw_value,
  File "/home/jonathan/.cache/pypoetry/virtualenvs/tagger-cli-SSoXMnv6-py3.10/lib/python3.10/site-packages/piexif/_dump.py", line 261, in _value_to_bytes
    four_bytes_over = new_value
UnboundLocalError: local variable 'new_value' referenced before assignment

Is this a bug in piexif or am I using it wrong? Piexif 1.1.3 on Python 3.10.2.

@wpl123
Copy link

wpl123 commented Mar 25, 2022

I get the same error on Python 3.8.10 with piexif 1.1.3 using code taken directly from the repository example exif.py

gps_ifd = {
piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0),
piexif.GPSIFD.GPSAltitudeRef: 0,
piexif.GPSIFD.GPSAltitude: ele,
piexif.GPSIFD.GPSLatitudeRef: lat_ref,
piexif.GPSIFD.GPSLatitude: (lat_deg, lat_min, lat_sec),
piexif.GPSIFD.GPSLongitudeRef: lon_ref,
piexif.GPSIFD.GPSLongitude: (lon_deg, lon_min, lon_sec),
}

exif_dict = {"GPS": gps_ifd}
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, fpath)

Traceback (most recent call last):
File "/home/wplaird/Documents/Middle Creek/Maps/GIS/GoogleMaps/Flyover/src/app/make_layer_files.py", line 291, in
updated = update_images(df_img_files,df_points)
File "/home/wplaird/Documents/Middle Creek/Maps/GIS/GoogleMaps/Flyover/src/app/make_layer_files.py", line 193, in update_images
exif_bytes = piexif.dump(exif_dict)
File "/home/wplaird/Documents/Middle Creek/Maps/GIS/GoogleMaps/Flyover/src/venv/lib/python3.8/site-packages/piexif/_dump.py", line 74, in dump
gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length)
File "/home/wplaird/Documents/Middle Creek/Maps/GIS/GoogleMaps/Flyover/src/venv/lib/python3.8/site-packages/piexif/_dump.py", line 335, in _dict_to_bytes
length_str, value_str, four_bytes_over = _value_to_bytes(raw_value,
File "/home/wplaird/Documents/Middle Creek/Maps/GIS/GoogleMaps/Flyover/src/venv/lib/python3.8/site-packages/piexif/_dump.py", line 247, in _value_to_bytes
four_bytes_over = new_value
UnboundLocalError: local variable 'new_value' referenced before assignment

@aaronwmorris
Copy link

aaronwmorris commented Apr 24, 2023

I think I figured this out. The root problem is piexif has terrible error checking.

This error means a value you passed, likely an integer or float, needs to be expressed as a Rational (or SRational).

For instance, ExposureTime is a rational and must be passed like (1, 300) which translates to 1/300s.

In the case of GPS, piexif.GPSIFD.GPSLatitude and piexif.GPSIFD.GPSLongitude values need to be passed as 3 rational numbers... ((84, 1), (30, 1), (1, 2))

@djjudas21
Copy link
Author

Nice one @aaronwmorris. I gave up on piexif ages ago because of this (and other) issues, but this week I started using pyexiv2 and found that it too handles rationals in this way. I've already got functions in my code for converting decimal GPS to degrees/minutes/seconds GPS, so I'm going to tweak that to return rationals instead. Thanks for the tip.

@djjudas21
Copy link
Author

Here's what I've come up with. Note that it returns the 3 rationals as a single formatted string, as required for pyexiv2, e.g. 'Exif.GPSInfo.GPSLatitude': '51/1 27/1 3148/100'

def deg_to_dms(decdegrees):
    """
    Convert from decimal degrees to degrees, minutes, seconds.
    """
    decdegrees = Decimal(decdegrees)
    # Multiply degrees up 3600 to get integer second resolution, divide by 60 to get mins and secs
    mins, secs = divmod(abs(decdegrees)*3600, 60)
    # Further divide by 60 to get degrees and mins
    degs, mins = divmod(mins, 60)
    # round down degs and mins. Secs remains a float
    degs, mins = int(degs), int(mins)
    return degs, mins, secs


def deg_to_dms_rational(decdegrees):
    """
    Convert from decimal degrees to degrees, minutes, seconds expressed as rationals
    This returns 3 rationals formatted as a string suitable for pyexiv2, e.g.
    'Exif.GPSInfo.GPSLatitude': '51/1 27/1 3148/100'
    """
    (degs, mins, secs) = deg_to_dms(decdegrees)
    # As secs is a float, we multiply by 100 for increased precision in the rational
    roundedsecs = round(secs * 100)
    return f"{degs}/1 {mins}/1 {roundedsecs}/100"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants