Behaviour of cv2.imwrite with np.float64 data

Question:

Looking at the cv2 documentation, I got the impression that one should be able in Python to match the writing out of np.float64 data by a np.clip followed by type coercion to np.uint8. However, this doesn’t seem to be the case. Reproducible example:

import cv2
import numpy as np

arr = np.random.uniform(low=0, high=300, size=(64,48,3))
arr2 = np.clip(arr, 0, 255)
arr3 = arr2.astype(np.uint8)

cv2.imwrite('out1.png', arr)
cv2.imwrite('out2.png', arr2)
cv2.imwrite('out3.png', arr3)

arr1a = cv2.imread('out1.png')
arr2a = cv2.imread('out2.png')
arr3a = cv2.imread('out3.png')
print((arr1a==arr2a).all())
print((arr1a==arr3a).all())

yields True for the first check and False for the second check.

This indicates that (1) cv2.imwrite indeed calls np.clip(arr, 0, 255) on the array before writing it out; (2) it doesn’t then call .astype(np.uint8) (or at least, that’s not all).

My goal is to understand what exactly cv2.imwrite is doing to arr. In other words, how can I calculate an array identical to arr1a in my example above, without saving an intermediate file?

Asked By: Mobeus Zoom

||

Answers:

The difference between arr2 and arr3 is that arr3 has had the fractional component of each value truncated. The difference between arr2a and arr3a is sometimes 0, and sometimes 1. It looks like imwrite() might round the values before converting to integers, rather than just removing the fractional part as astype() does.

Let’s do some experimenting to see if this is true.

cv2.imwrite('out2b.png', arr2-0.5)

produces a PNG file that is identical to out3.png. Likewise,

arr3 = np.round(arr2).astype(np.uint8)
cv2.imwrite('out3b.png', arr3)

produces a PNG file that is identical to out2.png. Thus, it seems that indeed imwrite() will round the input floating-point values.

Answered By: Cris Luengo