Success with default conversion from j2c to jp2.
This commit is contained in:
parent
19336fc15e
commit
2bb1d874b0
3 changed files with 127 additions and 12 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue