Insert edited image patch back into original image in Python - python

I am fairly new to Python, so excuse any blindingly obvious oversights.
I have indexed part of an image, performed some processing on the indexed patch (not shown), and now need to reinsert the processed patch back into the original image. Ideally, the transition between original image and processed patch would be smooth and not introduce artifacts.
Here's what I have so far:
import numpy
import rasterio as rio
import imageio
# read image to be processed
img = rio.open('path/to/image')
img = img.read(1) # convert to array for processing
# define image patch to be processed (original image size: 19,968px x 19,968px)
ind = 1536 # first index position
L = 16898 # length of image
img = img[ind:ind+L,ind:ind+L]
## PROCESSING OF PATCH DONE HERE ##
# convert to uint8 to match original image
img_done = img_done.astype(np.uint8)
# what I tried so far
img_done[ind:ind+L,ind:ind+L] = img
The final line gives the broadcast error ValueError: could not broadcast input array from shape (16898,16898) into shape (15362,15362)
How do I go about inserting the processed patch correctly?

Related

Convert python wand hdr image to numpy array and back

Python wand supports converting images directly to a Numpy arrays, such as can be seen in related questions.
However, when doing this for .hdr (high dynamic range) images, this appears to compress the image to 0/255. As a result, converting from a Python Wand image to a np array and back drastically reduces file size/quality.
# Without converting to a numpy array
img = Image('image.hdr') # Open with Python Wand Image
img.save(filename='test.hdr') # Save with Python wand
Running this opens the image and saves it again, which creates a file with a size of 41.512kb. However, if we convert it to numpy before saving it again..
# With converting to a numpy array
img = Image(filename=os.path.join(path, 'N_SYNS_89.hdr')) # Open with Python Wand Image
arr = np.asarray(img, dtype='float32') # convert to np array
img = Image.from_array(arr) # convert back to Python Wand Image
img.save(filename='test.hdr') # Save with Python wand
This results in a file with a size of 5.186kb.
Indeed, if I look at arr.min() and arr.max() I see that the min and max values for the numpy array are 0 and 255. If I open the .hdr image with cv2 however as an numpy array, the range is much higher.
img = cv2.imread('image.hdr'), -1)
img.min() # returns 0
img.max() # returns 868352.0
Is there a way to convert back and forth between numpy arrays and Wand images without this loss?
As per the comment of #LudvigH, the following worked as in this answer.
img = Image(filename='image.hdr'))
img.format = 'rgb'
img.alpha_channel = False # was not required for me, including it for completion
img_array = np.asarray(bytearray(img.make_blob()), dtype='float32')
Now we much reshape the returned img_array. In my case I could not run the following
img_array.reshape(img.shape)
Instead, for my img.size was a (x,y) tuple that should have been an (x,y,z) tuple.
n_channels = img_array.size / img.size[0] / img.size[1]
img_array = img_array.reshape(img.size[0],img.size[1],int(n_channels))
After manually calculating z as above, it worked fine. Perhaps this is also what caused the original fault in converting using arr = np.asarray(img, dtype='float32')

How to get only possible non overlapping blocks from images of any given size and show them in Python

I have images of varying resolutions, and I would like to extract non-overlapping blocks from these images.
However, because the images have not fixed size and my block size is big (64x64), I would like to get only non-overlapping blocks that could be found in an image. If the block exceeds the image borders, I don't want to get them.
I tried the view_as_blocks function from scikit-image as below:
from skimage.util import view_as_blocks
for elem in listOfFiles:
# Reading image
print("Reading image "+elem)
img = cv2.imread(elem)
print(img.shape) #for example, one image is (2059, 2059, 3)
Blocks = view_as_blocks(img, block_shape=(64, 64, 3))
The code returns the following error:
ValueError: 'block_shape' is not compatible with 'arr_in'
I also tried the Patch Extractor from scikit-learn, as follows:
from sklearn.feature_extraction import image
import cv2
import numpy
for elem in listOfFiles:
# Reading image
print("Reading image "+elem)
img = cv2.imread(elem)
print(img.shape)
pe = image.PatchExtractor(patch_size=(64,64))
pe_fit = pe.fit(img)
pe_trans = pe.transform(img)
print('Patches shape: {}'.format(pe_trans.shape))
The error that returns to me is the following:
ValueError: negative dimensions are not allowed
the function image.extract_patches_2d from sklearns runs perfectly, but unfortunatelly it works only for overlapping blocks as you can see here.
These functions also don't help me because I also want to show the image with these blocks selected, so I also need another matrix with coordinates of such blocks and show the selected blocks.
Is that possible to that in Python?
Since you don't care about the incomplete blocks at the edges, you can manually check the number of blocks along each dimension, and crop your image to that shape:
from skimage.util import view_as_blocks
for elem in listOfFiles:
# Reading image
print("Reading image "+elem)
img = cv2.imread(elem)
print(img.shape) #for example, one image is (2059, 2059, 3)
block_shape = np.array((64, 64, 3))
nblocks = np.array(img.shape) // block_shape # integer division
crop_r, crop_c, crop_ch = nblocks * block_shape
cropped_img = img[:crop_r, :crop_c, :crop_ch]
Blocks = view_as_blocks(cropped_img, block_shape=(64, 64, 3))

Convert a raw RGB array into a png image.

I am trying to read a image file using PIL and then obtaining the raw pixel values in form of numpy array and then i am trying to put together the values to form a copy of original image. The code does not produce any runtime error but the image formed ("my.png") is unreadable.
from PIL import Image
import numpy as np
img_filename = "image.png"
img = Image.open(img_filename)
img = img.convert("RGB")
img.show()
aa = np.array(img.getdata())
alpha = Image.fromarray(aa,"RGB")
alpha.save('my.png')
alpha.show()
np.array(img.getdata()) gives a 2D array of shape (X, 3), where X depends on the dimensions of the original image.
Just change the relevant line of code to:
aa = np.array(img)
This will assign a 3D array to aa, and thus solve your problem.

In Rasterio - how to tile an array representing a geo-referenced image when the Affine object is separate?

I am attempting to tile a large image (.img format, but could be geotiff), however I have already cropped the image using rasterio mask which returns a masked array and a separate Affine object.
from rasterio import mask
import fiona
image = rasterio.open(image_path)
with fiona.open(shapefile_path, 'r') as shapefile:
cropping_polygon = [polygon['geometry'] for polygon in shapefile]
smaller_image, smaller_image_affine = mask.mask(image, cropping_polygon, crop=True)
Now I want to split the smaller_image into tiles of a fixed size. I have looked at rasterio windowed reading and writing but this seems to rely on the image having the image.affine attribute in order not to lose the geo-referencing.
Is it possible to tile the masked array, and produce a new affine for each tile?
I think you are looking for rasterio.windows.transform.
tile_window = rasterio.windows.Window(0, 0, 256, 256)
tile_affine = rasterio.windows.transform(tile_window, smaller_image_affine)
tile_image = smaller_image[(slice(None),) + tile_window.toslices()]
Then with tile_image and tile_affine you have all the pieces you need to write this to a new file.

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)

Categories