Merge branch 'devel'

This commit is contained in:
jevans 2013-06-05 20:47:18 -04:00
commit bae1217aaa
14 changed files with 803 additions and 89 deletions

View file

@ -1,3 +1,5 @@
Jun 05, 2013 - v0.1.4 Added Exif UUID read support.
Jun 02, 2013 - v0.1.3p1 Raising IOErrors when code block size and precinct Jun 02, 2013 - v0.1.3p1 Raising IOErrors when code block size and precinct
sizes are not in harmony. Added statement to docs about upstream library sizes are not in harmony. Added statement to docs about upstream library
dependence. Added roadmap to docs. dependence. Added roadmap to docs.

21
LICENSE.txt Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013 John Evans
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,5 +1,6 @@
---
API API
=== ---
Jp2k Jp2k
---- ----

View file

@ -3,7 +3,8 @@
# glymur documentation build configuration file, created by # glymur documentation build configuration file, created by
# sphinx-quickstart on Sun May 26 11:29:54 2013. # sphinx-quickstart on Sun May 26 11:29:54 2013.
# #
# This file is execfile()d with the current directory set to its containing dir. # This file is execfile()d with the current directory set to its containing
# dir.
# #
# Note that not all possible configuration values are present in this # Note that not all possible configuration values are present in this
# autogenerated file. # autogenerated file.
@ -11,7 +12,9 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import sys, os import sys
import os
class Mock(object): class Mock(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -41,13 +44,13 @@ for mod_name in MOCK_MODULES:
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.')) #sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ----------------------------------------------------- # -- General configuration ----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0' #needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions # Add any Sphinx extension module names here, as strings. They can be
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
'sphinx.ext.autosummary', 'sphinx.ext.autosummary',
'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'numpydoc'] 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'numpydoc']
@ -75,7 +78,7 @@ copyright = u'2013, John Evans'
# The short X.Y version. # The short X.Y version.
version = '0.1' version = '0.1'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.1.3p1' release = '0.1.4'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@ -91,7 +94,8 @@ release = '0.1.3p1'
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
exclude_patterns = [] exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents. # The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None #default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text. # If true, '()' will be appended to :func: etc. cross-reference text.
@ -112,7 +116,7 @@ pygments_style = 'sphinx'
#modindex_common_prefix = [] #modindex_common_prefix = []
# -- Options for HTML output --------------------------------------------------- # -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
@ -192,9 +196,8 @@ html_show_sourcelink = True
htmlhelp_basename = 'glymurdoc' htmlhelp_basename = 'glymurdoc'
# -- Options for LaTeX output -------------------------------------------------- # -- Options for LaTeX output -------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper', #'papersize': 'letterpaper',
@ -203,14 +206,13 @@ latex_elements = {
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
#'preamble': '', #'preamble': '',
} latex_elements = {}
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author, documentclass
latex_documents = [ # [howto/manual]).
('index', 'glymur.tex', u'glymur Documentation', latex_documents = [('index', 'glymur.tex', u'glymur Documentation',
u'John Evans', 'manual'), u'John Evans', 'manual'), ]
]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
# the title page. # the title page.
@ -233,7 +235,7 @@ latex_documents = [
#latex_domain_indices = True #latex_domain_indices = True
# -- Options for manual page output -------------------------------------------- # -- Options for manual page output -------------------------------------------
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
@ -246,16 +248,14 @@ man_pages = [
#man_show_urls = False #man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------ # -- Options for Texinfo output -----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples # Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [('index', 'glymur', u'glymur Documentation',
('index', 'glymur', u'glymur Documentation', u'John Evans', 'glymur',
u'John Evans', 'glymur', 'One line description of project.', 'One line description of project.', 'Miscellaneous'), ]
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
#texinfo_appendices = [] #texinfo_appendices = []

View file

@ -1,15 +1,18 @@
************ ------------
How do I...? How do I...?
************ ------------
Get the code? Get the code?
============= =============
Go to http://github.com/quintusdias/glymur Go to either of
* http://github.com/quintusdias/glymur (bleeding edge)
* https://pypi.python.org/pypi/Glymur/ (stable releases)
Display metadata? Display metadata?
================= =================
There are two ways. From the unix command line, the script **jp2dump** is There are two ways. From the unix command line, the script *jp2dump* is
available. :: available. ::
$ jp2dump /path/to/glymur/installation/data/nemo.jp2 $ jp2dump /path/to/glymur/installation/data/nemo.jp2
@ -22,8 +25,40 @@ From within Python, it is as simple as printing the Jp2k object, i.e. ::
>>> j = Jp2k(file) >>> j = Jp2k(file)
>>> print(j) >>> print(j)
The primary emphasis is on JP2 metadata, but it is possible to This prints the metadata found in the JP2 boxes, but in the case of the
display just raw codestream as well. This will display metadata present in the codestream box, only the main header is printed. It is possible to print
codestream's main header only. :: **only** the codestream information as well, i.e. ::
>>> print(j.get_codestream()) >>> print(j.get_codestream())
Work with XMP UUIDs?
====================
The example JP2 file shipped with glymur has an XMP UUID::
>>> import pkg_resources
>>> from glymur import Jp2k
>>> file = pkg_resources.resource_filename(glymur.__name__, "data/nemo.jp2")
>>> j = Jp2k(file)
>>> print(j.box[4])
UUID Box (uuid) @ (715, 2412)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data:
<ns0:xmpmeta xmlns:ns0="adobe:ns:meta/" xmlns:ns2="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ns0:xmptk="XMP Core 4.4.0-Exiv2">
<rdf:RDF>
<rdf:Description ns2:CreatorTool="glymur" rdf:about="" />
</rdf:RDF>
</ns0:xmpmeta>
Since the UUID data in this case is returned as an ElementTree Element, one can
use ElementTree to access the data. For example, to extract the
**CreatorTool** attribute value, the following would work::
>>> xmp = j.box[4].data
>>> ns0 = '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}'
>>> ns1 = '{http://ns.adobe.com/xap/1.0/}'
>>> name = '{0}RDF/{0}Description'.format(ns0)
>>> elt = xmp.find(name)
>>> elt
<Element '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description' at 0xb4baa93c>
>>> elt.attrib['{0}CreatorTool'.format(ns1)]
'glymur'

View file

@ -3,8 +3,9 @@
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. contain the root `toctree` directive.
==================================
Welcome to glymur's documentation! Welcome to glymur's documentation!
=================================== ==================================
Contents: Contents:
@ -16,9 +17,9 @@ Contents:
how_do_i how_do_i
roadmap roadmap
------------------
Indices and tables Indices and tables
================== ------------------
* :ref:`genindex` * :ref:`genindex`
* :ref:`modindex` * :ref:`modindex`

View file

@ -1,6 +1,6 @@
========================================= ----------------------------------------
Glymur: a Python interface for JPEG 2000 Glymur: a Python interface for JPEG 2000
========================================= ----------------------------------------
**Glymur** contains a Python interface to the OpenJPEG library **Glymur** contains a Python interface to the OpenJPEG library
which allows linux and mac users to read and write JPEG 2000 files. For more which allows linux and mac users to read and write JPEG 2000 files. For more
@ -13,15 +13,15 @@ Glymur supports both reading and writing of JPEG 2000 images (part 1). Writing
JPEG 2000 images is currently limited to images that can fit in memory, JPEG 2000 images is currently limited to images that can fit in memory,
however. however.
Of particular focus is retrieval of metadata. Reading XMP UUID Of particular focus is retrieval of metadata. Reading Exif UUIDs is supported,
boxes is supported, as the data block consists of XML. There is as is reading XMP UUIDs as the XMP data packet is just XML. There is
some very limited support for reading JPX metadata. For instance, some very limited support for reading JPX metadata. For instance,
**asoc** and **labl** boxes are recognized, so GMLJP2 metadata can **asoc** and **labl** boxes are recognized, so GMLJP2 metadata can
be retrieved from such JPX files. be retrieved from such JPX files.
------------ ''''''''''''
Requirements Requirements
------------ ''''''''''''
glymur works on Python 2.7 and 3.3. Python 3.3 is strongly recommended. glymur works on Python 2.7 and 3.3. Python 3.3 is strongly recommended.
OpenJPEG OpenJPEG
@ -32,7 +32,8 @@ you can retrieve via subversion. As of this time of writing, svn
revision 2345 works. In addition, you should also retrieve their test data, as revision 2345 works. In addition, you should also retrieve their test data, as
you will need it when running glymur's test suite. you will need it when running glymur's test suite.
Earlier versions of OpenJPEG through the 2.0 official release are not supported. Earlier versions of OpenJPEG through the 2.0 official release will **NOT**
work and are not supported.
Be sure to have the following ports/RPMs/debs installed. Be sure to have the following ports/RPMs/debs installed.
@ -132,9 +133,9 @@ Windows
------- -------
Not currently supported. Not currently supported.
------------------------------------ ''''''''''''''''''''''''''''''''''''
Installation, Testing, Configuration Installation, Testing, Configuration
------------------------------------ ''''''''''''''''''''''''''''''''''''
From this point forward, python3 will be referred to as just "python". From this point forward, python3 will be referred to as just "python".
@ -185,7 +186,10 @@ The test suite may then be run with::
$ cd /back/to/glymur/unpacking/directory $ cd /back/to/glymur/unpacking/directory
$ python -m unittest discover $ python -m unittest discover
At the moment, the development version of the library prints quite a few Quite a few tests are currently skipped. These include tests whose
warnings to stderr, which you may ignore. There are also more skipped tests OpenJPEG counterparts are already failing, and others which do pass but
on Python 2.7 than on Python 3.3. The important thing will be whether still produce heaps of output on stderr. Rather than let this swamp
or not any test errors are reported at the end. the signal (that most of the tests are actually passing), they've been
filtered out for now. There are also more skipped tests on Python 2.7
than on Python 3.3. The important point to remember is whether or not any test
errors are reported at the end.

View file

@ -1,9 +1,10 @@
-------
Roadmap Roadmap
======= -------
Here's an incomplete list of what I'd like to focus on in the near future. Here's an incomplete list of what I'd like to focus on in the near future.
* continue to monitor upstream changes in the **openjp2** library * continue to monitor upstream changes in the **openjp2** library
* add read support or Exif UUIDs
* investigate using CFFI or cython instead of ctypes to wrap **openjp2** * investigate using CFFI or cython instead of ctypes to wrap **openjp2**
* add read support for ICC profiles

View file

@ -14,6 +14,7 @@ References
import copy import copy
import math import math
import os import os
import pprint
import struct import struct
import sys import sys
import uuid import uuid
@ -1570,6 +1571,9 @@ class UUIDBox(Jp2kBox):
if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'): if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
uuid_type = ' (XMP)' uuid_type = ' (XMP)'
uuid_data = _pretty_print_xml(self.data) uuid_data = _pretty_print_xml(self.data)
elif self.uuid.bytes == b'JpgTiffExif->JP2':
uuid_type = ' (Exif)'
uuid_data = '\n' + pprint.pformat(self.data)
else: else:
uuid_type = '' uuid_type = ''
uuid_data = '{0} bytes'.format(len(self.data)) uuid_data = '{0} bytes'.format(len(self.data))
@ -1618,12 +1622,522 @@ class UUIDBox(Jp2kBox):
else: else:
text = buffer.decode('utf-8') text = buffer.decode('utf-8')
kwargs['data'] = ET.fromstring(text) kwargs['data'] = ET.fromstring(text)
elif kwargs['uuid'].bytes == b'JpgTiffExif->JP2':
e = Exif(buffer)
d = {}
d['Exif'] = e.exif_image
d['Photo'] = e.exif_photo
d['GPSInfo'] = e.exif_gpsinfo
d['Iop'] = e.exif_iop
kwargs['data'] = d
else: else:
kwargs['data'] = buffer kwargs['data'] = buffer
box = UUIDBox(**kwargs) box = UUIDBox(**kwargs)
return box return box
class Exif:
"""
Attributes
----------
buffer : bytes
Raw byte stream consisting of the UUID data.
endian : str
Either '<' for big-endian, or '>' for little-endian.
"""
def __init__(self, buffer):
"""Interpret raw buffer consisting of Exif IFD.
"""
self.exif_image = None
self.exif_photo = None
self.exif_gpsinfo = None
self.exif_iop = None
self.buffer = buffer
# Ignore the first six bytes.
# Next 8 should be (73, 73, 42, 8)
data = struct.unpack('<BBHI', buffer[6:14])
if data[0] == 73 and data[1] == 73:
# little endian
self.endian = '<'
else:
# big endian
self.endian = '>'
offset = data[3]
# This is the 'Exif Image' portion.
exif = ExifImageIfd(self.endian, buffer[6:], offset)
self.exif_image = exif.ifd
if 'ExifTag' in self.exif_image.keys():
offset = self.exif_image['ExifTag']
photo = ExifPhotoIfd(self.endian, buffer[6:], offset)
self.exif_photo = photo.ifd
if 'InteroperabilityTag' in self.exif_photo.keys():
offset = self.exif_photo['InteroperabilityTag']
interop = ExifInteroperabilityIfd(self.endian,
buffer[6:],
offset)
self.iop = interop.ifd
if 'GPSTag' in self.exif_image.keys():
offset = self.exif_image['GPSTag']
gps = ExifGPSInfoIfd(self.endian, buffer[6:], offset)
self.exif_gpsinfo = gps.ifd
class Ifd:
"""
Attributes
----------
buffer : bytes
Raw byte stream consisting of the UUID data.
datatype2fmt : dictionary
Class attribute, maps the TIFF enumerated datatype to the python
datatype and data width.
endian : str
Either '<' for big-endian, or '>' for little-endian.
num_tags : int
Number of tags in the IFD.
raw_ifd : dictionary
Maps tag number to "mildly-interpreted" tag value.
"""
datatype2fmt = {1: ('B', 1),
2: ('B', 1),
3: ('H', 2),
4: ('I', 4),
5: ('II', 8),
7: ('B', 1),
9: ('i', 4),
10: ('ii', 8)}
def __init__(self, endian, buffer, offset):
self.endian = endian
self.buffer = buffer
self.num_tags, = struct.unpack(endian + 'H',
buffer[offset:offset + 2])
fmt = self.endian + 'HHII' * self.num_tags
ifd_buffer = buffer[offset + 2:offset + 2 + self.num_tags * 12]
data = struct.unpack(fmt, ifd_buffer)
self.raw_ifd = {}
for j, tag in enumerate(data[0::4]):
# The offset to the tag offset/payload is the offset to the IFD
# plus 2 bytes for the number of tags plus 12 bytes for each
# tag entry plus 8 bytes to the offset/payload itself.
toffp = buffer[offset + 10 + j * 12:offset + 10 + j * 12 + 4]
tag_data = self.parse_tag(data[j * 4 + 1],
data[j * 4 + 2],
toffp)
self.raw_ifd[tag] = tag_data
def parse_tag(self, dtype, count, offset_buf):
"""Interpret an Exif image tag data payload.
"""
fmt = self.datatype2fmt[dtype][0] * count
payload_size = self.datatype2fmt[dtype][1] * count
if payload_size <= 4:
# Interpret the payload from the 4 bytes in the tag entry.
target_buffer = offset_buf[:payload_size]
else:
# Interpret the payload at the offset specified by the 4 bytes in
# the tag entry.
offset, = struct.unpack(self.endian + 'I', offset_buf)
target_buffer = self.buffer[offset:offset + payload_size]
if dtype == 2:
# ASCII
if sys.hexversion < 0x03000000:
payload = target_buffer.rstrip('\x00')
else:
payload = target_buffer.decode('utf-8').rstrip('\x00')
else:
payload = struct.unpack(self.endian + fmt, target_buffer)
if dtype == 5 or dtype == 10:
# Rational or Signed Rational. Construct the list of values.
rational_payload = []
for j in range(count):
value = float(payload[j * 2]) / float(payload[j * 2 + 1])
rational_payload.append(value)
payload = rational_payload
if count == 1:
# If just a single value, then return a scalar instead of a
# tuple.
payload = payload[0]
return payload
class ExifImageIfd(Ifd):
"""
Attributes
----------
tagnum2name : dict
Maps Exif image tag numbers to the tag names.
ifd : dict
Maps tag names to tag values.
"""
tagnum2name = {11: 'ProcessingSoftware',
254: 'NewSubfileType',
255: 'SubfileType',
256: 'ImageWidth',
257: 'ImageLength',
258: 'BitsPerSample',
259: 'Compression',
262: 'PhotometricInterpretation',
263: 'Threshholding',
264: 'CellWidth',
265: 'CellLength',
266: 'FillOrder',
269: 'DocumentName',
270: 'ImageDescription',
271: 'Make',
272: 'Model',
273: 'StripOffsets',
274: 'Orientation',
277: 'SamplesPerPixel',
278: 'RowsPerStrip',
279: 'StripByteCounts',
282: 'XResolution',
283: 'YResolution',
284: 'PlanarConfiguration',
290: 'GrayResponseUnit',
291: 'GrayResponseCurve',
292: 'T4Options',
293: 'T6Options',
296: 'ResolutionUnit',
301: 'TransferFunction',
305: 'Software',
306: 'DateTime',
315: 'Artist',
316: 'HostComputer',
317: 'Predictor',
318: 'WhitePoint',
319: 'PrimaryChromaticities',
320: 'ColorMap',
321: 'HalftoneHints',
322: 'TileWidth',
323: 'TileLength',
324: 'TileOffsets',
325: 'TileByteCounts',
330: 'SubIFDs',
332: 'InkSet',
333: 'InkNames',
334: 'NumberOfInks',
336: 'DotRange',
337: 'TargetPrinter',
338: 'ExtraSamples',
339: 'SampleFormat',
340: 'SMinSampleValue',
341: 'SMaxSampleValue',
342: 'TransferRange',
343: 'ClipPath',
344: 'XClipPathUnits',
345: 'YClipPathUnits',
346: 'Indexed',
347: 'JPEGTables',
351: 'OPIProxy',
512: 'JPEGProc',
513: 'JPEGInterchangeFormat',
514: 'JPEGInterchangeFormatLength',
515: 'JPEGRestartInterval',
517: 'JPEGLosslessPredictors',
518: 'JPEGPointTransforms',
519: 'JPEGQTables',
520: 'JPEGDCTables',
521: 'JPEGACTables',
529: 'YCbCrCoefficients',
530: 'YCbCrSubSampling',
531: 'YCbCrPositioning',
532: 'ReferenceBlackWhite',
700: 'XMLPacket',
18246: 'Rating',
18249: 'RatingPercent',
32781: 'ImageID',
33421: 'CFARepeatPatternDim',
33422: 'CFAPattern',
33423: 'BatteryLevel',
33432: 'Copyright',
33434: 'ExposureTime',
33437: 'FNumber',
33723: 'IPTCNAA',
34377: 'ImageResources',
34665: 'ExifTag',
34675: 'InterColorProfile',
34850: 'ExposureProgram',
34852: 'SpectralSensitivity',
34853: 'GPSTag',
34855: 'ISOSpeedRatings',
34856: 'OECF',
34857: 'Interlace',
34858: 'TimeZoneOffset',
34859: 'SelfTimerMode',
36867: 'DateTimeOriginal',
37122: 'CompressedBitsPerPixel',
37377: 'ShutterSpeedValue',
37378: 'ApertureValue',
37379: 'BrightnessValue',
37380: 'ExposureBiasValue',
37381: 'MaxApertureValue',
37382: 'SubjectDistance',
37383: 'MeteringMode',
37384: 'LightSource',
37385: 'Flash',
37386: 'FocalLength',
37387: 'FlashEnergy',
37388: 'SpatialFrequencyResponse',
37389: 'Noise',
37390: 'FocalPlaneXResolution',
37391: 'FocalPlaneYResolution',
37392: 'FocalPlaneResolutionUnit',
37393: 'ImageNumber',
37394: 'SecurityClassification',
37395: 'ImageHistory',
37396: 'SubjectLocation',
37397: 'ExposureIndex',
37398: 'TIFFEPStandardID',
37399: 'SensingMethod',
40091: 'XPTitle',
40092: 'XPComment',
40093: 'XPAuthor',
40094: 'XPKeywords',
40095: 'XPSubject',
50341: 'PrintImageMatching',
50706: 'DNGVersion',
50707: 'DNGBackwardVersion',
50708: 'UniqueCameraModel',
50709: 'LocalizedCameraModel',
50710: 'CFAPlaneColor',
50711: 'CFALayout',
50712: 'LinearizationTable',
50713: 'BlackLevelRepeatDim',
50714: 'BlackLevel',
50715: 'BlackLevelDeltaH',
50716: 'BlackLevelDeltaV',
50717: 'WhiteLevel',
50718: 'DefaultScale',
50719: 'DefaultCropOrigin',
50720: 'DefaultCropSize',
50721: 'ColorMatrix1',
50722: 'ColorMatrix2',
50723: 'CameraCalibration1',
50724: 'CameraCalibration2',
50725: 'ReductionMatrix1',
50726: 'ReductionMatrix2',
50727: 'AnalogBalance',
50728: 'AsShotNeutral',
50729: 'AsShotWhiteXY',
50730: 'BaselineExposure',
50731: 'BaselineNoise',
50732: 'BaselineSharpness',
50733: 'BayerGreenSplit',
50734: 'LinearResponseLimit',
50735: 'CameraSerialNumber',
50736: 'LensInfo',
50737: 'ChromaBlurRadius',
50738: 'AntiAliasStrength',
50739: 'ShadowScale',
50740: 'DNGPrivateData',
50741: 'MakerNoteSafety',
50778: 'CalibrationIlluminant1',
50779: 'CalibrationIlluminant2',
50780: 'BestQualityScale',
50781: 'RawDataUniqueID',
50827: 'OriginalRawFileName',
50828: 'OriginalRawFileData',
50829: 'ActiveArea',
50830: 'MaskedAreas',
50831: 'AsShotICCProfile',
50832: 'AsShotPreProfileMatrix',
50833: 'CurrentICCProfile',
50834: 'CurrentPreProfileMatrix',
50879: 'ColorimetricReference',
50931: 'CameraCalibrationSignature',
50932: 'ProfileCalibrationSignature',
50934: 'AsShotProfileName',
50935: 'NoiseReductionApplied',
50936: 'ProfileName',
50937: 'ProfileHueSatMapDims',
50938: 'ProfileHueSatMapData1',
50939: 'ProfileHueSatMapData2',
50940: 'ProfileToneCurve',
50941: 'ProfileEmbedPolicy',
50942: 'ProfileCopyright',
50964: 'ForwardMatrix1',
50965: 'ForwardMatrix2',
50966: 'PreviewApplicationName',
50967: 'PreviewApplicationVersion',
50968: 'PreviewSettingsName',
50969: 'PreviewSettingsDigest',
50970: 'PreviewColorSpace',
50971: 'PreviewDateTime',
50972: 'RawImageDigest',
50973: 'OriginalRawFileDigest',
50974: 'SubTileBlockSize',
50975: 'RowInterleaveFactor',
50981: 'ProfileLookTableDims',
50982: 'ProfileLookTableData',
51008: 'OpcodeList1',
51009: 'OpcodeList2',
51022: 'OpcodeList3',
51041: 'NoiseProfile'}
def __init__(self, endian, buffer, offset):
Ifd.__init__(self, endian, buffer, offset)
# Now post process the raw IFD.
self.ifd = {}
for tag, value in self.raw_ifd.items():
tag_name = self.tagnum2name[tag]
self.ifd[tag_name] = value
class ExifPhotoIfd(Ifd):
tagnum2name = {33434: 'ExposureTime',
33437: 'FNumber',
34850: 'ExposureProgram',
34852: 'SpectralSensitivity',
34855: 'ISOSpeedRatings',
34856: 'OECF',
34864: 'SensitivityType',
34865: 'StandardOutputSensitivity',
34866: 'RecommendedExposureIndex',
34867: 'ISOSpeed',
34868: 'ISOSpeedLatitudeyyy',
34869: 'ISOSpeedLatitudezzz',
36864: 'ExifVersion',
36867: 'DateTimeOriginal',
36868: 'DateTimeDigitized',
37121: 'ComponentsConfiguration',
37122: 'CompressedBitsPerPixel',
37377: 'ShutterSpeedValue',
37378: 'ApertureValue',
37379: 'BrightnessValue',
37380: 'ExposureBiasValue',
37381: 'MaxApertureValue',
37382: 'SubjectDistance',
37383: 'MeteringMode',
37384: 'LightSource',
37385: 'Flash',
37386: 'FocalLength',
37396: 'SubjectArea',
37500: 'MakerNote',
37510: 'UserComment',
37520: 'SubSecTime',
37521: 'SubSecTimeOriginal',
37522: 'SubSecTimeDigitized',
40960: 'FlashpixVersion',
40961: 'ColorSpace',
40962: 'PixelXDimension',
40963: 'PixelYDimension',
40964: 'RelatedSoundFile',
40965: 'InteroperabilityTag',
41483: 'FlashEnergy',
41484: 'SpatialFrequencyResponse',
41486: 'FocalPlaneXResolution',
41487: 'FocalPlaneYResolution',
41488: 'FocalPlaneResolutionUnit',
41492: 'SubjectLocation',
41493: 'ExposureIndex',
41495: 'SensingMethod',
41728: 'FileSource',
41729: 'SceneType',
41730: 'CFAPattern',
41985: 'CustomRendered',
41986: 'ExposureMode',
41987: 'WhiteBalance',
41988: 'DigitalZoomRatio',
41989: 'FocalLengthIn35mmFilm',
41990: 'SceneCaptureType',
41991: 'GainControl',
41992: 'Contrast',
41993: 'Saturation',
41994: 'Sharpness',
41995: 'DeviceSettingDescription',
41996: 'SubjectDistanceRange',
42016: 'ImageUniqueID',
42032: 'CameraOwnerName',
42033: 'BodySerialNumber',
42034: 'LensSpecification',
42035: 'LensMake',
42036: 'LensModel',
42037: 'LensSerialNumber'}
def __init__(self, endian, buffer, offset):
Ifd.__init__(self, endian, buffer, offset)
# Now post process the raw IFD.
self.ifd = {}
for tag, value in self.raw_ifd.items():
tag_name = self.tagnum2name[tag]
self.ifd[tag_name] = value
class ExifGPSInfoIfd(Ifd):
tagnum2name = {0: 'GPSVersionID',
1: 'GPSLatitudeRef',
2: 'GPSLatitude',
3: 'GPSLongitudeRef',
4: 'GPSLongitude',
5: 'GPSAltitudeRef',
6: 'GPSAltitude',
7: 'GPSTimeStamp',
8: 'GPSSatellites',
9: 'GPSStatus',
10: 'GPSMeasureMode',
11: 'GPSDOP',
12: 'GPSSpeedRef',
13: 'GPSSpeed',
14: 'GPSTrackRef',
15: 'GPSTrack',
16: 'GPSImgDirectionRef',
17: 'GPSImgDirection',
18: 'GPSMapDatum',
19: 'GPSDestLatitudeRef',
20: 'GPSDestLatitude',
21: 'GPSDestLongitudeRef',
22: 'GPSDestLongitude',
23: 'GPSDestBearingRef',
24: 'GPSDestBearing',
25: 'GPSDestDistanceRef',
26: 'GPSDestDistance',
27: 'GPSProcessingMethod',
28: 'GPSAreaInformation',
29: 'GPSDateStamp',
30: 'GPSDifferential'}
def __init__(self, endian, buffer, offset):
Ifd.__init__(self, endian, buffer, offset)
# Now post process the raw IFD.
self.ifd = {}
for tag, value in self.raw_ifd.items():
tag_name = self.tagnum2name[tag]
self.ifd[tag_name] = value
class ExifInteroperabilityIfd(Ifd):
tagnum2name = {1: 'InteroperabilityIndex',
2: 'InteroperabilityVersion',
4096: 'RelatedImageFileFormat',
4097: 'RelatedImageWidth',
4098: 'RelatedImageLength'}
def __init__(self, endian, buffer, offset):
Ifd.__init__(self, endian, buffer, offset)
# Now post process the raw IFD.
self.ifd = {}
for tag, value in self.raw_ifd.items():
tag_name = self.tagnum2name[tag]
self.ifd[tag_name] = value
# Map each box ID to the corresponding class. # Map each box ID to the corresponding class.
_box_with_id = { _box_with_id = {
'asoc': AssociationBox, 'asoc': AssociationBox,
@ -1647,6 +2161,7 @@ _box_with_id = {
'uuid': UUIDBox, 'uuid': UUIDBox,
'xml ': XMLBox} 'xml ': XMLBox}
def _indent(elem, level=0): def _indent(elem, level=0):
"""Recipe for pretty printing XML. Please see """Recipe for pretty printing XML. Please see
@ -1666,6 +2181,7 @@ def _indent(elem, level=0):
if level and (not elem.tail or not elem.tail.strip()): if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i elem.tail = i
def _pretty_print_xml(xml, level=0): def _pretty_print_xml(xml, level=0):
"""Pretty print XML data. """Pretty print XML data.
""" """

View file

@ -52,6 +52,7 @@ class TestJp2k(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
# Setup a JP2 file with a bad XML box.
jp2file = pkg_resources.resource_filename(glymur.__name__, jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2") "data/nemo.jp2")
with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile: with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile:
@ -646,6 +647,16 @@ class TestJp2k(unittest.TestCase):
with self.assertWarns(UserWarning) as cw: with self.assertWarns(UserWarning) as cw:
imp.reload(glymur) imp.reload(glymur)
def test_xmp_attribute(self):
# Verify that we can read the XMP packet in our shipping example file.
j = Jp2k(self.jp2file)
xmp = j.box[4].data
ns0 = '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}'
ns1 = '{http://ns.adobe.com/xap/1.0/}'
name = '{0}RDF/{0}Description'.format(ns0)
elt = xmp.find(name)
attr_value = elt.attrib['{0}CreatorTool'.format(ns1)]
self.assertEqual(attr_value, 'glymur')
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -3,6 +3,7 @@ The tests defined here roughly correspond to what is in the OpenJPEG test
suite. suite.
""" """
from contextlib import contextmanager
import os import os
import platform import platform
import re import re
@ -11,6 +12,13 @@ from xml.etree import cElementTree as ET
import unittest import unittest
import warnings import warnings
if sys.hexversion <= 0x03030000:
from mock import patch
from StringIO import StringIO
else:
from unittest.mock import patch
from io import StringIO
import numpy as np import numpy as np
from glymur import Jp2k from glymur import Jp2k
@ -263,6 +271,7 @@ class TestSuite(unittest.TestCase):
np.testing.assert_array_equal(jpdata, pgxdata) np.testing.assert_array_equal(jpdata, pgxdata)
@unittest.skip("fprintf stderr output in r2343.")
def test_ETS_C0P0_p0_12_j2k(self): def test_ETS_C0P0_p0_12_j2k(self):
jfile = os.path.join(data_root, 'input/conformance/p0_12.j2k') jfile = os.path.join(data_root, 'input/conformance/p0_12.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -622,6 +631,7 @@ class TestSuite(unittest.TestCase):
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
np.testing.assert_array_equal(jpdata, pgxdata) np.testing.assert_array_equal(jpdata, pgxdata)
@unittest.skip("fprintf stderr output in r2343.")
def test_ETS_C1P0_p0_12_j2k(self): def test_ETS_C1P0_p0_12_j2k(self):
jfile = os.path.join(data_root, 'input/conformance/p0_12.j2k') jfile = os.path.join(data_root, 'input/conformance/p0_12.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -631,6 +641,7 @@ class TestSuite(unittest.TestCase):
pgxdata = read_pgx(pgxfile) pgxdata = read_pgx(pgxfile)
np.testing.assert_array_equal(jpdata, pgxdata) np.testing.assert_array_equal(jpdata, pgxdata)
@unittest.skip("fprintf stderr output in r2343.")
def test_ETS_C1P0_p0_13_j2k(self): def test_ETS_C1P0_p0_13_j2k(self):
jfile = os.path.join(data_root, 'input/conformance/p0_13.j2k') jfile = os.path.join(data_root, 'input/conformance/p0_13.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -750,6 +761,7 @@ class TestSuite(unittest.TestCase):
self.assertTrue(peak_tolerance(jpdata, pgxdata) < 624) self.assertTrue(peak_tolerance(jpdata, pgxdata) < 624)
self.assertTrue(mse(jpdata, pgxdata) < 3080) self.assertTrue(mse(jpdata, pgxdata) < 3080)
@unittest.skip("fprintf stderr output in r2343.")
def test_ETS_C1P1_p1_05_j2k(self): def test_ETS_C1P1_p1_05_j2k(self):
jfile = os.path.join(data_root, 'input/conformance/p1_05.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_05.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -770,6 +782,7 @@ class TestSuite(unittest.TestCase):
self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 40) self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 40)
self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 10.154) self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 10.154)
@unittest.skip("fprintf stderr output in r2343.")
def test_ETS_C1P1_p1_06_j2k(self): def test_ETS_C1P1_p1_06_j2k(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -790,6 +803,7 @@ class TestSuite(unittest.TestCase):
self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 2) self.assertTrue(peak_tolerance(jpdata[:, :, 2], pgxdata) < 2)
self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 0.6) self.assertTrue(mse(jpdata[:, :, 2], pgxdata) < 0.6)
@unittest.skip("fprintf stderr output in r2343.")
def test_ETS_C1P1_p1_07_j2k(self): def test_ETS_C1P1_p1_07_j2k(self):
jfile = os.path.join(data_root, 'input/conformance/p1_07.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_07.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -4313,6 +4327,7 @@ class TestSuite(unittest.TestCase):
# Comment value # Comment value
self.assertEqual(c.segment[4].Ccme.decode('latin-1'), "Kakadu-v6.3.1") self.assertEqual(c.segment[4].Ccme.decode('latin-1'), "Kakadu-v6.3.1")
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_illegalcolortransform_dump(self): def test_NR_illegalcolortransform_dump(self):
jfile = os.path.join(data_root, jfile = os.path.join(data_root,
'input/nonregression/illegalcolortransform.j2k') 'input/nonregression/illegalcolortransform.j2k')
@ -6690,6 +6705,7 @@ class TestSuite(unittest.TestCase):
data = jp2.read() data = jp2.read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_123_j2c_3_decode(self): def test_NR_DEC_123_j2c_3_decode(self):
jfile = os.path.join(data_root, jfile = os.path.join(data_root,
'input/nonregression/123.j2c') 'input/nonregression/123.j2c')
@ -6735,6 +6751,7 @@ class TestSuite(unittest.TestCase):
data = Jp2k(jfile).read() data = Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_bug_j2c_8_decode(self): def test_NR_DEC_bug_j2c_8_decode(self):
jfile = os.path.join(data_root, jfile = os.path.join(data_root,
'input/nonregression/bug.j2c') 'input/nonregression/bug.j2c')
@ -6771,6 +6788,7 @@ class TestSuite(unittest.TestCase):
data = Jp2k(jfile).read() data = Jp2k(jfile).read()
self.assertTrue(True) self.assertTrue(True)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_illegalcolortransform_j2k_14_decode(self): def test_NR_DEC_illegalcolortransform_j2k_14_decode(self):
# Stream too short, expected SOT. # Stream too short, expected SOT.
jfile = os.path.join(data_root, jfile = os.path.join(data_root,
@ -7102,6 +7120,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read(reduce=1) odata = jp2k.read(reduce=1)
np.testing.assert_array_equal(tdata, odata[64:128, 256:320]) np.testing.assert_array_equal(tdata, odata[64:128, 256:320])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_61_decode(self): def test_NR_DEC_p1_06_j2k_61_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7109,6 +7128,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[0:12, 0:12]) np.testing.assert_array_equal(ssdata, odata[0:12, 0:12])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_62_decode(self): def test_NR_DEC_p1_06_j2k_62_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7116,6 +7136,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[1:8, 8:11]) np.testing.assert_array_equal(ssdata, odata[1:8, 8:11])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_63_decode(self): def test_NR_DEC_p1_06_j2k_63_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7123,6 +7144,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[9:12, 9:12]) np.testing.assert_array_equal(ssdata, odata[9:12, 9:12])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_64_decode(self): def test_NR_DEC_p1_06_j2k_64_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7130,6 +7152,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[10:12, 4:10]) np.testing.assert_array_equal(ssdata, odata[10:12, 4:10])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_65_decode(self): def test_NR_DEC_p1_06_j2k_65_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7137,6 +7160,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[3:9, 3:9]) np.testing.assert_array_equal(ssdata, odata[3:9, 3:9])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_66_decode(self): def test_NR_DEC_p1_06_j2k_66_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7144,6 +7168,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[4:7, 4:7]) np.testing.assert_array_equal(ssdata, odata[4:7, 4:7])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_67_decode(self): def test_NR_DEC_p1_06_j2k_67_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7151,6 +7176,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read() odata = jp2k.read()
np.testing.assert_array_equal(ssdata, odata[4:5, 4: 5]) np.testing.assert_array_equal(ssdata, odata[4:5, 4: 5])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_68_decode(self): def test_NR_DEC_p1_06_j2k_68_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7158,6 +7184,7 @@ class TestSuite(unittest.TestCase):
odata = jp2k.read(reduce=1) odata = jp2k.read(reduce=1)
np.testing.assert_array_equal(ssdata, odata[0:6, 0:6]) np.testing.assert_array_equal(ssdata, odata[0:6, 0:6])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_69_decode(self): def test_NR_DEC_p1_06_j2k_69_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7201,6 +7228,7 @@ class TestSuite(unittest.TestCase):
with self.assertRaises((IOError, OSError)) as ce: with self.assertRaises((IOError, OSError)) as ce:
ssdata = jp2k.read(area=(9, 9, 12, 12), reduce=2) ssdata = jp2k.read(area=(9, 9, 12, 12), reduce=2)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_76_decode(self): def test_NR_DEC_p1_06_j2k_76_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7210,6 +7238,7 @@ class TestSuite(unittest.TestCase):
tiledata = jp2k.read(tile=0) tiledata = jp2k.read(tile=0)
np.testing.assert_array_equal(tiledata, fulldata[0:3, 0:3]) np.testing.assert_array_equal(tiledata, fulldata[0:3, 0:3])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_77_decode(self): def test_NR_DEC_p1_06_j2k_77_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7219,6 +7248,7 @@ class TestSuite(unittest.TestCase):
tiledata = jp2k.read(tile=5) tiledata = jp2k.read(tile=5)
np.testing.assert_array_equal(tiledata, fulldata[3:6, 3:6]) np.testing.assert_array_equal(tiledata, fulldata[3:6, 3:6])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_78_decode(self): def test_NR_DEC_p1_06_j2k_78_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7228,6 +7258,7 @@ class TestSuite(unittest.TestCase):
tiledata = jp2k.read(tile=9) tiledata = jp2k.read(tile=9)
np.testing.assert_array_equal(tiledata, fulldata[6:9, 3:6]) np.testing.assert_array_equal(tiledata, fulldata[6:9, 3:6])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_79_decode(self): def test_NR_DEC_p1_06_j2k_79_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
@ -7237,6 +7268,7 @@ class TestSuite(unittest.TestCase):
tiledata = jp2k.read(tile=15) tiledata = jp2k.read(tile=15)
np.testing.assert_array_equal(tiledata, fulldata[9:12, 9:12]) np.testing.assert_array_equal(tiledata, fulldata[9:12, 9:12])
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_80_decode(self): def test_NR_DEC_p1_06_j2k_80_decode(self):
# Just read the data, don't bother verifying. # Just read the data, don't bother verifying.
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
@ -7245,6 +7277,7 @@ class TestSuite(unittest.TestCase):
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
tiledata = jp2k.read(tile=0, reduce=2) tiledata = jp2k.read(tile=0, reduce=2)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_81_decode(self): def test_NR_DEC_p1_06_j2k_81_decode(self):
# Just read the data, don't bother verifying. # Just read the data, don't bother verifying.
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
@ -7253,6 +7286,7 @@ class TestSuite(unittest.TestCase):
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
tiledata = jp2k.read(tile=5, reduce=2) tiledata = jp2k.read(tile=5, reduce=2)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_82_decode(self): def test_NR_DEC_p1_06_j2k_82_decode(self):
# Just read the data, don't bother verifying. # Just read the data, don't bother verifying.
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
@ -7261,6 +7295,7 @@ class TestSuite(unittest.TestCase):
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
tiledata = jp2k.read(tile=9, reduce=2) tiledata = jp2k.read(tile=9, reduce=2)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_83_decode(self): def test_NR_DEC_p1_06_j2k_83_decode(self):
# tile size is 3x3. Reducing two levels results in no data. # tile size is 3x3. Reducing two levels results in no data.
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
@ -7270,11 +7305,12 @@ class TestSuite(unittest.TestCase):
with self.assertRaises((IOError, OSError)) as ce: with self.assertRaises((IOError, OSError)) as ce:
tiledata = jp2k.read(tile=15, reduce=2) tiledata = jp2k.read(tile=15, reduce=2)
@unittest.skip("fprintf stderr output in r2343.")
def test_NR_DEC_p1_06_j2k_84_decode(self): def test_NR_DEC_p1_06_j2k_84_decode(self):
# Just read the data, don't bother verifying. # Just read the data, don't bother verifying.
jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k') jfile = os.path.join(data_root, 'input/conformance/p1_06.j2k')
jp2k = Jp2k(jfile) jp2k = Jp2k(jfile)
tiledata = jp2k.read(reduce=4) data = jp2k.read(reduce=4)
def test_NR_DEC_p0_04_j2k_85_decode(self): def test_NR_DEC_p0_04_j2k_85_decode(self):
jfile = os.path.join(data_root, 'input/conformance/p0_04.j2k') jfile = os.path.join(data_root, 'input/conformance/p0_04.j2k')

View file

@ -11,6 +11,7 @@ else:
from io import StringIO from io import StringIO
import glymur import glymur
from glymur import Jp2k
try: try:
data_root = os.environ['OPJ_DATA_ROOT'] data_root = os.environ['OPJ_DATA_ROOT']
@ -22,6 +23,22 @@ except:
class TestPrinting(unittest.TestCase): class TestPrinting(unittest.TestCase):
@classmethod
def setUpClass(cls):
# Setup a plain JP2 file without the two UUID boxes.
jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile:
cls._plain_nemo_file = tfile.name
ijfile = Jp2k(jp2file)
data = ijfile.read(reduce=3)
ojfile = Jp2k(cls._plain_nemo_file, 'wb')
ojfile.write(data)
@classmethod
def tearDownClass(cls):
os.unlink(cls._plain_nemo_file)
def setUp(self): def setUp(self):
# Save sys.stdout. # Save sys.stdout.
self.stdout = sys.stdout self.stdout = sys.stdout
@ -30,15 +47,14 @@ class TestPrinting(unittest.TestCase):
"data/nemo.jp2") "data/nemo.jp2")
# Save the output of dumping nemo.jp2 for more than one test. # Save the output of dumping nemo.jp2 for more than one test.
lines = ['File: nemo.jp2', lines = ['JPEG 2000 Signature Box (jP ) @ (0, 12)',
'JPEG 2000 Signature Box (jP ) @ (0, 12)',
' Signature: 0d0a870a', ' Signature: 0d0a870a',
'File Type Box (ftyp) @ (12, 20)', 'File Type Box (ftyp) @ (12, 20)',
' Brand: jp2 ', ' Brand: jp2 ',
" Compatibility: ['jp2 ']", " Compatibility: ['jp2 ']",
'JP2 Header Box (jp2h) @ (32, 45)', 'JP2 Header Box (jp2h) @ (32, 45)',
' Image Header Box (ihdr) @ (40, 22)', ' Image Header Box (ihdr) @ (40, 22)',
' Size: [1456 2592 3]', ' Size: [182 324 3]',
' Bitdepth: 8', ' Bitdepth: 8',
' Signed: False', ' Signed: False',
' Compression: wavelet', ' Compression: wavelet',
@ -47,45 +63,29 @@ class TestPrinting(unittest.TestCase):
' Method: enumerated colorspace', ' Method: enumerated colorspace',
' Precedence: 0', ' Precedence: 0',
' Colorspace: sRGB', ' Colorspace: sRGB',
'UUID Box (uuid) @ (77, 638)', 'Contiguous Codestream Box (jp2c) @ (77, 112814)',
' UUID: 4a706754-6966-6645-7869-662d3e4a5032',
' UUID Data: 614 bytes',
'UUID Box (uuid) @ (715, 2412)',
' UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)',
' UUID Data: ',
' <ns0:xmpmeta xmlns:ns0="adobe:ns:meta/" '
+ 'xmlns:ns2="http://ns.adobe.com/xap/1.0/" '
+ 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" '
+ 'ns0:xmptk="XMP Core 4.4.0-Exiv2">',
' <rdf:RDF>',
' <rdf:Description ns2:CreatorTool="glymur" '
+ 'rdf:about="" />',
' </rdf:RDF>',
' </ns0:xmpmeta>',
' ',
'Contiguous Codestream Box (jp2c) @ (3127, 1133427)',
' Main header:', ' Main header:',
' SOC marker segment @ (3135, 0)', ' SOC marker segment @ (85, 0)',
' SIZ marker segment @ (3137, 47)', ' SIZ marker segment @ (87, 47)',
' Profile: 2', ' Profile: 2',
' Reference Grid Height, Width: (1456 x 2592)', ' Reference Grid Height, Width: (182 x 324)',
' Vertical, Horizontal Reference Grid Offset: ' ' Vertical, Horizontal Reference Grid Offset: '
+ '(0 x 0)', + '(0 x 0)',
' Reference Tile Height, Width: (512 x 512)', ' Reference Tile Height, Width: (182 x 324)',
' Vertical, Horizontal Reference Tile Offset: ' ' Vertical, Horizontal Reference Tile Offset: '
+ '(0 x 0)', + '(0 x 0)',
' Bitdepth: (8, 8, 8)', ' Bitdepth: (8, 8, 8)',
' Signed: (False, False, False)', ' Signed: (False, False, False)',
' Vertical, Horizontal Subsampling: ' ' Vertical, Horizontal Subsampling: '
+ '((1, 1), (1, 1), (1, 1))', + '((1, 1), (1, 1), (1, 1))',
' COD marker segment @ (3186, 12)', ' COD marker segment @ (136, 12)',
' Coding style:', ' Coding style:',
' Entropy coder, without partitions', ' Entropy coder, without partitions',
' SOP marker segments: False', ' SOP marker segments: False',
' EPH marker segments: False', ' EPH marker segments: False',
' Coding style parameters:', ' Coding style parameters:',
' Progression order: LRCP', ' Progression order: LRCP',
' Number of layers: 3', ' Number of layers: 1',
' Multiple component transformation usage: ' ' Multiple component transformation usage: '
+ 'reversible', + 'reversible',
' Number of resolutions: 6', ' Number of resolutions: 6',
@ -102,26 +102,29 @@ class TestPrinting(unittest.TestCase):
+ 'False', + 'False',
' Predictable termination: False', ' Predictable termination: False',
' Segmentation symbols: False', ' Segmentation symbols: False',
' QCD marker segment @ (3200, 19)', ' QCD marker segment @ (150, 19)',
' Quantization style: no quantization, ' ' Quantization style: no quantization, '
+ '2 guard bits', + '2 guard bits',
' Step size: [(0, 8), (0, 9), (0, 9), ' ' Step size: [(0, 8), (0, 9), (0, 9), '
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' + '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
+ '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), ' + '(0, 10), (0, 9), (0, 9), (0, 10), (0, 9), (0, 9), '
+ '(0, 10)]'] + '(0, 10)]']
self.expectedNemo = '\n'.join(lines) self.expectedPlain = '\n'.join(lines)
def tearDown(self): def tearDown(self):
# Restore stdout. # Restore stdout.
sys.stdout = self.stdout sys.stdout = self.stdout
#import pdb; pdb.set_trace()
def test_jp2dump(self): def test_jp2dump(self):
glymur.jp2dump(self.jp2file) glymur.jp2dump(self._plain_nemo_file)
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
self.actual = actual
self.expected = self.expectedNemo # Get rid of the filename line, as it is not set in stone.
self.assertEqual(actual, self.expectedNemo) lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, self.expectedPlain)
def test_COC_segment(self): def test_COC_segment(self):
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
@ -429,17 +432,42 @@ class TestPrinting(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_entire_file(self): def test_xmp(self):
# Verify the printing of a UUID/XMP box.
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
print(j.box[4])
actual = sys.stdout.getvalue().strip()
lst = ['UUID Box (uuid) @ (715, 2412)',
' UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)',
' UUID Data: ',
' <ns0:xmpmeta xmlns:ns0="adobe:ns:meta/" '
+ 'xmlns:ns2="http://ns.adobe.com/xap/1.0/" '
+ 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" '
+ 'ns0:xmptk="XMP Core 4.4.0-Exiv2">',
' <rdf:RDF>',
' <rdf:Description ns2:CreatorTool="glymur" '
+ 'rdf:about="" />',
' </rdf:RDF>',
' </ns0:xmpmeta>']
expected = '\n'.join(lst)
self.assertEqual(actual, expected)
def test_entire_file(self):
j = glymur.Jp2k(self._plain_nemo_file)
print(j) print(j)
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
self.assertEqual(actual, self.expectedNemo)
# Get rid of the filename line, as it is not set in stone.
lst = actual.split('\n')
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, self.expectedPlain)
def test_codestream(self): def test_codestream(self):
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
print(j.get_codestream()) print(j.get_codestream())
actual = sys.stdout.getvalue().strip() actual = sys.stdout.getvalue().strip()
lst = ['Codestream:', lst = ['Codestream:',
' SOC marker segment @ (3135, 0)', ' SOC marker segment @ (3135, 0)',
' SIZ marker segment @ (3137, 47)', ' SIZ marker segment @ (3137, 47)',
@ -782,5 +810,63 @@ class TestPrinting(unittest.TestCase):
expected = '\n'.join(lines) expected = '\n'.join(lines)
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_exif_uuid(self):
j = glymur.Jp2k(self.jp2file)
print(j.box[3])
actual = sys.stdout.getvalue().strip()
lines = ["UUID Box (uuid) @ (77, 638)",
" UUID: 4a706754-6966-6645-7869-662d3e4a5032 (Exif)",
" UUID Data: ",
"{'Exif': {'ExifTag': 138,",
" 'GPSTag': 354,",
" 'Make': 'HTC',",
" 'Model': 'HTC Glacier',",
" 'ResolutionUnit': 2,",
" 'XResolution': 72.0,",
" 'YCbCrPositioning': 1,",
" 'YResolution': 72.0},",
" 'GPSInfo': {'GPSAltitude': 0.0,",
" 'GPSAltitudeRef': 0,",
" 'GPSDateStamp': '2013:02:09',",
" 'GPSLatitude': [42.0, 20.0, 33.61],",
" 'GPSLatitudeRef': 'N',",
" 'GPSLongitude': [71.0, 5.0, 17.32],",
" 'GPSLongitudeRef': 'W',",
" 'GPSMapDatum': 'WGS-84',",
" 'GPSProcessingMethod': (65,",
" 83,",
" 67,",
" 73,",
" 73,",
" 0,",
" 0,",
" 0,",
" 78,",
" 69,",
" 84,",
" 87,",
" 79,",
" 82,",
" 75),",
" 'GPSTimeStamp': [19.0, 47.0, 53.0],",
" 'GPSVersionID': (2, 2, 0)},",
" 'Iop': None,",
" 'Photo': {'ColorSpace': 1,",
" 'ComponentsConfiguration': (1, 2, 3, 0),",
" 'DateTimeDigitized': '2013:02:09 14:47:53',",
" 'DateTimeOriginal': '2013:02:09 14:47:53',",
" 'ExifVersion': (48, 50, 50, 48),",
" 'FlashpixVersion': (48, 49, 48, 48),",
" 'FocalLength': 3.53,",
" 'ISOSpeedRatings': 76,",
" 'InteroperabilityTag': 324,",
" 'PixelXDimension': 2528,",
" 'PixelYDimension': 1424}}"]
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -1,7 +1,7 @@
from distutils.core import setup from distutils.core import setup
kwargs = {'name': 'Glymur', kwargs = {'name': 'Glymur',
'version': '0.1.3p1', 'version': '0.1.4',
'description': 'Tools for accessing JPEG2000 files', 'description': 'Tools for accessing JPEG2000 files',
'long_description': open('README.md').read(), 'long_description': open('README.md').read(),
'author': 'John Evans', 'author': 'John Evans',