Merge branch 'issue77' into devel

This commit is contained in:
John Evans 2013-07-24 07:51:50 -04:00
commit 6a65bdb0a4
11 changed files with 205 additions and 18 deletions

View file

@ -1,3 +1,6 @@
Jul 23, 2013 - v0.2.5 Fixed inconsistency in XML handling, now all instances
are always ElementTree objects (issue82).
Jul 21, 2013 - v0.2.4 Fixed markdown bug for Fedora 17 information, fixed
out-of-date windows information (issue79). Fixed incorrect
interpretation of Psot parameter (issue78).

View file

@ -78,7 +78,7 @@ copyright = u'2013, John Evans'
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0.2.4'
release = '0.2.5'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -137,8 +137,17 @@ platforms.
Testing
'''''''
If you wish to run the tests (strongly recommended :-), you can either run them
from within python as follows ... ::
There are two environment variables you may wish to set before running the
tests.
* **OPJ_DATA_ROOT** - points to directory for OpenJPEG test data
* **FORMAT_CORPUS_ROOT** - points to directory for format-corpus repository (see https://github.com/openplanets/format-corpus)
Setting these two environment variables is not required, as any tests using
either of them will be skipped.
In order to run the tests, you can either run them from within
python as follows ... ::
>>> import glymur
>>> glymur.runtests()

View file

@ -185,9 +185,8 @@ Work with XMP UUIDs?
====================
The example JP2 file shipped with glymur has an XMP UUID. ::
>>> from glymur import Jp2k
>>> file = glymur.data.nemo()
>>> j = Jp2k(file)
>>> import glymur
>>> j = glymur.Jp2k(glymur.data.nemo())
>>> print(j.box[4])
UUID Box (uuid) @ (715, 2412)
UUID: be7acfcb-97a9-42e8-9c71-999491e3afac (XMP)
@ -198,7 +197,7 @@ The example JP2 file shipped with glymur has an XMP UUID. ::
</rdf:RDF>
</ns0:xmpmeta>
Since the UUID data in this case is returned as an ElementTree Element, one can
Since the UUID data in this case is returned as an ElementTree instance, one can
use ElementTree to access the data. For example, to extract the
**CreatorTool** attribute value, the following would work::

View file

@ -6,7 +6,7 @@ from .jp2k import Jp2k
from .jp2dump import jp2dump
from . import data
from . import test
#from . import test
def runtests():

View file

@ -102,7 +102,14 @@ class Codestream(object):
while True:
read_buffer = fptr.read(2)
marker_id, = struct.unpack('>H', read_buffer)
try:
marker_id, = struct.unpack('>H', read_buffer)
except struct.error:
# Treat this as a warning.
msg = "Marker had length {0} instead of expected length of 2 "
msg += "bytes. Codestream parsing terminated."
warnings.warn(msg.format(len(read_buffer)))
break
if marker_id == 0xff90 and header_only:
# Start-of-tile (SOT) means that we are out of the main header

View file

@ -22,6 +22,7 @@ from .codestream import Codestream
from .core import SRGB
from .core import GREYSCALE
from .core import PROGRESSION_ORDER
from .core import ENUMERATED_COLORSPACE, RESTRICTED_ICC_PROFILE
from .jp2box import Jp2kBox
from .jp2box import JPEG2000SignatureBox
from .jp2box import FileTypeBox
@ -159,6 +160,24 @@ class Jp2k(Jp2kBox):
# boxes) here.
fptr.seek(0)
self.box = self.parse_superbox(fptr)
self._validate()
def _validate(self):
"""Validate the JPEG 2000 outermost superbox.
"""
# A jp2-branded file cannot contain an "any ICC profile
ftyp = self.box[1]
if ftyp.brand == 'jp2 ':
jp2h = [box for box in self.box if box.box_id == 'jp2h'][0]
colrs = [box for box in jp2h.box if box.box_id == 'colr']
for colr in colrs:
if colr.method not in (ENUMERATED_COLORSPACE,
RESTRICTED_ICC_PROFILE):
msg = "Color Specification box method must specify either "
msg += "an enumerated colorspace or a restricted ICC "
msg += "profile if the file type box brand is 'jp2 '."
warnings.warn(msg)
# pylint: disable-msg=W0221
def write(self, img_array, cratios=None, eph=False, psnr=None, numres=None,

View file

@ -0,0 +1,133 @@
"""
These tests deal with JPX/JP2/J2K images in the format-corpus repository.
"""
#pylint: disable-all
import os
import sys
if sys.hexversion < 0x02070000:
import unittest2 as unittest
else:
import unittest
import warnings
from glymur import Jp2k
import glymur
try:
format_corpus_data_root = os.environ['FORMAT_CORPUS_DATA_ROOT']
except KeyError:
format_corpus_data_root = None
except:
raise
try:
opj_data_root = os.environ['OPJ_DATA_ROOT']
except KeyError:
opj_data_root = None
except:
raise
@unittest.skipIf(format_corpus_data_root is None,
"FORMAT_CORPUS_DATA_ROOT environment variable not set")
@unittest.skipIf(sys.hexversion < 0x03020000,
"Requires features introduced in 3.2 (assertWarns)")
class TestSuiteFormatCorpus(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_balloon_trunc1(self):
# Has one byte shaved off of EOC marker.
jfile = os.path.join(format_corpus_data_root,
'jp2k-test/byteCorruption/balloon_trunc1.jp2')
j2k = Jp2k(jfile)
with self.assertWarns(UserWarning):
c = j2k.get_codestream(header_only=False)
# The last segment is truncated, so there should not be an EOC marker.
self.assertNotEqual(c.segment[-1].marker_id, 'EOC')
# The codestream is not as long as claimed.
with self.assertRaises(OSError):
j2k.read(rlevel=-1)
def test_balloon_trunc2(self):
# Shortened by 5000 bytes.
jfile = os.path.join(format_corpus_data_root,
'jp2k-test/byteCorruption/balloon_trunc2.jp2')
j2k = Jp2k(jfile)
with self.assertWarns(UserWarning):
c = j2k.get_codestream(header_only=False)
# The last segment is truncated, so there should not be an EOC marker.
self.assertNotEqual(c.segment[-1].marker_id, 'EOC')
# The codestream is not as long as claimed.
with self.assertRaises(OSError):
j2k.read(rlevel=-1)
def test_balloon_trunc3(self):
# Most of last tile is missing.
jfile = os.path.join(format_corpus_data_root,
'jp2k-test/byteCorruption/balloon_trunc3.jp2')
j2k = Jp2k(jfile)
with self.assertWarns(UserWarning):
c = j2k.get_codestream(header_only=False)
# The last segment is truncated, so there should not be an EOC marker.
self.assertNotEqual(c.segment[-1].marker_id, 'EOC')
# Should error out, it does not.
#with self.assertRaises(OSError):
# j2k.read(rlevel=-1)
def test_jp2_brand_vs_any_icc_profile(self):
# If 'jp2 ', then the method cannot be any icc profile.
jfile = os.path.join(format_corpus_data_root,
'jp2k-test', 'icc',
'balloon_eciRGBv2_ps_adobeplugin.jpf')
with self.assertWarns(UserWarning):
j2k = Jp2k(jfile)
def test_jp2_brand_vs_any_icc_profile_multiple_colr(self):
# Has colr box, one that conforms, one that does not.
# Wrong 'brand' field; contains two versions of ICC profile: one
# embedded using "Any ICC" method; other embedded using "Restricted
# ICC" method, with description ("Modified eciRGB v2") and profileClass
# ("Input Device") changed relative to original profile.
lst = [format_corpus_data_root, 'jp2k-test', 'icc',
'balloon_eciRGBv2_ps_adobeplugin_jp2compatible.jpf']
jfile = os.path.join(*lst)
with self.assertWarns(UserWarning):
j2k = Jp2k(jfile)
@unittest.skipIf(opj_data_root is None,
"OPJ_DATA_ROOT environment variable not set")
@unittest.skipIf(sys.hexversion < 0x03020000,
"Requires features introduced in 3.2 (assertWarns)")
class TestSuiteOpj(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_jp2_brand_vs_any_icc_profile(self):
# If 'jp2 ', then the method cannot be any icc profile.
filename = os.path.join(opj_data_root,
'input/nonregression/text_GBR.jp2')
with self.assertWarns(UserWarning):
j2k = Jp2k(filename)
if __name__ == "__main__":
unittest.main()

View file

@ -990,7 +990,11 @@ class TestSuite(unittest.TestCase):
def test_NR_DEC_text_GBR_jp2_29_decode(self):
jfile = os.path.join(data_root,
'input/nonregression/text_GBR.jp2')
data = Jp2k(jfile).read()
with warnings.catch_warnings():
# brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(jfile)
data = jp2.read()
self.assertTrue(True)
def test_NR_DEC_pacs_ge_j2k_30_decode(self):
@ -7260,7 +7264,10 @@ class TestSuiteDump(unittest.TestCase):
def test_NR_text_GBR_dump(self):
jfile = os.path.join(data_root,
'input/nonregression/text_GBR.jp2')
jp2 = Jp2k(jfile)
with warnings.catch_warnings():
# brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(jfile)
ids = [box.box_id for box in jp2.box]
lst = ['jP ', 'ftyp', 'rreq', 'jp2h',

View file

@ -4,6 +4,7 @@ import pkg_resources
import struct
import sys
import tempfile
import warnings
if sys.hexversion < 0x02070000:
import unittest2 as unittest
@ -277,9 +278,12 @@ class TestPrinting(unittest.TestCase):
"OPJ_DATA_ROOT environment variable not set")
def test_icc_profile(self):
filename = os.path.join(data_root, 'input/nonregression/text_GBR.jp2')
j = glymur.Jp2k(filename)
with warnings.catch_warnings():
# brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[3].box[1])
print(jp2.box[3].box[1])
actual = fake_out.getvalue().strip()
lin27 = ["Colour Specification Box (colr) @ (179, 1339)",
" Method: any ICC profile",
@ -902,10 +906,13 @@ class TestPrinting(unittest.TestCase):
# ICC profiles may be used in JP2, but the approximation field should
# be zero unless we have jpx. This file does both.
filename = os.path.join(data_root, 'input/nonregression/text_GBR.jp2')
j = glymur.Jp2k(filename)
with warnings.catch_warnings():
# brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[3].box[1])
print(jp2.box[3].box[1])
actual = fake_out.getvalue().strip()
lines = ["Colour Specification Box (colr) @ (179, 1339)",
" Method: any ICC profile",
@ -942,10 +949,13 @@ class TestPrinting(unittest.TestCase):
def test_uuid(self):
# UUID box
filename = os.path.join(data_root, 'input/nonregression/text_GBR.jp2')
j = glymur.Jp2k(filename)
with warnings.catch_warnings():
# brand is 'jp2 ', but has any icc profile.
warnings.simplefilter("ignore")
jp2 = Jp2k(filename)
with patch('sys.stdout', new=StringIO()) as fake_out:
print(j.box[4])
print(jp2.box[4])
actual = fake_out.getvalue().strip()
lines = ['UUID Box (uuid) @ (1544, 25)',
' UUID: 3a0d0218-0ae9-4115-b376-4bca41ce0e71',

View file

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
import sys
kwargs = {'name': 'Glymur',
'version': '0.2.4',
'version': '0.2.5',
'description': 'Tools for accessing JPEG2000 files',
'long_description': open('README.md').read(),
'author': 'John Evans',