Convert Np.Array of Type Float64 to Type Uint8 Scaling Values

Convert np.array of type float64 to type uint8 scaling values

A better way to normalize your image is to take each value and divide by the largest value experienced by the data type. This ensures that images that have a small dynamic range in your image remain small and they're not inadvertently normalized so that they become gray. For example, if your image had a dynamic range of [0-2], the code right now would scale that to have intensities of [0, 128, 255]. You want these to remain small after converting to np.uint8.

Therefore, divide every value by the largest value possible by the image type, not the actual image itself. You would then scale this by 255 to produced the normalized result. Use numpy.iinfo and provide it the type (dtype) of the image and you will obtain a structure of information for that type. You would then access the max field from this structure to determine the maximum value.

So with the above, do the following modifications to your code:

import numpy as np
import cv2
[...]
info = np.iinfo(data.dtype) # Get the information of the incoming image type
data = data.astype(np.float64) / info.max # normalize the data to 0 - 1
data = 255 * data # Now scale by 255
img = data.astype(np.uint8)
cv2.imshow("Window", img)

Note that I've additionally converted the image into np.float64 in case the incoming data type is not so and to maintain floating-point precision when doing the division.

Fast conversion from np.float64 to np.uint8 (with scaling and clipping)

I found this bug regarding exactly that issue on the OpenCV bugtracker:
https://github.com/opencv/opencv/issues/7231

It seems currently the fastest way to go is:

cv2.addWeighted(image, alpha=255, src2=0, beta=0, gamma=0, dtype=cv2.CV_8U)

which is a bit faster than the convertScaleAbs version.

Conversion of image type int16 to uint8

All of these do different things.

np.uint8 considers only the lowest byte of your number. It's like doing value & 0xff.

>>> img = np.array([2000, -150, 11], dtype=np.int16)
>>> np.uint8(img)
array([208, 106, 11], dtype=uint8)

cv2.normalize with the cv2.NORM_MINMAX norm type normalises your values according to the normalisation function

img_new = (img - img.min()) * ((max_new - min_new) / (img.max() - img.min())) + min_new

It effectively changes one range to another and all the values in the between are scaled accordingly. By definition the original min/max values become the targetted min/max values.

>>> cv2.normalize(img, out, 0, 255, cv2.NORM_MINMAX)
array([255, 0, 19], dtype=int16)

uint8 in Matlab simply saturates your values. Everything above 255 becomes 255 and everything below 0 becomes 0.

>> uint8([2000 -150 11])

ans =

255 0 11

If you want to replicate Matlab's functionality, you can do

>>> img[img > 255] = 255
>>> img[img < 0] = 0

Which one you want to use depends on what you're trying to do. If your int16 covers the range of your pixel values and you want to rescale those to uint8, then cv2.normalize is the answer.

CV2 not displaying colors when converting to uint8

Hopefully this will help you see how to extract the Red, Green and Blue channels from the 32-bit values. I started off with the first row of your array.

im=np.array([-12763847,-12763847,-12763847,-5590160,-12039396,-12434915],dtype=np.int32)

R = ((im & 0xff).astype(np.uint8)
# array([ 57, 57, 57, 112, 28, 29], dtype=uint8)

G = ((im>>8) & 0xff).astype(np.uint8)
# array([ 61, 61, 61, 179, 75, 66], dtype=uint8)

B = ((im>>16) & 0xff).astype(np.uint8)
# array([ 61, 61, 61, 170, 72, 66], dtype=uint8)

If those values look correct, you should be able to merge them into a colour image with:

img = cv2.merge((B,G,R))

bearing in mind that OpenCV uses BGR channel ordering rather than the more conventional RGB.


The ordering of the bytes in your 32-bit number may be different from what I am guessing above. The easiest way to test is to put a red card in front of your camera, and see what comes through, then a green card, then a blue one. The four channels (R,G,B,A) should be given by the following but which is which may be a matter for experiment:

(im    ) & 0xff
(im>>8 ) & 0xff
(im>>16) & 0xff
(im>>24) & 0xff

Lossy conversion from float64 to uint8

The warning is self explanatory: color.convert_colorspace(in_hsv_h, 'HSV', 'RGB') is of type float64, and imsave, convert elements to uint8.

The pixels of PNG image, are stored as one byte per component (one byte for red, one for green and one for blue).

Each component is an integer value in range [0, 255] (type uint8).

The output of color.convert_colorspace is of float64, each color component is in range [0, 1] of type float64 (stored as 64 bits in memory, and much more accurate than uint8).

The conversion from float64 range [0, 1] to uint8 range [0, 255] is performed like: uint8_val = round(float64_val*255).

The rounding operation loose some data (for example: in case float64_val*255 = 132.658, the result is rounded to 133).

Convert image to uint8 prior to saving to suppress this warning

Tells you to convert the image elements to uint8 prior to saving.

Solution is simple.

Multiply by 255, and add .astype(np.uint8).

imsave('testing-sorted-hue.png', (color.convert_colorspace(in_hsv_h, 'HSV', 'RGB')*255).astype(np.uint8))

In order for your code to work, you should also add .astype(np.uint8) when building newImage:

newImage = np.random.randint(0, 255, (300, 300, 3)).astype(np.uint8)

Complete code:

from imageio import imsave
from skimage import color

import numpy as np

newImage = np.random.randint(0, 255, (300, 300, 3)).astype(np.uint8)


in_hsv_h = color.convert_colorspace(newImage, 'RGB', 'HSV')
in_hsv_s = in_hsv_h.copy()
in_hsv_v = in_hsv_h.copy()

for i in range(newImage.shape[0]):
in_hsv_h[i,:,0] = np.sort(in_hsv_h[i,:,0])
in_hsv_s[i,:,1] = np.sort(in_hsv_s[i,:,1])
in_hsv_v[i,:,2] = np.sort(in_hsv_v[i,:,2])

imsave('testing-sorted-hue.png', (color.convert_colorspace(in_hsv_h, 'HSV', 'RGB')*255).astype(np.uint8))
imsave('testing-sorted-saturation.png', (color.convert_colorspace(in_hsv_s, 'HSV', 'RGB')*255).astype(np.uint8))

Remark:

The example in makeartwithpython uses from imageio import imsave instead of from scipy.misc import imsave, and the example in the site is working correctly.

Note:

I don't have a lot of Python programming experience, please take my answer with some caution.

Convert to np.array, some values automatically being divided

The e represents scientific notation. I am not sure why this only happens with the land case, but this StackOverflow question could be helpful in setting up numpy.set_print_options with suppress=True to force floating-point formatting.



Related Topics



Leave a reply



Submit