numpy image float array, to int 0..255 value

Tags: , , ,



I got 3 NumPy data arrays r,g,b represented as a 2D float64 array (720×1024)
Essentially per row, each channel is a bunch of floats:

[[0.94984896 0.96076077 0.9599071  ... 0.80338298 0.80253267 0.80560301], 
 [0.94958899 0.95265769 0.95964721 ... 0.80312296 0.80619422 0.80534302], 
 [0.94932439 0.95239315 0.95938273 ... 0.80285821 0.80592952 0.80115679], ..., 
 [0.97768162 0.98087441 0.98014583 ... 0.87006474 0.87325722 0.87252819], 
 [0.97711028 0.98030547 0.9795793  ... 0.86948629 0.87268113 0.87587604], 
 [0.97653759 0.97581362 0.9711683  ... 0.8610633  0.86818208 0.87530095]]

What I would like to to do is making it a channel that I can use in cv2.merge((r,g,b))
So that the float64 values per row get multiplied by 255 and something that cv2.merge() accepts.
I think NumPy can do such things but I’m confused, is there a way to do this?.

Answer

lets consider your three arrays to be r and g and b respectively. In such a case the following code should be what you need-

Edit-1: based on Mark’s comment, I suggest the following changes

r = (r*255).round(0).astype(np.uint8)
g = (g*255).round(0).astype(np.uint8)
b = (b*255).round(0).astype(np.uint8)

img = np.stack((r,g,b), axis=-1)

Edit-2: I may be wrong, but I’m not convinced that multiplying with 255.99999 is better. Doing so will result in many other pixel values getting bumped to the next value than they are originally or were supposed to be, here is small example I could come up with

# random fraction values
a:  [3.2000e-04 5.5111e-01 9.9999e-01 9.8850e-01]

a*255.99999:  [8.19199968e-02 1.41084154e+02 2.55997430e+02 2.53055990e+02]
(a*255.99999).astype(np.uint8):  [  0 141 255 253] 

a*255:  [8.1600000e-02 1.4053305e+02 2.5499745e+02 2.5206750e+02]
(a*255).round(0).astype(np.uint8):  [  0 141 255 252]

Additionally, I tied another small experiment with all possible pixel values and this surprisingly tells that both the methods are accurate

pixels = np.arange(256, dtype=np.float64)
pixels /= 255

print(sum((pixels*255.99999).astype(np.uint8) == np.arange(256)))
# output is 256
print(sum((pixels*255).round(0).astype(np.uint8) == np.arange(256)))
# output is 256


Source: stackoverflow