Starting on the _write method.

This commit is contained in:
John Evans 2013-06-23 07:41:31 -04:00
commit 19336fc15e
4 changed files with 89 additions and 5 deletions

View file

@ -63,7 +63,8 @@ class Jp2kBox(object):
def _write(self, f):
"""Must be implemented in a subclass.
"""
raise NotImplementedError("Not supported for this box.")
msg = "Not supported for {0} box.".format(self.longname)
raise NotImplementedError(msg)
def _parse_superbox(self, f):
"""Parse a superbox (box consisting of nothing but other boxes.
@ -617,8 +618,8 @@ class FileTypeBox(Jp2kBox):
self.brand = 'jp2 '
if 'minor_version' not in kwargs.keys():
self.minor_version = 0
if 'compatibility_box' not in kwargs.keys():
self.compatibility_box = ['jp2 ']
if 'compatibility_list' not in kwargs.keys():
self.compatibility_list = ['jp2 ']
def __str__(self):
lst = [Jp2kBox.__str__(self),
@ -629,6 +630,18 @@ class FileTypeBox(Jp2kBox):
return msg
def _write(self, f):
"""Write a File Type box to file.
"""
length = 16 + 4*len(self.compatibility_list)
f.write(struct.pack('>I', length))
f.write('ftyp'.encode())
f.write(self.brand.encode())
f.write(struct.pack('>I', self.minor_version))
for item in self.compatibility_list:
f.write(item.encode())
@staticmethod
def _parse(f, id, offset, length):
"""Parse JPEG 2000 file type box.
@ -876,6 +889,21 @@ class JP2HeaderBox(Jp2kBox):
msg += ''.join(strs)
return msg
def _write(self, f):
"""Write a JP2 Header box to file.
"""
# Write the contained boxes, then come back and write the length.
orig_pos = f.tell()
f.write(struct.pack('>I', 0))
f.write('jp2h'.encode())
for box in self.box:
box._write(f)
end_pos = f.tell()
f.seek(orig_pos)
f.write(struct.pack('>I', end_pos - orig_pos))
f.seek(end_pos)
@staticmethod
def _parse(f, id, offset, length):
"""Parse JPEG 2000 header box.
@ -938,6 +966,13 @@ class JPEG2000SignatureBox(Jp2kBox):
msg = msg.format(*self.signature)
return msg
def _write(self, f):
"""Write a JPEG 2000 Signature box to file.
"""
f.write(struct.pack('>I', 12))
f.write(self.id.encode())
f.write(struct.pack('>BBBB', *self.signature))
@staticmethod
def _parse(f, id, offset, length):
"""Parse JPEG 2000 signature box.

View file

@ -17,7 +17,8 @@ import numpy as np
from .codestream import Codestream
from .core import progression_order
from .jp2box import Jp2kBox
from .core import SRGB
from .jp2box import *
from .lib import openjp2 as opj2
_cspace_map = {'rgb': opj2._CLRSPC_SRGB,
@ -398,6 +399,26 @@ class Jp2k(Jp2kBox):
self._parse()
def tojp2(self, filename):
"""Write a codestream, wrapping it in the JP2 format.
Parameters
----------
filename : str
JP2 file to be created from a raw codestream.
"""
boxes = [JPEG2000SignatureBox(),
FileTypeBox(),
JP2HeaderBox(),
ContiguousCodestreamBox()]
boxes[2].box = [ImageHeaderBox(height=1456,
width=2592,
num_components=3),
ColourSpecificationBox(colorspace=SRGB)]
with open(filename, 'wb') as fp:
for box in boxes:
box._write(fp)
def read(self, reduce=0, layer=0, area=None, tile=None, verbose=False):
"""Read a JPEG 2000 image.

View file

@ -6,3 +6,4 @@ from .test_printing import TestPrinting as printing
from .test_opj_suite import TestSuite as suite
from .test_opj_suite_write import TestSuiteWrite as suitew
from .test_opj_suite_neg import TestSuiteNegative as suiteneg
from .test_jp2box import TestJp2Boxes as box

View file

@ -1,10 +1,12 @@
import doctest
import tempfile
import unittest
import numpy as np
import pkg_resources
import glymur
from glymur import Jp2k
from glymur.jp2box import *
@ -18,6 +20,26 @@ def load_tests(loader, tests, ignore):
"Missing openjp2 library.")
class TestJp2Boxes(unittest.TestCase):
@classmethod
def setUpClass(cls):
# We need a raw codestream, so use the one in nemo.jp2.
jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
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 open(jp2file, 'rb') as ifile:
# Everything up until the jp2c box.
ifile.seek(c.offset+8)
ofile.write(ifile.read(c.length))
cls.raw_codestream = ofile.name
@classmethod
def tearDownClass(cls):
os.unlink(cls.raw_codestream)
def setUp(self):
self.jp2file = pkg_resources.resource_filename(glymur.__name__,
"data/nemo.jp2")
@ -35,7 +57,7 @@ class TestJp2Boxes(unittest.TestCase):
b = glymur.jp2box.FileTypeBox()
self.assertEqual(b.brand, 'jp2 ')
self.assertEqual(b.minor_version, 0)
self.assertEqual(b.compatibility_box, ['jp2 '])
self.assertEqual(b.compatibility_list, ['jp2 '])
def test_default_ImageHeaderBox(self):
# Should be able to instantiate an image header box.
@ -85,5 +107,10 @@ class TestJp2Boxes(unittest.TestCase):
self.assertEqual(b.id, 'jp2c')
self.assertEqual(b.main_header, [])
def test_tojp2(self):
j = Jp2k(self.raw_codestream)
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
j.tojp2(tfile.name)
if __name__ == "__main__":
unittest.main()