Simplest way to save array into raster file in Python - python

With one 2-d array in the shape of (100, 100), I want to save it into raster file in .tiff format.
I can use gdal package to read tiff files which are already exist. But I still can't find a simple way to transform the 2-d array into tiff file.
Using plt.imsave("xx.tif",array) or
def array_to_raster(array):
"""Array > Raster
Save a raster from a C order array.
:param array: ndarray
"""
dst_filename = 'xxx.tiff'
x_pixels = 100 # number of pixels in x
y_pixels = 100 # number of pixels in y
driver = gdal.GetDriverByName('GTiff')
dataset = driver.Create(
dst_filename,
x_pixels,
y_pixels,
1,
gdal.GDT_Float32, )
dataset.GetRasterBand(1).WriteArray(array)
dataset.FlushCache() # Write to disk.
return dataset, dataset.GetRasterBand(1)
They all failed to achieve my target. The second method was adapted from here which can transform an array into a geotiff with a projection.
Is there some simple way to save array into .tiff, so I can call it by import the tiff file next time.
Any advices would be appreciate.

A tif raster could be considered as 'array+proj+geotransforms'.
If you want to write an array to a tiff ,you can refer to the following code:
dst_filename = 'xxx.tiff'
x_pixels = 100 # number of pixels in x
y_pixels = 100 # number of pixels in y
driver = gdal.GetDriverByName('GTiff')
dataset = driver.Create(dst_filename,x_pixels, y_pixels, 1,gdal.GDT_Float32)
dataset.GetRasterBand(1).WriteArray(array)
# follow code is adding GeoTranform and Projection
geotrans=data0.GetGeoTransform() #get GeoTranform from existed 'data0'
proj=data0.GetProjection() #you can get from a exsited tif or import
dataset.SetGeoTransform(geotrans)
dataset.SetProjection(proj)
dataset.FlushCache()
dataset=None

Easiest way with imageio
If you don't care about its projection it's a oneliner:
import imageio
imageio.imwrite('test.tiff', [[0,255],[255,0]])

Related

How can i reproduce an image out of randomly shuffled pixels?

my output my input Hi I am using this python code to generate an shuffle pixel image is there any way to make this process opposite ? for example I give this code output's photo to the program and it reproduce the original photo again.
I am trying to generate an static style image and reverse it back into the original image and I am open into any other ideas for replacing this code
from PIL import Image
import numpy as np
orig = Image.open('lena.jpg')
orig_px = orig.getdata()
orig_px = np.reshape(orig_px, (orig.height * orig.width, 3))
np.random.shuffle(orig_px)
orig_px = np.reshape(orig_px, (orig.height, orig.width, 3))
res = Image.fromarray(orig_px.astype('uint8'))
res.save('out.jpg')
Firstly, bear in mind that JPEG is lossy - so you will never get back what you write with JPEG - it changes your data! So, use PNG if you want to read back losslessly exactly what you started with.
You can do what you ask like this:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
def shuffleImage(im, seed=42):
# Get pixels and put in Numpy array for easy shuffling
pix = np.array(im.getdata())
# Generate an array of shuffled indices
# Seed random number generation to ensure same result
np.random.seed(seed)
indices = np.random.permutation(len(pix))
# Shuffle the pixels and recreate image
shuffled = pix[indices].astype(np.uint8)
return Image.fromarray(shuffled.reshape(im.width,im.height,3))
def unshuffleImage(im, seed=42):
# Get shuffled pixels in Numpy array
shuffled = np.array(im.getdata())
nPix = len(shuffled)
# Generate unshuffler
np.random.seed(seed)
indices = np.random.permutation(nPix)
unshuffler = np.zeros(nPix, np.uint32)
unshuffler[indices] = np.arange(nPix)
unshuffledPix = shuffled[unshuffler].astype(np.uint8)
return Image.fromarray(unshuffledPix.reshape(im.width,im.height,3))
# Load image and ensure RGB, i.e. not palette image
orig = Image.open('lena.png').convert('RGB')
result = shuffleImage(orig)
result.save('shuffled.png')
unshuffled = unshuffleImage(result)
unshuffled.save('unshuffled.png')
Which turns Lena into this:
It's impossible to do that reliably as far as I know. Theoretically you could brute force it by shuffling the pixels over and over and feeding the result into Amazon Rekognition, but you would end up with a huge AWS bill and probably only something that is approximately the original picture.

How to save a new raster with the projections of the previous raster

I tried to make an algorithm in Python where I entered a georeferenced raster (known coordinate system), all its negative values were transformed to zero, and then a new image was saved with the georeference of the initial image.
import skimage.io
import pandas as pd
import numpy as np
pathhr = 'C:\\Users\\dataset\\S30W051.tif'
HR = skimage.io.imread(pathhr)
df1 = pd.DataFrame(HR)
HR_changed = df1[df1 < 0] = 0
#save function
savedata = df1.to_numpy()
skimage.io.imsave('C:\\Users\\dataset\\S30W051_TEST.tif', savedata)
But when I save my raster at the end of this script, I get a non-georeferenced TIFF raster.
How do I keep the same coordinate system as the initial raster (without transforming the output raster into local coordinates)?
I ask for help in solving this problem. Thanks.
You could use rasterio for opening and saving your tiff files, and copy the metadata of the initial raster to the new raster.
import rasterio as rio
# Load the original image
with rio.open(pathhr, 'r') as r:
HR = r.read()
meta = r.meta
# Do any transformation you like (on the numpy array)
HR_changed = HR[HR < 0] = 0
# Save the changed raster
with rio.open('C:\\Users\\dataset\\S30W051_TEST.tif', 'w', **meta) as dst:
dst.write(HR_change)

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

Opening a single image

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

Averaging multiple images in python

I'm trying to average 300 .tif images with this code :
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlist=[filename for filename in allfiles if filename[-4:] in[".tif",".TIF"]]
# Assuming all images are the same size, get dimensions of first image
w,h = Image.open(imlist[0]).size
N = len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr = numpy.zeros((h,w,3),numpy.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr = numpy.array(Image.open(im),dtype=numpy.float)
arr = arr+imarr/N
# Round values in array and cast as 16-bit integer
arr = numpy.array(numpy.round(arr),dtype=numpy.uint16)
# Generate, save and preview final image
out = Image.fromarray(arr,mode="RGB")
out.save("Average.tif")
And it gives me a TypeError like that :
imarr = numpy.array(Image.open(im),dtype=numpy.float)
TypeError: float() argument must be a string or a number, not 'TiffImageFile'
I understand that it doesn't really like to put a TIF image in the numpy array (it also doesn't work with PNG images). What should I do ? Splitting each image into R, G and B arrays to average and then merge everything seems too memory consuming.
It should work as is, checked right now with PIL (pillow 2.9.0) and numpy 1.9.2.

Categories

Resources