Merge branch 'issue158' into devel

This commit is contained in:
jevans 2014-02-09 12:52:07 -05:00
commit 012eef71e1
5 changed files with 557 additions and 130 deletions

View file

@ -1,10 +1,11 @@
Feb 08, 2014 - Removed support for Python 2.6. Added write support for JP2
Feb 09, 2014 - Removed support for Python 2.6. Added write support for JP2
UUID, DataEntryURL, Palette and Component Mapping boxes, JPX
Association, NumberList and DataReference boxes. Added read
support for JPX free, number list, data reference, fragment
table, and fragment list boxes. Palette box now a 2D numpy
array instead of a list of 1D arrays. JP2 super box constructors
now take optional box list argument. Fixed bug where JPX files
table, and fragment list boxes. Added get_printoptions,
set_printoptions functions. Palette box now a 2D numpy array
instead of a list of 1D arrays. JP2 super box constructors now
take optional box list argument. Fixed bug where JPX files
with more than one codestream but advertising jp2 compatibility
were not being read.

View file

@ -8,6 +8,7 @@ __version__ = version.version
from .jp2k import Jp2k
from .jp2dump import jp2dump
from .jp2box import get_printoptions, set_printoptions
from . import data

View file

@ -14,9 +14,7 @@ References
# pylint: disable=C0302,R0903,R0913
from collections import OrderedDict
import copy
import datetime
import io
import math
import os
import pprint
@ -24,7 +22,6 @@ import struct
import sys
import uuid
import warnings
import xml
import xml.etree.cElementTree as ET
import numpy as np
@ -244,6 +241,8 @@ class ColourSpecificationBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n Method: {0}'.format(_METHOD_DISPLAY[self.method])
msg += '\n Precedence: {0}'.format(self.precedence)
@ -501,6 +500,9 @@ class ChannelDefinitionBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for j in range(len(self.association)):
color_type_string = _COLOR_TYPE_MAP_DISPLAY[self.channel_type[j]]
if self.association[j] == 0:
@ -591,6 +593,9 @@ class CodestreamHeaderBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for box in self.box:
boxstr = str(box)
@ -655,6 +660,9 @@ class CompositingLayerHeaderBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for box in self.box:
boxstr = str(box)
@ -727,6 +735,9 @@ class ComponentMappingBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for k in range(len(self.component_index)):
if self.mapping_type[k] == 1:
@ -746,7 +757,7 @@ class ComponentMappingBox(Jp2kBox):
fptr.write(write_buffer)
for j in range(len(self.component_index)):
write_buffer = struct.pack('>HBB',
write_buffer = struct.pack('>HBB',
self.component_index[j],
self.mapping_type[j],
self.palette_index[j])
@ -812,6 +823,11 @@ class ContiguousCodestreamBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
if _printoptions['codestream'] == False:
return msg
msg += '\n Main header:'
for segment in self.main_header.segment:
segstr = str(segment)
@ -889,6 +905,9 @@ class DataReferenceBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for box in self.DR:
msg += '\n ' + str(box)
return msg
@ -976,7 +995,11 @@ class FileTypeBox(Jp2kBox):
return msg
def __str__(self):
lst = [Jp2kBox.__str__(self),
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
lst = [msg,
' Brand: {0}',
' Compatibility: {1}']
msg = '\n'.join(lst)
@ -1067,6 +1090,9 @@ class FragmentListBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for j in range(len(self.fragment_offset)):
msg += "\n Offset {0}: {1}"
msg += "\n Fragment Length {2}: {3}"
@ -1092,13 +1118,13 @@ class FragmentListBox(Jp2kBox):
Returns
-------
FreeBox instance
FragmentListBox instance
"""
read_buffer = fptr.read(2)
nf, = struct.unpack('>H', read_buffer)
num_fragments, = struct.unpack('>H', read_buffer)
read_buffer = fptr.read(nf * 14)
lst = struct.unpack('>' + 'QIH' * nf, read_buffer)
read_buffer = fptr.read(num_fragments * 14)
lst = struct.unpack('>' + 'QIH' * num_fragments, read_buffer)
frag_offset = lst[0::3]
frag_len = lst[1::3]
data_reference = lst[2::3]
@ -1131,6 +1157,9 @@ class FragmentTableBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for box in self.box:
boxstr = str(box)
@ -1191,6 +1220,9 @@ class FreeBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
return msg
@staticmethod
@ -1280,6 +1312,10 @@ class ImageHeaderBox(Jp2kBox):
return msg
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg = "{0}"
msg += '\n Size: [{1} {2} {3}]'
msg += '\n Bitdepth: {4}'
@ -1380,6 +1416,9 @@ class AssociationBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for box in self.box:
boxstr = str(box)
@ -1513,6 +1552,9 @@ class JPEG2000SignatureBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n Signature: {0:02x}{1:02x}{2:02x}{3:02x}'
msg = msg.format(self.signature[0], self.signature[1],
self.signature[2], self.signature[3])
@ -1584,16 +1626,15 @@ class PaletteBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
return msg
def write(self, fptr):
"""Write a Palette box to file.
"""
# Box length is usual header (8)
# + num entries NE (2) + num columns NC (1)
# + (bps/8, /signed) for each column (3) + bps * NC
# +
bytes_per_row = sum(self.bits_per_component) / 8
bytes_per_palette = bytes_per_row * self.palette.shape[0]
box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette
@ -1608,7 +1649,7 @@ class PaletteBox(Jp2kBox):
fptr.write(write_buffer)
bps_signed = [x - 1 for x in self.bits_per_component]
for j, item in enumerate(bps_signed):
for j, _ in enumerate(bps_signed):
if self.signed[j]:
bps_signed[j] |= 0x80
write_buffer = struct.pack('>' + 'B' * self.palette.shape[1],
@ -1616,13 +1657,10 @@ class PaletteBox(Jp2kBox):
fptr.write(write_buffer)
if self.bits_per_component[0] <= 8:
dtype = np.uint8
code = 'B'
elif self.bits_per_component[0] <= 16:
dtype = np.uint16
code = 'H'
elif self.bits_per_component[0] <= 32:
dtype = np.uint32
code = 'I'
fmt = '>' + code * self.palette.shape[1]
@ -1820,6 +1858,9 @@ class ReaderRequirementsBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n Standard Features:'
for j in range(len(self.standard_flag)):
@ -2044,6 +2085,9 @@ class CaptureResolutionBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n VCR: {0}'.format(self.vertical_resolution)
msg += '\n HCR: {0}'.format(self.horizontal_resolution)
return msg
@ -2106,6 +2150,9 @@ class DisplayResolutionBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n VDR: {0}'.format(self.vertical_resolution)
msg += '\n HDR: {0}'.format(self.horizontal_resolution)
return msg
@ -2162,6 +2209,9 @@ class LabelBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n Label: {0}'.format(self.label)
return msg
@ -2218,6 +2268,9 @@ class NumberListBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for j, association in enumerate(self.associations):
msg += '\n Association[{0}]: '.format(j)
if association == 0:
@ -2315,6 +2368,11 @@ class XMLBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
if _printoptions['xml'] == False:
return msg
xml = self.xml
if self.xml is not None:
msg += _pretty_print_xml(self.xml)
@ -2416,9 +2474,12 @@ class UUIDListBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
for j, uuid_item in enumerate(self.ulst):
msg += '\n UUID[{0}]: {1}'.format(j, uuid_item)
return(msg)
return msg
@staticmethod
def parse(fptr, offset, length):
@ -2446,7 +2507,7 @@ class UUIDListBox(Jp2kBox):
ulst.append(uuid.UUID(bytes=read_buffer))
box = UUIDListBox(ulst, length=length, offset=offset)
return(box)
return box
class UUIDInfoBox(Jp2kBox):
@ -2477,7 +2538,6 @@ class UUIDInfoBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
for box in self.box:
box_str = str(box)
@ -2485,7 +2545,7 @@ class UUIDInfoBox(Jp2kBox):
lst = [('\n ' + x) for x in box_str.split('\n')]
msg += ''.join(lst)
return(msg)
return msg
@staticmethod
def parse(fptr, offset, length):
@ -2561,6 +2621,9 @@ class DataEntryURLBox(Jp2kBox):
def __str__(self):
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg += '\n '
lines = ['Version: {0}',
@ -2671,15 +2734,30 @@ class UUIDBox(Jp2kBox):
return msg.format(repr(self.uuid), len(self.raw_data))
def __str__(self):
msg = '{0}\n UUID: {1}'.format(Jp2kBox.__str__(self), self.uuid)
msg = Jp2kBox.__str__(self)
if _printoptions['short'] == True:
return msg
msg = '{0}\n UUID: {1}'.format(msg, self.uuid)
if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
msg += ' (XMP)'
elif self.uuid.bytes == b'JpgTiffExif->JP2':
msg += ' (EXIF)'
else:
msg += ' (unknown)'
if (((_printoptions['xml'] == False) and
(self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')))):
# If it's an XMP UUID, don't print the XML contents.
return msg
if self.uuid == uuid.UUID('be7acfcb-97a9-42e8-9c71-999491e3afac'):
line = ' (XMP)\n UUID Data: {0}'
line = '\n UUID Data: {0}'
msg += line.format(_pretty_print_xml(self.data))
elif self.uuid.bytes == b'JpgTiffExif->JP2':
msg += ' (EXIF)\n UUID Data: {0}'.format(str(self.data))
msg += '\n UUID Data: {0}'.format(str(self.data))
else:
line = ' (unknown)\n UUID Data: {0} bytes'
line = '\n UUID Data: {0} bytes'
msg += line.format(len(self.raw_data))
return msg
@ -2748,3 +2826,58 @@ _BOX_WITH_ID = {
'url ': DataEntryURLBox,
'uuid': UUIDBox,
'xml ': XMLBox}
_printoptions = {'short': False, 'xml': True, 'codestream': True}
def set_printoptions(short=False, xml=True, codestream=True):
"""Set printing options.
These options determine the way JPEG 2000 boxes are displayed.
Parameters
----------
short : bool, optional
When True, only the box ID, offset, and length are displayed. Useful
for displaying only the basic structure or skeleton of a JPEG 2000 file.
xml : bool, optional
When False, printing of the XML contents of any XML boxes or UUID XMP
boxes is suppressed.
codestream : bool, optional
When False, printing of the codestream contents is suppressed.
See also
--------
get_printoptions
Examples
--------
To put back the default options, you can use:
>>> import glymur
>>> glymur.set_printoptions(short=False, xml=True, codestream=True)
"""
_printoptions['short'] = short
_printoptions['xml'] = xml
_printoptions['codestream'] = codestream
def get_printoptions():
"""Return the current print options.
Returns
-------
print_opts : dict
Dictionary of current print options with keys
- short : bool
- xml : bool
- codestream : bool
For a full description of these options, see `set_printoptions`.
See also
--------
set_printoptions
"""
return _printoptions

View file

@ -350,3 +350,335 @@ text_gbr_34 = """Colour Specification Box (colr) @ (179, 1339)
'Rendering Intent': 'perceptual',
'Illuminant': array([ 0.96420288, 1. , 0.8249054 ]),
'Creator': 'appl'}"""
# Metadata dump of nemo.
nemo_dump_full = r'''JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Size: [1456 2592 3]
Bitdepth: 8
Signed: False
Compression: wavelet
Colorspace Unknown: False
Colour Specification Box (colr) @ (62, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data:
<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">
<rdf:RDF>
<rdf:Description rdf:about="">
<ns2:CreatorTool>Google</ns2:CreatorTool>
<ns2:CreateDate>2013-02-09T14:47:53</ns2:CreateDate>
</rdf:Description>
<rdf:Description rdf:about="">
<ns3:YCbCrPositioning>1</ns3:YCbCrPositioning>
<ns3:XResolution>72/1</ns3:XResolution>
<ns3:YResolution>72/1</ns3:YResolution>
<ns3:ResolutionUnit>2</ns3:ResolutionUnit>
<ns3:Make>HTC</ns3:Make>
<ns3:Model>HTC Glacier</ns3:Model>
<ns3:ImageWidth>2592</ns3:ImageWidth>
<ns3:ImageLength>1456</ns3:ImageLength>
<ns3:BitsPerSample>
<rdf:Seq>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
</rdf:Seq>
</ns3:BitsPerSample>
<ns3:PhotometricInterpretation>2</ns3:PhotometricInterpretation>
<ns3:SamplesPerPixel>3</ns3:SamplesPerPixel>
<ns3:WhitePoint>
<rdf:Seq>
<rdf:li>1343036288/4294967295</rdf:li>
<rdf:li>1413044224/4294967295</rdf:li>
</rdf:Seq>
</ns3:WhitePoint>
<ns3:PrimaryChromaticities>
<rdf:Seq>
<rdf:li>2748779008/4294967295</rdf:li>
<rdf:li>1417339264/4294967295</rdf:li>
<rdf:li>1288490240/4294967295</rdf:li>
<rdf:li>2576980480/4294967295</rdf:li>
<rdf:li>644245120/4294967295</rdf:li>
<rdf:li>257698032/4294967295</rdf:li>
</rdf:Seq>
</ns3:PrimaryChromaticities>
</rdf:Description>
<rdf:Description rdf:about="">
<ns4:ColorSpace>1</ns4:ColorSpace>
<ns4:PixelXDimension>2528</ns4:PixelXDimension>
<ns4:PixelYDimension>1424</ns4:PixelYDimension>
<ns4:FocalLength>353/100</ns4:FocalLength>
<ns4:GPSAltitudeRef>0</ns4:GPSAltitudeRef>
<ns4:GPSAltitude>0/1</ns4:GPSAltitude>
<ns4:GPSMapDatum>WGS-84</ns4:GPSMapDatum>
<ns4:DateTimeOriginal>2013-02-09T14:47:53</ns4:DateTimeOriginal>
<ns4:ISOSpeedRatings>
<rdf:Seq>
<rdf:li>76</rdf:li>
</rdf:Seq>
</ns4:ISOSpeedRatings>
<ns4:ExifVersion>0220</ns4:ExifVersion>
<ns4:FlashpixVersion>0100</ns4:FlashpixVersion>
<ns4:ComponentsConfiguration>
<rdf:Seq>
<rdf:li>1</rdf:li>
<rdf:li>2</rdf:li>
<rdf:li>3</rdf:li>
<rdf:li>0</rdf:li>
</rdf:Seq>
</ns4:ComponentsConfiguration>
<ns4:GPSLatitude>42,20.56N</ns4:GPSLatitude>
<ns4:GPSLongitude>71,5.29W</ns4:GPSLongitude>
<ns4:GPSTimeStamp>2013-02-09T19:47:53Z</ns4:GPSTimeStamp>
<ns4:GPSProcessingMethod>NETWORK</ns4:GPSProcessingMethod>
</rdf:Description>
<rdf:Description rdf:about="">
<ns5:DateCreated>2013-02-09T14:47:53</ns5:DateCreated>
</rdf:Description>
<rdf:Description rdf:about="">
<dc:Creator>
<rdf:Seq>
<rdf:li>Glymur</rdf:li>
<rdf:li>Python XMP Toolkit</rdf:li>
</rdf:Seq>
</dc:Creator>
</rdf:Description>
</rdf:RDF>
</ns0:xmpmeta>
Contiguous Codestream Box (jp2c) @ (3223, 1132296)
Main header:
SOC marker segment @ (3231, 0)
SIZ marker segment @ (3233, 47)
Profile: 2
Reference Grid Height, Width: (1456 x 2592)
Vertical, Horizontal Reference Grid Offset: (0 x 0)
Reference Tile Height, Width: (1456 x 2592)
Vertical, Horizontal Reference Tile Offset: (0 x 0)
Bitdepth: (8, 8, 8)
Signed: (False, False, False)
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
COD marker segment @ (3282, 12)
Coding style:
Entropy coder, without partitions
SOP marker segments: False
EPH marker segments: False
Coding style parameters:
Progression order: LRCP
Number of layers: 2
Multiple component transformation usage: reversible
Number of resolutions: 2
Code block height, width: (64 x 64)
Wavelet transform: 5-3 reversible
Precinct size: default, 2^15 x 2^15
Code block context:
Selective arithmetic coding bypass: False
Reset context probabilities on coding pass boundaries: False
Termination on each coding pass: False
Vertically stripe causal context: False
Predictable termination: False
Segmentation symbols: False
QCD marker segment @ (3296, 7)
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
CME marker segment @ (3305, 37)
"Created by OpenJPEG version 2.0.0"'''
nemo_dump_short = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
File Type Box (ftyp) @ (12, 20)
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Colour Specification Box (colr) @ (62, 15)
UUID Box (uuid) @ (77, 3146)
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
nemo_dump_no_xml = '''JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Size: [1456 2592 3]
Bitdepth: 8
Signed: False
Compression: wavelet
Colorspace Unknown: False
Colour Specification Box (colr) @ (62, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
Contiguous Codestream Box (jp2c) @ (3223, 1132296)
Main header:
SOC marker segment @ (3231, 0)
SIZ marker segment @ (3233, 47)
Profile: 2
Reference Grid Height, Width: (1456 x 2592)
Vertical, Horizontal Reference Grid Offset: (0 x 0)
Reference Tile Height, Width: (1456 x 2592)
Vertical, Horizontal Reference Tile Offset: (0 x 0)
Bitdepth: (8, 8, 8)
Signed: (False, False, False)
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
COD marker segment @ (3282, 12)
Coding style:
Entropy coder, without partitions
SOP marker segments: False
EPH marker segments: False
Coding style parameters:
Progression order: LRCP
Number of layers: 2
Multiple component transformation usage: reversible
Number of resolutions: 2
Code block height, width: (64 x 64)
Wavelet transform: 5-3 reversible
Precinct size: default, 2^15 x 2^15
Code block context:
Selective arithmetic coding bypass: False
Reset context probabilities on coding pass boundaries: False
Termination on each coding pass: False
Vertically stripe causal context: False
Predictable termination: False
Segmentation symbols: False
QCD marker segment @ (3296, 7)
Quantization style: no quantization, 2 guard bits
Step size: [(0, 8), (0, 9), (0, 9), (0, 10)]
CME marker segment @ (3305, 37)
"Created by OpenJPEG version 2.0.0"'''
nemo_dump_no_codestream = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Size: [1456 2592 3]
Bitdepth: 8
Signed: False
Compression: wavelet
Colorspace Unknown: False
Colour Specification Box (colr) @ (62, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
UUID Data:
<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">
<rdf:RDF>
<rdf:Description rdf:about="">
<ns2:CreatorTool>Google</ns2:CreatorTool>
<ns2:CreateDate>2013-02-09T14:47:53</ns2:CreateDate>
</rdf:Description>
<rdf:Description rdf:about="">
<ns3:YCbCrPositioning>1</ns3:YCbCrPositioning>
<ns3:XResolution>72/1</ns3:XResolution>
<ns3:YResolution>72/1</ns3:YResolution>
<ns3:ResolutionUnit>2</ns3:ResolutionUnit>
<ns3:Make>HTC</ns3:Make>
<ns3:Model>HTC Glacier</ns3:Model>
<ns3:ImageWidth>2592</ns3:ImageWidth>
<ns3:ImageLength>1456</ns3:ImageLength>
<ns3:BitsPerSample>
<rdf:Seq>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
<rdf:li>8</rdf:li>
</rdf:Seq>
</ns3:BitsPerSample>
<ns3:PhotometricInterpretation>2</ns3:PhotometricInterpretation>
<ns3:SamplesPerPixel>3</ns3:SamplesPerPixel>
<ns3:WhitePoint>
<rdf:Seq>
<rdf:li>1343036288/4294967295</rdf:li>
<rdf:li>1413044224/4294967295</rdf:li>
</rdf:Seq>
</ns3:WhitePoint>
<ns3:PrimaryChromaticities>
<rdf:Seq>
<rdf:li>2748779008/4294967295</rdf:li>
<rdf:li>1417339264/4294967295</rdf:li>
<rdf:li>1288490240/4294967295</rdf:li>
<rdf:li>2576980480/4294967295</rdf:li>
<rdf:li>644245120/4294967295</rdf:li>
<rdf:li>257698032/4294967295</rdf:li>
</rdf:Seq>
</ns3:PrimaryChromaticities>
</rdf:Description>
<rdf:Description rdf:about="">
<ns4:ColorSpace>1</ns4:ColorSpace>
<ns4:PixelXDimension>2528</ns4:PixelXDimension>
<ns4:PixelYDimension>1424</ns4:PixelYDimension>
<ns4:FocalLength>353/100</ns4:FocalLength>
<ns4:GPSAltitudeRef>0</ns4:GPSAltitudeRef>
<ns4:GPSAltitude>0/1</ns4:GPSAltitude>
<ns4:GPSMapDatum>WGS-84</ns4:GPSMapDatum>
<ns4:DateTimeOriginal>2013-02-09T14:47:53</ns4:DateTimeOriginal>
<ns4:ISOSpeedRatings>
<rdf:Seq>
<rdf:li>76</rdf:li>
</rdf:Seq>
</ns4:ISOSpeedRatings>
<ns4:ExifVersion>0220</ns4:ExifVersion>
<ns4:FlashpixVersion>0100</ns4:FlashpixVersion>
<ns4:ComponentsConfiguration>
<rdf:Seq>
<rdf:li>1</rdf:li>
<rdf:li>2</rdf:li>
<rdf:li>3</rdf:li>
<rdf:li>0</rdf:li>
</rdf:Seq>
</ns4:ComponentsConfiguration>
<ns4:GPSLatitude>42,20.56N</ns4:GPSLatitude>
<ns4:GPSLongitude>71,5.29W</ns4:GPSLongitude>
<ns4:GPSTimeStamp>2013-02-09T19:47:53Z</ns4:GPSTimeStamp>
<ns4:GPSProcessingMethod>NETWORK</ns4:GPSProcessingMethod>
</rdf:Description>
<rdf:Description rdf:about="">
<ns5:DateCreated>2013-02-09T14:47:53</ns5:DateCreated>
</rdf:Description>
<rdf:Description rdf:about="">
<dc:Creator>
<rdf:Seq>
<rdf:li>Glymur</rdf:li>
<rdf:li>Python XMP Toolkit</rdf:li>
</rdf:Seq>
</dc:Creator>
</rdf:Description>
</rdf:RDF>
</ns0:xmpmeta>
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""
nemo_dump_no_codestream_no_xml = r"""JPEG 2000 Signature Box (jP ) @ (0, 12)
Signature: 0d0a870a
File Type Box (ftyp) @ (12, 20)
Brand: jp2
Compatibility: ['jp2 ']
JP2 Header Box (jp2h) @ (32, 45)
Image Header Box (ihdr) @ (40, 22)
Size: [1456 2592 3]
Bitdepth: 8
Signed: False
Compression: wavelet
Colorspace Unknown: False
Colour Specification Box (colr) @ (62, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB
UUID Box (uuid) @ (77, 3146)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
Contiguous Codestream Box (jp2c) @ (3223, 1132296)"""

View file

@ -11,7 +11,6 @@
# pylint: disable=R0904
import os
import re
import struct
import sys
import tempfile
@ -31,104 +30,76 @@ else:
import glymur
from glymur import Jp2k
from . import fixtures
from .fixtures import OPJ_DATA_ROOT, opj_data_file, nemo_xmp_box
from .fixtures import text_gbr_27, text_gbr_33, text_gbr_34
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
@unittest.skipIf(re.match(r"""1\.[01234]""", glymur.version.openjpeg_version),
"Need at least 1.5 in order to write jp2 files.")
class TestPrintingNeedsLib(unittest.TestCase):
"""These tests require the library, mostly in order to just setup the test.
"""
@classmethod
def setUpClass(cls):
# Setup a plain JP2 file without the two UUID boxes.
jp2file = glymur.data.nemo()
with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as tfile:
cls._plain_nemo_file = tfile.name
ijfile = Jp2k(jp2file)
data = ijfile.read(rlevel=1)
ojfile = Jp2k(cls._plain_nemo_file, 'wb')
ojfile.write(data)
@classmethod
def tearDownClass(cls):
os.unlink(cls._plain_nemo_file)
class TestPrinting(unittest.TestCase):
"""Tests for verifying how printing works."""
def setUp(self):
self.jp2file = glymur.data.nemo()
self.j2kfile = glymur.data.goodstuff()
# Save the output of dumping nemo.jp2 for more than one test.
lines = ['JPEG 2000 Signature Box (jP ) @ (0, 12)',
' Signature: 0d0a870a',
'File Type Box (ftyp) @ (12, 20)',
' Brand: jp2 ',
" Compatibility: ['jp2 ']",
'JP2 Header Box (jp2h) @ (32, 45)',
' Image Header Box (ihdr) @ (40, 22)',
' Size: [728 1296 3]',
' Bitdepth: 8',
' Signed: False',
' Compression: wavelet',
' Colorspace Unknown: False',
' Colour Specification Box (colr) @ (62, 15)',
' Method: enumerated colorspace',
' Precedence: 0',
' Colorspace: sRGB',
'Contiguous Codestream Box (jp2c) @ (77, 1632355)',
' Main header:',
' SOC marker segment @ (85, 0)',
' SIZ marker segment @ (87, 47)',
' Profile: 2',
' Reference Grid Height, Width: (728 x 1296)',
' Vertical, Horizontal Reference Grid Offset: '
+ '(0 x 0)',
' Reference Tile Height, Width: (728 x 1296)',
' Vertical, Horizontal Reference Tile Offset: '
+ '(0 x 0)',
' Bitdepth: (8, 8, 8)',
' Signed: (False, False, False)',
' Vertical, Horizontal Subsampling: '
+ '((1, 1), (1, 1), (1, 1))',
' COD marker segment @ (136, 12)',
' Coding style:',
' Entropy coder, without partitions',
' SOP marker segments: False',
' EPH marker segments: False',
' Coding style parameters:',
' Progression order: LRCP',
' Number of layers: 1',
' Multiple component transformation usage: '
+ 'reversible',
' Number of resolutions: 6',
' Code block height, width: (64 x 64)',
' Wavelet transform: 5-3 reversible',
' Precinct size: default, 2^15 x 2^15',
' Code block context:',
' Selective arithmetic coding bypass: '
+ 'False',
' Reset context probabilities on '
+ 'coding pass boundaries: False',
' Termination on each coding pass: False',
' Vertically stripe causal context: '
+ 'False',
' Predictable termination: False',
' Segmentation symbols: False',
' QCD marker segment @ (150, 19)',
' Quantization style: no quantization, '
+ '2 guard bits',
' 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)]']
self.expected_plain = '\n'.join(lines)
# Reset printoptions for every test.
glymur.set_printoptions(short=False, xml=True, codestream=True)
def tearDown(self):
pass
def test_printopt_no_codestr_or_xml(self):
"""Verify printed output when codestream=False and xml=False"""
glymur.set_printoptions(codestream=False, xml=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# 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, fixtures.nemo_dump_no_codestream_no_xml)
def test_printoptions_no_codestream(self):
"""Verify printed output when codestream=False"""
glymur.set_printoptions(codestream=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# 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, fixtures.nemo_dump_no_codestream)
def test_printoptions_no_xml(self):
"""Verify printed output when xml=False"""
glymur.set_printoptions(xml=False)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# 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, fixtures.nemo_dump_no_xml)
def test_printoptions_short(self):
"""Verify printed output when short=True"""
glymur.set_printoptions(short=True)
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# 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, fixtures.nemo_dump_short)
def test_asoc_label_box(self):
"""verify printing of asoc, label boxes"""
# Construct a fake file with an asoc and a label box, as
@ -185,18 +156,18 @@ class TestPrintingNeedsLib(unittest.TestCase):
def test_jp2dump(self):
"""basic jp2dump test"""
with patch('sys.stdout', new=StringIO()) as fake_out:
glymur.jp2dump(self._plain_nemo_file)
glymur.jp2dump(self.jp2file)
actual = fake_out.getvalue().strip()
# 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.expected_plain)
self.assertEqual(actual, fixtures.nemo_dump_full)
def test_entire_file(self):
"""verify output from printing entire file"""
j = glymur.Jp2k(self._plain_nemo_file)
j = glymur.Jp2k(self.jp2file)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
actual = fake_out.getvalue().strip()
@ -206,18 +177,7 @@ class TestPrintingNeedsLib(unittest.TestCase):
lst = lst[1:]
actual = '\n'.join(lst)
self.assertEqual(actual, self.expected_plain)
class TestPrinting(unittest.TestCase):
"""Test suite for printing where the libraries are not needed"""
def setUp(self):
# Save sys.stdout.
self.jp2file = glymur.data.nemo()
def tearDown(self):
pass
self.assertEqual(actual, fixtures.nemo_dump_full)
def test_coc_segment(self):
"""verify printing of COC segment"""