[Git][debian-gis-team/glymur][upstream] New upstream version 0.10.2
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sun Jul 17 09:16:34 BST 2022
Antonio Valentino pushed to branch upstream at Debian GIS Project / glymur
Commits:
8a787577 by Antonio Valentino at 2022-07-17T07:56:40+00:00
New upstream version 0.10.2
- - - - -
13 changed files:
- CHANGES.txt
- appveyor.yml
- docs/source/conf.py
- docs/source/whatsnew/0.10.rst
- docs/source/whatsnew/0.9.rst
- glymur/_tiff.py
- glymur/config.py
- glymur/tiff.py
- glymur/version.py
- setup.cfg
- + tests/data/issue555.xmp
- tests/test_libtiff.py
- tests/test_tiff2jp2.py
Changes:
=====================================
CHANGES.txt
=====================================
@@ -1,3 +1,9 @@
+July 16, 2022 - v0.10.2
+ Fix appveyor builds
+ Fix tiff2jp2 when ExifTag is present
+ Address warnings in TIFF interface
+ Update glymur.config documentation
+
June 28, 2022 - v0.10.1
Add write capability for Resolution boxes
Add example documentation for reading layers
=====================================
appveyor.yml
=====================================
@@ -23,18 +23,21 @@ environment:
PYTHON_ARCH: "64"
CONDA_PY: "38"
CONDA_NPY: "21"
+ USE_PATH_FOR_GDAL_PYTHON: "YES"
- CONDA_ROOT: "C:\\Miniconda3_64"
PYTHON_VERSION: "3.9"
PYTHON_ARCH: "64"
CONDA_PY: "39"
CONDA_NPY: "21"
+ USE_PATH_FOR_GDAL_PYTHON: "YES"
- CONDA_ROOT: "C:\\Miniconda3_64"
PYTHON_VERSION: "3.10"
PYTHON_ARCH: "64"
CONDA_PY: "310"
CONDA_NPY: "21"
+ USE_PATH_FOR_GDAL_PYTHON: "YES"
# We always use a 64-bit machine, but can build x86 distributions
# with the PYTHON_ARCH variable (which is used by CMD_IN_ENV).
=====================================
docs/source/conf.py
=====================================
@@ -78,7 +78,7 @@ copyright = '2013-2022, John Evans'
# The short X.Y version.
version = '0.10'
# The full version, including alpha/beta/rc tags.
-release = '0.10.1'
+release = '0.10.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
=====================================
docs/source/whatsnew/0.10.rst
=====================================
@@ -2,18 +2,27 @@
Changes in glymur 0.10
######################
+*****************
+Changes in 0.10.2
+*****************
+
+ * Fix appveyor builds.
+ * Fix tiff2jp2 when ExifTag is present.
+ * Address warnings in TIFF interface.
+ * Update glymur.config documentation.
+
*****************
Changes in 0.10.1
*****************
- Add write capability for Resolution boxes
- Add example documentation for reading layers
+ * Add write capability for Resolution boxes.
+ * Add example documentation for reading layers.
*****************
Changes in 0.10.0
*****************
- Allow parsing of Exif UUIDs missing the EXIF\00\00 lead-in
- Add read support for additional Exif tags
- Add support for TLM segment generation
+ * Allow parsing of Exif UUIDs missing the EXIF\00\00 lead-in.
+ * Add read support for additional Exif tags.
+ * Add support for TLM segment generation.
=====================================
docs/source/whatsnew/0.9.rst
=====================================
@@ -6,21 +6,21 @@ Changes in glymur 0.9
Changes in 0.9.9
****************
- Fix bug retrieving some TIFF tags on big endian systems.
+ * Fix bug retrieving some TIFF tags on big endian systems.
****************
Changes in 0.9.8
****************
- Fix install requirements.
+ * Fix install requirements.
****************
Changes in 0.9.7
****************
- Remove distutils in favor of setuptools.
- Add recognition of IMF profiles.
- Add ndim, dtype properties.
+ * Remove distutils in favor of setuptools.
+ * Add recognition of IMF profiles.
+ * Add ndim, dtype properties.
****************
Changes in 0.9.6
=====================================
glymur/_tiff.py
=====================================
@@ -292,7 +292,7 @@ class ExifImageIfd(Ifd):
36881: 'OffsetTimeOriginal',
36882: 'OffsetTimeDigitized',
36867: 'DateTimeOriginal',
- 36867: 'DateTimeDigitized',
+ 36868: 'DateTimeDigitized',
37121: 'ComponentsConfiguration',
37122: 'CompressedBitsPerPixel',
37377: 'ShutterSpeedValue',
=====================================
glymur/config.py
=====================================
@@ -36,7 +36,7 @@ def glymurrc_fname():
def _determine_full_path(libname):
"""
- Try to determine the path to the openjp2 library.
+ Try to determine the path to the library.
Parameters
----------
@@ -116,8 +116,7 @@ def glymur_config(libname):
Returns
-------
- tuple
- tuple of library handles
+ loaded shared library
"""
path = _determine_full_path(libname)
=====================================
glymur/tiff.py
=====================================
@@ -108,7 +108,7 @@ class Tiff2Jp2k(object):
data = struct.pack('<BBHI', 73, 73, 42, 8)
b.write(data)
- self._process_tags(b)
+ self._process_tags(b, self.tags)
if self.found_geotiff_tags:
# geotiff UUID
@@ -136,104 +136,128 @@ class Tiff2Jp2k(object):
with open(self.tiff_filename, 'rb') as tfp:
- self.get_endianness(tfp)
+ self.read_tiff_header(tfp)
- self.found_geotiff_tags = False
+ self.tags = self.read_ifd(tfp)
- tag_length = 20 if self.version == _BIGTIFF else 12
+ if 34665 in self.tags:
+ # we have an EXIF IFD
+ offset = self.tags[34665]['payload'][0]
+ tfp.seek(offset)
+ exif_ifd = self.read_ifd(tfp)
- # how many tags?
- if self.version == _BIGTIFF:
- buffer = tfp.read(8)
- num_tags, = struct.unpack(self.endian + 'Q', buffer)
- else:
- buffer = tfp.read(2)
- num_tags, = struct.unpack(self.endian + 'H', buffer)
+ self.tags[34665]['payload'] = exif_ifd
- # Ok, so now we have the IFD main body, but following that we have
- # the tag payloads that cannot fit into 4 bytes.
+ def read_ifd(self, tfp):
+ """
+ Process either the main IFD or an Exif IFD
- # the IFD main body in the TIFF. As it might be big endian, we
- # cannot just process it as one big chunk.
- buffer = tfp.read(num_tags * tag_length)
+ Parameters
+ ----------
+ tfp : file-like
+ FILE pointer for TIFF
- if self.version == _BIGTIFF:
- tag_format_str = self.endian + 'HHQQ'
- tag_payload_offset = 12
- max_tag_payload_length = 8
- else:
- tag_format_str = self.endian + 'HHII'
- tag_payload_offset = 8
- max_tag_payload_length = 4
+ Returns
+ -------
+ dictionary of the TIFF IFD
+ """
- self.tags = {}
+ self.found_geotiff_tags = False
- for idx in range(num_tags):
+ tag_length = 20 if self.version == _BIGTIFF else 12
- self.logger.debug(f'tag #: {idx}')
+ # how many tags?
+ if self.version == _BIGTIFF:
+ buffer = tfp.read(8)
+ num_tags, = struct.unpack(self.endian + 'Q', buffer)
+ else:
+ buffer = tfp.read(2)
+ num_tags, = struct.unpack(self.endian + 'H', buffer)
- tag_data = buffer[idx * tag_length:(idx + 1) * tag_length]
+ # Ok, so now we have the IFD main body, but following that we have
+ # the tag payloads that cannot fit into 4 bytes.
- tag, dtype, nvalues, offset = struct.unpack(tag_format_str, tag_data) # noqa : E501
+ # the IFD main body in the TIFF. As it might be big endian, we
+ # cannot just process it as one big chunk.
+ buffer = tfp.read(num_tags * tag_length)
- if tag == 34735:
- self.found_geotiff_tags = True
+ if self.version == _BIGTIFF:
+ tag_format_str = self.endian + 'HHQQ'
+ tag_payload_offset = 12
+ max_tag_payload_length = 8
+ else:
+ tag_format_str = self.endian + 'HHII'
+ tag_payload_offset = 8
+ max_tag_payload_length = 4
+
+ tags = {}
+
+ for idx in range(num_tags):
+
+ self.logger.debug(f'tag #: {idx}')
+
+ tag_data = buffer[idx * tag_length:(idx + 1) * tag_length]
- payload_length = tag_dtype[dtype]['nbytes'] * nvalues
+ tag, dtype, nvalues, offset = struct.unpack(tag_format_str, tag_data) # noqa : E501
+
+ if tag == 34735:
+ self.found_geotiff_tags = True
+
+ payload_length = tag_dtype[dtype]['nbytes'] * nvalues
+
+ if payload_length > max_tag_payload_length:
+ # the payload does not fit into the tag entry, so use the
+ # offset to seek to that position
+ current_position = tfp.tell()
+ tfp.seek(offset)
+ payload_buffer = tfp.read(payload_length)
+ tfp.seek(current_position)
- if payload_length > max_tag_payload_length:
- # the payload does not fit into the tag entry, so use the
- # offset to seek to that position
- current_position = tfp.tell()
- tfp.seek(offset)
- payload_buffer = tfp.read(payload_length)
- tfp.seek(current_position)
+ # read the payload from the TIFF
+ payload_format = tag_dtype[dtype]['format'] * nvalues
+ payload = struct.unpack(
+ self.endian + payload_format, payload_buffer
+ )
- # read the payload from the TIFF
- payload_format = tag_dtype[dtype]['format'] * nvalues
- payload = struct.unpack(
- self.endian + payload_format, payload_buffer
- )
+ else:
+ # the payload DOES fit into the TIFF tag entry
+ payload_buffer = tag_data[tag_payload_offset:]
+ # read ALL of the payload buffer
+ fmt = tag_dtype[dtype]['format']
+ num_items = (
+ int(max_tag_payload_length / tag_dtype[dtype]['nbytes'])
+ )
+ payload_format = self.endian + fmt * num_items
+ payload = struct.unpack(payload_format, payload_buffer)
+
+ # Extract the actual payload. Two things going
+ # on here. First of all, not all of the items may
+ # be used. For example, if the payload length is
+ # 4 bytes but the format string was HHH, the that
+ # last 16 bit value is not wanted, so we should
+ # discard it. Second thing is that the signed and
+ # unsigned rational datatypes effectively have twice
+ # the number of values so we need to account for that.
+ if dtype in [5, 10]:
+ payload = payload[:2 * nvalues]
else:
- # the payload DOES fit into the TIFF tag entry
- payload_buffer = tag_data[tag_payload_offset:]
-
- # read ALL of the payload buffer
- payload_format = (
- tag_dtype[dtype]['format']
- * int(max_tag_payload_length / tag_dtype[dtype]['nbytes']) # noqa : E501
- )
-
- payload = struct.unpack(
- self.endian + payload_format, payload_buffer
- )
-
- # Extract the actual payload. Two things going
- # on here. First of all, not all of the items may
- # be used. For example, if the payload length is
- # 4 bytes but the format string was HHH, the that
- # last 16 bit value is not wanted, so we should
- # discard it. Second thing is that the signed and
- # unsigned rational datatypes effectively have twice
- # the number of values so we need to account for that.
- if dtype in [5, 10]:
- payload = payload[:2 * nvalues]
- else:
- payload = payload[:nvalues]
+ payload = payload[:nvalues]
+
+ tags[tag] = {
+ 'dtype': dtype,
+ 'nvalues': nvalues,
+ 'payload': payload
+ }
- self.tags[tag] = {
- 'dtype': dtype,
- 'nvalues': nvalues,
- 'payload': payload
- }
+ return tags
- def _process_tags(self, b):
+ def _process_tags(self, b, tags):
# keep this for writing to the UUID, which will always be 32-bit
little_tiff_tag_length = 12
- num_tags = len(self.tags)
+ num_tags = len(tags)
write_buffer = struct.pack('<H', num_tags)
b.write(write_buffer)
@@ -244,21 +268,23 @@ class Tiff2Jp2k(object):
# the IFD main body in the TIFF. As it might be big endian, we cannot
# just process it as one big chunk.
- tag_start_loc = b.tell()
- after_ifd_position = tag_start_loc + num_tags * little_tiff_tag_length
+ ifd_start_loc = b.tell()
+ after_ifd_position = ifd_start_loc + num_tags * little_tiff_tag_length
# We write a little-TIFF IFD
max_tag_payload_length = 4
- for idx, tag in enumerate(self.tags):
+ for idx, tag in enumerate(tags):
- self.logger.debug(f'tag #: {tag}')
+ tag_offset = ifd_start_loc + idx * little_tiff_tag_length
+ self.logger.debug(f'tag #: {tag}, writing to {tag_offset}')
+ self.logger.debug(f'tag #: {tag}, after IFD {after_ifd_position}')
- b.seek(tag_start_loc + idx * little_tiff_tag_length)
+ b.seek(tag_offset)
- dtype = self.tags[tag]['dtype']
- nvalues = self.tags[tag]['nvalues']
- payload = self.tags[tag]['payload']
+ dtype = tags[tag]['dtype']
+ nvalues = tags[tag]['nvalues']
+ payload = tags[tag]['payload']
payload_length = tag_dtype[dtype]['nbytes'] * nvalues
@@ -291,54 +317,38 @@ class Tiff2Jp2k(object):
else:
# the payload DOES fit into the TIFF tag entry
- # read ALL of the payload buffer
payload_format = (
tag_dtype[dtype]['format']
* int(max_tag_payload_length / tag_dtype[dtype]['nbytes'])
)
- # Does it fit into the UUID tag entry (4 bytes)?
- if payload_length <= 4:
+ # the payload DOES fit into the UUID tag entry
- # so write it back into the tag entry in the UUID
- outbuffer = struct.pack('<HHI', tag, dtype, nvalues)
- b.write(outbuffer)
+ # so write it back into the tag entry in the UUID
+ outbuffer = struct.pack('<HHI', tag, dtype, nvalues)
+ b.write(outbuffer)
- payload_format = tag_dtype[dtype]['format'] * nvalues
+ payload_format = tag_dtype[dtype]['format'] * nvalues
- # we may need to alter the output format
- if payload_format in ['H', 'B', 'I']:
- # just write it as an integer
- payload_format = 'I'
+ # we may need to alter the output format
+ if payload_format in ['H', 'B', 'I']:
+ # just write it as an integer
+ payload_format = 'I'
- outbuffer = struct.pack('<' + payload_format, *payload)
+ if tag == 34665:
+ # special case for an EXIF IFD
+ outbuffer = struct.pack('<I', after_ifd_position)
b.write(outbuffer)
-
+ b.seek(after_ifd_position)
+ self._process_tags(b, payload)
else:
-
- # UUID: write the tag entry after the IFD
- new_offset = after_ifd_position
- outbuffer = struct.pack(
- '<HHII', tag, dtype, nvalues, new_offset
- )
- b.write(outbuffer)
-
- # now write the payload at the outlying position and then
- # come back to the same position in the file stream
- cpos = b.tell()
- b.seek(new_offset)
-
- out_format = '<' + tag_dtype[dtype]['format'] * nvalues
- outbuffer = struct.pack(out_format, *payload)
+ # write a normal tag
+ outbuffer = struct.pack('<' + payload_format, *payload)
b.write(outbuffer)
- # keep track of the next position to write out-of-IFD data
- after_ifd_position = b.tell()
- b.seek(cpos)
-
- def get_endianness(self, tfp):
+ def read_tiff_header(self, tfp):
"""
- Set the endian-ness of the TIFF
+ Get the endian-ness of the TIFF, seek to the main IFD
"""
buffer = tfp.read(4)
=====================================
glymur/version.py
=====================================
@@ -21,7 +21,7 @@ from .lib import tiff
# Do not change the format of this next line! Doing so risks breaking
# setup.py
-version = "0.10.1"
+version = "0.10.2"
version_tuple = parse(version).release
=====================================
setup.cfg
=====================================
@@ -1,6 +1,6 @@
[metadata]
name = Glymur
-version = 0.10.1
+version = 0.10.2
author = 'John Evans'
author_email = "John Evans" <john.g.evans.ne at gmail.com>
license = 'MIT'
=====================================
tests/data/issue555.xmp
=====================================
@@ -0,0 +1,255 @@
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Public XMP Toolkit Core 3.5">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
+ <tiff:Make>Canon</tiff:Make>
+ <tiff:Model>Canon EOS 20D</tiff:Model>
+ <tiff:Orientation>1</tiff:Orientation>
+ <tiff:ImageWidth>3504</tiff:ImageWidth>
+ <tiff:ImageLength>2336</tiff:ImageLength>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/">
+ <exif:ExifVersion>0221</exif:ExifVersion>
+ <exif:ExposureTime>15/1</exif:ExposureTime>
+ <exif:ShutterSpeedValue>-3906891/1000000</exif:ShutterSpeedValue>
+ <exif:FNumber>8/1</exif:FNumber>
+ <exif:ApertureValue>6/1</exif:ApertureValue>
+ <exif:ExposureProgram>1</exif:ExposureProgram>
+ <exif:DateTimeOriginal>2006-12-07T23:20:43-05:00</exif:DateTimeOriginal>
+ <exif:DateTimeDigitized>2006-12-07T23:20:43-05:00</exif:DateTimeDigitized>
+ <exif:ExposureBiasValue>0/2</exif:ExposureBiasValue>
+ <exif:MaxApertureValue>3625/1000</exif:MaxApertureValue>
+ <exif:MeteringMode>5</exif:MeteringMode>
+ <exif:FocalLength>32/1</exif:FocalLength>
+ <exif:CustomRendered>0</exif:CustomRendered>
+ <exif:ExposureMode>1</exif:ExposureMode>
+ <exif:WhiteBalance>0</exif:WhiteBalance>
+ <exif:SceneCaptureType>0</exif:SceneCaptureType>
+ <exif:FocalPlaneXResolution>3504000/885</exif:FocalPlaneXResolution>
+ <exif:FocalPlaneYResolution>2336000/590</exif:FocalPlaneYResolution>
+ <exif:FocalPlaneResolutionUnit>2</exif:FocalPlaneResolutionUnit>
+ <exif:ISOSpeedRatings>
+ <rdf:Seq>
+ <rdf:li>100</rdf:li>
+ </rdf:Seq>
+ </exif:ISOSpeedRatings>
+ <exif:Flash rdf:parseType="Resource">
+ <exif:Fired>False</exif:Fired>
+ <exif:Return>0</exif:Return>
+ <exif:Mode>2</exif:Mode>
+ <exif:Function>False</exif:Function>
+ <exif:RedEyeMode>False</exif:RedEyeMode>
+ </exif:Flash>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xap="http://ns.adobe.com/xap/1.0/">
+ <xap:ModifyDate>2006-12-07T23:20:43-05:00</xap:ModifyDate>
+ <xap:Rating>3</xap:Rating>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <dc:creator>
+ <rdf:Seq>
+ <rdf:li>unknown</rdf:li>
+ </rdf:Seq>
+ </dc:creator>
+ <dc:rights>
+ <rdf:Alt>
+ <rdf:li xml:lang="x-default">2006, Hubert Figuiere</rdf:li>
+ </rdf:Alt>
+ </dc:rights>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>night</rdf:li>
+ <rdf:li>ontario</rdf:li>
+ <rdf:li>ottawa</rdf:li>
+ <rdf:li>parliament of canada</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:aux="http://ns.adobe.com/exif/1.0/aux/">
+ <aux:SerialNumber>420103070</aux:SerialNumber>
+ <aux:LensInfo>24/1 85/1 0/0 0/0</aux:LensInfo>
+ <aux:Lens>24.0-85.0 mm</aux:Lens>
+ <aux:ImageNumber>176</aux:ImageNumber>
+ <aux:FlashCompensation>-2/1</aux:FlashCompensation>
+ <aux:OwnerName>unknown</aux:OwnerName>
+ <aux:Firmware>1.1.0</aux:Firmware>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/">
+ <crs:AlreadyApplied>False</crs:AlreadyApplied>
+ <crs:WhiteBalance>As Shot</crs:WhiteBalance>
+ <crs:Exposure>0.00</crs:Exposure>
+ <crs:Shadows>5</crs:Shadows>
+ <crs:Brightness>+50</crs:Brightness>
+ <crs:Contrast>+25</crs:Contrast>
+ <crs:Saturation>0</crs:Saturation>
+ <crs:Sharpness>25</crs:Sharpness>
+ <crs:LuminanceSmoothing>0</crs:LuminanceSmoothing>
+ <crs:ColorNoiseReduction>25</crs:ColorNoiseReduction>
+ <crs:ChromaticAberrationR>0</crs:ChromaticAberrationR>
+ <crs:ChromaticAberrationB>0</crs:ChromaticAberrationB>
+ <crs:VignetteAmount>0</crs:VignetteAmount>
+ <crs:ShadowTint>0</crs:ShadowTint>
+ <crs:RedHue>0</crs:RedHue>
+ <crs:RedSaturation>0</crs:RedSaturation>
+ <crs:GreenHue>0</crs:GreenHue>
+ <crs:GreenSaturation>0</crs:GreenSaturation>
+ <crs:BlueHue>0</crs:BlueHue>
+ <crs:BlueSaturation>0</crs:BlueSaturation>
+ <crs:FillLight>0</crs:FillLight>
+ <crs:Vibrance>0</crs:Vibrance>
+ <crs:HighlightRecovery>0</crs:HighlightRecovery>
+ <crs:Clarity>0</crs:Clarity>
+ <crs:Defringe>0</crs:Defringe>
+ <crs:HueAdjustmentRed>0</crs:HueAdjustmentRed>
+ <crs:HueAdjustmentOrange>0</crs:HueAdjustmentOrange>
+ <crs:HueAdjustmentYellow>0</crs:HueAdjustmentYellow>
+ <crs:HueAdjustmentGreen>0</crs:HueAdjustmentGreen>
+ <crs:HueAdjustmentAqua>0</crs:HueAdjustmentAqua>
+ <crs:HueAdjustmentBlue>0</crs:HueAdjustmentBlue>
+ <crs:HueAdjustmentPurple>0</crs:HueAdjustmentPurple>
+ <crs:HueAdjustmentMagenta>0</crs:HueAdjustmentMagenta>
+ <crs:SaturationAdjustmentRed>0</crs:SaturationAdjustmentRed>
+ <crs:SaturationAdjustmentOrange>0</crs:SaturationAdjustmentOrange>
+ <crs:SaturationAdjustmentYellow>0</crs:SaturationAdjustmentYellow>
+ <crs:SaturationAdjustmentGreen>0</crs:SaturationAdjustmentGreen>
+ <crs:SaturationAdjustmentAqua>0</crs:SaturationAdjustmentAqua>
+ <crs:SaturationAdjustmentBlue>0</crs:SaturationAdjustmentBlue>
+ <crs:SaturationAdjustmentPurple>0</crs:SaturationAdjustmentPurple>
+ <crs:SaturationAdjustmentMagenta>0</crs:SaturationAdjustmentMagenta>
+ <crs:LuminanceAdjustmentRed>0</crs:LuminanceAdjustmentRed>
+ <crs:LuminanceAdjustmentOrange>0</crs:LuminanceAdjustmentOrange>
+ <crs:LuminanceAdjustmentYellow>0</crs:LuminanceAdjustmentYellow>
+ <crs:LuminanceAdjustmentGreen>0</crs:LuminanceAdjustmentGreen>
+ <crs:LuminanceAdjustmentAqua>0</crs:LuminanceAdjustmentAqua>
+ <crs:LuminanceAdjustmentBlue>0</crs:LuminanceAdjustmentBlue>
+ <crs:LuminanceAdjustmentPurple>0</crs:LuminanceAdjustmentPurple>
+ <crs:LuminanceAdjustmentMagenta>0</crs:LuminanceAdjustmentMagenta>
+ <crs:SplitToningShadowHue>0</crs:SplitToningShadowHue>
+ <crs:SplitToningShadowSaturation>0</crs:SplitToningShadowSaturation>
+ <crs:SplitToningHighlightHue>0</crs:SplitToningHighlightHue>
+ <crs:SplitToningHighlightSaturation>0</crs:SplitToningHighlightSaturation>
+ <crs:SplitToningBalance>0</crs:SplitToningBalance>
+ <crs:ParametricShadows>0</crs:ParametricShadows>
+ <crs:ParametricDarks>0</crs:ParametricDarks>
+ <crs:ParametricLights>0</crs:ParametricLights>
+ <crs:ParametricHighlights>0</crs:ParametricHighlights>
+ <crs:ParametricShadowSplit>25</crs:ParametricShadowSplit>
+ <crs:ParametricMidtoneSplit>50</crs:ParametricMidtoneSplit>
+ <crs:ParametricHighlightSplit>75</crs:ParametricHighlightSplit>
+ <crs:SharpenRadius>+1.0</crs:SharpenRadius>
+ <crs:SharpenDetail>25</crs:SharpenDetail>
+ <crs:SharpenEdgeMasking>0</crs:SharpenEdgeMasking>
+ <crs:ConvertToGrayscale>False</crs:ConvertToGrayscale>
+ <crs:ToneCurveName>Medium Contrast</crs:ToneCurveName>
+ <crs:ToneCurve>
+ <rdf:Seq>
+ <rdf:li>0, 0</rdf:li>
+ <rdf:li>32, 22</rdf:li>
+ <rdf:li>64, 56</rdf:li>
+ <rdf:li>128, 128</rdf:li>
+ <rdf:li>192, 196</rdf:li>
+ <rdf:li>255, 255</rdf:li>
+ </rdf:Seq>
+ </crs:ToneCurve>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:crx="http://ns.adobe.com/lightroom-settings-experimental/1.0/">
+ <crx:ConvertToGrayscale>false</crx:ConvertToGrayscale>
+ <crx:SplitToningHighlightHue>0</crx:SplitToningHighlightHue>
+ <crx:Sharpness>25</crx:Sharpness>
+ <crx:CropBottom>1</crx:CropBottom>
+ <crx:GreenSaturation>0</crx:GreenSaturation>
+ <crx:Exposure>0</crx:Exposure>
+ <crx:LuminanceAdjustmentGreens>0</crx:LuminanceAdjustmentGreens>
+ <crx:SplitToningShadowHue>0</crx:SplitToningShadowHue>
+ <crx:BlueHue>0</crx:BlueHue>
+ <crx:GreenHue>0</crx:GreenHue>
+ <crx:FillLight>0</crx:FillLight>
+ <crx:HueAdjustmentCyans>0</crx:HueAdjustmentCyans>
+ <crx:SaturationAdjustmentGreens>0</crx:SaturationAdjustmentGreens>
+ <crx:Saturation>0</crx:Saturation>
+ <crx:SaturationAdjustmentBlues>0</crx:SaturationAdjustmentBlues>
+ <crx:VignetteMidpoint>50</crx:VignetteMidpoint>
+ <crx:ToneHighlightSplit>75</crx:ToneHighlightSplit>
+ <crx:ToneShadowSplit>25</crx:ToneShadowSplit>
+ <crx:Contrast>25</crx:Contrast>
+ <crx:WhiteBalance>As Shot</crx:WhiteBalance>
+ <crx:HueAdjustmentMagentas>0</crx:HueAdjustmentMagentas>
+ <crx:HighlightRecovery>0</crx:HighlightRecovery>
+ <crx:SaturationAdjustmentCyans>0</crx:SaturationAdjustmentCyans>
+ <crx:GrayMixerYellows>100</crx:GrayMixerYellows>
+ <crx:HueAdjustmentGreens>0</crx:HueAdjustmentGreens>
+ <crx:ToneShadows>0</crx:ToneShadows>
+ <crx:GrayMixerGreens>71</crx:GrayMixerGreens>
+ <crx:GrayMixerBlues>0</crx:GrayMixerBlues>
+ <crx:ColorNoiseReduction>25</crx:ColorNoiseReduction>
+ <crx:ShadowTint>0</crx:ShadowTint>
+ <crx:Shadows>5</crx:Shadows>
+ <crx:RedHue>0</crx:RedHue>
+ <crx:Brightness>50</crx:Brightness>
+ <crx:BlueSaturation>0</crx:BlueSaturation>
+ <crx:CropTop>0</crx:CropTop>
+ <crx:SaturationAdjustmentMagentas>0</crx:SaturationAdjustmentMagentas>
+ <crx:GrayMixerReds>29</crx:GrayMixerReds>
+ <crx:AutoBrightness>false</crx:AutoBrightness>
+ <crx:AutoTonality>false</crx:AutoTonality>
+ <crx:AutoExposure>false</crx:AutoExposure>
+ <crx:RedSaturation>0</crx:RedSaturation>
+ <crx:LuminanceAdjustmentMagentas>0</crx:LuminanceAdjustmentMagentas>
+ <crx:LuminanceAdjustmentBlues>0</crx:LuminanceAdjustmentBlues>
+ <crx:HueAdjustmentBlues>0</crx:HueAdjustmentBlues>
+ <crx:SaturationAdjustmentReds>0</crx:SaturationAdjustmentReds>
+ <crx:LuminanceAdjustmentYellows>0</crx:LuminanceAdjustmentYellows>
+ <crx:SplitToningShadowSaturation>0</crx:SplitToningShadowSaturation>
+ <crx:ChromaticAberrationR>0</crx:ChromaticAberrationR>
+ <crx:LuminanceAdjustmentCyans>0</crx:LuminanceAdjustmentCyans>
+ <crx:CropAngle>0</crx:CropAngle>
+ <crx:ChromaticAberrationB>0</crx:ChromaticAberrationB>
+ <crx:AutoShadows>false</crx:AutoShadows>
+ <crx:CropRight>1</crx:CropRight>
+ <crx:ToneLights>0</crx:ToneLights>
+ <crx:HueAdjustmentReds>0</crx:HueAdjustmentReds>
+ <crx:Vibrance>0</crx:Vibrance>
+ <crx:ToneDarks>0</crx:ToneDarks>
+ <crx:GrayMixerMagentas>29</crx:GrayMixerMagentas>
+ <crx:LuminanceSmoothing>0</crx:LuminanceSmoothing>
+ <crx:SplitToningHighlightSaturation>0</crx:SplitToningHighlightSaturation>
+ <crx:HueAdjustmentYellows>0</crx:HueAdjustmentYellows>
+ <crx:GrayMixerCyans>71</crx:GrayMixerCyans>
+ <crx:ToneMidtoneSplit>50</crx:ToneMidtoneSplit>
+ <crx:VignetteAmount>0</crx:VignetteAmount>
+ <crx:AutoContrast>false</crx:AutoContrast>
+ <crx:CropLeft>0</crx:CropLeft>
+ <crx:ToneHighlights>0</crx:ToneHighlights>
+ <crx:AutoGrayscaleWeights>true</crx:AutoGrayscaleWeights>
+ <crx:SaturationAdjustmentYellows>0</crx:SaturationAdjustmentYellows>
+ <crx:LuminanceAdjustmentReds>0</crx:LuminanceAdjustmentReds>
+ <crx:Tint>1</crx:Tint>
+ <crx:Temperature>4350</crx:Temperature>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:lr="http://ns.adobe.com/lightroom/1.0/">
+ <lr:hierarchicalKeywords>aGllcmFyY2hpY2FsS2V5d29yZHMgPSB7Cgl7CgkJZmxhdCA9IHsKCQkJIm5pZ2h0IiwKCQl9LAoJCXBhdGggPSB7CgkJCSJuaWdodCIsCgkJfSwKCQlwcmltYXJ5ID0gIm5pZ2h0IiwKCQl1dWlkID0gIkU1RDEwQTFELTU3QUYtMTFEQi04RTMzLTAwMEQ5MzVDODY5QSIsCgl9LAoJewoJCWZsYXQgPSB7CgkJCSJvbnRhcmlvIiwKCQkJIm90dGF3YSIsCgkJfSwKCQlwYXRoID0gewoJCQkib250YXJpbyIsCgkJCSJvdHRhd2EiLAoJCX0sCgkJcHJpbWFyeSA9ICJvdHRhd2EiLAoJCXV1aWQgPSAiQjgzMTc4RkItNTdBRi0xMURCLThFMzMtMDAwRDkzNUM4NjlBIiwKCX0sCgl7CgkJZmxhdCA9IHsKCQkJInBhcmxpYW1lbnQgb2YgY2FuYWRhIiwKCQkJIm9udGFyaW8iLAoJCQkib3R0YXdhIiwKCQl9LAoJCXBhdGggPSB7CgkJCSJvbnRhcmlvIiwKCQkJIm90dGF3YSIsCgkJCSJwYXJsaWFtZW50IG9mIGNhbmFkYSIsCgkJfSwKCQlwcmltYXJ5ID0gInBhcmxpYW1lbnQgb2YgY2FuYWRhIiwKCQl1dWlkID0gIkU1RDMzMEZDLTU3QUYtMTFEQi04RTMzLTAwMEQ5MzVDODY5QSIsCgl9LAp9Cg==</lr:hierarchicalKeywords>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/">
+ <xmpTPg:MaxPageSize>
+ <rdf:Description
+ xmlns:stDim="http:ns.adobe.com/xap/1.0/sType/Dimensions#">
+ <stDim:w>4</stDim:w>
+ <stDim:h>3</stDim:h>
+ <stDim:unit>inches</stDim:unit>
+ </rdf:Description>
+ </xmpTPg:MaxPageSize>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/">
+ <Iptc4xmpCore:Location>Parliament Hill, Ottawa, Ontario, Canada</Iptc4xmpCore:Location>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
=====================================
tests/test_libtiff.py
=====================================
@@ -26,7 +26,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(self.temp_tiff_filename, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'TileLength', th)
=====================================
tests/test_tiff2jp2.py
=====================================
@@ -3,6 +3,7 @@ import importlib.resources as ir
import logging
import pathlib
import shutil
+import struct
import sys
import tempfile
import unittest
@@ -26,6 +27,121 @@ from glymur.lib import tiff as libtiff
@unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
class TestSuite(fixtures.TestCommon):
+ @classmethod
+ def setup_exif(cls, path):
+ """
+ Create a simple TIFF file that is constructed to contain an EXIF IFD.
+ """
+
+ with path.open(mode='wb') as f:
+
+ w = 256
+ h = 256
+ rps = 64
+ header_length = 8
+
+ # write the header (8 bytes). The IFD will follow the image data
+ # (256x256 bytes), so the offset to the IFD will be 8 + h * w.
+ main_ifd_offset = header_length + h * w
+ buffer = struct.pack('<BBHI', 73, 73, 42, main_ifd_offset)
+ f.write(buffer)
+
+ # write the image data, 4 64x256 strips of all zeros
+ strip = bytes([0] * rps * w)
+ f.write(strip)
+ f.write(strip)
+ f.write(strip)
+ f.write(strip)
+
+ # write a minimal IFD. with 10 tags
+ main_ifd_data_offset = main_ifd_offset + 2 + 10 * 12 + 4
+
+ buffer = struct.pack('<H', 10)
+ f.write(buffer)
+
+ # width and length and bitspersample
+ buffer = struct.pack('<HHII', 256, 4, 1, w)
+ f.write(buffer)
+ buffer = struct.pack('<HHII', 257, 4, 1, h)
+ f.write(buffer)
+ buffer = struct.pack('<HHII', 258, 4, 1, 8)
+ f.write(buffer)
+
+ # photometric
+ buffer = struct.pack('<HHII', 262, 4, 1, 1)
+ f.write(buffer)
+
+ # strip offsets
+ buffer = struct.pack('<HHII', 273, 4, 4, main_ifd_data_offset)
+ f.write(buffer)
+
+ # spp
+ buffer = struct.pack('<HHII', 277, 4, 1, 1)
+ f.write(buffer)
+
+ # rps
+ buffer = struct.pack('<HHII', 278, 4, 1, 64)
+ f.write(buffer)
+
+ # strip byte counts
+ buffer = struct.pack('<HHII', 279, 4, 4, main_ifd_data_offset + 16)
+ f.write(buffer)
+
+ # XMP
+ with ir.path('tests.data', 'issue555.xmp') as xmp_path:
+ with xmp_path.open() as f2:
+ xmp = f2.read()
+ xmp = xmp + '\0'
+ buffer = struct.pack(
+ '<HHII', 700, 1, len(xmp), main_ifd_data_offset + 32
+ )
+ f.write(buffer)
+
+ # exif tag
+ exif_ifd_offset = main_ifd_data_offset + 32 + len(xmp)
+ buffer = struct.pack('<HHII', 34665, 4, 1, exif_ifd_offset)
+ f.write(buffer)
+
+ # terminate the IFD
+ buffer = struct.pack('<I', 0)
+ f.write(buffer)
+
+ # write the strip offsets here
+ buffer = struct.pack(
+ '<IIII', 8, 8 + rps*w, 8 + 2*rps*w, 8 + 3*rps*w
+ )
+ f.write(buffer)
+
+ # write the strip byte counts
+ buffer = struct.pack('<IIII', rps*w, rps*w, rps*w, rps*w)
+ f.write(buffer)
+
+ # write the XMP data
+ f.write(xmp.encode('utf-8'))
+
+ # write a minimal Exif IFD
+ buffer = struct.pack('<H', 2)
+ f.write(buffer)
+
+ # exposure program
+ buffer = struct.pack('<HHIHH', 34850, 3, 1, 2, 0)
+ f.write(buffer)
+
+ # lens model
+ data_location = exif_ifd_offset + 2 + 2*12 + 4
+ buffer = struct.pack('<HHII', 42036, 2, 6, data_location)
+ f.write(buffer)
+
+ # terminate the IFD
+ buffer = struct.pack('<I', 0)
+ f.write(buffer)
+
+ data = 'Canon\0'.encode('utf-8')
+ buffer = struct.pack('<BBBBBB', *data)
+ f.write(buffer)
+
+ cls.exif_tiff = path
+
@classmethod
def setup_minisblack_spp1(cls, path):
"""
@@ -38,7 +154,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'TileLength', th)
@@ -89,7 +205,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', h)
libtiff.setField(fp, 'ImageWidth', w)
libtiff.setField(fp, 'TileLength', th)
@@ -121,7 +237,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'TileLength', th)
@@ -159,7 +275,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'RowsPerStrip', rps)
@@ -191,7 +307,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'RowsPerStrip', rps)
@@ -223,7 +339,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.RGB)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'TileLength', th)
@@ -328,7 +444,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w8')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.RGB)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'TileLength', th)
@@ -379,7 +495,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.RGB)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'TileLength', th)
@@ -423,6 +539,8 @@ class TestSuite(fixtures.TestCommon):
cls.test_tiff_dir = tempfile.mkdtemp()
cls.test_tiff_path = pathlib.Path(cls.test_tiff_dir)
+ cls.setup_exif(cls.test_tiff_path / 'exif.tif')
+
cls.setup_minisblack_spp1(cls.test_tiff_path / 'moon.tif')
cls.setup_minisblack_3x3(cls.test_tiff_path / 'minisblack_3x3.tif')
@@ -448,6 +566,23 @@ class TestSuite(fixtures.TestCommon):
def tearDownClass(cls):
shutil.rmtree(cls.test_tiff_dir)
+ def test_exif(self):
+ """
+ Scenario: Convert TIFF with Exif IFD to JP2
+
+ Expected Result: No errors. The Exif LensModel tag is recoverable
+ from the UUIDbox.
+ """
+ with Tiff2Jp2k(
+ self.exif_tiff, self.temp_jp2_filename, verbosity=logging.DEBUG
+ ) as p:
+ p.run()
+
+ j = Jp2k(self.temp_jp2_filename)
+
+ tags = j.box[-1].data
+ self.assertEqual(tags['ExifTag']['LensModel'], 'Canon')
+
def test_smoke(self):
"""
SCENARIO: Convert TIFF file to JP2
@@ -829,7 +964,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(self.temp_tiff_filename, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'SampleFormat', libtiff.SampleFormat.UINT)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
@@ -863,7 +998,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(self.temp_tiff_filename, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'SampleFormat', libtiff.SampleFormat.IEEEFP)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
@@ -1232,7 +1367,7 @@ class TestSuite(fixtures.TestCommon):
fp = libtiff.open(self.temp_tiff_filename, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.SEPARATED)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'RowsPerStrip', rps)
@@ -1276,7 +1411,7 @@ class TestSuiteNoScikitImage(fixtures.TestCommon):
fp = libtiff.open(path, mode='w')
libtiff.setField(fp, 'Photometric', libtiff.Photometric.RGB)
- libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE)
+ libtiff.setField(fp, 'Compression', libtiff.Compression.ADOBE_DEFLATE)
libtiff.setField(fp, 'ImageLength', data.shape[0])
libtiff.setField(fp, 'ImageWidth', data.shape[1])
libtiff.setField(fp, 'RowsPerStrip', rps)
View it on GitLab: https://salsa.debian.org/debian-gis-team/glymur/-/commit/8a78757778a99a7c92414abce78b97560f44a3b2
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/glymur/-/commit/8a78757778a99a7c92414abce78b97560f44a3b2
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20220717/3d796ed6/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list