From f8406fc20a5ac0f0c28cd74b46ba610a3739e085 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 24 Jul 2013 11:59:45 -0400 Subject: [PATCH 1/2] No real need to import the test packages. Discovery works just fine. #84 --- glymur/__init__.py | 1 - glymur/test/__init__.py | 11 ----------- glymur/test/test_conformance.py | 4 ---- 3 files changed, 16 deletions(-) diff --git a/glymur/__init__.py b/glymur/__init__.py index fb37ef0..fb1c504 100644 --- a/glymur/__init__.py +++ b/glymur/__init__.py @@ -6,7 +6,6 @@ from .jp2k import Jp2k from .jp2dump import jp2dump from . import data -#from . import test def runtests(): diff --git a/glymur/test/__init__.py b/glymur/test/__init__.py index 89c7eea..e69de29 100644 --- a/glymur/test/__init__.py +++ b/glymur/test/__init__.py @@ -1,11 +0,0 @@ -from .test_callbacks import TestCallbacks as callbacks -from .test_codestream import TestCodestream as codestream -from .test_config import TestSuite as config -from .test_jp2k import TestJp2k as jp2k -from .test_icc import TestICC as icc -from .test_printing import TestPrinting as printing -from .test_opj_suite import TestSuite as suite -from .test_opj_suite import TestSuiteDump as suitedump -from .test_opj_suite_write import TestSuiteWrite as suitew -from .test_opj_suite_neg import TestSuiteNegative as suiteneg -from .test_jp2box import TestJp2Boxes as box diff --git a/glymur/test/test_conformance.py b/glymur/test/test_conformance.py index ec179c2..9f40b3e 100644 --- a/glymur/test/test_conformance.py +++ b/glymur/test/test_conformance.py @@ -20,15 +20,11 @@ try: format_corpus_data_root = os.environ['FORMAT_CORPUS_DATA_ROOT'] except KeyError: format_corpus_data_root = None -except: - raise try: opj_data_root = os.environ['OPJ_DATA_ROOT'] except KeyError: opj_data_root = None -except: - raise @unittest.skipIf(format_corpus_data_root is None, From 0edbbfd2e1860b46a8a102512b55d274adc04691 Mon Sep 17 00:00:00 2001 From: John Evans Date: Wed, 24 Jul 2013 12:02:29 -0400 Subject: [PATCH 2/2] Added tests for jpch, jplh superboxes. Closes #84 --- glymur/jp2box.py | 195 ++++++++++++++++++++++++++++++++++++- glymur/test/test_jp2box.py | 39 ++++++++ 2 files changed, 232 insertions(+), 2 deletions(-) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 15471a5..b5637f6 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -520,6 +520,195 @@ class ChannelDefinitionBox(Jp2kBox): return box +class CodestreamHeaderBox(Jp2kBox): + """Container for codestream header box information. + + Attributes + ---------- + box_id : str + 4-character identifier for the box. + length : int + length of the box in bytes. + offset : int + offset of the box from the start of the file. + longname : str + more verbose description of the box. + box : list + List of boxes contained in this superbox. + """ + def __init__(self, length=0, offset=-1): + Jp2kBox.__init__(self, box_id='jpch', longname='Codestream Header') + self.length = length + self.offset = offset + self.box = [] + + def __str__(self): + msg = Jp2kBox.__str__(self) + for box in self.box: + boxstr = str(box) + + # Add indentation. + strs = [('\n ' + x) for x in boxstr.split('\n')] + msg += ''.join(strs) + return msg + + @staticmethod + def parse(fptr, offset, length): + """Parse codestream header box. + + Parameters + ---------- + fptr : file + Open file object. + offset : int + Start position of box in bytes. + length : int + Length of the box in bytes. + + Returns + ------- + AssociationBox instance + """ + box = CodestreamHeaderBox(length=length, offset=offset) + + # The codestream header box is a superbox, so go ahead and parse its + # child boxes. + box.box = box.parse_superbox(fptr) + + return box + + +class CompositingLayerHeaderBox(Jp2kBox): + """Container for compositing layer header box information. + + Attributes + ---------- + box_id : str + 4-character identifier for the box. + length : int + length of the box in bytes. + offset : int + offset of the box from the start of the file. + longname : str + more verbose description of the box. + box : list + List of boxes contained in this superbox. + """ + def __init__(self, length=0, offset=-1): + Jp2kBox.__init__(self, box_id='jplh', + longname='Compositing Layer Header') + self.length = length + self.offset = offset + self.box = [] + + def __str__(self): + msg = Jp2kBox.__str__(self) + for box in self.box: + boxstr = str(box) + + # Add indentation. + strs = [('\n ' + x) for x in boxstr.split('\n')] + msg += ''.join(strs) + return msg + + @staticmethod + def parse(fptr, offset, length): + """Parse compositing layer header box. + + Parameters + ---------- + fptr : file + Open file object. + offset : int + Start position of box in bytes. + length : int + Length of the box in bytes. + + Returns + ------- + AssociationBox instance + """ + box = CompositingLayerHeaderBox(length=length, offset=offset) + + # This box is a superbox, so go ahead and parse its # child boxes. + box.box = box.parse_superbox(fptr) + + return box + + +class JP2HeaderBox(Jp2kBox): + """Container for JP2 header box information. + + Attributes + ---------- + box_id : str + 4-character identifier for the box. + length : int + length of the box in bytes. + offset : int + offset of the box from the start of the file. + longname : str + more verbose description of the box. + box : list + List of boxes contained in this superbox. + """ + def __init__(self, length=0, offset=-1): + Jp2kBox.__init__(self, box_id='jp2h', longname='JP2 Header') + self.length = length + self.offset = offset + self.box = [] + + def __str__(self): + msg = Jp2kBox.__str__(self) + for box in self.box: + boxstr = str(box) + + # Add indentation. + strs = [('\n ' + x) for x in boxstr.split('\n')] + msg += ''.join(strs) + return msg + + def write(self, fptr): + """Write a JP2 Header box to file. + """ + # Write the contained boxes, then come back and write the length. + orig_pos = fptr.tell() + fptr.write(struct.pack('>I', 0)) + fptr.write('jp2h'.encode()) + for box in self.box: + box.write(fptr) + + end_pos = fptr.tell() + fptr.seek(orig_pos) + fptr.write(struct.pack('>I', end_pos - orig_pos)) + fptr.seek(end_pos) + + @staticmethod + def parse(fptr, offset, length): + """Parse JPEG 2000 header box. + + Parameters + ---------- + fptr : file + Open file object. + offset : int + Start position of box in bytes. + length : int + Length of the box in bytes. + + Returns + ------- + JP2HeaderBox instance + """ + box = JP2HeaderBox(length=length, offset=offset) + + # The JP2 header box is a superbox, so go ahead and parse its child + # boxes. + box.box = box.parse_superbox(fptr) + + return box + + class ComponentMappingBox(Jp2kBox): """Container for channel identification information. @@ -2509,11 +2698,13 @@ _BOX_WITH_ID = { 'cdef': ChannelDefinitionBox, 'cmap': ComponentMappingBox, 'colr': ColourSpecificationBox, - 'jP ': JPEG2000SignatureBox, 'ftyp': FileTypeBox, 'ihdr': ImageHeaderBox, - 'jp2h': JP2HeaderBox, + 'jP ': JPEG2000SignatureBox, + 'jpch': CodestreamHeaderBox, + 'jplh': CompositingLayerHeaderBox, 'jp2c': ContiguousCodestreamBox, + 'jp2h': JP2HeaderBox, 'lbl ': LabelBox, 'pclr': PaletteBox, 'res ': ResolutionBox, diff --git a/glymur/test/test_jp2box.py b/glymur/test/test_jp2box.py index 178d4a0..8fdd3c3 100644 --- a/glymur/test/test_jp2box.py +++ b/glymur/test/test_jp2box.py @@ -19,6 +19,11 @@ from glymur.jp2box import * from glymur.core import COLOR, OPACITY from glymur.core import RED, GREEN, BLUE, GREY, WHOLE_IMAGE +try: + format_corpus_data_root = os.environ['FORMAT_CORPUS_DATA_ROOT'] +except KeyError: + format_corpus_data_root = None + # Doc tests should be run as well. def load_tests(loader, tests, ignore): @@ -673,5 +678,39 @@ class TestJp2Boxes(unittest.TestCase): with self.assertRaises(IOError): j2k.wrap(tfile.name, boxes=boxes) + +class TestJpxBoxes(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + @unittest.skipIf(format_corpus_data_root is None, + "FORMAT_CORPUS_DATA_ROOT environment variable not set") + def test_codestream_header(self): + # Should recognize codestream header box. + jfile = os.path.join(format_corpus_data_root, + 'jp2k-formats/balloon.jpf') + jpx = Jp2k(jfile) + + # This superbox just happens to be empty. + self.assertEqual(jpx.box[4].box_id, 'jpch') + self.assertEqual(len(jpx.box[4].box), 0) + + @unittest.skipIf(format_corpus_data_root is None, + "FORMAT_CORPUS_DATA_ROOT environment variable not set") + def test_compositing_layer_header(self): + # Should recognize compositing layer header box. + jfile = os.path.join(format_corpus_data_root, + 'jp2k-formats/balloon.jpf') + jpx = Jp2k(jfile) + + # This superbox just happens to be empty. + self.assertEqual(jpx.box[5].box_id, 'jplh') + self.assertEqual(len(jpx.box[5].box), 0) + + if __name__ == "__main__": unittest.main()