singrium
singrium

Reputation: 3026

Why the same pixels have different values using PIL and Opencv?

I downloaded a random image from the internet, opened it using PIL.Image.open() and cv2.imread() then I checked some pixels' values. The problem is that I got different values for the same pixels using PIL and Opencv!
This is the image I tried:
enter image description here This is what I did:

>>> import cv2
>>> from PIL import Image
>>> img = cv2.imread('img.jpg')
>>> im = Image.open('img.jpg')
>>> img[0][0]
>>> array([142, 152, 146], dtype=uint8)
>>> im.getpixel((0, 0))
>>> (138, 158, 131)

The R, G, B values ((138 != 146), (158 != 152), (131 != 142)) of both im and img don't match, although it is the same pixel and the same image!
I looked into SO posts, I found this post talking about the same issue, so I used the code that was posted to check the difference again:

from PIL import Image
import cv2
import sys
from hashlib import md5
import numpy as np

def hashIm(im):
    imP = np.array(Image.open(im))

    # Convert to BGR and drop alpha channel if it exists
    imP = imP[..., 2::-1]
    # Make the array contiguous again
    imP = np.array(imP)
    im = cv2.imread(im)

    diff = im.astype(int)-imP.astype(int)

    cv2.imshow('cv2', im)
    cv2.imshow('PIL', imP)
    cv2.imshow('diff', np.abs(diff).astype(np.uint8))
    cv2.imshow('diff_overflow', diff.astype(np.uint8))

    with open('dist.csv', 'w') as outfile:
        diff = im-imP
        for i in range(-256, 256):
            outfile.write('{},{}\n'.format(i, np.count_nonzero(diff==i)))

    cv2.waitKey(0)
    cv2.destroyAllWindows()

    return md5(im).hexdigest() + '   ' + md5(imP).hexdigest()

if __name__ == '__main__':
    print(hashIm('img.jpg'))

The hashes I got are different, also the difference between the images is not black!

Additional info:
- Os: Ubuntu 18.04
- Python: 3.6
- Opencv: opencv-python==4.0.0.21
- PIL: Pillow==5.4.1

Is there any explanation for this?

Upvotes: 6

Views: 3433

Answers (2)

s.durand
s.durand

Reputation: 51

This is probably not a numpy / rounding problem but a jpg decoding variation : https://github.com/python-pillow/Pillow/issues/3833

In particular:

JPEG decoders are allowed to produce slightly different results, "a maximum of one bit of difference for each pixel component" according to Wikipedia.

(https://github.com/python-pillow/Pillow/issues/3833#issuecomment-585211263)

Upvotes: 5

Arkistarvh Kltzuonstev
Arkistarvh Kltzuonstev

Reputation: 6935

Opencv stores image as a numpy ndarray.

import cv2
cv_img = cv2.imread(img_path)
from PIL import Image
pil_img = Image.open(img_path)

When you do cv_img[x][y] you are accessing yth column and xth row, but if you do pil_img.getpixel((x,y)) pillow access pixels of xth column and yth row.

Another factor is pillow returns in (R, G, B) format where as opencv returns in (B, G, R) format.

For me cv_img[20][10] gives array([127, 117, 129], dtype=uint8). Check here B = 127, G = 117, R = 129.

But pil_img[10][20] gives (129, 117, 127). Check here R = 129, G = 117, B = 127.

Upvotes: 2

Related Questions