how to convert flattened array of RGB image(1-D) back to original image - numpy

I have flattened 1D array of (1*3072) created from RGB image of dimension(32*32*3). I want to extract back the original RGB image of dimension(32*32*3) and plot it.
I have tried the solution suggested in how to convert a 1-dimensional image array to PIL image in Python
But it's not working for me, As it seems it is for a greyscale image
from PIL import Image
from numpy import array
img = Image.open("sampleImage.jpg")
arr = array(img)
arr = arr.flatten()
print(arr.shape)
#tried with 'L' & 'RGB' both
img2 = Image.fromarray(arr.reshape(200,300), 'RGB')
plt.imshow(img2, interpolation='nearest')
plt.show()
"Getting below error which expected because it is not able covert RGB"
ValueError: cannot reshape array of size 180000 into shape (200,300)

In order to interpret an array as an RGB image, it needs to have 3 channels. A channel is the 3rd dimension in the numpy array. So change your code to this:
img2 = Image.fromarray(arr.reshape(200,300,3), 'RGB')
I should mention that you talk about your flattened array being 1x3072, yet the example code seems to assume 200x300x3, which would be 1x180,000 when flattened. Which of these two is the truth, I couldn't tell you.

Related

Why does image_dataset_from_directory return a different array than loading images normally?

I noticed that the output from TensorFlow's image_dataset_from_directory is different than directly loading images (either by PIL, Keras' load_img, etc.). I set up an experiment: I have a single RGB image with dimensions 2400x1800x3, and tried comparing the resulting numpy arrays from the different methods:
from PIL import Image
from tensorflow.keras.utils import image_dataset_from_directory, load_img, img_to_array
train_set = image_dataset_from_directory(
'../data/',
image_size=(2400, 1800), # I'm using original image size
label_mode=None,
batch_size=1
)
for batch in train_set:
img_from_dataset = np.squeeze(batch.numpy()) # remove batch dimension
img_from_keras = img_to_array(load_img(img_path))
img_from_pil = img_to_array(Image.open(img_path))
print(np.all(img_from_dataset == img_from_keras)) # False
print(np.all(img_from_dataset == img_from_pil)) # False
print(np.all(img_from_keras == img_from_pil)) # True
So, even though all methods return the same shape numpy array, the values from image_dataset_from_directory are different. Why is this? And what can/should I do about it?
This is a particular problem during prediction time where I'm taking a single image (i.e. not using image_dataset_from_directory to load the image).
This is strange but I have not figured out exactly why but if you print out a pixel values from the img_from_dataset, img_from_keras and img_from_pil I found that the pixel values for img_from_data are sometimes lower by 1, that is it looks like some kind of rounding is going on. All 3 are supposed to return float32 so I can't see why they should be different. I also tried using
ImageDataGenerator().flow_from_directory and it matches the data for img_from_keras and img_from_pil. Now img_from_dataset return a A tf.data.Dataset object it yields float32 tensors of shape (batch_size, image_size[0], image_size[1], num_channels).
I used this code to detect the pixel value difference where I used a 224 X 224 X3 image
match=True
for i in range(224):
for j in range(224):
for k in range (3):
if img_from_dataset[i,j,k] != img_from_keras[i,j,k]:
match=False
print(img_from_dataset[i,j,k], img_from_keras[i,j,k], i, j, k)
break
if match==False:
break
if match == False:
break
print(match)
An example output of the code is
86.0 87.0 0 0 2
False
If you ever figured out why the difference let me know. I expect one will have to go through the detailed code. I took a quick look. Even though you specified the image size as being the same as the original image, image_dataset_from_directory still resizes the image using tf.image.resize with the iterpolation as interpolation='bilinear'. Maybe the load_img(img_path) and PIL image.open use different interpolations.

Convert np.array of PIL image to binary

Im trying to convert the numpy array of the PIL image I got to a binary one but anything I have tried doesn't work.
this is what I got so far:
from PIL import Image
import numpy as np
pixels=np.array(Image.open("covid_encrypted_new.png").getdata())
def to_bin(pixels):
return [format(i,"08b") for i in pixels]
also when I tried to iterate over the array and change each value to type bin it also didnt go well for me.
What else can I try?
thanks
This could be what your looking for
Ori here: How to read the file and convert it to a binary image in Python
# Read Image
img= Image.open(file_path)
# Convert Image to Numpy as array
img = np.array(img)
# Put threshold to make it binary
binarr = np.where(img>128, 255, 0)
# Covert numpy array back to image
binimg = Image.fromarray(binarr)
You could even use opencv to convert
img = np.array(Image.open(file_path))
_, bin_img = cv2. threshold(img,127,255,cv2.THRESH_BINARY)

Slicing the channels of image and storing the channels into numpy array(same size as image). Plotting the numpy array not giving the original image

I separated the 3 channels of an colour image. I created a new NumPy array of the same size as the image, and stored the 3 channels of the image into 3 slices of the 3D NumPy array. After plotting the NumPy array, the plotted image is not same as original image. Why is this happening?
Both img and new_img array have same elements, but image is different.
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
img=mpimg.imread('/storage/emulated/0/1sumint/kali5.jpg')
new_img=np.empty(img.shape)
new_img[:,:,0]=img[:,:,0]
new_img[:,:,1]=img[:,:,1]
new_img[:,:,2]=img[:,:,2]
plt.imshow(new_img)
plt.show()
Expect the same image as original image.
The problem is that your new image will be created with the default data type of float64 on this line:
new_img=np.empty(img.shape)
unless you specify a different dtype.
You can either (best) copy the original image's dtype like this:
new_img = np.empty(im.shape, dtype=img.dtype)
or use something like this:
new_img = np.zeros_like(im)
or (worst) specify one you happen to know matches your data, like this,
new_img = np.empty(im.shape, dtype=np.uint8)
I presume you have some reason for copying one channel at a time, but if not, you can avoid all the foregoing issues and just do:
new_img = np.copy(img)

my picture after using tf.image.resize_images becomes horrible picture

original_picture (size:128*128) like this:
after using this function
image = tf.image.resize_images(original_image,(128,128))
finally I use plt.imshow() to show my hand picture
The problem comes from tensorflow's resize_images function returning floats.
To properly resize and view the image you would need something like:
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
with tf.Session() as sess:
tf.global_variables_initializer().run()
image = tf.image.resize_images(original_image,(128,128))
# Cast image to np.uint8 so it can be properly viewed
# eval() tensor to get numpy array.
image = tf.cast(image, np.uint8).eval()
plt.imshow(image)
The colours are inverted, i.e. each pixel's colour [r, g, b] is being displayed as [255 - r, 255 - g, 255 - b].
This could have something to do with the data type of the image you obtain in step 2. Try the following after resizing the image:
image = image.astype(np.uint8)
I will be using tensorflow library as tf.
tf.image.resize resizes the images(correctly) and then when we use plt.imshow on it .
But plt.imshow if it sees a float value be it .5 or 221.3 it clips that into the range[0,1].
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
This was the problem in my case ,
Original Image pixels[ 91 105 166] .
After resizing tf.Tensor([ 91.01 105.01 166.01], shape=(3,), dtype=float32)
You can see that the resizing is correct but the clipping is the one hurting .
To use the function properly .
img_resize = tf.image.resize(random_img,[250,250])
img_resize = tf.cast(img_resize,'int64')
plt.imshow(img_resize)
This should take care of the issues .

Saving an imshow-like image while preserving resolution

I have an (n, m) array that I've been visualizing with matplotlib.pyplot.imshow. I'd like to save this data in some type of raster graphics file (e.g. a png) so that:
The colors are the ones shown with imshow
Each element of the underlying array is exactly one pixel in the saved image -- meaning that if the underlying array is (n, m) elements, the image is NxM pixels. (I'm not interested in interpolation='nearest' in imshow.)
There is nothing in the saved image except for the pixels corresponding to the data in the array. (I.e. there's no white space around the edges, axes, etc.)
How can I do this?
I've seen some code that can kind of do this by using interpolation='nearest' and forcing matplotlib to (grudgingly) turn off axes, whitespace, etc. However, there must be some way to do this more directly -- maybe with PIL? After all, I have the underlying data. If I can get an RGB value for each element of the underlying array, then I can save it with PIL. Is there some way to extract the RGB data from imshow? I can write my own code to map the array values to RGB values, but I don't want to reinvent the wheel, since that functionality already exists in matplotlib.
As you already guessed there is no need to create a figure. You basically need three steps. Normalize your data, apply the colormap, save the image. matplotlib provides all the necessary functionality:
import numpy as np
import matplotlib.pyplot as plt
# some data (512x512)
import scipy.misc
data = scipy.misc.lena()
# a colormap and a normalization instance
cmap = plt.cm.jet
norm = plt.Normalize(vmin=data.min(), vmax=data.max())
# map the normalized data to colors
# image is now RGBA (512x512x4)
image = cmap(norm(data))
# save the image
plt.imsave('test.png', image)
While the code above explains the single steps, you can also let imsave do all three steps (similar to imshow):
plt.imsave('test.png', data, cmap=cmap)
Result (test.png):