Jakob Elias
Jakob Elias

Reputation: 21

Why is cv2.imencode('jpg', ...) seemingly changing the color of an image?

I create and view a random image. Then, this image is encoded as a jpg file with opencv. However, after decoding this image the colors have been changed slightly. This behavior is not seen using png to encode. Can anyone explain why this occurs? Is it a negative result of the jpeg compression? Am I doing something wrong? Code sample below to recreate this.

import cv2
import numpy as np

random_image = np.random.randint(255, size=(4,4,3), dtype=np.uint8)    
cv2.imshow('Image', random_image)
cv2.waitKey()

_, img_encoded = cv2.imencode('.jpg', random_image)
img_string = img_encoded.tostring()
npimg = np.fromstring(img_string, dtype=np.uint8)
img = cv2.imdecode(npimg, 1)
cv2.imshow('Image', img)
cv2.waitKey()

# Does not happen with png
_, img_encoded = cv2.imencode('.png', random_image)
img_string = img_encoded.tostring()
npimg = np.fromstring(img_string, dtype=np.uint8)
img = cv2.imdecode(npimg, 1)
cv2.imshow('Image', img)
cv2.waitKey()

Edited to add some 4x4 images.

Original:

Original Image

JPG

JPG

PNG

PNG

Edited again with 512x512 images

Original 512x512

JPG 512x512

PNG 512x512

Upvotes: 2

Views: 5941

Answers (3)

Xiaode
Xiaode

Reputation: 11

you can try this to get a lossless compression:

import cv2
import numpy as np

img = cv2.imread('input_image.png')

#  encode image with 'png'
retval, buffer = cv2.imencode('.png', img)

if not retval:
    raise ValueError("can't encode image")

decoded_img = cv2.imdecode(buffer, cv2.IMREAD_UNCHANGED)

if np.array_equal(img, decoded_img):
    print("image pixe are same.")
else:
    print("image pixels are different.")

Upvotes: 0

PC_User
PC_User

Reputation: 97

I don't know why your PNG image had the correct colors as in my case it was also affected. Anyway, this should work for any extensions.

OpenCV uses BRG instead of RGB, so it inverts the red and blue colors. To correct this, make sure to re-convert the image colors before encoding them:

correct_color = cv2.cvtColor(img_array , cv2.COLOR_BGR2RGB)
encode_image = cv2.imencode('.jpg', correct_color)

Note that img_array is the NumPy array resulting from a cv2.imread()

Upvotes: -1

Moshel
Moshel

Reputation: 420

JPG is a lossy compression. It actually works by modifying colours in a way that should be "unoffensive" to the eye. PNG is a lossless compression, so you get exactly what to had after encode/decode.

You can control how much JPG will be free to modify the image by specifying the IMWRITE_JPEG_QUALITY parameter like this:

cv2.imencode(('.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])

Higher values means less compression and result closer to original. For example, 100 should be no compression at all and result identical to original.

Upvotes: 3

Related Questions