mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-03-03 19:45:56 +03:00
Merge pull request #5416 from radarhere/tiff_exif
Allow getexif() to access TIFF tag_v2 data
This commit is contained in:
commit
6d94376d1f
|
@ -399,6 +399,50 @@ class TestFileTiff:
|
||||||
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
|
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
|
||||||
assert 0x8825 in im.tag_v2
|
assert 0x8825 in im.tag_v2
|
||||||
|
|
||||||
|
def test_exif(self):
|
||||||
|
with Image.open("Tests/images/ifd_tag_type.tiff") as im:
|
||||||
|
exif = im.getexif()
|
||||||
|
|
||||||
|
assert sorted(exif.keys()) == [
|
||||||
|
256,
|
||||||
|
257,
|
||||||
|
258,
|
||||||
|
259,
|
||||||
|
262,
|
||||||
|
271,
|
||||||
|
272,
|
||||||
|
273,
|
||||||
|
277,
|
||||||
|
278,
|
||||||
|
279,
|
||||||
|
282,
|
||||||
|
283,
|
||||||
|
284,
|
||||||
|
296,
|
||||||
|
297,
|
||||||
|
305,
|
||||||
|
339,
|
||||||
|
700,
|
||||||
|
34665,
|
||||||
|
34853,
|
||||||
|
50735,
|
||||||
|
]
|
||||||
|
assert exif[256] == 640
|
||||||
|
assert exif[271] == "FLIR"
|
||||||
|
|
||||||
|
gps = exif.get_ifd(0x8825)
|
||||||
|
assert list(gps.keys()) == [0, 1, 2, 3, 4, 5, 6, 18]
|
||||||
|
assert gps[0] == b"\x03\x02\x00\x00"
|
||||||
|
assert gps[18] == "WGS-84"
|
||||||
|
|
||||||
|
def test_exif_frames(self):
|
||||||
|
# Test that EXIF data can change across frames
|
||||||
|
with Image.open("Tests/images/g4-multi.tiff") as im:
|
||||||
|
assert im.getexif()[273] == (328, 815)
|
||||||
|
|
||||||
|
im.seek(1)
|
||||||
|
assert im.getexif()[273] == (1408, 1907)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
with Image.open(filename) as im:
|
with Image.open(filename) as im:
|
||||||
|
|
|
@ -773,6 +773,27 @@ class TestImage:
|
||||||
reloaded_exif.load(exif.tobytes())
|
reloaded_exif.load(exif.tobytes())
|
||||||
assert reloaded_exif.get_ifd(0x8769) == exif.get_ifd(0x8769)
|
assert reloaded_exif.get_ifd(0x8769) == exif.get_ifd(0x8769)
|
||||||
|
|
||||||
|
def test_exif_load_from_fp(self):
|
||||||
|
with Image.open("Tests/images/flower.jpg") as im:
|
||||||
|
data = im.info["exif"]
|
||||||
|
if data.startswith(b"Exif\x00\x00"):
|
||||||
|
data = data[6:]
|
||||||
|
fp = io.BytesIO(data)
|
||||||
|
|
||||||
|
exif = Image.Exif()
|
||||||
|
exif.load_from_fp(fp)
|
||||||
|
assert exif == {
|
||||||
|
271: "Canon",
|
||||||
|
272: "Canon PowerShot S40",
|
||||||
|
274: 1,
|
||||||
|
282: 180.0,
|
||||||
|
283: 180.0,
|
||||||
|
296: 2,
|
||||||
|
306: "2003:12:14 12:01:44",
|
||||||
|
531: 1,
|
||||||
|
34665: 196,
|
||||||
|
}
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
sys.version_info < (3, 7), reason="Python 3.7 or greater required"
|
sys.version_info < (3, 7), reason="Python 3.7 or greater required"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1323,11 +1323,16 @@ class Image:
|
||||||
self._exif = Exif()
|
self._exif = Exif()
|
||||||
|
|
||||||
exif_info = self.info.get("exif")
|
exif_info = self.info.get("exif")
|
||||||
if exif_info is None and "Raw profile type exif" in self.info:
|
if exif_info is None:
|
||||||
exif_info = bytes.fromhex(
|
if "Raw profile type exif" in self.info:
|
||||||
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
exif_info = bytes.fromhex(
|
||||||
)
|
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
||||||
self._exif.load(exif_info)
|
)
|
||||||
|
elif hasattr(self, "tag_v2"):
|
||||||
|
self._exif.endian = self.tag_v2._endian
|
||||||
|
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||||
|
if exif_info is not None:
|
||||||
|
self._exif.load(exif_info)
|
||||||
|
|
||||||
# XMP tags
|
# XMP tags
|
||||||
if 0x0112 not in self._exif:
|
if 0x0112 not in self._exif:
|
||||||
|
@ -3308,7 +3313,7 @@ atexit.register(core.clear_cache)
|
||||||
|
|
||||||
|
|
||||||
class Exif(MutableMapping):
|
class Exif(MutableMapping):
|
||||||
endian = "<"
|
endian = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._data = {}
|
self._data = {}
|
||||||
|
@ -3343,6 +3348,12 @@ class Exif(MutableMapping):
|
||||||
info.load(self.fp)
|
info.load(self.fp)
|
||||||
return self._fixup_dict(info)
|
return self._fixup_dict(info)
|
||||||
|
|
||||||
|
def _get_head(self):
|
||||||
|
if self.endian == "<":
|
||||||
|
return b"II\x2A\x00\x08\x00\x00\x00"
|
||||||
|
else:
|
||||||
|
return b"MM\x00\x2A\x00\x00\x00\x08"
|
||||||
|
|
||||||
def load(self, data):
|
def load(self, data):
|
||||||
# Extract EXIF information. This is highly experimental,
|
# Extract EXIF information. This is highly experimental,
|
||||||
# and is likely to be replaced with something better in a future
|
# and is likely to be replaced with something better in a future
|
||||||
|
@ -3355,8 +3366,8 @@ class Exif(MutableMapping):
|
||||||
self._loaded_exif = data
|
self._loaded_exif = data
|
||||||
self._data.clear()
|
self._data.clear()
|
||||||
self._ifds.clear()
|
self._ifds.clear()
|
||||||
self._info = None
|
|
||||||
if not data:
|
if not data:
|
||||||
|
self._info = None
|
||||||
return
|
return
|
||||||
|
|
||||||
if data.startswith(b"Exif\x00\x00"):
|
if data.startswith(b"Exif\x00\x00"):
|
||||||
|
@ -3371,6 +3382,27 @@ class Exif(MutableMapping):
|
||||||
self.fp.seek(self._info.next)
|
self.fp.seek(self._info.next)
|
||||||
self._info.load(self.fp)
|
self._info.load(self.fp)
|
||||||
|
|
||||||
|
def load_from_fp(self, fp, offset=None):
|
||||||
|
self._loaded_exif = None
|
||||||
|
self._data.clear()
|
||||||
|
self._ifds.clear()
|
||||||
|
|
||||||
|
# process dictionary
|
||||||
|
from . import TiffImagePlugin
|
||||||
|
|
||||||
|
self.fp = fp
|
||||||
|
if offset is not None:
|
||||||
|
self.head = self._get_head()
|
||||||
|
else:
|
||||||
|
self.head = self.fp.read(8)
|
||||||
|
self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head)
|
||||||
|
if self.endian is None:
|
||||||
|
self.endian = self._info._endian
|
||||||
|
if offset is None:
|
||||||
|
offset = self._info.next
|
||||||
|
self.fp.seek(offset)
|
||||||
|
self._info.load(self.fp)
|
||||||
|
|
||||||
def _get_merged_dict(self):
|
def _get_merged_dict(self):
|
||||||
merged_dict = dict(self)
|
merged_dict = dict(self)
|
||||||
|
|
||||||
|
@ -3389,10 +3421,7 @@ class Exif(MutableMapping):
|
||||||
def tobytes(self, offset=8):
|
def tobytes(self, offset=8):
|
||||||
from . import TiffImagePlugin
|
from . import TiffImagePlugin
|
||||||
|
|
||||||
if self.endian == "<":
|
head = self._get_head()
|
||||||
head = b"II\x2A\x00\x08\x00\x00\x00"
|
|
||||||
else:
|
|
||||||
head = b"MM\x00\x2A\x00\x00\x00\x08"
|
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
|
ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
|
||||||
for tag, value in self.items():
|
for tag, value in self.items():
|
||||||
if tag in [0x8769, 0x8225, 0x8825] and not isinstance(value, dict):
|
if tag in [0x8769, 0x8225, 0x8825] and not isinstance(value, dict):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user