Changed "tojp2" to "wrap". Added goodstuff.j2k
This commit is contained in:
parent
2bb1d874b0
commit
79a26135bf
5 changed files with 152 additions and 24 deletions
|
|
@ -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
BIN
glymur/data/goodstuff.j2k
Normal file
Binary file not shown.
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue