Merge branch 'issue217' into devel

This commit is contained in:
jevans 2014-04-23 20:06:53 -04:00
commit 275324fce8
10 changed files with 399 additions and 83 deletions

View file

@ -2,6 +2,7 @@
"""
Part of glymur.
"""
from collections import OrderedDict
import pprint
import re
import struct
@ -10,12 +11,6 @@ import warnings
import lxml.etree as ET
if sys.hexversion < 0x02070000:
# pylint: disable=F0401,E0611
from ordereddict import OrderedDict
else:
from collections import OrderedDict
def xml(raw_data):
"""
XMP data to be parsed as XML.

View file

@ -188,15 +188,7 @@ class Codestream(object):
while True:
read_buffer = fptr.read(2)
try:
self._marker_id, = struct.unpack('>H', read_buffer)
except struct.error:
# Treat this as a warning.
msg = "Marker had length {0} instead of expected length of 2 "
msg += "bytes. Codestream parsing terminated."
warnings.warn(msg.format(len(read_buffer)))
break
self._marker_id, = struct.unpack('>H', read_buffer)
self._offset = fptr.tell() - 2
if self._marker_id == 0xff90 and header_only:

View file

@ -1011,7 +1011,7 @@ class ContiguousCodestreamBox(Jp2kBox):
if self._main_header is None:
if self._filename is not None:
with open(self._filename, 'rb') as fptr:
fptr.seek(self._offset + 8)
fptr.seek(self.main_header_offset)
main_header = Codestream(fptr, self._length, header_only=True)
self._main_header = main_header
return self._main_header
@ -1059,7 +1059,6 @@ class ContiguousCodestreamBox(Jp2kBox):
length=length, offset=offset)
box._filename = fptr.name
box._length = length
box._offset = offset
return box
@ -1110,7 +1109,7 @@ class DataReferenceBox(Jp2kBox):
"""
self._write_validate()
# Very similar to the say a superbox is written.
# Very similar to the way a superbox is written.
orig_pos = fptr.tell()
fptr.write(struct.pack('>I4s', 0, b'dtbl'))
@ -1176,7 +1175,7 @@ class DataReferenceBox(Jp2kBox):
box = DataEntryURLBox.parse(box_fptr, 0, box_length)
# Need to adjust the box start to that of the "real" file.
box.start = offset + box_offset
box.offset = offset + 8 + box_offset
data_entry_url_box_list.append(box)
# Point to the next embedded URL box.
@ -1341,7 +1340,10 @@ class FragmentListBox(Jp2kBox):
self._dispatch_validation_error(msg, writing=writing)
def __repr__(self):
msg = "glymur.jp2box.FragmentListBox()"
msg = "glymur.jp2box.FragmentListBox({0}, {1}, {2})"
msg = msg.format(str(self.fragment_offset),
str(self.fragment_length),
str(self.data_reference))
return msg
def __str__(self):
@ -1426,7 +1428,8 @@ class FragmentTableBox(Jp2kBox):
self.box = box if box is not None else []
def __repr__(self):
msg = "glymur.jp2box.FragmentTableBox()"
msg = "glymur.jp2box.FragmentTableBox(box={0})"
msg = msg.format(None) if (len(self.box) == 0) else msg.format(self.box)
return msg
def __str__(self):
@ -1884,6 +1887,12 @@ class PaletteBox(Jp2kBox):
msg = "The length of the 'bits_per_component' and the 'signed' "
msg += "members must equal the number of columns of the palette."
self._dispatch_validation_error(msg, writing=writing)
bps = self.bits_per_component
if writing and not all(b == bps[0] for b in bps):
# We don't support writing palettes with bit depths that are
# different.
msg = "Writing palettes with varying bit depths is not supported."
self._dispatch_validation_error(msg, writing=writing)
def __repr__(self):
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
@ -1925,26 +1934,14 @@ class PaletteBox(Jp2kBox):
fptr.write(write_buffer)
bps = self.bits_per_component
if all(b == bps[0] for b in bps):
# All components are the same. Writing is straightforward.
if self.bits_per_component[0] <= 8:
write_buffer = memoryview(self.palette.astype(np.uint8))
elif self.bits_per_component[0] <= 16:
write_buffer = memoryview(self.palette.astype(np.uint16))
elif self.bits_per_component[0] <= 32:
write_buffer = memoryview(self.palette.astype(np.uint32))
fptr.write(write_buffer)
else:
# Not all the components are the same. More general, but much rarer
# case. Does this even happen.
code_dict = {8: 'B', 16: 'H', 32: 'I'}
codes = ''
for width in bps:
codes += code_dict[width]
fmt = '>' + codes
for row in self.palette:
write_buffer = struct.pack(fmt, *row)
fptr.write(write_buffer)
# All components are the same. Writing is straightforward.
if self.bits_per_component[0] <= 8:
write_buffer = memoryview(self.palette.astype(np.uint8))
elif self.bits_per_component[0] <= 16:
write_buffer = memoryview(self.palette.astype(np.uint16))
elif self.bits_per_component[0] <= 32:
write_buffer = memoryview(self.palette.astype(np.uint32))
fptr.write(write_buffer)
@classmethod
def parse(cls, fptr, offset, length):
@ -2635,18 +2632,19 @@ class NumberListBox(Jp2kBox):
msg += 'the rendered result'
elif (association >> 24) == 1:
idx = association & 0x00FFFFFF
msg += 'Codestream {0}'
msg += 'codestream {0}'
msg = msg.format(idx)
elif (association >> 24) == 2:
idx = association & 0x00FFFFFF
msg += 'Compositing Layer {0}'
msg += 'compositing layer {0}'
msg = msg.format(idx)
else:
msg += 'unrecognized'
return msg
def __repr__(self):
msg = 'glymur.jp2box.NumberListBox()'
msg = 'glymur.jp2box.NumberListBox(associations={0})'
msg = msg.format(self.associations)
return msg
@classmethod

View file

@ -547,13 +547,13 @@ class Jp2k(Jp2kBox):
opj2.setup_encoder(codec, cparams, image)
if _OPENJP2_IS_OFFICIAL_V2:
if re.match("2.0", version.openjpeg_version) is not None:
fptr = libc.fopen(self.filename, 'wb')
strm = opj2.stream_create_default_file_stream(fptr, False)
stack.callback(opj2.stream_destroy, strm)
stack.callback(libc.fclose, fptr)
else:
# This routine introduced in 2.0 devel series.
# Introduced in 2.1 devel series.
strm = opj2.stream_create_default_file_stream_v3(self.filename,
False)
stack.callback(opj2.stream_destroy_v3, strm)
@ -1080,17 +1080,17 @@ class Jp2k(Jp2kBox):
layer=layer, tile=tile, area=area)
with ExitStack() as stack:
if hasattr(opj2.OPENJP2,
'opj_stream_create_default_file_stream_v3'):
filename = self.filename
stream = opj2.stream_create_default_file_stream_v3(filename,
True)
stack.callback(opj2.stream_destroy_v3, stream)
else:
if re.match("2.0", version.openjpeg_version):
fptr = libc.fopen(self.filename, 'rb')
stack.callback(libc.fclose, fptr)
stream = opj2.stream_create_default_file_stream(fptr, True)
stack.callback(opj2.stream_destroy, stream)
else:
# API change in 2.1+
filename = self.filename
stream = opj2.stream_create_default_file_stream_v3(filename,
True)
stack.callback(opj2.stream_destroy_v3, stream)
codec = opj2.create_decompress(self._codec_format)
stack.callback(opj2.destroy_codec, codec)
@ -1147,12 +1147,6 @@ class Jp2k(Jp2kBox):
Bitdepth: (8, 8, 8)
Signed: (False, False, False)
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))
Raises
------
IOError
If the file is JPX with more than one codestream and no JP2
compatibility is advertised.
"""
with open(self.filename, 'rb') as fptr:
if self._codec_format == opj2.CODEC_J2K:
@ -1161,11 +1155,6 @@ class Jp2k(Jp2kBox):
else:
ftyp = self.box[1]
box = [x for x in self.box if x.box_id == 'jp2c']
if len(box) > 1 and 'jp2 ' not in ftyp.compatibility_list:
msg = "If more than one codestream exists, JP2 "
msg += "compatibiltity must be advertised by the FileType "
msg += "box."
raise RuntimeError(msg)
fptr.seek(box[0].offset)
read_buffer = fptr.read(8)
(box_length, _) = struct.unpack('>I4s', read_buffer)
@ -1183,7 +1172,7 @@ class Jp2k(Jp2kBox):
return codestream
def component2dtype(component):
def _component2dtype(component):
"""Take an OpenJPEG component structure and determine the numpy datatype.
Parameters
@ -1474,7 +1463,7 @@ def extract_image_cube(image):
"""
ncomps = image.contents.numcomps
component = image.contents.comps[0]
dtype = component2dtype(component)
dtype = _component2dtype(component)
nrows = component.h
ncols = component.w
@ -1508,7 +1497,7 @@ def extract_image_bands(image):
for k in range(image.contents.numcomps):
component = image.contents.comps[k]
dtype = component2dtype(component)
dtype = _component2dtype(component)
nrows = component.h
ncols = component.w
@ -1698,7 +1687,7 @@ def _validate_compression_params(img_array, cparams):
msg = "{0}D imagery is not allowed.".format(img_array.ndim)
raise IOError(msg)
if _OPENJP2_IS_OFFICIAL_V2:
if re.match("2.0", version.openjpeg_version) is not None:
if (((img_array.ndim != 2) and
(img_array.shape[2] != 1 and img_array.shape[2] != 3))):
msg = "Writing images is restricted to single-channel "
@ -1711,14 +1700,6 @@ def _validate_compression_params(img_array, cparams):
msg = "Only uint8 and uint16 images are currently supported."
raise RuntimeError(msg)
# Need to known if openjp2 library is the officially release v2.0.0 or not.
_OPENJP2_IS_OFFICIAL_V2 = False
if opj2.OPENJP2 is not None:
if opj2.version() == '2.0.0':
if not hasattr(opj2.OPENJP2,
'opj_stream_create_default_file_stream_v3'):
_OPENJP2_IS_OFFICIAL_V2 = True
_COLORSPACE_MAP = {'rgb': opj2.CLRSPC_SRGB,
'gray': opj2.CLRSPC_GRAY,
'grey': opj2.CLRSPC_GRAY,

View file

@ -622,3 +622,57 @@ cinema2k_profile = """SIZ marker segment @ (2, 47)
Bitdepth: (12, 12, 12)
Signed: (False, False, False)
Vertical, Horizontal Subsampling: ((1, 1), (1, 1), (1, 1))"""
jplh_color_group_box = r"""Compositing Layer Header Box (jplh) @ (314227, 31)
Colour Group Box (cgrp) @ (314235, 23)
Colour Specification Box (colr) @ (314243, 15)
Method: enumerated colorspace
Precedence: 0
Colorspace: sRGB"""
fragment_list_box = r"""Fragment List Box (flst) @ (-1, 0)
Offset 0: 89
Fragment Length 0: 1132288
Data Reference 0: 0"""
number_list_box = r"""Number List Box (nlst) @ (-1, 0)
Association[0]: the rendered result
Association[1]: codestream 0
Association[2]: compositing layer 0"""
goodstuff = r"""Codestream:
SOC marker segment @ (0, 0)
SIZ marker segment @ (2, 47)
Profile: no profile
Reference Grid Height, Width: (800 x 480)
Vertical, Horizontal Reference Grid Offset: (0 x 0)
Reference Tile Height, Width: (800 x 480)
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 @ (51, 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 @ (65, 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)]"""

View file

@ -543,6 +543,16 @@ class TestPaletteBox(unittest.TestCase):
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
def test_writing_with_different_bitdepths(self):
"""Bitdepths must be the same when writing."""
palette = np.array([[255, 0, 255], [0, 255, 0]], dtype=np.uint16)
bps = (8, 16, 8)
signed = (False, False, False)
pclr = glymur.jp2box.PaletteBox(palette, bits_per_component=bps,
signed=signed)
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile:
with self.assertRaises(IOError):
pclr.write(tfile)
class TestAppend(unittest.TestCase):
"""Tests for append method."""
@ -733,6 +743,49 @@ class TestWrap(unittest.TestCase):
boxes = [box.box_id for box in jp2.box]
self.assertEqual(boxes, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
def test_wrap_jp2_Lzero(self):
"""Wrap jp2 with jp2c box length is zero"""
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
with open(self.jp2file, 'rb') as ifile:
tfile.write(ifile.read())
# Rewrite with codestream length as zero.
tfile.seek(3223)
tfile.write(struct.pack('>I', 0))
tfile.flush()
jp2 = Jp2k(tfile.name)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
jp2 = jp2.wrap(tfile2.name)
boxes = [box for box in jp2.box]
self.assertEqual(boxes[3].length, 1132296)
def test_wrap_jp2_Lone(self):
"""Wrap jp2 with jp2c box length is 1, implies Q field"""
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
with open(self.jp2file, 'rb') as ifile:
tfile.write(ifile.read(3223))
# Write new L, T, Q fields
tfile.write(struct.pack('>I4sQ', 1, b'jp2c', 1132296 + 8))
# skip over the old L, T fields
ifile.seek(3231)
tfile.write(ifile.read())
tfile.flush()
jp2 = Jp2k(tfile.name)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile2:
jp2 = jp2.wrap(tfile2.name)
boxes = [box for box in jp2.box]
self.assertEqual(boxes[3].length, 1132296 + 8)
def test_wrap_compatibility_not_jp2(self):
"""File type compatibility must contain jp2"""
jp2 = Jp2k(self.jp2file)
boxes = [box for box in jp2.box]
boxes[1].compatibility_list = ['jpx ']
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
with self.assertRaises(IOError):
jp2.wrap(tfile.name, boxes=boxes)
def test_empty_jp2h(self):
"""JP2H box list cannot be empty."""
jp2 = Jp2k(self.jp2file)
@ -992,6 +1045,59 @@ class TestRepr(unittest.TestCase):
self.assertTrue(isinstance(newbox, glymur.jp2box.JPEG2000SignatureBox))
self.assertEqual(newbox.signature, (13, 10, 135, 10))
def test_free(self):
"""Should be able to instantiate a free box"""
free = glymur.jp2box.FreeBox()
# Test the representation instantiation.
newbox = eval(repr(free))
self.assertTrue(isinstance(newbox, glymur.jp2box.FreeBox))
def test_nlst(self):
"""Should be able to instantiate a number list box"""
assn = (0, 1, 2)
nlst = glymur.jp2box.NumberListBox(assn)
# Test the representation instantiation.
newbox = eval(repr(nlst))
self.assertTrue(isinstance(newbox, glymur.jp2box.NumberListBox))
self.assertEqual(newbox.associations, (0, 1, 2))
def test_ftbl(self):
"""Should be able to instantiate a fragment table box"""
ftbl = glymur.jp2box.FragmentTableBox()
# Test the representation instantiation.
newbox = eval(repr(ftbl))
self.assertTrue(isinstance(newbox, glymur.jp2box.FragmentTableBox))
def test_dref(self):
"""Should be able to instantiate a data reference box"""
dref = glymur.jp2box.DataReferenceBox()
# Test the representation instantiation.
newbox = eval(repr(dref))
self.assertTrue(isinstance(newbox, glymur.jp2box.DataReferenceBox))
def test_flst(self):
"""Should be able to instantiate a fragment list box"""
flst = glymur.jp2box.FragmentListBox([89], [1132288], [0])
# Test the representation instantiation.
newbox = eval(repr(flst))
self.assertTrue(isinstance(newbox, glymur.jp2box.FragmentListBox))
self.assertEqual(newbox.fragment_offset, [89])
self.assertEqual(newbox.fragment_length, [1132288])
self.assertEqual(newbox.data_reference, [0])
def test_default_cgrp(self):
"""Should be able to instantiate a color group box"""
cgrp = glymur.jp2box.ColourGroupBox()
# Test the representation instantiation.
newbox = eval(repr(cgrp))
self.assertTrue(isinstance(newbox, glymur.jp2box.ColourGroupBox))
def test_default_ftyp(self):
"""Should be able to instantiate a FileTypeBox"""
ftyp = glymur.jp2box.FileTypeBox()

View file

@ -3,6 +3,7 @@
Test suite specifically targeting JPX box layout.
"""
import ctypes
import os
import struct
import sys
@ -52,6 +53,7 @@ class TestJPXWrap(unittest.TestCase):
tfile1.write(fptr.read())
tfile1.flush()
jp2_1 = Jp2k(tfile1.name)
jp2h = jp2_1.box[2]
jp2c = [box for box in jp2_1.box if box.box_id == 'jp2c'][0]
@ -61,12 +63,13 @@ class TestJPXWrap(unittest.TestCase):
clen = []
dr_idx = []
coff.append(jp2c.offset + 8)
coff.append(jp2c.main_header_offset)
clen.append(jp2c.length - (coff[0] - jp2c.offset))
dr_idx.append(1)
# Make the url box for this codestream.
url1 = DataEntryURLBox(0, [0, 0, 0], 'file://' + tfile1.name)
url1_name_len = len(url1.url) + 1
with tempfile.NamedTemporaryFile(suffix='.jp2') as tfile2:
@ -74,7 +77,7 @@ class TestJPXWrap(unittest.TestCase):
jp2_2 = j2k.wrap(tfile2.name)
jp2c = [box for box in jp2_2.box if box.box_id == 'jp2c'][0]
coff.append(jp2c.offset + 8)
coff.append(jp2c.main_header_offset)
clen.append(jp2c.length - (coff[0] - jp2c.offset))
dr_idx.append(2)
@ -85,7 +88,7 @@ class TestJPXWrap(unittest.TestCase):
FileTypeBox(brand='jpx ',
compatibility_list=['jpx ',
'jp2 ', 'jpxb']),
jp2_1.box[2]]
jp2h]
with tempfile.NamedTemporaryFile(suffix='.jpx') as tjpx:
for box in boxes:
box.write(tjpx)
@ -99,6 +102,15 @@ class TestJPXWrap(unittest.TestCase):
dtbl.write(tjpx)
tjpx.flush()
jpx_no_jp2c = Jp2k(tjpx.name)
jpx_boxes = [box.box_id for box in jpx_no_jp2c.box]
self.assertEqual(jpx_boxes, ['jP ', 'ftyp', 'jp2h',
'ftbl', 'dtbl'])
self.assertEqual(jpx_no_jp2c.box[4].DR[0].offset, 141)
offset = 141 + 8 + 4 + url1_name_len
self.assertEqual(jpx_no_jp2c.box[4].DR[1].offset, offset)
def test_jp2_with_jpx_box(self):
"""If the brand is jp2, then no jpx boxes are allowed."""
jp2 = Jp2k(self.jp2file)
@ -154,8 +166,8 @@ class TestJPXWrap(unittest.TestCase):
self.assertEqual(jpx.box[-1].box[0].box_id, 'colr')
self.assertEqual(jpx.box[-1].box[1].box_id, 'colr')
def test_cgrp_neg(self):
"""Can't write a cgrp with anything but colr sub boxes"""
def test_label_neg(self):
"""Can't write a label box embedded in any old box."""
jp2 = Jp2k(self.jp2file)
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
@ -173,6 +185,26 @@ class TestJPXWrap(unittest.TestCase):
with self.assertRaises(IOError):
jpx = jp2.wrap(tfile.name, boxes=boxes)
def test_cgrp_neg(self):
"""Can't write a cgrp with anything but colr sub boxes"""
jp2 = Jp2k(self.jp2file)
boxes = [jp2.box[idx] for idx in [0, 1, 2, 4]]
# The ftyp box must be modified to jpx.
boxes[1].brand = 'jpx '
boxes[1].compatibility_list = ['jp2 ', 'jpxb']
the_xml = ET.fromstring('<?xml version="1.0"?><data>0</data>')
xmlb = glymur.jp2box.XMLBox(xml=the_xml)
box = [xmlb]
cgrp = glymur.jp2box.ColourGroupBox(box=box)
boxes.append(cgrp)
with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile:
with self.assertRaises(IOError):
jpx = jp2.wrap(tfile.name, boxes=boxes)
def test_ftbl(self):
"""Write a fragment table box."""
# Add a negative test where offset < 0
@ -459,7 +491,7 @@ class TestJPX(unittest.TestCase):
self.assertEqual(jpx.box[2].box_id, 'rreq')
self.assertEqual(type(jpx.box[2]),
glymur.jp2box.ReaderRequirementsBox)
self.assertEqual(jpx.box[2].standard_flag,
self.asserwrite_buffertEqual(jpx.box[2].standard_flag,
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
@unittest.skip("Requires unnecessarily complicated code")
@ -552,6 +584,52 @@ class TestJPX(unittest.TestCase):
self.assertEqual(jpx.box[-1].box[0].fragment_length, (170246,))
self.assertEqual(jpx.box[-1].box[0].data_reference, (3,))
def test_rreq3(self):
"""Verify that we can read a rreq box with mask length 3 bytes"""
rreq_buffer = ctypes.create_string_buffer(74)
struct.pack_into('>I4s', rreq_buffer, 0, 74, b'rreq')
# mask length
struct.pack_into('>B', rreq_buffer, 8, 3)
# fuam, dcm. 6 bytes, two sets of 3.
lst = (255, 224, 0, 0, 31, 252)
struct.pack_into('>BBBBBB', rreq_buffer, 9, *lst)
# number of standard features: 11
struct.pack_into('>H', rreq_buffer, 15, 11)
standard_flags = [5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20]
standard_masks = [8388608, 4194304, 2097152, 1048576, 524288, 262144,
131072, 65536, 32768, 16384, 8192]
for j in range(len(standard_flags)):
mask = (standard_masks[j] >> 16,
standard_masks[j] & 0x0000ffff>> 8,
standard_masks[j] & 0x000000ff)
struct.pack_into('>HBBB', rreq_buffer, 17 + j * 5,
standard_flags[j], *mask)
# num vendor features: 0
struct.pack_into('>H', rreq_buffer, 72, 0)
# Ok, done with the box, we can now insert it into the jpx file after
# the ftyp box.
with tempfile.NamedTemporaryFile(suffix=".jpx") as ofile:
with open(self.jpxfile, 'rb') as ifile:
ofile.write(ifile.read(40))
ofile.write(rreq_buffer)
ofile.write(ifile.read())
ofile.flush()
jpx = Jp2k(ofile.name)
self.assertEqual(jpx.box[2].box_id, 'rreq')
self.assertEqual(type(jpx.box[2]),
glymur.jp2box.ReaderRequirementsBox)
self.assertEqual(jpx.box[2].standard_flag,
(5, 42, 45, 2, 18, 19, 1, 8, 12, 31, 20))
def test_nlst(self):
"""Verify that we can handle a number list box."""
j = Jp2k(self.jpxfile)

View file

@ -779,6 +779,17 @@ class TestParsing(unittest.TestCase):
with self.assertWarns(UserWarning):
jp2 = Jp2k(filename)
@unittest.skip("blah")
def test_main_header(self):
"""Verify that the main header is not loaded when parsing turned off."""
# The hidden _main_header attribute should show up after accessing it.
glymur.set_parseoptions(codestream=False)
jp2 = Jp2k(self.jp2file)
jp2c = jp2.box[4]
self.assertIsNone(jp2c._main_header)
main_header = jp2c.main_header
self.assertIsNotNone(jp2c._main_header)
@unittest.skipIf(OPJ_DATA_ROOT is None,
"OPJ_DATA_ROOT environment variable not set")
class TestJp2kOpjDataRoot(unittest.TestCase):

View file

@ -16,6 +16,13 @@ import unittest
import numpy as np
try:
import skimage.io
skimage.io.use_plugin('freeimage', 'imread')
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = True
except ((ImportError, RuntimeError)):
_HAS_SKIMAGE_FREEIMAGE_SUPPORT = False
from .fixtures import OPJ_DATA_ROOT, opj_data_file, read_image
from .fixtures import NO_READ_BACKEND, NO_READ_BACKEND_MSG
@ -35,6 +42,21 @@ class TestSuiteNegative(unittest.TestCase):
def tearDown(self):
pass
@unittest.skipIf(not _HAS_SKIMAGE_FREEIMAGE_SUPPORT,
"Cannot read input image without scikit-image/freeimage")
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_cinema2K_bad_frame_rate(self):
"""Cinema2k frame rate must be either 24 or 48."""
relfile = 'input/nonregression/X_5_2K_24_235_CBR_STEM24_000.tif'
infile = opj_data_file(relfile)
data = skimage.io.imread(infile)
with tempfile.NamedTemporaryFile(suffix='.j2k') as tfile:
j = Jp2k(tfile.name, 'wb')
with self.assertRaises(IOError):
j.write(data, cinema2k=36)
@unittest.skipIf(NO_READ_BACKEND, NO_READ_BACKEND_MSG)
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
def test_psnr_with_cratios(self):

View file

@ -50,6 +50,18 @@ class TestPrinting(unittest.TestCase):
def tearDown(self):
pass
def test_codestream(self):
"""Should be able to print a raw codestream."""
j = glymur.Jp2k(self.j2kfile)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j)
actual = fake_out.getvalue().strip()
# Remove the file line, as that is filesystem-dependent.
lines = actual.split('\n')
actual = '\n'.join(lines[1:])
self.assertEqual(actual, fixtures.codestream)
def test_version_info(self):
"""Should be able to print(glymur.version.info)"""
with patch('sys.stdout', new=StringIO()) as fake_out:
@ -598,6 +610,63 @@ class TestPrinting(unittest.TestCase):
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_flst(self):
"""Verify printing of fragment list box."""
flst = glymur.jp2box.FragmentListBox([89], [1132288], [0])
with patch('sys.stdout', new=StringIO()) as fake_out:
print(flst)
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.fragment_list_box)
def test_dref(self):
"""Verify printing of data reference box."""
dref = glymur.jp2box.DataReferenceBox()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(dref)
actual = fake_out.getvalue().strip()
self.assertEqual(actual, 'Data Reference Box (dtbl) @ (-1, 0)')
def test_jplh_cgrp(self):
"""Verify printing of compositing layer header box, color group box."""
jpx = glymur.Jp2k(self.jpxfile)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(jpx.box[7])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.jplh_color_group_box)
def test_free(self):
"""Verify printing of Free box."""
free = glymur.jp2box.FreeBox()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(free)
actual = fake_out.getvalue().strip()
self.assertEqual(actual, 'Free Box (free) @ (-1, 0)')
def test_nlst(self):
"""Verify printing of number list box."""
assn = (0, 16777216, 33554432)
nlst = glymur.jp2box.NumberListBox(assn)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(nlst)
actual = fake_out.getvalue().strip()
self.assertEqual(actual, fixtures.number_list_box)
def test_ftbl(self):
"""Verify printing of fragment table box."""
ftbl = glymur.jp2box.FragmentTableBox()
with patch('sys.stdout', new=StringIO()) as fake_out:
print(ftbl)
actual = fake_out.getvalue().strip()
self.assertEqual(actual, 'Fragment Table Box (ftbl) @ (-1, 0)')
def test_jpch(self):
"""Verify printing of JPCH box."""
jpx = glymur.Jp2k(self.jpxfile)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(jpx.box[3])
actual = fake_out.getvalue().strip()
self.assertEqual(actual, 'Codestream Header Box (jpch) @ (887, 8)')
@unittest.skipIf(sys.hexversion < 0x03000000,
"Ordered dicts not printing well in 2.7")
def test_exif_uuid(self):
@ -893,6 +962,17 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
def test_componentmapping_box_alpha(self):
"""Verify __repr__ method on cmap box."""
cmap = glymur.jp2box.ComponentMappingBox(component_index=(0, 0, 0),
mapping_type=(1, 1, 1),
palette_index=(0, 1, 2))
newbox = eval(repr(cmap))
self.assertEqual(newbox.box_id, 'cmap')
self.assertEqual(newbox.component_index, (0, 0, 0))
self.assertEqual(newbox.mapping_type, (1, 1, 1))
self.assertEqual(newbox.palette_index, (0, 1, 2))
def test_palette7(self):
"""verify printing of pclr box"""
filename = opj_data_file('input/conformance/file9.jp2')
@ -905,7 +985,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
expected = '\n'.join(lines)
self.assertEqual(actual, expected)
@unittest.skip("file7 no longer has a rreq")
def test_rreq(self):
"""verify printing of reader requirements box"""
filename = opj_data_file('input/nonregression/text_GBR.jp2')