Success with default conversion from j2c to jp2.

This commit is contained in:
John Evans 2013-06-23 11:36:56 -04:00
commit 2bb1d874b0
3 changed files with 127 additions and 12 deletions

View file

@ -167,7 +167,7 @@ class ColourSpecificationBox(Jp2kBox):
colorspace is not None, then icc_profile must be empty.
"""
def __init__(self, method=ENUMERATED_COLORSPACE, precedence=0,
approximation=1, colorspace=None, icc_profile=None, **kwargs):
approximation=0, colorspace=None, icc_profile=None, **kwargs):
Jp2kBox.__init__(self, id='', longname='Colour Specification')
if colorspace is not None and icc_profile is not None:
@ -208,6 +208,20 @@ class ColourSpecificationBox(Jp2kBox):
return msg
def _write(self, f):
"""Write an Colour Specification box to file.
"""
length = 15 if self.icc_profile is None else 11 + len(self.icc_profile)
f.write(struct.pack('>I', length))
f.write('colr'.encode())
buffer = struct.pack('>BBBI',
self.method,
self.precedence,
self.approximation,
self.colorspace)
f.write(buffer)
@staticmethod
def _parse(f, id, offset, length):
"""Parse JPEG 2000 color specification box.
@ -756,6 +770,25 @@ class ImageHeaderBox(Jp2kBox):
self.colorspace_unknown)
return msg
def _write(self, f):
"""Write an Image Header box to file.
"""
f.write(struct.pack('>I', 22))
f.write('ihdr'.encode())
# signedness and bps are stored together in a single byte
bit_depth_signedness = 0x80 if self.signed else 0x00
bit_depth_signedness |= self.bits_per_component - 1
buffer = struct.pack('>IIHBBBB',
self.height,
self.width,
self.num_components,
bit_depth_signedness,
self.compression,
1 if self.colorspace_unknown else 0,
1 if self.ip_provided else 0)
f.write(buffer)
@staticmethod
def _parse(f, id, offset, length):
"""Parse JPEG 2000 image header box.

View file

@ -407,17 +407,37 @@ class Jp2k(Jp2kBox):
filename : str
JP2 file to be created from a raw codestream.
"""
if len(self.box) > 0:
msg = "This method can only be used on files consisting only of "
msg += "a raw codestream. It cannot be used on a file that "
msg += "already has the JP2 format."
raise IOError(msg)
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox(),
ContiguousCodestreamBox()]
boxes[2].box = [ImageHeaderBox(height=1456,
width=2592,
num_components=3),
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 fp:
with open(filename, 'wb') as ofile:
for box in boxes:
box._write(fp)
box._write(ofile)
# The codestream gets written last.
ofile.write(struct.pack('>I', self.length + 8))
ofile.write('jp2c'.encode())
with open(self.filename, 'rb') as ifile:
ofile.write(ifile.read())
ofile.flush()
def read(self, reduce=0, layer=0, area=None, tile=None, verbose=False):
"""Read a JPEG 2000 image.

View file

@ -28,7 +28,7 @@ class TestJp2Boxes(unittest.TestCase):
j = Jp2k(jp2file)
c = [box for box in j.box if box.id == 'jp2c'][0]
with tempfile.NamedTemporaryFile(suffix='.jp2', delete=False) as ofile:
with tempfile.NamedTemporaryFile(suffix='.j2c', delete=False) as ofile:
with open(jp2file, 'rb') as ifile:
# Everything up until the jp2c box.
ifile.seek(c.offset+8)
@ -74,7 +74,7 @@ class TestJp2Boxes(unittest.TestCase):
b = glymur.jp2box.ColourSpecificationBox(colorspace=glymur.core.SRGB)
self.assertEqual(b.method, glymur.core.ENUMERATED_COLORSPACE)
self.assertEqual(b.precedence, 0)
self.assertEqual(b.approximation, 1)
self.assertEqual(b.approximation, 0)
self.assertEqual(b.colorspace, glymur.core.SRGB)
self.assertIsNone(b.icc_profile)
@ -108,9 +108,71 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(b.main_header, [])
def test_tojp2(self):
j = Jp2k(self.raw_codestream)
j2k = Jp2k(self.raw_codestream)
with tempfile.NamedTemporaryFile(suffix=".jp2", mode="wb") as tfile:
j2k.tojp2(tfile.name)
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)
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):
# Should not use "tojp2" method on file that is already jp2.
j = glymur.Jp2k(self.jp2file)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
j.tojp2(tfile.name)
with self.assertRaises(IOError):
j.tojp2(tfile.name)
if __name__ == "__main__":
unittest.main()