osamu620 / OpenHTJ2K

An open source implementation of ITU-T Rec.814 | ISO 15444-15 (a.k.a. HTJ2K).
BSD 3-Clause "New" or "Revised" License
36 stars 10 forks source link

Bits per component value in JPH image header box is off by 1 bit #149

Closed bitsgalore closed 1 year ago

bitsgalore commented 1 year ago

It seems that for JPH output, OpenHTJ2K writes an erroneous Bits per component value to the image header box. I'll illustrate this using one of OpenHTJ2K's conformance data images as a starting point.

Here's the command I used to create the JPH file:

open_htj2k_enc -i kodim23.ppm -o kodim23.jph Qfactor=30

Validation with jpylyzer results in this error:

    <isValid format="jph">False</isValid>
    <tests>
        <bPCDepthConsistentWithSIZ>False</bPCDepthConsistentWithSIZ>
    </tests>

This error means that the Bits per component value in the JPH image header is not consistent with the information in the code stream.

The Jpylyzer output for the image header box shows a bPCDepth of 9:

                <nC>3</nC>
                <bPCSign>unsigned</bPCSign>
                <bPCDepth>9</bPCDepth>

However, the ssizDepth values in the SIZ marker segment indicate a bit depth of 8 bits for all components:

                <csiz>3</csiz>
                <ssizSign>unsigned</ssizSign>
                <ssizDepth>8</ssizDepth>
                <xRsiz>1</xRsiz>
                <yRsiz>1</yRsiz>
                <ssizSign>unsigned</ssizSign>
                <ssizDepth>8</ssizDepth>
                <xRsiz>1</xRsiz>
                <yRsiz>1</yRsiz>
                <ssizSign>unsigned</ssizSign>
                <ssizDepth>8</ssizDepth>

I double checked with ExifTool, which confirms the odd bits per component values in the JPH image header box:

 <Jpeg2000:NumberOfComponents>3</Jpeg2000:NumberOfComponents>
 <Jpeg2000:BitsPerComponent>9 Bits, Unsigned</Jpeg2000:BitsPerComponent>

Possible underlying cause: ISO 15444-1 defines the BPC parameter as follows (section I.5.3.1):

BPC: Bits per component. This parameter specifies the bit depth of the components in the codestream, minus 1, and is stored as a 1-byte field.

So my best guess is that OpenHTJ2K directly writes the bits per component values to the image header box, without first subtracting 1.

osamu620 commented 1 year ago

Thanks @bitsgalore for your findings. You are absolutely right.

The following lines are problematic. https://github.com/osamu620/OpenHTJ2K/blob/3abbdb035d82401bc936047ffdee1a45115fc740/source/core/jph/jph.cpp#L74-L80 val shall be hdr.SIZ->get_bitdepth(0) - 1 or hdr.SIZ->get_bitdepth(c) - 1 and The signed case shall also be considered per I.5.3.1.

I will fix this as soon as possible. It won't take long :)

bitsgalore commented 1 year ago

Thanks @osamu620 for looking into this. I just ran the fixed version, and I can confirm it now works (and the JPHs pass Jpylyzer validation)!