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.
-
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
-
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
-
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
-
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
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,
Pillowis reading these images incorrectly.For
Pillow<9.0.0the error was that the decoded values were twice as large as they should be. Therefore a simple fix of dividing values by2solved the problem. This can be seen from code snippet 2.For
Pillow>=9.0.0the 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
rasteriopackages (code snippets 3 and 4).What actually happened?
Pillowreads values incorrectly.What are your OS, Python and Pillow versions?
3.8)>=9.0.0(used9.1.0)Code snippets
For all code snippets we use a small 15-bit JP2 image from
sentinelhubpackage unit tests. For simplicity we compare only mean values.Reading with
Pillow>=9.0.0(usedPillow==9.1.0):Reading with
Pillow<9.0.0(usedPillow==8.4.0):Reading with OpenCV (used
opencv-python==4.5.5.64):Reading with
rasterio(usedrasterio==1.2.10):