1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
|
# -*- coding: utf-8 -*-
"""
Test suite for codestream oddities
"""
# Standard library imports ...
import importlib.resources as ir
from io import BytesIO
import pathlib
import struct
import tempfile
import unittest
import warnings
# Local imports ...
import glymur
from glymur import Jp2k, Jp2kr
from glymur.jp2box import InvalidJp2kError
from . import fixtures
class TestSuite(fixtures.TestCommon):
"""Test suite for codestreams."""
def setUp(self):
super().setUp()
self.p0_03 = ir.files("tests.data").joinpath("p0_03.j2k")
self.p0_06 = ir.files("tests.data").joinpath("p0_06.j2k")
self.p1_06 = ir.files("tests.data").joinpath("p1_06.j2k")
self.issue142 = ir.files("tests.data").joinpath("issue142.j2k")
self.htj2k = ir.files("tests.data.from-openjpeg") \
.joinpath("oj-ht-byte.jph")
def test_cap_marker_segment(self):
"""
SCENARIO: the file has a CAP marker segment for the 3rd segment
EXPECTED RESULT: the segment metadata is verified
"""
j = Jp2k(self.htj2k)
cap = j.codestream.segment[2]
self.assertEqual(cap.pcap, 131072)
self.assertEqual(cap.ccap, (3,))
def test_unrecognized_marker(self):
"""
SCENARIO: There is an unrecognized marker just after an SOT marker but
before the EOC marker. All markers must have a leading byte value of
0xff.
EXPECTED RESULT: InvalidJp2kError
"""
with open(self.temp_j2k_filename, mode="wb") as tfile:
with open(self.j2kfile, "rb") as ifile:
# Everything up until the SOT marker.
read_buffer = ifile.read(98)
tfile.write(read_buffer)
# Write the bad marker 0xd900
read_buffer = struct.pack(">H", 0xD900)
tfile.write(read_buffer)
# Get the rest of the input file.
read_buffer = ifile.read()
tfile.write(read_buffer)
tfile.flush()
with self.assertRaises(InvalidJp2kError):
Jp2k(tfile.name).get_codestream(header_only=False)
def test_bad_tile_part_pointer(self):
"""
SCENARIO: A bad SOT marker segment is encountered (Psot value pointing
far beyond the end of the EOC marker) when requesting a fully parsed
codestream.
EXPECTED RESULT: InvalidJp2kError
"""
with open(self.temp_jp2_filename, "wb") as ofile:
with open(self.jp2file, "rb") as ifile:
# Copy up until Psot field.
ofile.write(ifile.read(204))
# Write a bad Psot value.
ofile.write(struct.pack(">I", 2000000))
# copy the rest of the file as-is.
ifile.seek(208)
ofile.write(ifile.read())
ofile.flush()
j = Jp2kr(self.temp_jp2_filename)
with self.assertRaises(InvalidJp2kError):
j.get_codestream(header_only=False)
def test_tile_height_is_zero(self):
"""
Scenario: A tile has height of zero.
Expected result: ZeroDivisionError
Original test file was input/nonregression/2539.pdf.SIGFPE.706.1712.jp2
"""
fp = BytesIO()
buffer = struct.pack(">H", 47) # length
# kwargs = {'rsiz': 1,
# 'xysiz': (1000, 1000),
# 'xyosiz': (0, 0),
# 'xytsiz': (0, 1000),
# 'xytosiz': (0, 0),
# 'Csiz': 3,
# 'bitdepth': (8, 8, 8),
# 'signed': (False, False, False),
# 'xyrsiz': ((1, 1, 1), (1, 1, 1)),
# 'length': 47,
# 'offset': 2}
buffer += struct.pack(">HIIIIIIIIH", 1, 1000, 1000, 0, 0, 0, 1000, 0, 0, 3) # noqa : E501
buffer += struct.pack(">BBBBBBBBB", 7, 1, 1, 7, 1, 1, 7, 1, 1)
fp.write(buffer)
fp.seek(0)
with self.assertRaises(ZeroDivisionError):
glymur.codestream.Codestream._parse_siz_segment(fp)
def test_invalid_codestream_past_header(self):
"""
Scenario: the codestream is ok thru the header, but invalid after
that. The codestream header for the complete test file ends at byte
Expected result: InvalidJp2kError
"""
path = ir.files("tests.data.conformance").joinpath("p1_06.j2k")
with tempfile.TemporaryDirectory() as tdir:
with open(path, mode="rb") as ifile:
with open(pathlib.Path(tdir) / "tmp.j2k", mode="wb") as ofile:
ofile.write(ifile.read(555))
with self.assertRaises(InvalidJp2kError):
j = Jp2k(pathlib.Path(tdir) / "tmp.j2k")
j.get_codestream(header_only=False)
def test_tlm_segment(self):
"""
Verify parsing of the TLM segment.
In this case there's only a single tile.
"""
path = ir.files("tests.data.conformance").joinpath("p0_06.j2k")
j2k = Jp2k(path)
buffer = b"\xffU\x00\x08\x00@\x00\x00YW"
b = BytesIO(buffer[2:])
segment = j2k.codestream._parse_tlm_segment(b)
self.assertEqual(segment.ztlm, 0)
# ttlm is an array, but None is the singleton element
self.assertIsNone(segment.ttlm.item())
self.assertEqual(segment.ptlm, (22871,))
def test_ppt_segment(self):
"""
Verify parsing of the PPT segment
"""
path = ir.files("tests.data.conformance").joinpath("p1_06.j2k")
j2k = Jp2k(path)
c = j2k.get_codestream(header_only=False)
self.assertEqual(c.segment[6].zppt, 0)
def test_plt_segment(self):
"""
Verify parsing of the PLT segment
"""
path = ir.files("tests.data.from-openjpeg").joinpath("issue142.j2k")
c = Jp2k(path).get_codestream(header_only=False)
self.assertEqual(c.segment[7].zplt, 0)
self.assertEqual(len(c.segment[7].iplt), 59)
def test_ppm_segment(self):
"""
Verify parsing of the PPM segment
"""
with warnings.catch_warnings():
# Lots of things wrong with this file.
warnings.simplefilter("ignore")
p = ir.files("tests.data.from-openjpeg") \
.joinpath("edf_c2_1178956.jp2")
jp2 = Jp2k(p)
c = jp2.get_codestream()
self.assertEqual(c.segment[2].zppm, 0)
self.assertEqual(len(c.segment[2].data), 9)
def test_crg_segment(self):
"""
Verify parsing of the CRG segment
"""
path = ir.files("tests.data.conformance").joinpath("p0_03.j2k")
j2k = Jp2k(path)
c = j2k.get_codestream()
self.assertEqual(c.segment[6].xcrg, (65424,))
self.assertEqual(c.segment[6].ycrg, (32558,))
def test_rgn_segment(self):
"""
Verify parsing of the RGN segment
"""
path = ir.files("tests.data.conformance").joinpath("p0_06.j2k")
j2k = Jp2k(path)
c = j2k.get_codestream()
self.assertEqual(c.segment[-1].crgn, 0)
self.assertEqual(c.segment[-1].srgn, 0)
self.assertEqual(c.segment[-1].sprgn, 11)
def test_reserved_marker_segment(self):
"""
SCENARIO: Rewrite a J2K file to include a marker segment with a
reserved marker 0xff6f (65391).
EXPECTED RESULT: The marker segment should be properly parsed.
"""
with open(self.temp_j2k_filename, "wb") as tfile:
with open(self.j2kfile, "rb") as ifile:
# Everything up until the first QCD marker.
read_buffer = ifile.read(65)
tfile.write(read_buffer)
# Write the new marker segment, 0xff6f = 65391
read_buffer = struct.pack(">HHB", int(65391), int(3), int(0))
tfile.write(read_buffer)
# Get the rest of the input file.
read_buffer = ifile.read()
tfile.write(read_buffer)
tfile.flush()
codestream = Jp2k(tfile.name).get_codestream()
self.assertEqual(codestream.segment[3].marker_id, "0xff6f")
self.assertEqual(codestream.segment[3].length, 3)
self.assertEqual(codestream.segment[3].data, b"\x00")
def test_siz_segment_ssiz_unsigned(self):
"""ssiz attribute to be removed in future release"""
j = Jp2k(self.jp2file)
codestream = j.get_codestream()
# The ssiz attribute was simply a tuple of raw bytes.
# The first 7 bits are interpreted as the bitdepth, the MSB determines
# whether or not it is signed.
self.assertEqual(codestream.segment[1].ssiz, (7, 7, 7))
def test_626(self):
"""
Scenario: After parsing the SOC and SIZ segments, an unknown segment
(probably invalid) is hit, and then the file ends, leaving us trying
to interpret EOF as another marker segment.
Expected result: InvalidJp2kError
"""
path = ir.files("tests.data.from-openjpeg").joinpath("issue626.j2k")
with self.assertRaises(InvalidJp2kError):
Jp2k(path)
class TestCodestreamRepr(unittest.TestCase):
def setUp(self):
self.jp2file = glymur.data.nemo()
def tearDown(self):
pass
def test_soc(self):
"""Test SOC segment repr"""
segment = glymur.codestream.SOCsegment()
newseg = eval(repr(segment))
self.assertEqual(newseg.marker_id, "SOC")
def test_siz(self):
"""Test SIZ segment repr"""
kwargs = {
"rsiz": 0,
"xysiz": (2592, 1456),
"xyosiz": (0, 0),
"xytsiz": (2592, 1456),
"xytosiz": (0, 0),
"Csiz": 3,
"bitdepth": (8, 8, 8),
"signed": (False, False, False),
"xyrsiz": ((1, 1, 1), (1, 1, 1)),
}
segment = glymur.codestream.SIZsegment(**kwargs)
newseg = eval(repr(segment))
self.assertEqual(newseg.marker_id, "SIZ")
self.assertEqual(newseg.xsiz, 2592)
self.assertEqual(newseg.ysiz, 1456)
self.assertEqual(newseg.xosiz, 0)
self.assertEqual(newseg.yosiz, 0)
self.assertEqual(newseg.xtsiz, 2592)
self.assertEqual(newseg.ytsiz, 1456)
self.assertEqual(newseg.xtosiz, 0)
self.assertEqual(newseg.ytosiz, 0)
self.assertEqual(newseg.xrsiz, (1, 1, 1))
self.assertEqual(newseg.yrsiz, (1, 1, 1))
self.assertEqual(newseg.bitdepth, (8, 8, 8))
self.assertEqual(newseg.signed, (False, False, False))
|