Coder Social home page Coder Social logo

piexif's Introduction

Piexif

Build Status Windows Build Coverage Status docs

To simplify exif manipulations with Python. Writing, reading, and more... Piexif is pure Python. To everywhere with Python.

Document: http://piexif.readthedocs.org/en/latest/

Online demo: http://piexif-demo.appspot.com/demo

Install

'easy_install':

$ easy_install piexif

or 'pip':

$ pip install piexif

or download .zip, extract it. Put 'piexif' directory into your environment.

Why Choose Piexif

How to Use

There are only just five functions.

  • load(filename) - Get exif data as dict.
  • dump(exif_dict) - Get exif as bytes.
  • insert(exif_bytes, filename) - Insert exif into JPEG, or WebP.
  • remove(filename) - Remove exif from JPEG, or WebP.
  • transplant(filename, filename) - Transplant exif from JPEG to JPEG.

Example

exif_dict = piexif.load("foo1.jpg")
for ifd in ("0th", "Exif", "GPS", "1st"):
    for tag in exif_dict[ifd]:
        print(piexif.TAGS[ifd][tag]["name"], exif_dict[ifd][tag])

With PIL(Pillow)

from PIL import Image
import piexif

im = Image.open(filename)
exif_dict = piexif.load(im.info["exif"])
# process im and exif_dict...
w, h = im.size
exif_dict["0th"][piexif.ImageIFD.XResolution] = (w, 1)
exif_dict["0th"][piexif.ImageIFD.YResolution] = (h, 1)
exif_bytes = piexif.dump(exif_dict)
im.save(new_file, "jpeg", exif=exif_bytes)

Environment

Tested on Python 2.7, 3.5+ and PyPy3. Piexif would run even on IronPython. Piexif is OS independent and can run on Google App Engine.

License

This software is released under the MIT license, see LICENSE.txt.

piexif's People

Contributors

cfinnberg avatar hmatoba avatar hugovk avatar jaddison avatar mpenkov avatar ndbroadbent avatar radarhere avatar savenr avatar smurfix avatar toaarnio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

piexif's Issues

insert() raises ValueError with non-jpg files

Hi,

I'm trying to write exif data to tif images. As I already noticed in the docs piexif seems to support jpg only. As a result I get a ValueError from _insert.py (line 26 / 27). Would it be possible to add tif support to piexif? If not, do you know about an alternative for writing exif to tif?

Thanks...

Prolems treating xmp and Photoshop fields

I have a tiff file produced from a CR2 file using dcraw v 9.16.
This has xmp and photoshop data (keys 700, 34377:).
pycharm gives error in function _pack_byte
statement struct.pack("B" * len(args), *args)
PyCharm Community Edition 2017.1.2
Build #PC-171.4249.47, built on April 26, 2017
JRE: 1.8.0_112-release-736-b21 amd64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.0

No Exif.Photo read and write capability

It seems piexif does not support Exif.Photo tags like 37510 Exif.Photo.UserComment (see http://www.exiv2.org/tags.html)

the field Exif.Photo.UserComment is correctly written (can be read by gwenview) by
jhead -cl "test comment" file.jpg
However then Exif.Photo.UserComment is not read with
exif_dict = piexif.load(filename)
print(exif_dict)

Also writing it, does not mofiy the original "test comment"

Loading Binary File?

Hi,

I'm trying to load an image from bytes (as a file object), but the code below. However, I am getting an error:

TypeError: 'file' object has no attribute 'getitem'

with open(image_path, 'rb') as image_file_data:

    exif_dict = piexif.load(image_file_data)
    for ifd in ("0th", "Exif", "GPS", "1st"):
        for tag in exif_dict[ifd]:
            print(piexif.TAGS[ifd][tag]["name"], exif_dict[ifd][tag])

The documentation says I can supply bytes as the parameter. Am I missing something?

Thank you for this great library and for your help :)

~Victor

Problem with unicode text in exif

Using python 3, where strings are unicode by default, unicode characters are not properly embedded in EXIF data when using piexif.dump:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import urllib.request
from subprocess import call
import piexif

#NB adding e.g. chinese such as 仁 causes a 'ValueError: "dump" got wrong type of exif value.'
unicode_string = 'Some accented characters éé' 

test_jpg = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Felis_silvestris_silvestris.jpg/208px-Felis_silvestris_silvestris.jpg"
urllib.request.urlretrieve(test_jpg, "test1.jpg")
urllib.request.urlretrieve(test_jpg, "test2.jpg")

#try piexif unicode
piexif.insert(piexif.dump({"0th":{piexif.ImageIFD.Copyright:unicode_string}, "Exif":{}}), "test1.jpg")
#try exiftool unicode
call(['exiftool', u'-copyright='+unicode_string, "test2.jpg"])

#print result
call(['exiftool', '-copyright', "test2.jpg", "test1.jpg"])

load failing

About

Load fails with some images, in some cases ValueError is thrown, but in some it will try to unpack and fail there.

Notes
EXIF can not be loaded, ValueError is not always thrown. Command line tools like ExifTool can read these files, but also have problems editing/writing back.
pexif can read and write
There seems to be an issue with the first tag (InteropIndex). Could this tag be skipped in such cases?
FYI, most common make and model we experience this issues:

SONY	 HDR-AS300
SONY	 HDR-AS200V
Garmin	 VIRBX
Garmin	 VIRBXE

Data
0hqfncihmssmmhqgzqanpq
0tx1netbeqatazdn588kta
1nxubaunj5-z8eqkik4e5q
1vzo1xk8xf8nzmg7uwgjtg
2xgoqkiztq7ww-fowy85sq
3deweymbpzhjvqqik--ulw
3iq7cnod2ln2h7dvw78etg
4necgkt2drys0di82lez3w
7k42oeosvv-ksb7t5ahqta
7shuhfkreihwztjfd4y7yg
8xd7n82jz8lw8lsduoaavg
9bcjc7ma8knhhq8ra35gxg
9dnztz3bsuxqf5mnpwjwoa
9l4v9tqjek5xtcesdji7uq
17wfgoygkwlvlory9kyiyw
35u4dqsw6z-jqpim8wiysg
96taipdqt27avd3d-dbfmw
adk64r3waaqsipou22mzva
adwkxpub0461dpt2nfmoaw
ae2-mloiizpk8agkw6i5-g
aeqb6vstcl0vxlnojzh0uw
altmrsp5dfn1-u-2jqw5cg
bii7prnxkdykb2xoqxyb6q
bnbmt-d5tchp9opw0se4iq
cqaesa5m4ngdvnqdg0bwow

Buggy example - yet: this is amazing!

Hi, your example doesn't work when using tuples:

gps_ifd = {
pyxif.GPSIFD.GPSVersionID: 1,
pyxif.GPSIFD.GPSDateStamp: "1999:99:99 99:99:99",
}

Traceback:

File "pyxif_load_and_dump.py", line 373, in dict_to_bytes
value_str = raw_value + b"\x00" * (4 - length)
TypeError: can only concatenate tuple (not "str") to tuple

It works when using pyxif.GPSIFD.GPSVersionID: "1" -> string instead of int as value.

Apart from that, your lib looks very promising! If this works, we'll make use of it on Pixabay.com and we'll certainly write about it on our blog :-) Keep up the great work!

It clean and simple and doesn't require unreliable third party libs, such as pyexiv2 ...

Add support for type double and geotiff tags

Hi,

I've tried to add some geotiff tags as described here and here in _exif.py but some of them are in type Double and load() function doesn't support this type (and I really don't know how to implement this).

In _exif.py I've added the following types and tags

TYPES = {"Byte": 1,
         "Ascii": 2,
         "Short": 3,
         "Long": 4,
         "Rational": 5,
         "Undefined": 7,
         "SLong": 9,
         "SRational": 10,
         "Float":11,
         "Double":12}
               33550: {'name': 'ModelPixelScale', 'type': 'Double'},
               33920: {'name': 'IntergraphMatrix', 'type': 'Double'},
               33922: {'name': 'ModelTiepoint', 'type': 'Double'},
               34264: {'name': 'ModelTransformation', 'type': 'Double'},
               34735: {'name': 'GeoKeyDirectory', 'type': 'Byte'},
               34736: {'name': 'GeoDoubleParams', 'type': 'Double'},
               34737: {'name': 'GeoAsciiParams', 'type': 'Ascii'},
               42112: {'name': 'GDAL_METADATA', 'type': 'Byte'},
               42113: {'name': 'GDAL_NODATA', 'type': 'Byte'},

Rename project?

"Pyxif" is not good name for exif library, because it lacks "exif" in name.
I think I might rename it "Piexif" or any name for SEO. "Pyexif" has already been on PyPI.

import piexif # import pyxif

Whoops -sent too soon

I have a tiff file produced from a CR2 file using dcraw v 9.16.
This has xmp and photoshop data (keys 700, 34377)
Running from pycharm gives error in
function: _pack_byte
statement: struct.pack("B" * len(args), *args)
error message: error: cannot convert argument to integer

Is it me or a bug?

My configuration
PyCharm Community Edition 2017.1.2
Windows 10 10.0
Python 2.7

failed to load corrupted exif data

corrupted_exif

This image has corrupted exif data. I get a MemoryError when I try to load it with piexif.load.

Pillow can open it and I got these warnings.

/usr/lib/python3.6/site-packages/PIL/TiffImagePlugin.py:725: UserWarning: Possibly corrupt EXIF data.  Expecting to read 5517926400 bytes but only got 0. Skipping tag 0
  " Skipping tag %s" % (size, len(data), tag))
/usr/lib/python3.6/site-packages/PIL/TiffImagePlugin.py:725: UserWarning: Possibly corrupt EXIF data.  Expecting to read 3234119872 bytes but only got 424. Skipping tag 0
  " Skipping tag %s" % (size, len(data), tag))
/usr/lib/python3.6/site-packages/PIL/TiffImagePlugin.py:742: UserWarning: Corrupt EXIF data.  Expecting to read 4 bytes but only got 0.
  warnings.warn(str(msg))

These are _getexif returned results.

{271: 'Xiaomi\x00',
 272: 'MiTwo\x00\x00',
 282: (72, 1),
 283: (72, 1),
 296: 2,
 305: 'www.meitu.com',
 531: 1,
 33434: (6479, 100000),
 34665: 168,
 34853: {},
 34855: 1586,
 36864: b'0220',
 36867: '2015:11:22 16:52:18',
 36868: '2002:12:08 12:00:00',
 37121: b'\x01\x02\x03\x00',
 37378: (230000, 100000),
 37386: (385, 100),
 40960: b'0100',
 40961: 1,
 40962: 3264,
 40963: 2448,
 40965: 394}

failure on image with rotation information

I have a rotated image which fails when loading exif data like follows:

import PIL
import piexif

image = PIL.Image.open('IMG_7250.jpg')
exif_data = piexif.load(image.info['exif'])
exif_bytes = piexif.dump(exif_data)

if fails with:

*** error: cannot convert argument to integer

The image is attached. Image removed.

Files missing from the zip file and required for testing

While packaging this file to Fedora I noticed that there are two files that required to test the package and that are missing from the zip file:

tests/s_test.py
tests/images/01.tif

Could you, please, add them to the source package?

How can I get the thumbnail to dataUrl?

Hello !
I have trouble to get the thumbnail to a usable format in javascript.
When I load my image the thumbnail is in binary format and I failed to make it to base64.

Any tips?

module 'piexif' has no attribute 'load'

Thank you for your work!

Hitting this issue:

import piexif
im='abc.tiff'
exif_dict = piexif.load(im)

which produces this error:

AttributeError: module 'piexif' has no attribute 'load'

I tried this on Python:

  • 3.5.2
  • 3.6.0
  • 3.6.1

ValueError: Exif might be wrong. Got incorrect value type to decode.

Hi there,

Got this error with my photos taken with my DMC-FT5 Panasonic Camera.

ValueError: Exif might be wrong. Got incorrect value type to decode.
tag: 40962
type: 9

Which links to the comment here in your code

Attached is a sample code and photo to replay the bug.

Thanks for your lib which is very handy I intend to use it for my project lycheesync but if it can't recognize my own photos It will be a no go. I hope you can fix it, but I think the code is not commented for no reason.

Good luck with this one and thanks again for your lib !

attachement.zip

piexifをより速くする

問題ではありませんけどコメントがあります。今は私のプロジェクトにpyexiv2を使っています。しかしpyexiv2はPython2だけです。その上にpure Pythonではありません。piexifの方がいいと思います。

EXIFを読むことだけがほしいです。

それでpiexifを試みています。小さいJPGでpiexifの方がpyexiv2より速いです。しかし大きい(10MB)のJPGでpiexifはとても遅いです。例えば、10000の大きいJPGを読むことはpyexiv2 9秒けどpiexifは79秒でした。残念!

cProfileで調べました。遅いものは_load.pyのf.read() です。EXIFだけについて興味があるのに、何時も全部のファイルを読みます。それで修正しました:

_load.py:

class _ExifReader(object):
    def __init__(self, data):
        filename = data[:] # Save off the file name
        if data[0:2] == b"\xff\xd8":  # JPEG
[...]
        else:
            try:
                with open(data, 'rb') as f:
                    data = f.read(80000) # 80000 found by experiment
            except:
                raise ValueError("Got invalid value.")
            if data[0:2] == b"\xff\xd8":  # JPEG
                segments = split_into_segments(data)

                if (segments == 'Retry'): # We need more than the first 80000 bytes so read whole file
                    try:
                        with open(filename, 'rb') as f:
                            data = f.read()
                    except:
                        raise ValueError("Got invalid value.")
                    segments = split_into_segments(data) # End modification
[...]
_common.py:
# Add code to check whether we have a problem because we only read the first 80000 bytes.
        if (head >= len(data)):
          if (len(data) > 80000):
            raise ValueError("Wrong JPEG data.")
          else:
            segments = "Retry"
            break

私にいいです。その修正でpiexifはとても早くなりました:2万写真は
piexif: 10秒
pyexiv2: 25秒

凄い!exifreadなどはpiexifとpyexiv2より遅いです。

多分将来piexifは『読むことだけ』の速い_loadread()のAPIをもらいます。

どうもあがとうございます。

Exif Flag FocalLength cant be set

Hello I try to set my FocalLength exif parameter using

 `def setImageMeta(image_path, make = u"Basler", model = u"acA2440-35um", focal_length = 4, sensor_width = 8.4, max_image_circle = "2/3\""):
    o = io.BytesIO()
    thumb_im = Image.open(image_path)
    width, height = thumb_im.size
    width = int(width *0.5)
    height = int(height * 0.5)
    thumb_im.thumbnail((width,height), Image.ANTIALIAS)
    thumb_im.save(o, "jpeg")
    thumbnail = o.getvalue()
zeroth_ifd = {piexif.ImageIFD.Make: u"Basler",
          piexif.ImageIFD.Software: u"Inveox"
          }
exif_ifd = {
            #piexif.ExifIFD.LensMake: "LensMake",
            piexif.ExifIFD.FocalLength: 8/1,
            }
gps_ifd = {
           }
first_ifd = {piexif.ImageIFD.Make: u"Basler",
             piexif.ImageIFD.Software: u"Inveox"
             }

exif_dict = {"0th":zeroth_ifd, "Exif":exif_ifd, "GPS":gps_ifd, "1st":first_ifd, "thumbnail":thumbnail}
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, "Image_Rot10.jpg")
im = Image.open("Image_Rot10.jpg")
im.thumbnail((100, 100), Image.ANTIALIAS)
im.save("out.jpg", exif=exif_bytes)

setImageMeta("Image_Rot10.jpg")`

How ever I get an error that
2

Traceback (most recent call last):
File "C:\Users\eclipse-workspace\MetaData.py", line 70, in
setImageMeta("Image_Rot10.jpg")
File "C:\Users\eclipse-workspace\MetaData.py", line 65, in setImageMeta
Inveox
2
(38000.0,)
5
exif_bytes = piexif.dump(exif_dict)
File "C:\Users\AppData\Local\Programs\Python\Python35\lib\site-packages\piexif_dump.py", line 68, in dump
exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length)
File "C:\Users\AppData\Local\Programs\Python\Python35\lib\site-packages\piexif_dump.py", line 338, in _dict_to_bytes
offset)
File "C:\Users\AppData\Local\Programs\Python\Python35\lib\site-packages\piexif_dump.py", line 248, in _value_to_bytes

four_bytes_over = new_value

UnboundLocalError: local variable 'new_value' referenced before assignment

What am I doing wrong here? I have tried strings ratios and so on.......

handling wrong type tag values

###About

if one wants to edit an existing exif, where some tag values are written in the wrong type and those tags are not edited, piexif will crash when the edited dictionary is attempted to be dumped back into bytes, to be written back to the images, since some tag values are in the wrong type(when packing to struct in _dump)

###Notes
piexif is great to edit or create new exif in an image, but we have issues where we often get images that have valuable information in exif tags we don't edit, but the information is stored in the wrong type, in cases like this, just loading exif from image and trying to _dump back will fail.

Please give us a suggestion on how to solve this rather than looping through all the tags and either removing the tags which have the value in the wrong type or trying to force the value in the right type.

I agree that the exif standard should be enforced and the type of each tag value is well specified, but still many cameras or people processing images will write the tag values in the wrong type and your _dump script can not seem to handle that, did you happen to experience similar issues yourself?

Webp

Hello,
thanks for your plugin. How can I use your package with webp pictures?

GPS removal causes Bad IFD1 directory error

I am working on creating a tool to pull the exif data from an image and remove the GPS data. I use pillow to read in the file. I then use the following function to extract the exif and remove the GPS data.

def manipulate_exif(img, remove_gps):

    try:
        import piexif
    except ImportError:
        raise

    try:
        exif = piexif.load(img.info['exif'])

        if remove_gps:
            exif.get('GPS', None).clear()
            print '{}'.format(pprint.pformat(exif))

    except Exception:
        exif = {}

    return piexif.dump(exif)

When I use it with the remove_gps flag set to False the function works as expected. However when I set remove_gps to True, I get weird field overflow issues in some of the fields and I get Bad IFD1 directory error in the exif file where the GPS data existed before.

I am not sure if this is a bug in Piexif or I am using Piexif incorrectly. Any help is most appreciated!

Lat Lon in Deg,Min,Seconds

Hi there,
I am not able to do this:

assign gps coordinates

gps_ifd = {piexif.GPSIFD.GPSLatitudeRef: "S",
piexif.GPSIFD.GPSLatitude: "((40, 1), (41, 1), (476051999, 10000000))",
piexif.GPSIFD.GPSLongitudeRef: "W",
piexif.GPSIFD.GPSLongitude: "((165, 1), (22, 1), (14268000, 10000000))"
}
While dumping get "UnboundLocalError: local variable 'new_value' referenced before assignment"
Is there a proper way of doing this or not capable at this point?

Thanks

dump and load are not bi-directional

I am trying to do something like:

zeroth_ifd, exif_ifd, gps_ifd = pyxif.load(file_path)
zeroth_ifd[pyxif.ZerothIFD.Artist] = args.artist
zeroth_ifd[pyxif.ZerothIFD.Copyright] = args.copyright

exif_bytes = pyxif.dump(zeroth_ifd, exif_ifd, gps_ifd)

But apparently dump and load are not bi-directional. I think we should fix this.

Crash related to this image

I'm seeing a crash with the following image something related to saving a resized version of the image (and updating the exif info):

ice-cream-sandwiches-wedding-reception-food-truck original 1

Snippet of code that works for most images, but not the above:

        # ...
        if "exif" in image_info:
            exif = {}
            try:
                # image_info is the `info` from the above PILlow-open image
                exif = piexif.load(image_info["exif"])
            except Exception as e:
                logger.info("'piexif' loading error: %s", str(e))

            if exif:
                # update the exif dimensions as the width/height changed
                exif["Exif"][piexif.ExifIFD.PixelXDimension], exif["Exif"][piexif.ExifIFD.PixelYDimension] = image.size

                # blows up on this line
                options["info"]["exif"] = piexif.dump(exif)

Traceback:

Traceback (most recent call last):
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gunicorn/workers/async.py", line 52, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gunicorn/workers/ggevent.py", line 152, in handle_request
    super(GeventWorker, self).handle_request(*args)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gunicorn/workers/async.py", line 103, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/falcon/api.py", line 209, in __call__
    responder(req, resp, **params)
  File "/Users/me/projects/proj_server/proj_server/pipeline.py", line 269, in handler
    derivative = self.engine.process(data, operations, request_data)
  File "/Users/me/projects/proj_pil_engine/proj_pil_engine/engine.py", line 248, in process
    return self.image_processing_pool.apply(super(PILEngine, self).process, args=args, kwds=kwargs)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/pool.py", line 326, in apply
    return self.spawn(func, *args, **kwds).get()
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/event.py", line 385, in get
    return self.get(block=False)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/event.py", line 375, in get
    return self._raise_exception()
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/event.py", line 355, in _raise_exception
    reraise(*self.exc_info)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/_compat.py", line 33, in reraise
    raise value.with_traceback(tb)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/gevent/threadpool.py", line 211, in _worker
    value = func(*args, **kwargs)
  File "/Users/me/projects/proj_server/proj_server/core.py", line 167, in process
    image = self.operations[op_name].process(image, request_data, **op_params)
  File "/Users/me/projects/proj_pil_engine/proj_pil_engine/operations/resize.py", line 243, in process
    options["info"]["exif"] = piexif.dump(exif)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/piexif/_dump.py", line 93, in dump
    thumbnail = _get_thumbnail(exif_dict["thumbnail"])
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/piexif/_dump.py", line 156, in _get_thumbnail
    segments = split_into_segments(jpeg)
  File "/Users/me/.virtualenvs/proj/lib/python3.5/site-packages/piexif/_common.py", line 8, in split_into_segments
    raise ValueError("Given data isn't JPEG.")
ValueError: Given data isn't JPEG.

After a quick debug, it looks like exif_dict["thumbnail"] is b"".

Reading and writing exif values does not perserve values

When using the library to read exif data from an image (using load on a jpg), and rewriting the loaded exif data back to the image (using insert), not all exif values remain unchanged (as they should be).

For example, this example code lead (among others) to exif subsecond information being lost (apparently because it cannot be parsed correctly) and subsequently changed some offset values.

exif = piexif.load("sample.jpg")
piexif.insert(piexif.dump(exif), "sample.jpg")

should definitely be fixed.

Some of exif types are not supported

Due to this documentation, There are 12 types of data formats for exif files. Unfortunately some of these formats is not supported in this package (e.g. value 8), Which leads to a ValueError Exception.
Can you please fix it so it can be also possible to use these kinds of formats?

Differentiating between binary blobs and strings

Hi,

I recently stumbled on this looking for a way to parse EXIF in a photo gallery app. I'm using Tornado on Python 3.5, but hit a stumbling block in that the EXIF library gives me text as binary strings; I can't tell which are binary blobs and which are actual strings for me to decode.

As an example, take this photo: http://gallery.longlandclan.id.au/raw/snowys-2018/20180415-dscf7010.jpg

piexif gives me this:

In [5]: piexif.load('20180415-dscf7010.jpg', True)
Out[5]: 
{'0th': {'Copyright': b'    ',
  'DateTime': b'2018:04:15 09:20:09',
  'ExifTag': 300,
  'Make': b'FUJIFILM',
  'Model': b'FinePix S1000fd   ',
  'Orientation': 1,
  'PrintImageMatching': b'PrintIM\x000250\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x01\x01\x00\x00\x00\x00',
  'ResolutionUnit': 2,
  'Software': b'Digital Camera FinePix S1000fd Ver1.03   ',
  'XResolution': (72, 1),
  'YCbCrPositioning': 2,
  'YResolution': (72, 1)},
 '1st': {'Compression': 6,
  'JPEGInterchangeFormat': 1880,
  'JPEGInterchangeFormatLength': 8958,
  'Orientation': 1,
  'ResolutionUnit': 2,
  'XResolution': (72, 1),
  'YCbCrPositioning': 2,
  'YResolution': (72, 1)},
 'Exif': {'ApertureValue': (400, 100),
  'BrightnessValue': (500, 100),
  'ColorSpace': 1,
  'ComponentsConfiguration': b'\x01\x02\x03\x00',
  'CompressedBitsPerPixel': (40, 10),
  'CustomRendered': 0,
  'DateTimeDigitized': b'2018:04:15 09:20:09',
  'DateTimeOriginal': b'2018:04:15 09:20:09',
  'ExifVersion': b'0220',
  'ExposureBiasValue': (0, 100),
  'ExposureMode': 0,
  'ExposureProgram': 3,
  'ExposureTime': (10, 1200),
  'FNumber': (400, 100),
  'FileSource': b'\x03',
  'Flash': 16,
  'FlashpixVersion': b'0100',
  'FocalLength': (59, 10),
  'FocalPlaneResolutionUnit': 3,
  'FocalPlaneXResolution': (6129, 1),
  'FocalPlaneYResolution': (6129, 1),
  'ISOSpeedRatings': 64,
  'InteroperabilityTag': 1732,
  'LightSource': 0,
  'MakerNote': b'FUJIFILM\x0c\x00\x00\x00 \x00\x00\x00\x07\x00\x04\x00\x00\x000130\x10\x00\x02\x00\x1f\x00\x00\x00\n\x02\x00\x00\x00\x10\x02\x00\x08\x00\x00\x00*\x02\x00\x00\x01\x10\x03\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x10\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x10\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10\x10\x03\x00…

Some of these are clearly human-readable text, but MakerNote for example, most definitely is not text. Both are represented with bytes. I'd like to emit this data as JSON, however json.dumps takes great umbrage to the fact that I'm handing it bytes objects. Is there a flag somewhere I can set that will decode the byte strings representing text into str objects?

Read online image?

Hi

Can piexif read an online image using load function, or does it only work with local images?

Thanks

Get a `struct.error` when trying to load exif data

II am reading several images coming from the same device (which means the EXIF version should be the same for each image), and sometimes the exif loading works fine but sometimes I get the following error :

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/renaud/src/backoffice/venv/lib/site-packages/piexif/_load.py", line 51, in load
    exif_dict["1st"] = exifReader.get_ifd_dict(pointer, "1st")
  File "/home/renaud/src/backoffice/venv/lib/site-packages/piexif/_load.py", line 92, in get_ifd_dict
    self.tiftag[pointer: pointer+2])[0]
struct.error: unpack requires a string argument of length 2

If it can help, this seems to happen only at a certain orientation (exif orientation = 6).

Here is an example image which gives me this error.

test

Files with no or other suffix are false negatives

When performing piexif.load()on a valid JPEG-File named testpic.docx or testpic I get a ValueError saying

Given file is neither JPEG nor TIFF

It's a lie!

Piexif should not consider the suffix to check if the file is valid. I personally use the imghdr python module to check if a file is valid and would appreciate a check=False parameter for piexif.load() to skip unnecessary checks.

Thanks in advance
Felix

Issue with ProfileCopyright Field

When I run the following code there is no python issue however, the exif that is generated has a weird occurrence. I believe that ProfileCopyright in the exif is a UTF-8 byte array. When I encode the field, I just get back the integer values of the text I have inputed. I am sure that I am just misunderstanding how to encode this information. Any help is most appreciated. Sample code below.

import piexif
from PIL import Image


def manipulate_exif(img, remove_gps):

    author = 'Brian Levin'
    license = '{} 2016, All Rights Reserved'

    exif = piexif.load(img.info['exif'])
    exif['0th'][piexif.ImageIFD.Artist] = author
    exif['0th'][piexif.ImageIFD.XPAuthor] = bytearray(author, 'utf-16')

    # Line with issue
    exif['0th'][piexif.ImageIFD.ProfileCopyright] = bytearray(license.format(author), 'utf-8')
    # End Line with issue   

    exif['0th'][piexif.ImageIFD.Copyright] = license

    return piexif.dump(exif)


img = Image.open('./IMG_2223.jpg')
exif_copy = manipulate_exif(img, False)
img.save('./Test.jpg', 'JPEG', exif=exif_copy)

This produces the following exif statement for ProfileCopyright:

ProfileCopyright: 123 125 32 50 48 49 54 44 32 65 108 108 32 82 105 103 104 116 115 32 82 101 115 101 114 118 101 100

tags on github

Would be nice if you will do git tags on releases, so it will be easier for distros to package it (because PyPI is not real source archive).

Not writing in 1st

I think there is a mistake or a wrong behavior when adding information in the "1st" section.

In this example (web site documentation), no information are adding in section "ImageIFD".

Moreover, to be able to add information in this section, we need to also add a "thumbnail":
line 55 in _dump.py
if (("1st" in exif_dict) and ("thumbnail" in exif_dict) and (exif_dict["thumbnail"] is not None)):

If not thumbnail is adding in exif information, the function piexif.dump(exif_dict) will ignore every thing contain in "1st". I did not read something about this restriction in the documentation.

Is it possible to fix it?

Read `UserComment` when opened with Pillow

I save UserComment data with piexif using the UserComment helper and dump it as exif bytes and write to jpg using pillow.

When I load the jpg with pillow, what is the best way to extract the UserComment using piexif without opening the file again (because its already opened with pillow)?

For now I came up with the following:

img = Image.open(buf)
exif_data = img._getexif()
piexif.helper.UserComment.load(exif_data[piexif.ExifIFD.UserComment])

is that how it is intended to work?

no tar-ball or zip on pypi

Hi

1.0.5 doesn't seem to have a source release on pypi. Would be great to add it over there, especially for linux distribution who build their packages from these (I'm trying to package the latest version for opensuse).
(the changelog also doesn't seem to have 1.0.5 information)

thanks

Arun

cannot dump exif with type Unsigned Short

I cannot dump the attached image.
Those are the outputs of jhead and exifprobe

jhead tmp.jpg
File name    : tmp.jpg
File size    : 3270028 bytes
File date    : 2016:02:29 13:22:06
Camera make  : SAMSUNG
Camera model : GT-I9070
Date/Time    : 2012:01:01 02:34:26
Resolution   : 2560 x 1920
Flash used   : No
Focal length :  3.4mm
Exposure time: 0.0009 s  (1/1072)
Aperture     : f/2.7
ISO equiv.   : 50
Whitebalance : Auto
Metering Mode: center weight
Exposure     : aperture priority (semi-auto)
Comment      : User comments
exifprobe tmp.jpg 
File Name = tmp.jpg
File Type = JPEG
File Size = 3270028
@000000000=0       :  <JPEG_SOI>
@0x0000002=2       :    <JPEG_APP1> 0xffe1 length 18131, 'Exif'
@0x000000c=12      :      TIFF(II=0x4949) magic=0x002a='*\0' ifd offset = 8 (+ 12 = 0x14/20)
@0x0000014=20      :      <IFD 0> 12 entries starting at file offset 0x16=22
@0x0000016=22      :        <0x0100=  256> ImageWidth                  [4 =LONG          1]  = 2560
@0x0000022=34      :        <0x0101=  257> ImageLength                 [4 =LONG          1]  = 1920
@0x000002e=46      :        <0x010f=  271> Make                        [2 =ASCII         8]  = @0xbe=190
@0x000003a=58      :        <0x0110=  272> Model                       [2 =ASCII        10]  = @0xc6=198
@0x0000046=70      :        <0x0112=  274> Orientation                 [3 =SHORT         1]  = 1 = '0,0 is top left'
@0x0000052=82      :        <0x011a=  282> XResolution                 [5 =RATIONAL      1]  = @0xd0=208
@0x000005e=94      :        <0x011b=  283> YResolution                 [5 =RATIONAL      1]  = @0xd8=216
@0x000006a=106     :        <0x0128=  296> ResolutionUnit              [3 =SHORT         1]  = 2 = 'pixels per inch'
@0x0000076=118     :        <0x0131=  305> Software                    [2 =ASCII        12]  = @0xe0=224
@0x0000082=130     :        <0x0132=  306> DateTime                    [2 =ASCII        20]  = @0xaa=170
@0x000008e=142     :        <0x0213=  531> YCbCrPositioning            [3 =SHORT         1]  = 2 = 'co-sited'
@0x000009a=154     :        <0x8769=34665> ExifIFDPointer              [4 =LONG          1]  = @0xec=236
@0x00000a6=166     :        **** next IFD offset 736(+ 12 = 0x2ec/748)
@0x00000aa=170     :        ============= VALUES, IFD 0 ============
@0x00000be=190     :        Make                        = 'SAMSUNG\0'
@0x00000c6=198     :        Model                       = 'GT-I9070\0\0'
@0x00000d0=208     :        XResolution                 = 72
@0x00000d8=216     :        YResolution                 = 72
@0x00000e0=224     :        Software                    = 'I9070XXLQG\0\0'
@0x00000aa=170     :        DateTime                    = '2012:01:01 02:34:26\0'
@0x00000ec=236     :        <EXIF IFD> (in IFD 0) 23 entries starting at file offset 0xee=238
@0x00000ee=238     :          <0x829a=33434> ExposureTime                [5 =RATIONAL      1]  = @0x206=518
@0x00000fa=250     :          <0x829d=33437> FNumber                     [5 =RATIONAL      1]  = @0x20e=526
@0x0000106=262     :          <0x8822=34850> ExposureProgram             [8 =SSHORT        1]  = 3 = 'Aperture Priority'
@0x0000112=274     :          <0x8827=34855> ISOSpeedRatings             [3 =SHORT         1]  = 50
@0x000011e=286     :          <0x9000=36864> Version                     [7 =UNDEFINED     4]  = '0220'
@0x000012a=298     :          <0x9003=36867> DateTimeOriginal            [2 =ASCII        20]  = @0x22a=554
@0x0000136=310     :          <0x9004=36868> DateTimeDigitized           [2 =ASCII        20]  = @0x216=534
@0x0000142=322     :          <0x9101=37121> ComponentsConfiguration     [1 =BYTE          4]  = 1,2,3,0 = 'YCbCr'
@0x000014e=334     :          <0x9204=37380> ExposureBiasValue           [10=SRATIONAL     1]  = @0x23e=574
@0x000015a=346     :          <0x9205=37381> MaxApertureValue            [5 =RATIONAL      1]  = @0x246=582
@0x0000166=358     :          <0x9207=37383> MeteringMode                [3 =SHORT         1]  = 2 = 'Center Weighted Average'
@0x0000172=370     :          <0x9209=37385> Flash                       [3 =SHORT         1]  = 0 = 'no flash'
@0x000017e=382     :          <0x920a=37386> FocalLength                 [5 =RATIONAL      1]  = @0x24e=590
@0x000018a=394     :          <0x927c=37500> MakerNote                   [7 =UNDEFINED    98]  = @0x26c=620
@0x0000196=406     :          <0x9286=37510> UserComment                 [7 =UNDEFINED    22]  = @0x256=598
@0x00001a2=418     :          <0xa000=40960> FlashPixVersion             [7 =UNDEFINED     4]  = '0100'
@0x00001ae=430     :          <0xa001=40961> ColorSpace                  [3 =SHORT         1]  = 1 = 'sRGB'
@0x00001ba=442     :          <0xa002=40962> PixelXDimension             [4 =LONG          1]  = 2560
@0x00001c6=454     :          <0xa003=40963> PixelYDimension             [4 =LONG          1]  = 1920
@0x00001d2=466     :          <0xa005=40965> Interoperability            [4 =LONG          1]  = @0x2ce=718
@0x00001de=478     :          <0xa402=41986> ExposureMode                [3 =SHORT         1]  = 0 = 'Auto'
@0x00001ea=490     :          <0xa403=41987> WhiteBalance                [3 =SHORT         1]  = 0 = 'Auto'
@0x00001f6=502     :          <0xa406=41990> SceneCaptureType            [3 =SHORT         1]  = 0 = 'Standard'
@0x0000202=514     :          **** next IFD offset 0
@0x0000206=518     :          ============= VALUES, EXIF IFD ============
@0x0000206=518     :          ExposureTime                = 0.000933 sec
@0x000020e=526     :          FNumber                     = 2.7 APEX = 'f2.5'
@0x000022a=554     :          DateTimeOriginal            = '2012:01:01 02:34:26\0'
@0x0000216=534     :          DateTimeDigitized           = '2012:01:01 02:34:26\0'
@0x000023e=574     :          ExposureBiasValue           = 0 APEX
@0x0000246=582     :          MaxApertureValue            = 2.87 APEX = 'f2.7'
@0x000024e=590     :          FocalLength                 = 3.43 mm
@0x000026c=620     :          <MakerNote> length 98, Plain IFD scheme, 7 entries starting at offset 0x26e/622
@0x000026e=622     :            <0X0001=    1> TAG_0X0001                [ 7=UNDEFINED     4]  = 48,49,48,48 = '0100'
@0x000027a=634     :            <0X0002=    2> TAG_0X0002                [ 4=LONG          1]  = 73728
@0x0000286=646     :            <0X000C=   12> TAG_0X000C                [ 4=LONG          1]  = 0
@0x0000292=658     :            <0X0010=   16> TAG_0X0010                [ 5=RATIONAL      1]  = @0x66=102
@0x000029e=670     :            <0X0040=   64> TAG_0X0040                [ 4=LONG          1]  = 0
@0x00002aa=682     :            <0X0050=   80> TAG_0X0050                [ 4=LONG          1]  = 1
@0x00002b6=694     :            <0X0100=  256> TAG_0X0100                [ 3=SHORT         1]  = 0
@0x00002c2=706     :            **** next IFD offset 0
@0x00002c6=710     :            ============= VALUES, MakerNote ============
@0x0000066=102     :            TAG_0X0010                = 0.00103604
*0x00002c5=709     :            ---- End of values before end of MakerNote
 0x00002c6=710     :              00 00 00 00  00 00 00 00               |........    |
@0x00002cd=717     :          </MakerNote>
@0x0000256=598     :          UserComment                 = length 14+8: (CC=ASCII) = 'User comments\0'
@0x00002ce=718     :          <Interoperability SubIFD> 2 entries starting at file offset 0x2d0=720
@0x00002d0=720     :            <0x0001=    1> InteroperabilityIndex       [2 =ASCII         4]  = 'R98'
@0x00002dc=732     :            <0x0002=    2> InteroperabilityVersion     [7 =UNDEFINED     4]  = '0100'
@0x00002e8=744     :            **** next IFD offset 0
@0x00002eb=747     :          </Interoperability SubIFD>
-0x00002eb=747     :        </EXIF IFD>
@0x00002eb=747     :      </IFD 0>
@0x00002ec=748     :      <IFD 1> 9 entries starting at file offset 0x2ee=750
@0x00002ee=750     :        <0x0100=  256> ImageWidth                  [4 =LONG          1]  = 320
@0x00002fa=762     :        <0x0101=  257> ImageLength                 [4 =LONG          1]  = 240
@0x0000306=774     :        <0x0103=  259> Compression                 [3 =SHORT         1]  = 6 = 'Exif/old JPEG'
@0x0000312=786     :        <0x0112=  274> Orientation                 [3 =SHORT         1]  = 1 = '0,0 is top left'
@0x000031e=798     :        <0x011a=  282> XResolution                 [5 =RATIONAL      1]  = @0x35e=862
@0x000032a=810     :        <0x011b=  283> YResolution                 [5 =RATIONAL      1]  = @0x366=870
@0x0000336=822     :        <0x0128=  296> ResolutionUnit              [3 =SHORT         1]  = 2 = 'pixels per inch'
@0x0000342=834     :        <0x0201=  513> JPEGInterchangeFormat       [4 =LONG          1]  = @0x36e=878
@0x000034e=846     :        <0x0202=  514> JPEGInterchangeFormatLength [4 =LONG          1]  = 17257
@0x000035a=858     :        **** next IFD offset 0
@0x000035e=862     :        ============= VALUES, IFD 1 ============
@0x000035e=862     :        XResolution                 = 72
@0x0000366=870     :        YResolution                 = 72
@0x000036e=878     :        #### Start of JPEG thumbnail data for IFD 1, length 17257 ####
@0x000036e=878     :        <JPEG_SOI>
@0x0000370=880     :          <JPEG_APP0> 0xffe0 length 16, 'JFIF'
@0x0000379=889     :            Version       = 1.2
@0x000037b=891     :            Units         = 'dots/inch'
@0x000037c=892     :            Xdensity      = 72
@0x000037e=894     :            Ydensity      = 72
@0x0000380=896     :            XThumbnail    = 0
@0x0000381=897     :            YThumbnail    = 0
@0x0000381=897     :          </JPEG_APP0>
@0x0000382=898     :          <JPEG_DQT> length 67
@0x00003c7=967     :          <JPEG_DQT> length 67
@0x000040c=1036    :          <JPEG_SOF_0> length 17, 8 bits/sample, components=3, width=320, height=240
@0x000041f=1055    :          <JPEG_DHT> length 31 table class = 0 table id = 0
@0x0000440=1088    :          <JPEG_DHT> length 181 table class = 0 table id = 1
@0x00004f7=1271    :          <JPEG_DHT> length 31 table class = 1 table id = 0
@0x0000518=1304    :          <JPEG_DHT> length 181 table class = 1 table id = 1
@0x00005cf=1487    :          <JPEG_DRI> length 4
@0x00005d5=1493    :          <JPEG_SOS> length 12  start of JPEG data, 3 components 76800 pixels
@0x00046d5=18133   :        <JPEG_EOI> JPEG length 17257
@0x00046d6=18134   :        #### End of JPEG thumbnail data for IFD 1, length 17257 ####
@0x00046d6=18134   :      </IFD 1>
-0x00046d6=18134   :    </JPEG_APP1>
@0x00046d7=18135   :    <JPEG_DQT> length 67
@0x000471c=18204   :    <JPEG_DQT> length 67
@0x0004761=18273   :    <JPEG_SOF_0> length 17, 8 bits/sample, components=3, width=2560, height=1920
@0x0004774=18292   :    <JPEG_DHT> length 31 table class = 0 table id = 0
@0x0004795=18325   :    <JPEG_DHT> length 181 table class = 0 table id = 1
@0x000484c=18508   :    <JPEG_DHT> length 31 table class = 1 table id = 0
@0x000486d=18541   :    <JPEG_DHT> length 181 table class = 1 table id = 1
@0x0004924=18724   :    <JPEG_DRI> length 4
@0x000492a=18730   :    <JPEG_SOS> length 12  start of JPEG data, 3 components 4915200 pixels
@0x031d86e=3266670 :  <JPEG_EOI> JPEG length 3266672
-0x031e58b=3270027 :  END OF FILE (JPEG_EOI FOUND EARLY)
@000000000=0       :  Start of JPEG baseline DCT compressed primary image [2560x1920] length <= 3270028
-0x031e58b=3270027 :    End of JPEG primary image data
@0x000036e=878     :  Start of JPEG baseline DCT compressed reduced-resolution image [320x240] length 17257 (IFD 1)
-0x00046d6=18134   :    End of JPEG reduced-resolution image data
Number of images = 2
File Format = JPEG/APP1/TIFF/EXIF # with MakerNote

tmp

TypeError: can only concatenate tuple (not "str") to tuple

Congratulation, I really like your lib and I think it is very useful.
I am having some problems going through your example.
In specific when I execute the exif_bytes = piexif.dump(exif_dict)
I get the following error:
TypeError: can only concatenate tuple (not "str") to tuple

Let me know if it could be useful to get also the image file.
Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.