2

I have two functions that behave the same way with int64 but work differently with uint8:

A_int64 = np.array([1, 2, 3])
B_unit8 = A.astype(np.uint8)


def scale_image(image):
    minv = np.amin(image)
    maxv = np.amax(image)
    result = (255 * (image - minv) / (maxv - minv))
    return result.astype(np.uint8)

def scale_image_multiple_lines(image):
    minv = np.amin(image)
    maxv = np.amax(image)
    image = image - minv
    image = image / (maxv - minv)
    image = image * 255
    return image.astype(np.uint8)

print(scale_image(A_int64)) # [  0 127 255]
print(scale_image_multiple_lines(A_int64)) # [  0 127 255]

print(scale_image(B_unit8)) # [  0 127 127]
print(scale_image_multiple_lines(B_unit8)) # [  0 127 255]

The one that doesn't work as intended is print(scale_image(B_unit8)) # [ 0 127 127]

My guess is that the multiline version works with both datatypes because it casts earlier. However, since I don't believe I'm dealing with negatives (I subtract the minimum value but not more than that, I don't think) why should it make a difference?

Zev
  • 3,423
  • 1
  • 20
  • 41
  • This issue originally came up in [this question](https://stackoverflow.com/questions/50880843/casting-images-to-different-class-pil/50882986#50882986) involving PIL. I found a workaround for them but I'm trying to understand why it works. – Zev Jun 17 '18 at 15:15

1 Answers1

2

You are half-right. It's also the order of operations.

In the first function you are multiplying with 255 first, then dividing by max-min. The first gives an overflow in the last position -> (0, 255, 254) then we truedivide by 2 and cast to uint8

The second truedivides by two first (which entails a cast to float), so the subsequent multiplication by 255 does not overflow.

Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • Ah! I got caught up on the unsigned part and didn't think about the 8 bit part. – Zev Jun 17 '18 at 15:33