openpreserve / jpylyzer

JP2 (JPEG 2000 Part 1) validator and properties extractor. Jpylyzer was specifically created to check that a JP2 file really conforms to the format's specifications. Additionally jpylyzer is able to extract technical characteristics.
http://jpylyzer.openpreservation.org/
Other
69 stars 28 forks source link

Add consistency checks for CPFnum (CPF marker) vs Rsiz (SIZ marker) and PRFnum (PRF marker) #205

Closed bitsgalore closed 8 months ago

bitsgalore commented 9 months ago

From ISO 15444-15, A.6:

CPFnum shall be equal to the value found in bits 0 to 11 of Rsiz of the corresponding codestream, unless that value is 4095, in which case CPFnum shall be equal to the PRFnum value found in the PRF marker segment of the corresponding codestream.

bitsgalore commented 9 months ago

Done: https://github.com/openpreserve/jpylyzer/commit/9d4ec7f11b2cee2f61aa496d6e787afbb82cad4d

BUT for the only test image with a CPF marker this results in a validation error because CPFnum equals 257, whereas bits 0-11 of rsiz are all 0!

Double checked in hex editor, which shows following bytes for CPF marker:

FF 59 00 04 01 02

So there's one pcpf value, represented by the last 2 bytes (0x0102); corresponding decimal value is 258. Applying the equation in A.6 of ISO 15444-15:

CPFnum = −1 + ∑ Pcpf_i ∙ 2**(16∙(i−1))

For i = 1 this yields 257, which is the value reported by Jpylyzer.

Not sure if this is a fault of the encoder application or perhaps I don't understand how CPFnum works??

Also relevant:

https://github.com/openpreserve/jpylyzer/issues/207

bitsgalore commented 8 months ago

More tests with latest Kakadu version:

kdu_compress -i reference.ppm -o kdu-ht-cpf.jph Cmodes=HT -rate 1 Scpf_num={10}

Jpylyzer reports:

        <contiguousCodestreamBox>
            <CPFnumConsistentWithRsiz>False</CPFnumConsistentWithRsiz>
        </contiguousCodestreamBox>

Meanwhile rsiz:

            <siz>
                <lsiz>47</lsiz>
                <rsiz>16384</rsiz>
                <capability>CAP</capability>

and CPF marker output:

            <cpf>
                <lcpf>4</lcpf>
                <CPFnum>10</CPFnum>
            </cpf>

Opening JPH in Hex editor. Start of SIZ marker segment:

FF 51 00 2F 40 00

So Rsiz = 0x4000 = 16384 (as reported by Jpylyzer). Binary:

0100000000000000

CPF marker segment looks like this:

FF 59 00 04 00 0B

So there's one pcpf value, represented by the last 2 bytes (0x000B); corresponding decimal value is 11. Applying the equation in A.6 of ISO 15444-15:

CPFnum = −1 + ∑ Pcpf_i ∙ 2**(16∙(i−1))

For i = 1 this yields 10, which is the value reported by Jpylyzer.

But clause A.6 of ISO15444-15 (CPF marker segment) says:

CPFnum shall be equal to the value found in bits 0 to 11 of Rsiz of the corresponding codestream, unless that value is 4095, in which case CPFnum shall be equal to the PRFnum value found in the PRF marker segment of the corresponding codestream.

In this case bits 0 to 11 of Rsiz are all zero, so this condition is not met!

Instead, the expected Rsiz value here would be 0x400a (decimal 16394).

Also, while trying to create a test file with a Profile (PRF) marker segment:

kdu_compress -i reference.ppm -o kdu-ht-prf.jph Cmodes=HT -rate 1 Sprf_num={10}

Resulting file doesn't actually contain a profile marker segment at all (but it does contain a CPF marker with a CPFnum value which equals the entered Sprf_num value!

As a test I manually edited the Rsiz value accordingly in a Hex editor, and after this it passed validation in Jpylyzer.

bitsgalore commented 8 months ago

Status: sent mail to Kakadu support with description of the issue. Update: following discussion with Kakadu author files file is actually valid, also see https://github.com/openpreserve/jpylyzer/issues/218.