How to convert 3D file (.nii) to 2D sliced .tiff file? - python

I have a .nii file and I've tried to convert it to 2d sliced tiff file, this is what I do:
import nibabel as nib
import numpy as np
from tifffile import imsave
import tifffile as tiff
#load nii
a = nib.load('/a.nii').get_fdata()
print(np.shape(a)) #output: (512, 512, 300)
#some process need to be done(?)
#right away save the a into tiff file
imsave('/2da.tiff', a)
at first I thought above process will work but when i load the tiffile it is still in 3d dim
tiffedA = tiff.imread('/2da.tiff')
print(tiddefA) #output: (512, 512, 300)
I expected the shape will be (300, 512, 512). I realized there is some needed process first but I don't know how, any idea to covert this 3d .nii to 2d sliced .tif in the right way?

Related

Lanczos Interpolation in Python with 2D images

I try to rescale 2D images (greyscale).
The image size is 256x256 and the desired output is 224x224.
The pixel values range from 0 to 1300.
I tried 2 approaches to rescale them with Lanczos Interpolation:
First using PIL Image:
import numpy as np
from PIL import Image
import cv2
array = np.random.randint(0, 1300, size=(10, 256, 256))
array[0] = Image.fromarray(array[0]).resize(size=(224, 224), resample=Image.LANCZOS)
resulting in the error message: ValueError: image has wrong mode
And then CV2:
array[0] = cv2.resize(array[0], dsize=(224, 224), interpolation=cv2.INTER_LANCZOS4)
resulting in the error message: ValueError: could not broadcast input array from shape (224,224) into shape (256,256)
How to do it properly?
In the second case, you are resizing a 256x256 image to 224x224, then assigning it back into a slice of the original array. This slice still has size 256x256, so NumPy doesn't know how to do the data copy.
Instead, create a new output array of the right sizes:
array = np.random.randint(0, 1300, size=(10, 256, 256))
newarray = np.zeros((10, 224, 224))
newarray[0] = cv2.resize(array[0], dsize=(224, 224), interpolation=cv2.INTER_LANCZOS4)
In the PIL part, you have a few issues.
Firstly, you need to check the dtype of things you create! You create an array of np.int64 when you use np.random() like that. As you know your data only maxes out at 1300, an unsigned 16-bit is preferable:
array = np.random.randint(0, 1300, size=(10, 256, 256), dtype=np.uint16)
Secondly, when you create a PIL Image from the Numpy array, you need to tell PIL the mode - greyscale or Lightness here:
array[0] = Image.fromarray(array[0], 'L')
Thirdly, you are trying to stuff the newly created PIL Image back into a Numpy array - don't do that:
newVariable = Image.fromarray(...).resize()

Python conversion of PIL image to numpy array very slow

I am evaluating a Tensorflow model on open cv video frames. I need to reshape the incoming PIL image into reshaped numpy array so that i can run inference on it.
But i see that the conversion of the PIL image to numpy array is taking around 900+ milliseconds on my laptop with 16 GiB memory and 2.6 GHz Intel Core i7 processor. I need to get this down to a few milliseconds so that i can process multiple frames per second on my camera.
Can anyone suggest how to make the below method run faster?
def load_image_into_numpy_array(pil_image):
(im_width, im_height) = pil_image.size
data = pil_image.getdata()
data_array = np.array(data)
return data_array.reshape((im_height, im_width, 3)).astype(np.uint8)
On further instrumentation i realized that np.array(data) is taking the bulk of the time... close to 900+ milliseconds. So conversion of the image data to numpy array is the real culprit.
You can just let numpy handle the conversion instead of reshaping yourself.
def pil_image_to_numpy_array(pil_image):
return np.asarray(pil_image)
You are converting image into (height, width, channel) format. That is default conversion numpy.asarray function performs on PIL image so explicit reshaping should not be neccesary.
Thank you very much!! It works very fast!
def load_image_into_numpy_array(path):
"""Load an image from file into a numpy array.
Puts image into numpy array to feed into tensorflow graph.
Note that by convention we put it into a numpy array with shape
(height, width, channels), where channels=3 for RGB.
Args:
path: a file path (this can be local or on colossus)
Returns:
uint8 numpy array with shape (img_height, img_width, 3)
"""
img_data = tf.io.gfile.GFile(path, 'rb').read()
image = Image.open(BytesIO(img_data))
return np.array(image)
Image with (3684, 4912, 3) take 0.3~0.4 sec.

Convert back to 2D numpy array from .jpg image

I saved an numpy array to an image as follows:
plt.imshow(xNext[0,:,:,0]) #xNext has shape (1,64,25,1)
print(xNext[0,:,:,0].shape) #outputs (64,25)
plt.savefig(os.path.join(root,filename)+'.png')
np.save(os.path.join(root,filename)+'.npy',xNext[0,:,:,0])
How can I obtain the same numpy array back from the .png saved image? Can you also please show me if I had saved as .jpg image?
I've tried the following and works with 3D array (v1) where resulting image close to the original numpy array produced image (original).
image = Image.open(imageFilename) #brings in as 3D array
box = (315,60,500,540)
image = image.crop(box)
image = image.resize((25,64)) #to correct to desired shape
arr = np.asarray(image)
plt.imshow(arr)
plt.savefig('v1.png')
plt.close()
However, when I convert the 3D array to 2D array, the resulting image is different (v1b and v1c).
arr2 = arr[:,:,0]
plt.imshow(arr2)
plt.savefig('v1b.png')
plt.close()
arr3 = np.dot(arr[...,:3],[0.299,0.587,0.11])
plt.imshow(arr3)
plt.savefig('v1c.png')
plt.close()
How can I convert the 3D to 2D correctly? Thanks for your help.
original, v1 (saved from 3D array)
v1b, v1c (saved from 2D arrays)
original (with original size)
If your objective is to save a numpy array as an image, your approach have a problem. The function plt.savefig saves an image of the plot, not the array. Also transforming an array into an image may carry some precision loss (when converting from float64 or float32 to uint16). That been said, I suggest you use skimage and imageio:
import imageio
import numpy as np
from skimage import img_as_uint
data = np.load('0058_00086_brown_2_recording1.wav.npy')
print("original", data.shape)
img = img_as_uint(data)
imageio.imwrite('image.png', img)
load = imageio.imread('image.png')
print("image", load.shape)
This script loads the data you provided and prints the shape for verification
data = np.load('0058_00086_brown_2_recording1.wav.npy')
print("original", data.shape)
then it transform the data to uint, saves the image as png and loads it:
img = img_as_uint(data)
imageio.imwrite('image.png', img)
load = imageio.imread('image.png')
the output of the script is:
original (64, 25)
image (64, 25)
i.e. the image is loaded with the same shape that data. Some notes:
image.png is saved as a grayscale image
To save to .jpg just change to imageio.imwrite('image.jpg', img)
In the case of .png the absolute average distance from the original image was 3.890e-06 (this can be verified using np.abs(img_as_float(load) - data).sum() / data.size)
Information about skimage and imageio can be found in the respectives websites. More on saving numpy arrays as images can be found in the following answers: [1], [2], [3] and [4].
link
from scipy.misc import imread
image_data = imread('test.jpg').astype(np.float32)
This should give you the numpy array (I would suggest using imread from scipy)

Load a tiff stack in a numpy array with python

I am having a little issue with .tif files. I am sure it is only a minor problem that I canĀ“t get around (keep in mind, I am a relatively new programmer).
Basically: I have prepared .tif files that are 64x64xn in size (n up until 1000). The image is only a single file that contains all of this slices. I would like to load the image into a (multidimensional) numpy array. I have tried:
from PIL import Image as pilimage
file_path=(D:\luca\test\test.tif)
print("The selected stack is a .tif")
dataset = pilimage(file_path)
tiffarray = np.array(dataset)
expim = tiffarray.astype(np.double);
print(expim.shape)
and other things (like tifffile). I only seem to be able to read the first slice of the stack. Is it possible for "expim" to contain all information that is saved in the tiff stack?
I am not sure if there is a way to get PIL to open multiple slices of a tiff stack.
If you are not bound to using PIL, however, an alternative is scikit-image, which opens multiple slices from a tiff stack by default. Here is some sample code of how to load a tiff stack into a Numpy array using scikit-image:
>>> from skimage import io
>>> im = io.imread('an_image.tif')
>>> print(im.shape)
(2, 64, 64)
Note that the imread function loads the image directly into a Numpy array. Also, the dimensions of the resulting array are ordered (z, y, x) where z represents the depth, y represents the height, and x represents the width. Thus, to get a single slice from the stack all you have to do is:
>>> print(im[1].shape)
(64, 64)
PIL has a function seek to move to different slices of a tiff stack.
from PIL import Image
file_path=(D:\luca\test\test.tif)
print("The selected stack is a .tif")
dataset = Image.open(file_path)
h,w = np.shape(dataset)
tiffarray = np.zeros((h,w,dataset.n_frames))
for i in range(dataset.n_frames):
dataset.seek(i)
tiffarray[:,:,i] = np.array(dataset)
expim = tiffarray.astype(np.double);
print(expim.shape)

Create a multiframe .tif file

I have pixel data that I want to use to create a new .tif image that has multiple frames. How would I go about doing this? I have tried python PIL however I have only found it supports multiple frame reading not writing. See below for my attempt that didn't work.
new_Image = Image.new("I;16", (num_pixels,num_rows))
for frame in range((len(final_rows)/num_rows)):
pixels = new_Image.load()
for row in range(num_rows):
row_pixel = final_rows[row].getPixels()
for pixel in range(num_pixels):
pixels[pixel,row] = row_pixel[pixel]
print frame
new_Image.seek(frame)
For example, using numpy and scikit-image with FreeImage plugin:
import numpy as np
from skimage.io._plugins import freeimage_plugin as fi
image = np.zeros((32, 256, 256), 'uint16')
fi.write_multipage(image, 'multipage.tif')
Or save it uncompressed using numpy and tifffile.py:
import numpy as np
from tifffile import imsave
image = np.zeros((32, 256, 256), 'uint16')
imsave('multipage.tif', image)
This assumes that all pages have the same data shape and type and no additional tags need to be written.

Categories