Issue
I have a numpy array H_arr
representing the following image:
and I wish to convert its values in the range [0,1], let's call this new array as New_arr
, in such a way that the original image remains intact. What I mean is that the exact same image (IMG 1) should be displayed when I use plt.imshow(New_arr)
.
The data type of H_arr
is float32, with H_arr.min()
giving -24.198463 and H_arr.max()
giving 26.153196. H_arr.shape
gives (960, 1280, 3).
I had earlier thought that I would use the following formula to convert it to the 0-1 range:
newvalue= (new_max-new_min)/(max-min)*(value-max)+new_max
and implement it as:
New = np.zeros((H_arr.shape[0],H_arr.shape[1],H_arr.shape[2]),dtype = float)
for i in range(H_arr.shape[0]):
for j in range(H_arr.shape[1]):
for k in range(H_arr.shape[2]):
New[i][j][k]= (1-0)/(H_arr.max()-H_arr.min())*(H_arr[i][j][k]-H_arr.max())+1
But this is computationally quite expensive. Any input on how I should go about converting the original array is appreciated.
Edit: After incorporating the answers below, I can do it quite quickly within the [0,1] range, but the image drastically changes to
How do I make sure that my image remains the same as before?
Solution
Here is the function to scale your array into whatever interval you want:
def minmax_scaler(arr, *, vmin=0, vmax=1):
arr_min, arr_max = arr.min(), arr.max()
return ((arr - arr_min) / (arr_max - arr_min)) * (vmax - vmin) + vmin
Here is how you can use it:
import PIL
import numpy as np
import urllib
def read_image_from_url(ur):
return np.array(PIL.Image.open(urllib.request.urlopen(url)))
# Original
url = "https://i.stack.imgur.com/75y6Q.png"
arr = read_image_from_url(url).astype(np.float32)
print(f"{arr.min() = }")
print(f"{arr.max() = }")
# Scaling in [0, 1]
arr_scaled_01 = minmax_scaler(arr, vmin=0, vmax=1)
print(f"{arr_scaled_01.min() = }")
print(f"{arr_scaled_01.max() = }")
# Scaling into new [vmin, vmax]
vmin_new, vmax_new = -34, 34
arr_scaled = minmax_scaler(arr, vmin=vmin_new, vmax=vmax_new)
print(f"{arr_scaled.min() = }")
print(f"{arr_scaled.max() = }")
arr.min() = 0.0
arr.max() = 255.0
arr_scaled_01.min() = 0.0
arr_scaled_01.max() = 1.0
arr_scaled.min() = -34.0
arr_scaled.max() = 34.0
Normally, when using imshow you can set vmin and vmax, so whatever the range of the values of your array, the plot will be the same. However they will be ignored if the array is an RGB image, in which case you have to scale the array yourself in [0, 1] for floats or [0, 255] for ints.
import matplotlib.pyplot as plt
def imshow_minmax(arr, ax):
# vmin, vmax and cmap will be ignored if RGBA image
return ax.imshow(arr, vmin=arr.min(), vmax=arr.max(), cmap="gray")
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(8, 4))
axes[0, 0].set_ylabel("One band")
imshow_minmax(arr[..., 0], ax=axes[0, 0])
imshow_minmax(arr_scaled_01[..., 0], ax=axes[0, 1])
imshow_minmax(arr_scaled[..., 0], ax=axes[0, 2])
axes[1, 0].set_ylabel("RGB")
imshow_minmax(arr, ax=axes[1, 0])
imshow_minmax(arr_scaled_01, ax=axes[1, 1])
imshow_minmax(arr_scaled, ax=axes[1, 2])
axes[1, 0].set_xlabel(f"Original in {[arr.min(), arr.max()]}")
axes[1, 1].set_xlabel("Scaled in [0, 1]")
axes[1, 2].set_xlabel(f"Scaled in {[vmin_new, vmax_new]}")
fig.tight_layout()
plt.show()
Answered By - paime
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.