Pillow: Different amount of losses in Ubuntu 18.04 vs Ubuntu 20.04 after saving and reloading an image

Created on 28 Nov 2020  路  10Comments  路  Source: python-pillow/Pillow

I am making

a fake image detector which can generate a binary mask for an image. The white area representing the forged region and rest everything black. For this I am using a pre-processing called ELA (Error Level Analysis), in which _one of the steps is to re-save a given image in degraded quality and then compare the compression level variation in the original and the re-saved image. So, I just used PIL to load, save an image in 90% quality and then load it back in another object._

I was expecting that

the amount of losses in the re-saved image would be the same in all the operating systems.

However,

there is a variation in the values when I load the re-saved temporary image among Ubuntu 18.04.5 vs Ubuntu 20.04+ . This variation after further processing becomes concerningly big and as a result the binary mask which is supposed to be generated at the end comes out to be in very inaccurate in anything other than Ubuntu 18.04. (As Google Colab has Ubuntu 18.04.5 only, I am stuck with this runtime to train my models.)

OS, Python and Pillow versions.

  • OS 1: Ubuntu 18.04.5
  • Python: 3.6.9

    * Pillow: 7.0.0

  • OS 2: Ubuntu 20.04.1

  • Python: 3.8.2
  • Pillow: 8.0.0

Although, keep in mind that I have also made the comparison by installing python 3.8 and PIL 8.0.0 in Ubuntu 18.04 in a VM to reject this as potential reason.

Code

Loading an Image

img = Image.open('Sp_D_CNN_A_art0024_ani0032_0268.jpg')
np.sum(np.array(img))
OUTPUT : 28586794    (Same for all the OS)



md5-3e3842ffa786279903149af16d04491b



```lang-none
OUTPUT : 28581417    (for Ubuntu 18.04.5 and CentOS 8.2.2004)
         28582606    (for Ubuntu 20.04+, Fedora 33, Windows 10 20H2)
JPEG

All 10 comments

JPEGs are a lossy format, which means that perceptually, the image will be close to the original, but individual pixels and values may differ depending on implementations of the underlying jpeg library. If you need pixel for pixel identical results, you need to use a lossless compression method, like PNGs. Note that lossless compression usually results in images that are significantly larger than those using lossless compression.

If you're interested in checking which version of libjpeg Pillow is using - in case they are different across the operating systems - try this

from PIL import Image
print(Image.core.jpeglib_version)

JPEGs are a lossy format, which means that perceptually, the image will be close to the original, but individual pixels and values may differ depending on implementations of the underlying jpeg library. If you need pixel for pixel identical results, you need to use a lossless compression method, like PNGs. Note that lossless compression usually results in images that are significantly larger than those using lossless compression.

Actually I 'require' a lossy format in this case. That's the whole trick in Error Level Analysis. However, I need the losses to be uniform across different operating systems, otherwise I'll be getting inaccurate segmentation masks.

If you're interested in checking which version of libjpeg Pillow is using - in case they are different across the operating systems - try this

from PIL import Image
print(Image.core.jpeglib_version)

Ubuntu 18.04.5 has libjpeg version 9 and Ubuntu 20.04, fedora 33, Windows 10 20H2 has version 8. Ain't that strange?

Anyway, this might be it. How do I downgrade to version 8 in Ubuntu 18.04.5?

I would try

sudo apt remove libjpeg9-dev
sudo apt install libjpeg8-dev

and then install Pillow from source again. If you're using pip, then it could be

python3 -m pip install Pillow --no-binary :all:

The strange part is that there is no package for '9' in Ubuntu 18.04 but still the PIL picks up 9.0

On running:

sudo apt list --installed | grep libjpeg

I just get:

libjpeg-turbo8/bionic-updates,bionic-security,now 1.5.2-0ubuntu5.18.04.4 amd64 [installed,automatic]
libjpeg8/bionic,now 8c-2ubuntu8 amd64 [installed,automatic]

I think you might be using the binary wheel which includes its own libjpeg9. It might just be sufficient to reinstall Pillow using the command @radarhere provided above.

If you still get different results after that, you might want to check if you are using libjpeg-turbo (which pretends to be various versions of libjpeg based on compile time flags). You can use the following in Pillow 7.2.0+ to check both versions (None means that turbo is not enabled):

from PIL import features
print("libjpeg:", features.version("jpg"))
print("libjpeg-turbo:", features.version("libjpeg_turbo"))

I think you might be using the binary wheel which includes its own libjpeg9. It might just be sufficient to reinstall Pillow using the command @radarhere provided above.

If you still get different results after that, you might want to check if you are using libjpeg-turbo (which pretends to be various versions of libjpeg based on compile time flags). You can use the following in Pillow 7.2.0+ to check both versions (None means that turbo is not enabled):

from PIL import features
print("libjpeg:", features.version("jpg"))
print("libjpeg-turbo:", features.version("libjpeg_turbo"))

Thanks for the in-depth info :) . Unfortunately the command that @radarhere gave is throwing some error (long list of red lines which I haven't gine through yet). And removing libjpeg-turbo starts removing a hell lot of other desktop applications too so I might need some other workaround.

Unfortunately the command that @radarhere gave is throwing some error (long list of red lines which I haven't gine through yet)

I missed it before, but you probably need to install the dev packages for libjpeg and zlib.
Since the Ubuntu 20.x version is reporting libjpeg version 8 (which matches the latest version from libjpeg-turbo), I'm guessing you need the following on Ubuntu 18.x:

# Install required packages:
sudo apt-get install libjpeg-turbo8-dev zlib1g-dev

# Extra optional packages, if you need them, are listed here:
# https://pillow.readthedocs.io/en/stable/installation.html#building-on-linux

# Now try again:
python3 -m pip install Pillow --no-binary :all:

(For the non-turbo version of libjpeg you would use libjpeg8-dev instead of libjpeg-turbo8-dev.)

Thanks a lot @radarhere and @nulano . It worked. Consistent loss in both the OS now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

indirectlylit picture indirectlylit  路  4Comments

SysoevDV picture SysoevDV  路  3Comments

etc0de picture etc0de  路  4Comments

maxhumber picture maxhumber  路  3Comments

vytisb picture vytisb  路  4Comments