Opening a single image - python

I'm trying to open an image with size (520,696) but when I use this
array = np.array([np.array(Image.open(folder_path+folders+'/'+'images'+'/'+image))], np.int32).shape`
I'm getting the shape as
(1, 520, 696, 4)
The problem is with this shape I can't convert it to image using toimage(array); I get
'arr' does not have a suitable array shape for any mode.
Any suggestions on how may I read that image using only (520,696)?

The problem is the additional dumb dimension. You can remove it using:
arr = np.squeeze(arr)

You should load the picture as a single picture instead of loading it as a stack and then removing the irrelevant stack dimension. The basic procedure could be something like this:
from PIL import Image
pic = Image.open("test.jpg")
pic.show() #yup, that's the picture
arr = np.array(pic) #convert it to a numpy array
print(arr.shape, arr.dtype) #dimension and data type
arr //= 2 #now manipulate this array
new_pic = Image.fromarray(arr) #and keep it for later
new_pic.save("newpic.bmp") #maybe in a different format

Related

MemoryError when trying to normalize an array of images

I have a folder containing 110k images with shape (256, 256, 3) each. I'm reading one by one, converting to a numpy array and storing in a list. After that, I convert the list to a numpy array. The shape of the numpy array is (110000, 256, 256, 3). Then, when I try to normalize the images with images = images / float(255), this error is displayed:
File "loading_images.py", line 25, in <module>
images = images / float(255)
MemoryError: Unable to allocate 161. GiB for an array with shape (110000, 256, 256, 3) and data type float64
Is there any other way to do that?
My current code is this:
files = glob.glob(dir + "*.png")
images = []
for f in files
im = cv2.imread(f)
img_arr = np.asarray(im)
images.append(img_arr)
images = np.asarray(images)
images = images / float(255)
Think your issue is that cv2 gives an int8 (correct me if I'm wrong), and you're trying to cast the values into float64's
import numpy as np
print(np.float64(255).itemsize)
print(np.int8(255).itemsize)
Which means that after the typecast, you're left with approximately 8 times the bytes. you have 110000×256×256×3=21GB of image data to begin with, which is probably just within your RAM limitation. After the conversion to float, you get 8x21 = 168GB of data, which is above the RAM limit of anyone I know haha.
This is no solution however, do you really need to load all the images at the same time?

Turn cv2's numpy array to be the same as reading binary of an image?

Is there any way to turn the numpy array representation of an image to be the same as the reading binary(rb) of a file, So that code will work.
cv_img = cv2.imread("image.jpg")
# Some code here related to the cv_img var...
img = open("image.jpg", 'rb').read()
print(cv_img == img) # So that will be true.
I want that because I don't want to have to save the image to be able to get that
open("image.jpg", 'rb').read() output.
Thank you in advance for answering I am pretty new here so I am open to criticism about the way I ask questions :)
The short answer is No. What you get from open("image.jpg", 'rb').read() is 1-dimensional array of bytes of a JPEG encoded image. cv2.imread()gives you 3-dimensional (width x height x one channel each for blue, green, and red) array of 8-bit values. A one-dimensional array and a three dimensional array aren't directly comparable, and even if they were, the one-dimensional array contains an encoding that has been decoded to form the three-dimensional array.

How to convert 3D numpy array to nifti image in nibabel?

From this question How to convert Nifti file to Numpy array? , I created a 3D numpy array of nifti image. I made some modifications to this array, like I changed depth of the array by adding padding of zeroes. Now I want to convert this array back to nifti image, how can I do that?
I tried:
imga = Image.fromarray(img, 'RGB')
imga.save("modified/volume-20.nii")
but it doesn't identify nii extension. I also tried:
nib.save(img,'modified/volume-20.nii')
this is also not working, because img must be nibabel.nifti1.Nifti1Image if I want to use nib.save feature. In both of the examples above img is a 3D numpy array.
Assuming that you a numpy array and you want to use nib.save function, you need to first get the affine transformation.
Example:
# define the path to the data
func_filename = os.path.join(data_path, 'task-rest_bold.nii.gz')
# load the data
func = nib.load(func_filename)
# do computations that lead to a 3D numpy array called "output"
# bla bla bla
# output = np.array(....)
# to save this 3D (ndarry) numpy use this
ni_img = nib.Nifti1Image(output, func.affine)
nib.save(ni_img, 'output.nii.gz')
Now you will be able to overlay the output.nii.gz onto the task-rest_bold.nii.gz

Convert a one dimensional dataframe into a 3 dimensional for RGB Image

I have a data frame of 2304 columns , as it is a 48*48 image pixels, when I convert it into one channel using this code
x = (df.iloc[:,1:].values).astype('float32')
x = x.reshape(-1,48,48,1)
its perfectly output of shape
(48*48*1)
with generating exact image by this code:
plt.imshow(x[0][:,:,0])
I want to make it into a 3Dimentional like in three channels. I try to merged the df 3 times and do this (48*48*3) it successfully change the df shape but I cannot generate the image again,
If you essentially want to convert a single channel image (which should essentially be a greyscale image) into a 3 channel greyscale image, its the same as concatenating the same image array thrice along the last axis. You can use np.concatenate to achieve the desired result.
import numpy as np
a = np.zeros((2304), dtype = np.uint8) #Just a dummy array representing a single pic
single_channel = a.reshape(48, 48, 1)
result = np.concatenate([single_channel,single_channel,single_channel], axis = -1)
print(result.shape) #(48, 48, 3)
At this point you should have an array that can be accepted by any image library. Just throwing a sample code to show how you may proceed to create the image from the array.
import cv2
cv2.imwrite("hi.jpg", result)
As stated earlier, use numpy instead of pandas for image manipulation.
EDIT: If you were unfortunately starting with a dataframe in the first place, you can always convert it to a numpy array with an extra dimension representing each image.
import pandas as pd
import cv2
import numpy as np
a = np.zeros((2304), dtype = np.uint8) #dummy row
dummy_df = pd.DataFrame(np.concatenate([a.reshape(1,-1)]*10)) #dummy df with 10 rows.
print(dummy_df.shape) #(10, 2304)
arr_images = np.array(dummy_df, dtype = np.uint8)
print(arr_images.shape) #(10, 2304)
multiple_single_channel = arr_images.reshape(-1, 48, 48, 1)
print(multiple_single_channel.shape) #(10, 48, 48, 1)
result = np.concatenate([multiple_single_channel] * 3, axis = -1)
print(result.shape) #(10, 48, 48, 3)
for i,img in enumerate(result):
print(i)
cv2.imwrite("{}.jpg".format(i), img)
#do something with image. you PROBABLY don't want to run this for 35k images though.
The bottom line really is that you should not need to use a dataframe, even for multiple images.
1)Dont use pandas
2) you cant transform 1channel image into 3 channels,
3) Dont use float32, images are usually 8bit (np.uint8)
4) use numpy in combination with OpenCV or with Pillow.
5) Dont use matplotlib to generate images. use libraries mentioned in 4.
6) if you have array with shape (x,y,3) there is nothing more simply than generate image with opencv cv2.imshow('image',array)

Conversion between Pillow Image object and numpy array changes dimension

I am using Pillow and numpy, but have a problem with conversion between Pillow Image object and numpy array.
when I execute following code, the result is weird.
im = Image.open(os.path.join(self.img_path, ifname))
print im.size
in_data = np.asarray(im, dtype=np.uint8)
print in_data.shape
result is
(1024, 768)
(768, 1024)
Why dimension is changed?
im maybe column-major while arrays in numpy are row-major
do in_data = in_data.T to transpose the python array
probably should check in_data with matplotlib's imshow to make sure the picture looks right.
But do you know that matplotlib comes with its own loading functions that gives you numpy arrays directly? See: http://matplotlib.org/users/image_tutorial.html
If your image is greyscale do:
in_data = in_data.T
but if you are working with rbg images you want to make sure your transpose operation is along only two axis:
in_data = np.transpose(in_data, (1,0,2))
actually this is because most image libraries give you images that are transpozed compared to numpy arrays. this is (i think) because you write image files line by line, so the first index (let's say x) refers to the line number (so x is the vertical axis) and the second index (y) refers to the subsequent pixel in line (so y is the horizontal axis), which is against our everyday coordinates sense.
If you want to handle it correctly you need to remember to write:
image = library.LoadImage(path)
array = (library.FromImageToNumpyArray(image)).T
and consequently:
image = library.FromNumpyArrayToImage(array.T)
library.WriteImage(image, path)
Which works also for 3D images. But i'm not promising this is the case for ALL image libraries - just these i worked with.

Categories

Resources