Merge branch 'issue104' of https://github.com/quintusdias/glymur into issue104

Conflicts:
	glymur/_uuid_io/Exif.py
	glymur/_uuid_io/__init__.py
	glymur/jp2box.py
	glymur/test/test_jp2box_uuid.py
	glymur/test/test_jp2k.py
This commit is contained in:
jevans 2014-01-25 15:43:03 -05:00
commit 84c50c8499
11 changed files with 219 additions and 193 deletions

View file

@ -3,6 +3,7 @@
Handlers for Exif UUIDs. Be nice if we would find a standard for this. Handlers for Exif UUIDs. Be nice if we would find a standard for this.
""" """
import pprint import pprint
import re
import struct import struct
import sys import sys
import warnings import warnings

View file

@ -663,7 +663,7 @@ class Codestream(object):
bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3]) bitdepth = tuple(((x & 0x7f) + 1) for x in data[0::3])
signed = tuple(((x & 0xb0) > 0) for x in data[0::3]) signed = tuple(((x & 0xb0) > 0) for x in data[0::3])
xrsiz = data[1::3] xrsiz = data[1::3]
yrsiz = data[2::3] yrsiz = data[2::3]
@ -1529,7 +1529,7 @@ class SIZsegment(Segment):
signed=self.signed, signed=self.signed,
xyrsiz=(self.xrsiz, self.yrsiz)) xyrsiz=(self.xrsiz, self.yrsiz))
return msg return msg
def __str__(self): def __str__(self):
msg = Segment.__str__(self) msg = Segment.__str__(self)
msg += '\n ' msg += '\n '

View file

@ -19,7 +19,6 @@ import os
import pprint import pprint
import struct import struct
import sys import sys
import traceback
import uuid import uuid
import warnings import warnings
import xml.etree.cElementTree as ET import xml.etree.cElementTree as ET
@ -563,11 +562,11 @@ class CodestreamHeaderBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
def __init__(self, box=[], length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jpch', longname='Codestream Header') Jp2kBox.__init__(self, box_id='jpch', longname='Codestream Header')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box self.box = box if box is not None else []
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.CodestreamHeaderBox(box={0})".format(self.box) msg = "glymur.jp2box.CodestreamHeaderBox(box={0})".format(self.box)
@ -625,12 +624,12 @@ class CompositingLayerHeaderBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
def __init__(self, box=[], length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jplh', Jp2kBox.__init__(self, box_id='jplh',
longname='Compositing Layer Header') longname='Compositing Layer Header')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = [] self.box = box if box is not None else []
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.CompositingLayerHeaderBox(box={0})" msg = "glymur.jp2box.CompositingLayerHeaderBox(box={0})"
@ -1073,11 +1072,11 @@ class AssociationBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
def __init__(self, box=[], length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='asoc', longname='Association') Jp2kBox.__init__(self, box_id='asoc', longname='Association')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box self.box = box if box is not None else []
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.AssociationBox(box={0})".format(self.box) msg = "glymur.jp2box.AssociationBox(box={0})".format(self.box)
@ -1135,11 +1134,11 @@ class JP2HeaderBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
def __init__(self, box=[], length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header') Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box self.box = box if box is not None else []
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.JP2HeaderBox(box={0})".format(self.box) msg = "glymur.jp2box.JP2HeaderBox(box={0})".format(self.box)
@ -1621,11 +1620,11 @@ class ResolutionBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
def __init__(self, box=[], length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='res ', longname='Resolution') Jp2kBox.__init__(self, box_id='res ', longname='Resolution')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box self.box = box if box is not None else []
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.ResolutionBox(box={0})" msg = "glymur.jp2box.ResolutionBox(box={0})"
@ -2040,11 +2039,11 @@ class UUIDInfoBox(Jp2kBox):
box : list box : list
List of boxes contained in this superbox. List of boxes contained in this superbox.
""" """
def __init__(self, box=[], length=0, offset=-1): def __init__(self, box=None, length=0, offset=-1):
Jp2kBox.__init__(self, box_id='uinf', longname='UUIDInfo') Jp2kBox.__init__(self, box_id='uinf', longname='UUIDInfo')
self.length = length self.length = length
self.offset = offset self.offset = offset
self.box = box self.box = box if box is not None else []
def __repr__(self): def __repr__(self):
msg = "glymur.jp2box.UUIDInfoBox(box={0})".format(self.box) msg = "glymur.jp2box.UUIDInfoBox(box={0})".format(self.box)

View file

@ -484,17 +484,17 @@ class Jp2k(Jp2kBox):
stack.callback(opj2.image_destroy, image) stack.callback(opj2.image_destroy, image)
_populate_image_struct(cparams, image, img_array) _populate_image_struct(cparams, image, img_array)
codec = opj2.create_compress(cparams.codec_fmt) codec = opj2.create_compress(cparams.codec_fmt)
stack.callback(opj2.destroy_codec, codec) stack.callback(opj2.destroy_codec, codec)
info_handler = _INFO_CALLBACK if verbose else None info_handler = _INFO_CALLBACK if verbose else None
opj2.set_info_handler(codec, info_handler) opj2.set_info_handler(codec, info_handler)
opj2.set_warning_handler(codec, _WARNING_CALLBACK) opj2.set_warning_handler(codec, _WARNING_CALLBACK)
opj2.set_error_handler(codec, _ERROR_CALLBACK) opj2.set_error_handler(codec, _ERROR_CALLBACK)
opj2.setup_encoder(codec, cparams, image) opj2.setup_encoder(codec, cparams, image)
if _OPENJP2_IS_OFFICIAL_V2: if _OPENJP2_IS_OFFICIAL_V2:
fptr = libc.fopen(self.filename, 'wb') fptr = libc.fopen(self.filename, 'wb')
strm = opj2.stream_create_default_file_stream(fptr, False) strm = opj2.stream_create_default_file_stream(fptr, False)
@ -505,11 +505,11 @@ class Jp2k(Jp2kBox):
strm = opj2.stream_create_default_file_stream_v3(self.filename, strm = opj2.stream_create_default_file_stream_v3(self.filename,
False) False)
stack.callback(opj2.stream_destroy_v3, strm) stack.callback(opj2.stream_destroy_v3, strm)
opj2.start_compress(codec, image, strm) opj2.start_compress(codec, image, strm)
opj2.encode(codec, strm) opj2.encode(codec, strm)
opj2.end_compress(codec, strm) opj2.end_compress(codec, strm)
# Refresh the metadata. # Refresh the metadata.
self.parse() self.parse()

View file

@ -11,8 +11,8 @@ import numpy as np
from .config import glymur_config from .config import glymur_config
_, OPENJPEG = glymur_config() _, OPENJPEG = glymur_config()
# Maximum number of tile parts expected by JPWL: increase at your will # Maximum number of tile parts expected by JPWL: increase at your will
JPWL_MAX_NO_TILESPECS = 16 JPWL_MAX_NO_TILESPECS = 16
J2K_MAXRLVLS = 33 # Number of maximum resolution level authorized J2K_MAXRLVLS = 33 # Number of maximum resolution level authorized
PATH_LEN = 4096 # maximum allowed size for filenames PATH_LEN = 4096 # maximum allowed size for filenames
@ -58,7 +58,7 @@ class CommonStructType(ctypes.Structure):
("mj2_handle", ctypes.c_void_p)] ("mj2_handle", ctypes.c_void_p)]
STREAM_READ = 0x0001 # The stream was opened for reading. STREAM_READ = 0x0001 # The stream was opened for reading.
STREAM_WRITE = 0x0002 # The stream was opened for writing. STREAM_WRITE = 0x0002 # The stream was opened for writing.
class CioType(ctypes.Structure): class CioType(ctypes.Structure):
"""Byte input-output stream (CIO) """Byte input-output stream (CIO)
@ -81,7 +81,7 @@ class CioType(ctypes.Structure):
class CompressionInfoType(CommonStructType): class CompressionInfoType(CommonStructType):
"""Common fields between JPEG-2000 compression and decompression contexts. """Common fields between JPEG-2000 compression and decompression contexts.
This is for compression contexts. Corresponds to common_struct_t. This is for compression contexts. Corresponds to common_struct_t.
""" """
pass pass
@ -91,68 +91,68 @@ class PocType(ctypes.Structure):
"""Progression order changes.""" """Progression order changes."""
_fields_ = [("resno", ctypes.c_int), _fields_ = [("resno", ctypes.c_int),
# Resolution num start, Component num start, given by POC # Resolution num start, Component num start, given by POC
("compno0", ctypes.c_int), ("compno0", ctypes.c_int),
# Layer num end,Resolution num end, Component num end, given by POC # Layer num end,Resolution num end, Component num end, given by POC
("layno1", ctypes.c_int), ("layno1", ctypes.c_int),
("resno1", ctypes.c_int), ("resno1", ctypes.c_int),
("compno1", ctypes.c_int), ("compno1", ctypes.c_int),
# Layer num start,Precinct num start, Precinct num end # Layer num start,Precinct num start, Precinct num end
("layno0", ctypes.c_int), ("layno0", ctypes.c_int),
("precno0", ctypes.c_int), ("precno0", ctypes.c_int),
("precno1", ctypes.c_int), ("precno1", ctypes.c_int),
# Progression order enum # Progression order enum
# OPJ_PROG_ORDER prg1,prg; # OPJ_PROG_ORDER prg1,prg;
("prg1", ctypes.c_int), ("prg1", ctypes.c_int),
("prg", ctypes.c_int), ("prg", ctypes.c_int),
# Progression order string # Progression order string
# char progorder[5]; # char progorder[5];
("progorder", ctypes.c_char * 5), ("progorder", ctypes.c_char * 5),
# Tile number # Tile number
# int tile; # int tile;
("tile", ctypes.c_int), ("tile", ctypes.c_int),
# /** Start and end values for Tile width and height*/ # /** Start and end values for Tile width and height*/
# int tx0,tx1,ty0,ty1; # int tx0,tx1,ty0,ty1;
("tx0", ctypes.c_int), ("tx0", ctypes.c_int),
("tx1", ctypes.c_int), ("tx1", ctypes.c_int),
("ty0", ctypes.c_int), ("ty0", ctypes.c_int),
("ty1", ctypes.c_int), ("ty1", ctypes.c_int),
# /** Start value, initialised in pi_initialise_encode*/ # /** Start value, initialised in pi_initialise_encode*/
# int layS, resS, compS, prcS; # int layS, resS, compS, prcS;
("layS", ctypes.c_int), ("layS", ctypes.c_int),
("resS", ctypes.c_int), ("resS", ctypes.c_int),
("compS", ctypes.c_int), ("compS", ctypes.c_int),
("prcS", ctypes.c_int), ("prcS", ctypes.c_int),
# /** End value, initialised in pi_initialise_encode */ # /** End value, initialised in pi_initialise_encode */
# int layE, resE, compE, prcE; # int layE, resE, compE, prcE;
("layE", ctypes.c_int), ("layE", ctypes.c_int),
("resE", ctypes.c_int), ("resE", ctypes.c_int),
("compE", ctypes.c_int), ("compE", ctypes.c_int),
("prcE", ctypes.c_int), ("prcE", ctypes.c_int),
# Start and end values of Tile width and height, initialised in # Start and end values of Tile width and height, initialised in
# pi_initialise_encode int txS,txE,tyS,tyE,dx,dy; # pi_initialise_encode int txS,txE,tyS,tyE,dx,dy;
("txS", ctypes.c_int), ("txS", ctypes.c_int),
("txE", ctypes.c_int), ("txE", ctypes.c_int),
("tyS", ctypes.c_int), ("tyS", ctypes.c_int),
("tyE", ctypes.c_int), ("tyE", ctypes.c_int),
("dx", ctypes.c_int), ("dx", ctypes.c_int),
("dy", ctypes.c_int), ("dy", ctypes.c_int),
# Temporary values for Tile parts, initialised in pi_create_encode # Temporary values for Tile parts, initialised in pi_create_encode
# int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t; # int lay_t, res_t, comp_t, prc_t,tx0_t,ty0_t;
("lay_t", ctypes.c_int), ("lay_t", ctypes.c_int),
("res_t", ctypes.c_int), ("res_t", ctypes.c_int),
("comp_t", ctypes.c_int), ("comp_t", ctypes.c_int),
("prc_t", ctypes.c_int), ("prc_t", ctypes.c_int),
("tx0_t", ctypes.c_int), ("tx0_t", ctypes.c_int),
("ty0_t", ctypes.c_int)] ("ty0_t", ctypes.c_int)]
@ -374,23 +374,23 @@ class DecompressionParametersType(ctypes.Structure):
class ImageComptParmType(ctypes.Structure): class ImageComptParmType(ctypes.Structure):
"""Component parameters structure used by the opj_image_create function. """Component parameters structure used by the opj_image_create function.
""" """
_fields_ = [ _fields_ = [
# XRsiz: horizontal separation of a sample of ith component with # XRsiz: horizontal separation of a sample of ith component with
# respect to the reference grid # respect to the reference grid
("dx", ctypes.c_int), ("dx", ctypes.c_int),
# YRsiz: vertical separation of a sample of ith component with # YRsiz: vertical separation of a sample of ith component with
# respect to the reference grid */ # respect to the reference grid */
("dy", ctypes.c_int), ("dy", ctypes.c_int),
# data width, height
("w", ctypes.c_int),
("h", ctypes.c_int),
# x component offset compared to the whole image # data width, height
# y component offset compared to the whole image ("w", ctypes.c_int),
("x0", ctypes.c_int), ("h", ctypes.c_int),
("y0", ctypes.c_int),
# x component offset compared to the whole image
# y component offset compared to the whole image
("x0", ctypes.c_int),
("y0", ctypes.c_int),
# precision # precision
('prec', ctypes.c_int), ('prec', ctypes.c_int),
@ -398,7 +398,7 @@ class ImageComptParmType(ctypes.Structure):
# image depth in bits # image depth in bits
('bpp', ctypes.c_int), ('bpp', ctypes.c_int),
# signed (1) / unsigned (0) # signed (1) / unsigned (0)
('sgnd', ctypes.c_int)] ('sgnd', ctypes.c_int)]
@ -511,7 +511,7 @@ def destroy_compress(cinfo):
def encode(cinfo, cio, image): def encode(cinfo, cio, image):
"""Wrapper for openjpeg library function opj_encode. """Wrapper for openjpeg library function opj_encode.
Encodes an image into a JPEG-2000 codestream. Encodes an image into a JPEG-2000 codestream.
Parameters Parameters
---------- ----------
@ -540,7 +540,7 @@ def destroy_decompress(dinfo):
def image_cmptparm_t_from_np(np_image): def image_cmptparm_t_from_np(np_image):
"""Return appropriate image_cmptparm_t based on given numpy array. """Return appropriate image_cmptparm_t based on given numpy array.
""" """
try: try:
num_comps = np_image.shape[2] num_comps = np_image.shape[2]
except IndexError: except IndexError:
num_comps = 1 num_comps = 1
@ -557,17 +557,17 @@ def image_cmptparm_t_from_np(np_image):
bpp = 8 bpp = 8
sgnd = 1 sgnd = 1
elif np_image.dtype == np.uint16: elif np_image.dtype == np.uint16:
prec = 16 prec = 16
bpp = 16 bpp = 16
sgnd = 0 sgnd = 0
elif np_image.dtype == np.int16: elif np_image.dtype == np.int16:
prec = 16 prec = 16
bpp = 16 bpp = 16
sgnd = 1 sgnd = 1
else: else:
raise(TypeError("unhandled")) raise(TypeError("unhandled"))
for j in range(0, num_comps): for j in range(0, num_comps):
tarr[j].dx = 1 tarr[j].dx = 1
tarr[j].dy = 1 tarr[j].dy = 1
tarr[j].w = np_image.shape[1] tarr[j].w = np_image.shape[1]

View file

@ -1,5 +1,5 @@
""" """
Tests for libopenjp2 wrapping functions. Tests for libopenjp2 wrapping functions.
""" """
# R0904: Seems like pylint is fooled in this situation # R0904: Seems like pylint is fooled in this situation
# W0142: using kwargs is ok in this context # W0142: using kwargs is ok in this context
@ -212,7 +212,7 @@ class TestOpenJP2(unittest.TestCase):
"""Runs test designated tte3 in OpenJPEG test suite.""" """Runs test designated tte3 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx3_setup(tfile.name) xtx3_setup(tfile.name)
self.assertTrue(True) self.assertTrue(True)
def test_rta3(self): def test_rta3(self):
"""Runs test designated rta3 in OpenJPEG test suite.""" """Runs test designated rta3 in OpenJPEG test suite."""
@ -221,13 +221,13 @@ class TestOpenJP2(unittest.TestCase):
codec_format = openjp2.CODEC_J2K codec_format = openjp2.CODEC_J2K
self.j2k_random_tile_access(tfile.name, codec_format) self.j2k_random_tile_access(tfile.name, codec_format)
self.assertTrue(True) self.assertTrue(True)
def test_tte4(self): def test_tte4(self):
"""Runs test designated tte4 in OpenJPEG test suite.""" """Runs test designated tte4 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx4_setup(tfile.name) xtx4_setup(tfile.name)
self.assertTrue(True) self.assertTrue(True)
def test_rta4(self): def test_rta4(self):
"""Runs test designated rta4 in OpenJPEG test suite.""" """Runs test designated rta4 in OpenJPEG test suite."""
@ -241,7 +241,7 @@ class TestOpenJP2(unittest.TestCase):
"""Runs test designated tte5 in OpenJPEG test suite.""" """Runs test designated tte5 in OpenJPEG test suite."""
with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile: with tempfile.NamedTemporaryFile(suffix=".j2k") as tfile:
xtx5_setup(tfile.name) xtx5_setup(tfile.name)
self.assertTrue(True) self.assertTrue(True)
def test_rta5(self): def test_rta5(self):
"""Runs test designated rta5 in OpenJPEG test suite.""" """Runs test designated rta5 in OpenJPEG test suite."""
@ -332,8 +332,8 @@ def tile_encoder(**kwargs):
def tile_decoder(**kwargs): def tile_decoder(**kwargs):
"""Fixture called with various configurations by many tests. """Fixture called with various configurations by many tests.
Reads a tile. That's all it does. Reads a tile. That's all it does.
""" """
stream = openjp2.stream_create_default_file_stream_v3(kwargs['filename'], stream = openjp2.stream_create_default_file_stream_v3(kwargs['filename'],
True) True)
@ -355,7 +355,7 @@ def tile_decoder(**kwargs):
openjp2.setup_decoder(codec, dparam) openjp2.setup_decoder(codec, dparam)
image = openjp2.read_header(stream, codec) image = openjp2.read_header(stream, codec)
openjp2.set_decode_area(codec, image, openjp2.set_decode_area(codec, image,
kwargs['x0'], kwargs['y0'], kwargs['x0'], kwargs['y0'],
kwargs['x1'], kwargs['y1']) kwargs['x1'], kwargs['y1'])

View file

@ -11,6 +11,19 @@ import numpy as np
import glymur import glymur
# The Python XMP Toolkit may be used for XMP UUIDs, but only if available and
# if the version is at least 2.0.0.
try:
import libxmp
if hasattr(libxmp, 'version') and re.match(r'''[2-9].\d*.\d*''',
libxmp.version.VERSION):
from libxmp import XMPMeta
HAS_PYTHON_XMP_TOOLKIT = True
else:
HAS_PYTHON_XMP_TOOLKIT = False
except ImportError:
HAS_PYTHON_XMP_TOOLKIT = False
# Need to know of the libopenjp2 version is the official 2.0.0 release and NOT # Need to know of the libopenjp2 version is the official 2.0.0 release and NOT
# the 2.0+ development version. # the 2.0+ development version.
OPENJP2_IS_V2_OFFICIAL = False OPENJP2_IS_V2_OFFICIAL = False
@ -170,90 +183,96 @@ def read_pgx_header(pgx_file):
nemo_xmp_box = """UUID Box (uuid) @ (77, 3146) nemo_xmp_box = """UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP) UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data: UUID Data: <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<ns0:xmpmeta xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ns0="adobe:ns:meta/" xmlns:ns2="http://ns.adobe.com/xap/1.0/" xmlns:ns3="http://ns.adobe.com/tiff/1.0/" xmlns:ns4="http://ns.adobe.com/exif/1.0/" xmlns:ns5="http://ns.adobe.com/photoshop/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ns0:xmptk="Exempi + XMP Core 5.1.2"> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Exempi + XMP Core 4.4.0">
<rdf:RDF> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""> <rdf:Description rdf:about=""
<ns2:CreatorTool>Google</ns2:CreatorTool> xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<ns2:CreateDate>2013-02-09T14:47:53</ns2:CreateDate> <xmp:CreatorTool>Google</xmp:CreatorTool>
</rdf:Description> <xmp:CreateDate>2013-02-09T14:47:53</xmp:CreateDate>
<rdf:Description rdf:about=""> </rdf:Description>
<ns3:YCbCrPositioning>1</ns3:YCbCrPositioning> <rdf:Description rdf:about=""
<ns3:XResolution>72/1</ns3:XResolution> xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
<ns3:YResolution>72/1</ns3:YResolution> <tiff:YCbCrPositioning>1</tiff:YCbCrPositioning>
<ns3:ResolutionUnit>2</ns3:ResolutionUnit> <tiff:XResolution>72/1</tiff:XResolution>
<ns3:Make>HTC</ns3:Make> <tiff:YResolution>72/1</tiff:YResolution>
<ns3:Model>HTC Glacier</ns3:Model> <tiff:ResolutionUnit>2</tiff:ResolutionUnit>
<ns3:ImageWidth>2592</ns3:ImageWidth> <tiff:Make>HTC</tiff:Make>
<ns3:ImageLength>1456</ns3:ImageLength> <tiff:Model>HTC Glacier</tiff:Model>
<ns3:BitsPerSample> <tiff:ImageWidth>2592</tiff:ImageWidth>
<rdf:Seq> <tiff:ImageLength>1456</tiff:ImageLength>
<rdf:li>8</rdf:li> <tiff:BitsPerSample>
<rdf:li>8</rdf:li> <rdf:Seq>
<rdf:li>8</rdf:li> <rdf:li>8</rdf:li>
</rdf:Seq> <rdf:li>8</rdf:li>
</ns3:BitsPerSample> <rdf:li>8</rdf:li>
<ns3:PhotometricInterpretation>2</ns3:PhotometricInterpretation> </rdf:Seq>
<ns3:SamplesPerPixel>3</ns3:SamplesPerPixel> </tiff:BitsPerSample>
<ns3:WhitePoint> <tiff:PhotometricInterpretation>2</tiff:PhotometricInterpretation>
<rdf:Seq> <tiff:SamplesPerPixel>3</tiff:SamplesPerPixel>
<rdf:li>1343036288/4294967295</rdf:li> <tiff:WhitePoint>
<rdf:li>1413044224/4294967295</rdf:li> <rdf:Seq>
</rdf:Seq> <rdf:li>1343036288/4294967295</rdf:li>
</ns3:WhitePoint> <rdf:li>1413044224/4294967295</rdf:li>
<ns3:PrimaryChromaticities> </rdf:Seq>
<rdf:Seq> </tiff:WhitePoint>
<rdf:li>2748779008/4294967295</rdf:li> <tiff:PrimaryChromaticities>
<rdf:li>1417339264/4294967295</rdf:li> <rdf:Seq>
<rdf:li>1288490240/4294967295</rdf:li> <rdf:li>2748779008/4294967295</rdf:li>
<rdf:li>2576980480/4294967295</rdf:li> <rdf:li>1417339264/4294967295</rdf:li>
<rdf:li>644245120/4294967295</rdf:li> <rdf:li>1288490240/4294967295</rdf:li>
<rdf:li>257698032/4294967295</rdf:li> <rdf:li>2576980480/4294967295</rdf:li>
</rdf:Seq> <rdf:li>644245120/4294967295</rdf:li>
</ns3:PrimaryChromaticities> <rdf:li>257698032/4294967295</rdf:li>
</rdf:Description> </rdf:Seq>
<rdf:Description rdf:about=""> </tiff:PrimaryChromaticities>
<ns4:ColorSpace>1</ns4:ColorSpace> </rdf:Description>
<ns4:PixelXDimension>2528</ns4:PixelXDimension> <rdf:Description rdf:about=""
<ns4:PixelYDimension>1424</ns4:PixelYDimension> xmlns:exif="http://ns.adobe.com/exif/1.0/">
<ns4:FocalLength>353/100</ns4:FocalLength> <exif:ColorSpace>1</exif:ColorSpace>
<ns4:GPSAltitudeRef>0</ns4:GPSAltitudeRef> <exif:PixelXDimension>2528</exif:PixelXDimension>
<ns4:GPSAltitude>0/1</ns4:GPSAltitude> <exif:PixelYDimension>1424</exif:PixelYDimension>
<ns4:GPSMapDatum>WGS-84</ns4:GPSMapDatum> <exif:FocalLength>353/100</exif:FocalLength>
<ns4:DateTimeOriginal>2013-02-09T14:47:53</ns4:DateTimeOriginal> <exif:GPSAltitudeRef>0</exif:GPSAltitudeRef>
<ns4:ISOSpeedRatings> <exif:GPSAltitude>0/1</exif:GPSAltitude>
<rdf:Seq> <exif:GPSMapDatum>WGS-84</exif:GPSMapDatum>
<rdf:li>76</rdf:li> <exif:DateTimeOriginal>2013-02-09T14:47:53</exif:DateTimeOriginal>
</rdf:Seq> <exif:ISOSpeedRatings>
</ns4:ISOSpeedRatings> <rdf:Seq>
<ns4:ExifVersion>0220</ns4:ExifVersion> <rdf:li>76</rdf:li>
<ns4:FlashpixVersion>0100</ns4:FlashpixVersion> </rdf:Seq>
<ns4:ComponentsConfiguration> </exif:ISOSpeedRatings>
<rdf:Seq> <exif:ExifVersion>0220</exif:ExifVersion>
<rdf:li>1</rdf:li> <exif:FlashpixVersion>0100</exif:FlashpixVersion>
<rdf:li>2</rdf:li> <exif:ComponentsConfiguration>
<rdf:li>3</rdf:li> <rdf:Seq>
<rdf:li>0</rdf:li> <rdf:li>1</rdf:li>
</rdf:Seq> <rdf:li>2</rdf:li>
</ns4:ComponentsConfiguration> <rdf:li>3</rdf:li>
<ns4:GPSLatitude>42,20.56N</ns4:GPSLatitude> <rdf:li>0</rdf:li>
<ns4:GPSLongitude>71,5.29W</ns4:GPSLongitude> </rdf:Seq>
<ns4:GPSTimeStamp>2013-02-09T19:47:53Z</ns4:GPSTimeStamp> </exif:ComponentsConfiguration>
<ns4:GPSProcessingMethod>NETWORK</ns4:GPSProcessingMethod> <exif:GPSLatitude>42,20.56N</exif:GPSLatitude>
</rdf:Description> <exif:GPSLongitude>71,5.29W</exif:GPSLongitude>
<rdf:Description rdf:about=""> <exif:GPSTimeStamp>2013-02-09T19:47:53Z</exif:GPSTimeStamp>
<ns5:DateCreated>2013-02-09T14:47:53</ns5:DateCreated> <exif:GPSProcessingMethod>NETWORK</exif:GPSProcessingMethod>
</rdf:Description> </rdf:Description>
<rdf:Description rdf:about=""> <rdf:Description rdf:about=""
<dc:Creator> xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/">
<rdf:Seq> <photoshop:DateCreated>2013-02-09T14:47:53</photoshop:DateCreated>
<rdf:li>Glymur</rdf:li> </rdf:Description>
<rdf:li>Python XMP Toolkit</rdf:li> <rdf:Description rdf:about=""
</rdf:Seq> xmlns:dc="http://purl.org/dc/elements/1.1/">
</dc:Creator> <dc:Creator>
</rdf:Description> <rdf:Seq>
</rdf:RDF> <rdf:li>Glymur</rdf:li>
</ns0:xmpmeta>""" <rdf:li>Python XMP Toolkit</rdf:li>
</rdf:Seq>
</dc:Creator>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>"""
SimpleRDF = """<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> SimpleRDF = """<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about='Test:XMPCoreCoverage/kSimpleRDF' <rdf:Description rdf:about='Test:XMPCoreCoverage/kSimpleRDF'

View file

@ -894,9 +894,9 @@ class TestRepr(unittest.TestCase):
tree = ET.ElementTree(elt) tree = ET.ElementTree(elt)
box = glymur.jp2box.XMLBox(xml=tree) box = glymur.jp2box.XMLBox(xml=tree)
regexp = "glymur.jp2box.XMLBox" regexp = r"""glymur.jp2box.XMLBox"""
regexp += "\(xml=<(xml.etree.ElementTree.){0,1}ElementTree object " regexp += r"""\(xml=<(xml.etree.ElementTree.){0,1}ElementTree object """
regexp += "at 0x([a-f0-9]*)>\)" regexp += """at 0x([a-f0-9]*)>\)"""
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp) self.assertRegexpMatches(repr(box), regexp)
@ -927,9 +927,9 @@ class TestRepr(unittest.TestCase):
# Since the raw_data parameter is a sequence of bytes which could be # Since the raw_data parameter is a sequence of bytes which could be
# quite long, don't bother trying to make it conform to eval(repr()). # quite long, don't bother trying to make it conform to eval(repr()).
regexp = "glymur.jp2box.UUIDBox\(" regexp = r"""glymur.jp2box.UUIDBox\("""
regexp += "the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s" regexp += """the_uuid=UUID\('00000000-0000-0000-0000-000000000000'\),\s"""
regexp += "raw_data=<byte\sarray\s10\selements>\)" regexp += """raw_data=<byte\sarray\s10\selements>\)"""
if sys.hexversion < 0x03000000: if sys.hexversion < 0x03000000:
self.assertRegexpMatches(repr(box), regexp) self.assertRegexpMatches(repr(box), regexp)

View file

@ -35,7 +35,9 @@ if sys.hexversion <= 0x03030000:
else: else:
from unittest.mock import patch from unittest.mock import patch
from libxmp import XMPMeta from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPJ_DATA_ROOT
if HAS_PYTHON_XMP_TOOLKIT:
from libxmp import XMPMeta
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k

View file

@ -30,13 +30,14 @@ import warnings
import numpy as np import numpy as np
import pkg_resources import pkg_resources
import libxmp
from libxmp import XMPMeta
import glymur import glymur
from glymur import Jp2k from glymur import Jp2k
from .fixtures import OPENJP2_IS_V2_OFFICIAL from .fixtures import HAS_PYTHON_XMP_TOOLKIT, OPENJP2_IS_V2_OFFICIAL
if HAS_PYTHON_XMP_TOOLKIT:
import libxmp
from libxmp import XMPMeta
from .fixtures import OPJ_DATA_ROOT, opj_data_file from .fixtures import OPJ_DATA_ROOT, opj_data_file
@ -362,6 +363,8 @@ class TestJp2k(unittest.TestCase):
self.assertEqual(ET.tostring(jp2k.box[3].xml.getroot()), self.assertEqual(ET.tostring(jp2k.box[3].xml.getroot()),
b'<test>this is a test</test>') b'<test>this is a test</test>')
@unittest.skipIf(not HAS_PYTHON_XMP_TOOLKIT,
"Requires Python XMP Toolkit >= 2.0")
def test_xmp_attribute(self): def test_xmp_attribute(self):
"""Verify the XMP packet in the shipping example file can be read.""" """Verify the XMP packet in the shipping example file can be read."""
j = Jp2k(self.jp2file) j = Jp2k(self.jp2file)

View file

@ -1,10 +1,12 @@
# This file is part of glymur, a Python interface for accessing JPEG 2000. """
# This file is part of glymur, a Python interface for accessing JPEG 2000.
# http://glymur.readthedocs.org
# http://glymur.readthedocs.org
# Copyright 2013 John Evans
# Copyright 2013 John Evans
# License: MIT
License: MIT
"""
import sys import sys
import numpy as np import numpy as np