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.
There is only one JP2 file at the moment, "nemo.jp2", converted from the
original JPEG photo of the aftermath of NEMO, the nor'easter that shutdown
Boston in February of 2013.
These include:
nemo.jp2: converted from the original JPEG photo of the aftermath of NEMO,
the nor'easter that shutdown Boston in February of 2013.
goodstuff.j2k: my favorite bevorage.
"""
import pkg_resources
@ -17,3 +19,14 @@ def nemo():
"""
file = pkg_resources.resource_filename(__name__, "nemo.jp2")
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):
"""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)
f.write(struct.pack('>I', length))
f.write('colr'.encode())
@ -925,6 +929,12 @@ class JP2HeaderBox(Jp2kBox):
def _write(self, f):
"""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.
orig_pos = f.tell()
f.write(struct.pack('>I', 0))

View file

@ -399,13 +399,15 @@ class Jp2k(Jp2kBox):
self._parse()
def tojp2(self, filename):
def wrap(self, filename, boxes=None):
"""Write a codestream, wrapping it in the JP2 format.
Parameters
----------
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:
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."
raise IOError(msg)
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox()]
c = self.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=SRGB)]
if boxes is None:
# Try to create a reasonable default.
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox()]
c = self.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=SRGB)]
with open(filename, 'wb') as ofile:
for box in boxes:
box._write(ofile)

View file

@ -107,10 +107,10 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(b.id, 'jp2c')
self.assertEqual(b.main_header, [])
def test_tojp2(self):
def test_wrap(self):
j2k = Jp2k(self.raw_codestream)
with tempfile.NamedTemporaryFile(suffix=".jp2", mode="wb") as tfile:
j2k.tojp2(tfile.name)
j2k.wrap(tfile.name)
jp2 = Jp2k(tfile.name)
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.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):
# 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.
j = glymur.Jp2k(self.jp2file)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
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__":
unittest.main()