File: test_codestream.py

package info (click to toggle)
glymur 0.14.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,104 kB
  • sloc: python: 17,238; makefile: 129; xml: 102; sh: 62
file content (317 lines) | stat: -rw-r--r-- 10,755 bytes parent folder | download | duplicates (2)
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))