Skip to content

Wrong decoding of 15-bit JPEG2000 images #6194

@AleksMat

Description

@AleksMat

What did you do?

Satellite images from Sentinel-2 satellite, released by European Space Agency, are originally encoded in JPEG2000 format with an unusual 15-bit encoding. Since early versions, Pillow is reading these images incorrectly.

  • For Pillow<9.0.0 the error was that the decoded values were twice as large as they should be. Therefore a simple fix of dividing values by 2 solved the problem. This can be seen from code snippet 2.

  • For Pillow>=9.0.0 the decoded values seem to be completely arbitrary and such a fix doesn't work anymore. This can be seen from code snippet 1.

What did you expect to happen?

It is expected that values would be read correctly, like this is done by OpenCV and rasterio packages (code snippets 3 and 4).

What actually happened?

Pillow reads values incorrectly.

What are your OS, Python and Pillow versions?

  • OS: any OS (used Linux Ubuntu 20.04)
  • Python: any version (used 3.8)
  • Pillow: >=9.0.0 (used 9.1.0)

Code snippets

For all code snippets we use a small 15-bit JP2 image from sentinelhub package unit tests. For simplicity we compare only mean values.

  1. Reading with Pillow>=9.0.0 (used Pillow==9.1.0):

    from PIL import Image
    import numpy as np
    
    image = np.array(Image.open("img-15bit.jp2"))
    print(np.mean(image))
    
    >> 0.33467347487234617
  2. Reading with Pillow<9.0.0 (used Pillow==8.4.0):

    from PIL import Image
    import numpy as np
    
    image = np.array(Image.open("img-15bit.jp2"))
    image = image / 2  # A fix that produces correct values
    print(np.mean(image))
    
    >> 0.3041897339424886
  3. Reading with OpenCV (used opencv-python==4.5.5.64):

    import cv2
    import numpy as np
    
    image = np.array(cv2.imread("img-15bit.jp2", cv2.IMREAD_UNCHANGED))
    print(np.mean(image))
    
    >> 0.3041897339424886
  4. Reading with rasterio (used rasterio==1.2.10):

    import rasterio
    import numpy as np
    
    with rasterio.open("img-15bit.jp2", "r") as fp:
        image = np.array(fp.read())
    print(np.mean(image))
    
    >> 0.3041897339424886

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions