Changed "tojp2" to "wrap". Added goodstuff.j2k

This commit is contained in:
John Evans 2013-06-23 18:06:15 -04:00
commit 79a26135bf
5 changed files with 152 additions and 24 deletions

View file

@ -1,8 +1,10 @@
"""Shipping JPEG 2000 files. """Shipping JPEG 2000 files.
There is only one JP2 file at the moment, "nemo.jp2", converted from the These include:
original JPEG photo of the aftermath of NEMO, the nor'easter that shutdown nemo.jp2: converted from the original JPEG photo of the aftermath of NEMO,
Boston in February of 2013. the nor'easter that shutdown Boston in February of 2013.
goodstuff.j2k: my favorite bevorage.
""" """
import pkg_resources import pkg_resources
@ -17,3 +19,14 @@ def nemo():
""" """
file = pkg_resources.resource_filename(__name__, "nemo.jp2") file = pkg_resources.resource_filename(__name__, "nemo.jp2")
return file return file
def goodstuff():
"""Shortcut for specifying path to goodstuff.j2k.
Returns
-------
file : str
Platform-independent path to goodstuff.j2k.
"""
file = pkg_resources.resource_filename(__name__, "goodstuff.j2k")
return file

BIN
glymur/data/goodstuff.j2k Normal file

Binary file not shown.

View file

@ -211,6 +211,10 @@ class ColourSpecificationBox(Jp2kBox):
def _write(self, f): def _write(self, f):
"""Write an Colour Specification box to file. """Write an Colour Specification box to file.
""" """
if self.colorspace is None:
msg = "Writing Colour Specification boxes without enumerated "
msg += "colorspaces is not supported at this time."
raise NotImplementedError(msg)
length = 15 if self.icc_profile is None else 11 + len(self.icc_profile) length = 15 if self.icc_profile is None else 11 + len(self.icc_profile)
f.write(struct.pack('>I', length)) f.write(struct.pack('>I', length))
f.write('colr'.encode()) f.write('colr'.encode())
@ -925,6 +929,12 @@ class JP2HeaderBox(Jp2kBox):
def _write(self, f): def _write(self, f):
"""Write a JP2 Header box to file. """Write a JP2 Header box to file.
""" """
# Make sure they are in proper order.
if self.box[0].id != 'ihdr':
msg = "The first box in the JP2 Header superbox must be the Image "
msg += "Header box."
raise IOError(msg)
# Write the contained boxes, then come back and write the length. # Write the contained boxes, then come back and write the length.
orig_pos = f.tell() orig_pos = f.tell()
f.write(struct.pack('>I', 0)) f.write(struct.pack('>I', 0))

View file

@ -399,13 +399,15 @@ class Jp2k(Jp2kBox):
self._parse() self._parse()
def tojp2(self, filename): def wrap(self, filename, boxes=None):
"""Write a codestream, wrapping it in the JP2 format. """Write a codestream, wrapping it in the JP2 format.
Parameters Parameters
---------- ----------
filename : str filename : str
JP2 file to be created from a raw codestream. JP2 file to be created from a raw codestream.
boxes : list
JP2 box definitions to define the JP2 file format.
""" """
if len(self.box) > 0: if len(self.box) > 0:
msg = "This method can only be used on files consisting only of " msg = "This method can only be used on files consisting only of "
@ -413,17 +415,19 @@ class Jp2k(Jp2kBox):
msg += "already has the JP2 format." msg += "already has the JP2 format."
raise IOError(msg) raise IOError(msg)
boxes = [JPEG2000SignatureBox(), if boxes is None:
FileTypeBox(), # Try to create a reasonable default.
JP2HeaderBox()] boxes = [JPEG2000SignatureBox(),
c = self.get_codestream() FileTypeBox(),
height = c.segment[1].Ysiz JP2HeaderBox()]
width = c.segment[1].Xsiz c = self.get_codestream()
num_components = len(c.segment[1].XRsiz) height = c.segment[1].Ysiz
boxes[2].box = [ImageHeaderBox(height=height, width = c.segment[1].Xsiz
width=width, num_components = len(c.segment[1].XRsiz)
num_components=num_components), boxes[2].box = [ImageHeaderBox(height=height,
ColourSpecificationBox(colorspace=SRGB)] width=width,
num_components=num_components),
ColourSpecificationBox(colorspace=SRGB)]
with open(filename, 'wb') as ofile: with open(filename, 'wb') as ofile:
for box in boxes: for box in boxes:
box._write(ofile) box._write(ofile)

View file

@ -107,10 +107,10 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(b.id, 'jp2c') self.assertEqual(b.id, 'jp2c')
self.assertEqual(b.main_header, []) self.assertEqual(b.main_header, [])
def test_tojp2(self): def test_wrap(self):
j2k = Jp2k(self.raw_codestream) j2k = Jp2k(self.raw_codestream)
with tempfile.NamedTemporaryFile(suffix=".jp2", mode="wb") as tfile: with tempfile.NamedTemporaryFile(suffix=".jp2", mode="wb") as tfile:
j2k.tojp2(tfile.name) j2k.wrap(tfile.name)
jp2 = Jp2k(tfile.name) jp2 = Jp2k(tfile.name)
self.assertEqual(len(jp2.box), 4) self.assertEqual(len(jp2.box), 4)
@ -159,20 +159,121 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB)
self.assertIsNone(jp2.box[2].box[1].icc_profile) self.assertIsNone(jp2.box[2].box[1].icc_profile)
def test_default_layout_but_with_specified_boxes(self):
j2k = Jp2k(self.raw_codestream)
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox()]
c = j2k.get_codestream()
height = c.segment[1].Ysiz
width = c.segment[1].Xsiz
num_components = len(c.segment[1].XRsiz)
boxes[2].box = [ImageHeaderBox(height=height,
width=width,
num_components=num_components),
ColourSpecificationBox(colorspace=glymur.core.SRGB)]
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
j2k.wrap(tfile.name, boxes=boxes)
jp2 = Jp2k(tfile.name)
self.assertEqual(len(jp2.box), 4)
self.assertEqual(jp2.box[0].id, 'jP ')
self.assertEqual(jp2.box[0].offset, 0)
self.assertEqual(jp2.box[0].length, 12)
self.assertEqual(jp2.box[0].longname, 'JPEG 2000 Signature')
self.assertEqual(jp2.box[1].id, 'ftyp')
self.assertEqual(jp2.box[1].offset, 12)
self.assertEqual(jp2.box[1].length, 20)
self.assertEqual(jp2.box[1].longname, 'File Type')
self.assertEqual(jp2.box[2].id, 'jp2h')
self.assertEqual(jp2.box[2].offset, 32)
self.assertEqual(jp2.box[2].length, 45)
self.assertEqual(jp2.box[2].longname, 'JP2 Header')
self.assertEqual(jp2.box[3].id, 'jp2c')
self.assertEqual(jp2.box[3].offset, 77)
self.assertEqual(jp2.box[3].length, 1133427)
# jp2h super box
self.assertEqual(len(jp2.box[2].box), 2)
self.assertEqual(jp2.box[2].box[0].id, 'ihdr')
self.assertEqual(jp2.box[2].box[0].offset, 40)
self.assertEqual(jp2.box[2].box[0].length, 22)
self.assertEqual(jp2.box[2].box[0].longname, 'Image Header')
self.assertEqual(jp2.box[2].box[0].height, 1456)
self.assertEqual(jp2.box[2].box[0].width, 2592)
self.assertEqual(jp2.box[2].box[0].num_components, 3)
self.assertEqual(jp2.box[2].box[0].bits_per_component, 8)
self.assertEqual(jp2.box[2].box[0].signed, False)
self.assertEqual(jp2.box[2].box[0].compression, 7)
self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False)
self.assertEqual(jp2.box[2].box[0].ip_provided, False)
self.assertEqual(jp2.box[2].box[1].id, 'colr')
self.assertEqual(jp2.box[2].box[1].offset, 62)
self.assertEqual(jp2.box[2].box[1].length, 15)
self.assertEqual(jp2.box[2].box[1].longname,
'Colour Specification')
self.assertEqual(jp2.box[2].box[1].precedence, 0)
self.assertEqual(jp2.box[2].box[1].approximation, 0)
self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB)
self.assertIsNone(jp2.box[2].box[1].icc_profile)
def test_image_header_box_not_first_in_jp2_header(self): def test_image_header_box_not_first_in_jp2_header(self):
# The specification says that ihdr must be the first box in jp2h. # The specification says that ihdr must be the first box in jp2h.
self.assertTrue(False) j2k = Jp2k(self.raw_codestream)
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox()]
c = j2k.get_codestream()
height = c.segment[1].Ysiz
width = c.segment[1].Xsiz
num_components = len(c.segment[1].XRsiz)
boxes[2].box = [ColourSpecificationBox(colorspace=glymur.core.SRGB),
ImageHeaderBox(height=height,
width=width,
num_components=num_components)]
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
with self.assertRaises(IOError):
j2k.wrap(tfile.name, boxes=boxes)
def test_color_specification_box_with_icc_profile(self):
# Not sure how this should be done so don't allow it.
self.assertTrue(False)
def test_tojp2_on_jp2(self): def test_color_specification_box_with_out_enumerated_colorspace(self):
j2k = Jp2k(self.raw_codestream)
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox()]
c = j2k.get_codestream()
height = c.segment[1].Ysiz
width = c.segment[1].Xsiz
num_components = len(c.segment[1].XRsiz)
boxes[2].box = [ImageHeaderBox(height=height,
width=width,
num_components=num_components),
ColourSpecificationBox(colorspace=None)]
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
with self.assertRaises(NotImplementedError):
j2k.wrap(tfile.name, boxes=boxes)
def test_wrap_on_jp2(self):
# Should not use "tojp2" method on file that is already jp2. # Should not use "tojp2" method on file that is already jp2.
j = glymur.Jp2k(self.jp2file) j = glymur.Jp2k(self.jp2file)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile: with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
with self.assertRaises(IOError): with self.assertRaises(IOError):
j.tojp2(tfile.name) j.wrap(tfile.name)
def test_default_xml(self):
# Should be able to write an xml box.
self.assertTrue(False)
def test_default_component_definition(self):
# Should be able to specify a component definition box in order to,
# say, create an image with an alpha layer.
self.assertTrue(False)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()