Merge branch 'issue149' into devel
This commit is contained in:
commit
c9a25e47c6
3 changed files with 95 additions and 9 deletions
11
CHANGES.txt
11
CHANGES.txt
|
|
@ -1,8 +1,9 @@
|
||||||
Feb 03, 2014 - Added read support for JPX free, number list, data reference,
|
Feb 03, 2014 - Added write support for Palette and Component Mapping boxes.
|
||||||
fragment table, and fragment list boxes. Palette box now a 2D numpy array
|
Added read support for JPX free, number list, data reference, fragment
|
||||||
instead of a list of 1D arrays. JP2 super box constructors now take
|
table, and fragment list boxes. Palette box now a 2D numpy array instead
|
||||||
optional box list argument. Fixed bug where JPX files with more than one
|
of a list of 1D arrays. JP2 super box constructors now take optional box
|
||||||
codestream but advertising jp2 compatibility were not being read.
|
list argument. Fixed bug where JPX files with more than one codestream
|
||||||
|
but advertising jp2 compatibility were not being read.
|
||||||
|
|
||||||
Jan 28, 2014 - v0.5.10 Fixed bad warning when reader requirements box mask
|
Jan 28, 2014 - v0.5.10 Fixed bad warning when reader requirements box mask
|
||||||
length is unsupported.
|
length is unsupported.
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ References
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
import io
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
|
|
@ -23,6 +24,7 @@ import sys
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
import xml.etree.cElementTree as ET
|
import xml.etree.cElementTree as ET
|
||||||
|
|
||||||
if sys.hexversion < 0x02070000:
|
if sys.hexversion < 0x02070000:
|
||||||
# pylint: disable=F0401,E0611
|
# pylint: disable=F0401,E0611
|
||||||
from ordereddict import OrderedDict
|
from ordereddict import OrderedDict
|
||||||
|
|
@ -719,6 +721,20 @@ class ComponentMappingBox(Jp2kBox):
|
||||||
msg = msg.format(self.component_index[k], k)
|
msg = msg.format(self.component_index[k], k)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def write(self, fptr):
|
||||||
|
"""Write a Component Mapping box to file.
|
||||||
|
"""
|
||||||
|
length = 8 + 4 * len(self.component_index)
|
||||||
|
write_buffer = struct.pack('>I4s', length, self.box_id.encode())
|
||||||
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
|
for j in range(len(self.component_index)):
|
||||||
|
write_buffer = struct.pack('>HBB',
|
||||||
|
self.component_index[j],
|
||||||
|
self.mapping_type[j],
|
||||||
|
self.palette_index[j])
|
||||||
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse(fptr, offset, length):
|
def parse(fptr, offset, length):
|
||||||
"""Parse component mapping box.
|
"""Parse component mapping box.
|
||||||
|
|
@ -1527,9 +1543,9 @@ class PaletteBox(Jp2kBox):
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
msg = "glymur.jp2box.PaletteBox({0}, bits_per_component={1}, "
|
msg = "glymur.jp2box.PaletteBox(ndarray, bits_per_component={0}, "
|
||||||
msg += "signed={2})"
|
msg += "signed={1})"
|
||||||
msg = msg.format(self.palette, self.bits_per_component, self.signed)
|
msg = msg.format(self.bits_per_component, self.signed)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -1537,6 +1553,49 @@ class PaletteBox(Jp2kBox):
|
||||||
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
|
msg += '\n Size: ({0} x {1})'.format(*self.palette.shape)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def write(self, fptr):
|
||||||
|
"""Write a Palette box to file.
|
||||||
|
"""
|
||||||
|
# Box length is usual header (8)
|
||||||
|
# + num entries NE (2) + num columns NC (1)
|
||||||
|
# + (bps/8, /signed) for each column (3) + bps * NC
|
||||||
|
# +
|
||||||
|
bytes_per_row = sum(self.bits_per_component) / 8
|
||||||
|
bytes_per_palette = bytes_per_row * self.palette.shape[0]
|
||||||
|
box_length = 8 + 3 + self.palette.shape[1] + bytes_per_palette
|
||||||
|
|
||||||
|
# Write the usual header.
|
||||||
|
write_buffer = struct.pack('>I4s',
|
||||||
|
int(box_length), self.box_id.encode())
|
||||||
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
|
write_buffer = struct.pack('>HB', self.palette.shape[0],
|
||||||
|
self.palette.shape[1])
|
||||||
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
|
bps_signed = [x - 1 for x in self.bits_per_component]
|
||||||
|
for j, item in enumerate(bps_signed):
|
||||||
|
if self.signed[j]:
|
||||||
|
bps_signed[j] |= 0x80
|
||||||
|
write_buffer = struct.pack('>' + 'B' * self.palette.shape[1],
|
||||||
|
*bps_signed)
|
||||||
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
|
if self.bits_per_component[0] <= 8:
|
||||||
|
dtype = np.uint8
|
||||||
|
code = 'B'
|
||||||
|
elif self.bits_per_component[0] <= 16:
|
||||||
|
dtype = np.uint16
|
||||||
|
code = 'H'
|
||||||
|
elif self.bits_per_component[0] <= 32:
|
||||||
|
dtype = np.uint32
|
||||||
|
code = 'I'
|
||||||
|
|
||||||
|
fmt = '>' + code * self.palette.shape[1]
|
||||||
|
for row in self.palette:
|
||||||
|
write_buffer = struct.pack(fmt, *row)
|
||||||
|
fptr.write(write_buffer)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse(fptr, offset, length):
|
def parse(fptr, offset, length):
|
||||||
"""Parse palette box.
|
"""Parse palette box.
|
||||||
|
|
@ -1561,7 +1620,7 @@ class PaletteBox(Jp2kBox):
|
||||||
# Need to determine bps and signed or not
|
# Need to determine bps and signed or not
|
||||||
read_buffer = fptr.read(num_columns)
|
read_buffer = fptr.read(num_columns)
|
||||||
data = struct.unpack('>' + 'B' * num_columns, read_buffer)
|
data = struct.unpack('>' + 'B' * num_columns, read_buffer)
|
||||||
bps = [((x & 0x07f) + 1) for x in data]
|
bps = [((x & 0x7f) + 1) for x in data]
|
||||||
signed = [((x & 0x80) > 1) for x in data]
|
signed = [((x & 0x80) > 1) for x in data]
|
||||||
|
|
||||||
fmt = '>'
|
fmt = '>'
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import tempfile
|
||||||
import uuid
|
import uuid
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
import xml.etree.cElementTree as ET
|
import xml.etree.cElementTree as ET
|
||||||
|
import warnings
|
||||||
|
|
||||||
if sys.hexversion < 0x02070000:
|
if sys.hexversion < 0x02070000:
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
@ -496,6 +497,7 @@ class TestWrap(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.j2kfile = glymur.data.goodstuff()
|
self.j2kfile = glymur.data.goodstuff()
|
||||||
self.jp2file = glymur.data.nemo()
|
self.jp2file = glymur.data.nemo()
|
||||||
|
self.jpxfile = glymur.data.jpxfile()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
@ -557,6 +559,30 @@ class TestWrap(unittest.TestCase):
|
||||||
j2k.wrap(tfile.name)
|
j2k.wrap(tfile.name)
|
||||||
self.verify_wrapped_raw(tfile.name)
|
self.verify_wrapped_raw(tfile.name)
|
||||||
|
|
||||||
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
|
def test_palette(self):
|
||||||
|
"""basic test for rewrapping a jpx file"""
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
# This file has a rreq mask length that we do not recognize.
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
jpx = Jp2k(self.jpxfile)
|
||||||
|
idx = [0, 1, 3, 6]
|
||||||
|
boxes = [jpx.box[idx] for idx in [0, 1, 3, 6]]
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".jp2") as tfile:
|
||||||
|
jp2 = jpx.wrap(tfile.name, boxes=boxes)
|
||||||
|
|
||||||
|
# Verify the outer boxes.
|
||||||
|
boxes = [box.box_id for box in jp2.box]
|
||||||
|
self.assertEqual(boxes, ['jP ', 'ftyp', 'jp2h', 'jp2c'])
|
||||||
|
|
||||||
|
# Verify the inside boxes.
|
||||||
|
boxes = [box.box_id for box in jp2.box[2].box]
|
||||||
|
self.assertEqual(boxes, ['ihdr', 'colr', 'pclr', 'cmap'])
|
||||||
|
|
||||||
|
expected_offsets = [0, 12, 40, 887]
|
||||||
|
for j, offset in enumerate(expected_offsets):
|
||||||
|
self.assertEqual(jp2.box[j].offset, offset)
|
||||||
|
|
||||||
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
@unittest.skipIf(os.name == "nt", "Temporary file issue on window.")
|
||||||
def test_wrap_jp2(self):
|
def test_wrap_jp2(self):
|
||||||
"""basic test for rewrapping a jp2 file, no specified boxes"""
|
"""basic test for rewrapping a jp2 file, no specified boxes"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue