Don't error out when progression order is invalid. #186
Instead of a regular dictionary for the display of progression order, use a subclass of defaultdict instead. The defaultdict's __missing__ method is overridden to supply a custom error message that used the offending key. Everybody wins!!!
This commit is contained in:
parent
2708e85be1
commit
e15a8a6dba
4 changed files with 92 additions and 27 deletions
|
|
@ -17,6 +17,7 @@ codestreams.
|
|||
# the base Segment class.
|
||||
# pylint: disable=R0903
|
||||
|
||||
import collections
|
||||
import math
|
||||
import struct
|
||||
import sys
|
||||
|
|
@ -30,12 +31,26 @@ from .core import WAVELET_XFORM_5X3_REVERSIBLE
|
|||
from .core import _CAPABILITIES_DISPLAY
|
||||
from .lib import openjp2 as opj2
|
||||
|
||||
_PROGRESSION_ORDER_DISPLAY = {
|
||||
LRCP: 'LRCP',
|
||||
RLCP: 'RLCP',
|
||||
RPCL: 'RPCL',
|
||||
PCRL: 'PCRL',
|
||||
CPRL: 'CPRL'}
|
||||
class _keydefaultdict(collections.defaultdict):
|
||||
"""Unlisted keys help form their own error message.
|
||||
|
||||
Normally defaultdict uses a factory function with no input arguments, but
|
||||
that's not quite the behavior we want.
|
||||
"""
|
||||
def __missing__(self, key):
|
||||
if self.default_factory is None:
|
||||
raise KeyError(key)
|
||||
else:
|
||||
ret = self[key] = self.default_factory(key)
|
||||
return ret
|
||||
|
||||
_factory = lambda x: '{0} (invalid)'.format(x)
|
||||
_PROGRESSION_ORDER_DISPLAY = _keydefaultdict(_factory,
|
||||
{ LRCP: 'LRCP',
|
||||
RLCP: 'RLCP',
|
||||
RPCL: 'RPCL',
|
||||
PCRL: 'PCRL',
|
||||
CPRL: 'CPRL'})
|
||||
|
||||
_WAVELET_TRANSFORM_DISPLAY = {
|
||||
WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible',
|
||||
|
|
@ -371,6 +386,9 @@ class Codestream(object):
|
|||
numbytes = offset + 2 + length - fptr.tell()
|
||||
spcod = fptr.read(numbytes)
|
||||
spcod = np.frombuffer(spcod, dtype=np.uint8)
|
||||
if spcod[0] not in [LRCP, RLCP, RPCL, PCRL, CPRL]:
|
||||
msg = "Invalid progression order in COD segment: {0}."
|
||||
warnings.warn(msg.format(spcod[0]))
|
||||
|
||||
sop = (scod & 2) > 0
|
||||
eph = (scod & 4) > 0
|
||||
|
|
|
|||
|
|
@ -587,3 +587,26 @@ issue_183_colr = """Colour Specification Box (colr) @ (62, 12)
|
|||
Method: restricted ICC profile
|
||||
Precedence: 0
|
||||
ICC Profile: None"""
|
||||
|
||||
|
||||
# Progression order is invalid.
|
||||
issue_186_progression_order = """COD marker segment @ (174, 12)
|
||||
Coding style:
|
||||
Entropy coder, without partitions
|
||||
SOP marker segments: False
|
||||
EPH marker segments: False
|
||||
Coding style parameters:
|
||||
Progression order: 33 (invalid)
|
||||
Number of layers: 1
|
||||
Multiple component transformation usage: reversible
|
||||
Number of resolutions: 6
|
||||
Code block height, width: (32 x 32)
|
||||
Wavelet transform: 9-7 irreversible
|
||||
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"""
|
||||
|
|
|
|||
|
|
@ -29,8 +29,39 @@ class TestCodestream(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_siz_segment_ssiz_unsigned(self):
|
||||
"""ssiz attribute to be removed in future release"""
|
||||
j = Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
|
||||
# The ssiz attribute was simply a tuple of raw bytes.
|
||||
# The first 7 bits are interpreted as the bitdepth, the MSB determines
|
||||
# whether or not it is signed.
|
||||
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
||||
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
class TestCodestreamOpjData(unittest.TestCase):
|
||||
"""Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT"""
|
||||
|
||||
def setUp(self):
|
||||
self.jp2file = glymur.data.nemo()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_invalid_progression_order(self):
|
||||
"""Should still be able to parse even if prog order is invalid."""
|
||||
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
|
||||
if sys.hexversion < 0x03000000:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
Jp2k(jfile)
|
||||
else:
|
||||
with self.assertWarns(UserWarning):
|
||||
Jp2k(jfile)
|
||||
|
||||
def test_tile_height_is_zero(self):
|
||||
"""Zero tile height should not cause an exception."""
|
||||
filename = opj_data_file('input/nonregression/2539.pdf.SIGFPE.706.1712.jp2')
|
||||
|
|
@ -43,8 +74,6 @@ class TestCodestream(unittest.TestCase):
|
|||
Jp2k(filename)
|
||||
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
def test_reserved_marker_segment(self):
|
||||
"""Reserved marker segments are ok."""
|
||||
|
|
@ -76,8 +105,6 @@ class TestCodestream(unittest.TestCase):
|
|||
self.assertEqual(codestream.segment[2].length, 3)
|
||||
self.assertEqual(codestream.segment[2].data, b'\x00')
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
@unittest.skipIf(sys.hexversion < 0x03020000,
|
||||
"Uses features introduced in 3.2.")
|
||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||
|
|
@ -109,8 +136,6 @@ class TestCodestream(unittest.TestCase):
|
|||
self.assertEqual(codestream.segment[2].length, 3)
|
||||
self.assertEqual(codestream.segment[2].data, b'\x00')
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_psot_is_zero(self):
|
||||
"""Psot=0 in SOT is perfectly legal. Issue #78."""
|
||||
filename = os.path.join(OPJ_DATA_ROOT,
|
||||
|
|
@ -123,19 +148,6 @@ class TestCodestream(unittest.TestCase):
|
|||
self.assertEqual(codestream.segment[-1].marker_id, 'EOC')
|
||||
|
||||
|
||||
def test_siz_segment_ssiz_unsigned(self):
|
||||
"""ssiz attribute to be removed in future release"""
|
||||
j = Jp2k(self.jp2file)
|
||||
codestream = j.get_codestream()
|
||||
|
||||
# The ssiz attribute was simply a tuple of raw bytes.
|
||||
# The first 7 bits are interpreted as the bitdepth, the MSB determines
|
||||
# whether or not it is signed.
|
||||
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
|
||||
|
||||
|
||||
@unittest.skipIf(OPJ_DATA_ROOT is None,
|
||||
"OPJ_DATA_ROOT environment variable not set")
|
||||
def test_siz_segment_ssiz_signed(self):
|
||||
"""ssiz attribute to be removed in future release"""
|
||||
filename = os.path.join(OPJ_DATA_ROOT, 'input/conformance/p0_03.j2k')
|
||||
|
|
|
|||
|
|
@ -645,6 +645,18 @@ class TestPrintingOpjDataRoot(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_invalid_progression_order(self):
|
||||
"""Should still be able to print even if prog order is invalid."""
|
||||
jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2')
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
jp2 = Jp2k(jfile)
|
||||
codestream = jp2.get_codestream()
|
||||
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||
print(codestream.segment[2])
|
||||
actual = fake_out.getvalue().strip()
|
||||
self.assertEqual(actual, fixtures.issue_186_progression_order)
|
||||
|
||||
def test_crg(self):
|
||||
"""verify printing of CRG segment"""
|
||||
filename = opj_data_file('input/conformance/p0_03.j2k')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue