From cf4317df57190dd33711b0ade9929eac48bec4da Mon Sep 17 00:00:00 2001 From: jevans Date: Wed, 19 Mar 2014 20:14:46 -0400 Subject: [PATCH] Added Colour group box support. #188 --- glymur/jp2box.py | 70 ++++++++++++++++++++++++++++++++++ glymur/test/test_jp2box_jpx.py | 43 +++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/glymur/jp2box.py b/glymur/jp2box.py index ea8ca62..969ed2c 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -754,6 +754,75 @@ class CodestreamHeaderBox(Jp2kBox): return box +class ColourGroupBox(Jp2kBox): + """Container for colour group 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, box=None, length=0, offset=-1): + Jp2kBox.__init__(self, box_id='cgrp', longname='Colour Group') + self.length = length + self.offset = offset + self.box = box if box is not None else [] + + def __repr__(self): + msg = "glymur.jp2box.ColourGroupBox(box={0})".format(self.box) + return msg + + def __str__(self): + msg = self._str_superbox() + return msg + + def _validate(self, writing=True): + """Verify that the box obeys the specifications.""" + if any([box.box_id != 'colr' for box in self.box]): + msg = "Colour group boxes can only contain colour specification " + msg += "boxes." + self._dispatch_validation_error(msg, writing=writing) + + def write(self, fptr): + """Write an association box to file. + """ + self._validate(writing=True) + self._write_superbox(fptr) + + @staticmethod + def parse(fptr, offset, length): + """Parse colour group box. + + Parameters + ---------- + fptr : file + Open file object. + offset : int + Start position of box in bytes. + length : int + Length of the box in bytes. + + Returns + ------- + ColourGroupBox instance + """ + box = ColourGroupBox(length=length, offset=offset) + + # The colour group 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. @@ -3101,6 +3170,7 @@ class UUIDBox(Jp2kBox): _BOX_WITH_ID = { b'asoc': AssociationBox, b'cdef': ChannelDefinitionBox, + b'cgrp': ColourGroupBox, b'cmap': ComponentMappingBox, b'colr': ColourSpecificationBox, b'dtbl': DataReferenceBox, diff --git a/glymur/test/test_jp2box_jpx.py b/glymur/test/test_jp2box_jpx.py index 65fb925..75b9792 100644 --- a/glymur/test/test_jp2box_jpx.py +++ b/glymur/test/test_jp2box_jpx.py @@ -16,6 +16,7 @@ import glymur from glymur import Jp2k from glymur.jp2box import DataEntryURLBox, FileTypeBox, JPEG2000SignatureBox from glymur.jp2box import DataReferenceBox, FragmentListBox, FragmentTableBox +from glymur.jp2box import ColourSpecificationBox @unittest.skipIf(os.name == "nt", "Temporary file issue on window.") class TestJPXWrap(unittest.TestCase): @@ -109,6 +110,48 @@ class TestJPXWrap(unittest.TestCase): with self.assertRaises(IOError): jp2.wrap(tfile.name, boxes=boxes) + def test_cgrp(self): + """Write a color group box.""" + 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'] + + colr_rgb = ColourSpecificationBox(colorspace=glymur.core.SRGB) + colr_gr = ColourSpecificationBox(colorspace=glymur.core.GREYSCALE) + box = [colr_rgb, colr_gr] + + cgrp = glymur.jp2box.ColourGroupBox(box=box) + boxes.append(cgrp) + + with tempfile.NamedTemporaryFile(suffix=".jpx") as tfile: + jpx = jp2.wrap(tfile.name, boxes=boxes) + + self.assertEqual(jpx.box[-1].box_id, 'cgrp') + 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""" + 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'] + + lblb = glymur.jp2box.LabelBox("Just a test") + box = [lblb] + + 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