Using patchify to create patches from images with different shapes - python

I use patchify to generate patches from images. My folder, from which I take the data base, contains images which are of different shape (1536x2048 and 2048x1536).
If I use only one shape (no matter if 1536x2048 or 2048x1536) I get the reasonable number of patches.
But if I combine both shapes, I get some additional images, which are just duplicates of patches.
Why does my code not work when I use two different shapes, even though they should both produce even numbers in the number of resulting patches for both axes?
The core of my code comes from the following question (before this code, I just create lists with the corresponding information about the images I'm using):
Problem when using patchify library to create patches
import numpy as np
import cv2
from PIL import Image
import os
from patchify import patchify
List = []
destinationFile = "C:/.../Output/Images/"
for root, Lists, files in os.walk("C:/.../Input/Images/"):
for name in files:
if name.endswith(".png"):
List.append(os.path.join(root, name))
for filename in List:
img_no_ndarray = Image.open(filename)
img = np.array(img_no_ndarray)
patches_img = patchify(img, (512, 512, 3), step=512)
for i in range(patches_img.shape[0]):
for j in range(patches_img.shape[1]):
single_patch_img = patches_img[i, j, 0, :, :, :]
if not cv2.imwrite(destinationFile + str(i) + "_" + str(j) + "_" + name, single_patch_img):
raise Exception("Could not write the image")
Thanks

Two thoughts, if it works when all input has the same dimensions then have you tried adding a reshaping step to match all input shapes before running through patchify?
And, you might find that this line is better for avoiding duplications/overwriting your output patches:
cv2.imwrite(r'C:/destinationfilepath/image_{}{}.png'.format(str(i).zfill(4),str(j).zfill(4)), single_patch_img)

Related

How to save a 2-channel image to a folder?

I am concatenating two 1-channel grayscale images into one 2-channel image and writing it to a folder.
import struct
import zlib
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
import pandas as pd
import cv2
import numpy as np
import glob
import os
from keras.preprocessing import image
import imageio
filenames1 = glob.glob("folder1/*.png")
filenames1.sort()
filenames2 = glob.glob("folder2/*.png")
filenames2.sort()
for f1,f2 in zip(filenames1,filenames2):
img_name = os.path.basename(f1)
img_name = img_name[:-4] + ".png"
img1 = Image.open(f1)
img2 = Image.open(f2)
img1a = image.img_to_array(img1)
img2a = image.img_to_array(img2)
# Merged image
merge_image = np.concatenate((img1a, img2a), axis=2)
# plt.imsave('folder3/{}.png'.format(img_name[:-4]),merge_image)
imageio.imwrite('folder4/{}.png'.format(img_name[:-4]),merge_image)
When I used matplotlib's 'imsave' function, I got the following error:
ValueError: Third dimension must be 3 or 4
When I used Imageio 'imwrite' function, I got the following error:
ValueError: Image must be 2D (grayscale, RGB, or RGBA)
How can I write the 2-channel image to a folder in this case?
As your error stack says, you cannot use matplotlib.imsave or imageio.imwrite as they support only 1 (grayscale), 3 (rgb, bgr, hsv etc...) or 4 (same as 3 + alpha channel). I don't know if png format does support 2 channels at all, but if it does the result would be a single channel image (grayscale) + alpha channel.
The solution depends on what these images do represent and what you're actually trying to achieve:
if you want to save single channel image + alpha channel, you'd better to replicate the first channel 3 times, so that your channels are (BW, BW, BW, alpha)
if you're fusing two spatial informations, for example angle and magnitude of an optical flow, you have to do the conversion manually (OpenCV displaying a 2-channel image (optical flow)) and fill the remaining channel with something else.
if you're only trying to stack two images and save them, png is not the correct solution. You could stack them using numpy and save/store them as .npy objects.

ndarray object to array conversion, where object contains variable size images

I want to import images of different sizes in an array in format (Number of images, row_size, col_size). I am using following code to do this.
import os,cv2, numpy as np
PATH = os.getcwd()
data_path = PATH+'/data'
data_dir_list = os.listdir(data_path)
img_data_list=[]
for dataset in data_dir_list:
img_list=os.listdir(data_path+'/'+ dataset)
print ('Loaded the images of dataset-'+'{}\n'.format(dataset))
for img in img_list:
input_img=cv2.imread(data_path + '/'+ dataset + '/'+ img,0)
img_data_list.append(input_img)
img_data = np.array(img_data_list)
When i work with same size images i got proper result in img_data like example size=(1000,50,50) and type=uint8 but when i work with different size images (all 1000 images are of different sizes like 34*45, 25*43........) i got of img_data like size=(1000,) type=object value=ndarray object of numpy module . I am working on deep learning in keras and python.

How to use view as blocks in Scikit-Image?

I have a numpy array of shape (12,224,224). This is 12 images of size (244, 244). When I had a single image, this was simple. The image was of size (x,y). For example, x is an image of size (400,400), for which I could use view_as_blocks like this:
from skimage.util import view_as_blocks as vablks
xx = vablks(x, block_shape=(8,8))
This would result in a block of shape (50,50,8,8).
Now I would like to know how to apply this when I have a list of images. Either I lose shape, that is my 12 images are combined into one (224,224) block broken down into (28,28,8,8), or I run into a ValueError. Here is the code I tried to use for iterating over the 12 images and viewing the (224,224) shaped images
xx = []
for item_ in x:
xx.append(blockSplitter(item_))
where x is a list of images.
Here is the error:
ValueError: 'block_shape' is not compatible with 'arr_in'
Overall, I would like to know how to view the images as blocks of 8x8 without losing the images.
Help, Please and Thank You.
You have at least two options:
1) Convert the list to an array, as suggested by the commenter above. Then use view_as_blocks with the correct parameters:
from skimage.util import view_as_blocks
images = [np.zeros((50, 50)) for i in range(10)]
images = np.array(images)
all_blocks = view_as_blocks(images, block_shape=(1, 10, 10)).squeeze()
2) Convert each item in the list to a windowed view, and then convert the end result to an array:
from skimage.util import view_as_blocks
images = [np.zeros((50, 50)) for i in range(10)]
image_blocks = [view_as_blocks(image, block_shape=(10, 10)) for image in images]
all_blocks = np.array(image_blocks)

dicom image resizing before converting to numpy array

I have thousands of dicom images in a folder. I read them with pydicom like this
import numpy as np
import dicom
folder = "/images"
imgs = [dicom.read_file(folder + '/' + s) for s in os.listdir(folder)]
I then want to stack all images as a numpy array, like this:
data = np.stack([i.pixel_array for i in imgs])
However, the images are or different size and therefore cannot be stacked.
How can I add a step that resizes all images to 1000x1000 ?
If you stored then as a list of numpy arrays then they can be different size. Otherwise use scipy zoom function,
import numpy as np
import dicom
import scipy
xsize = 1000; ysize = 1000
folder = "/images"
data = np.zeros(xsize, ysize, len(os.listdir(folder)))
for i, s in enumerate(os.listdir(folder)):
img = np.array(dicom.read_file(folder + '/' + s).pixel_array)
xscale = xsize/img.shape[0]
yscale = ysize/img.shape[1]
data[:,:,i] = scipy.ndimage.interpolation.zoom(img, [xscale, yscale]))
You could save as a list and stack but seems easier to pre-allocate a numpy array of size 1000 by 1000 by len(os.listdir(folder)). I've not got dicom or the test files so cannot check your case but the idea certainly works (I've used it before to scale images to the right size). Also check the scale is correct for your case.

Create Numpy array of images

I have some (950) 150x150x3 .jpg image files that I want to read into an Numpy array.
Following is my code:
X_data = []
files = glob.glob ("*.jpg")
for myFile in files:
image = cv2.imread (myFile)
X_data.append (image)
print('X_data shape:', np.array(X_data).shape)
The output is (950, 150). Please let me know why the list is not getting converted to np.array correctly and whether there is a better way to create the array of images.
Of what I have read, appending to numpy arrays is easier done through python lists and then converting them to arrays.
EDIT: Some more information (if it helps), image.shape returns (150,150,3) correctly.
I tested your code. It works fine for me with output
('X_data shape:', (4, 617, 1021, 3))
however, all images were exactly the same dimension.
When I add another image with different extents I have this output:
('X_data shape:', (5,))
So I'd recommend checking the sizes and the same number of channels (as in are really all images coloured images)? Also you should check if either all images (or none) have alpha channels (see #Gughan Ravikumar's comment)
If only the number of channels vary (i.e. some images are grey), then force loading all into the color format with:
image = cv2.imread (myFile, cv2.IMREAD_COLOR)
EDIT:
I used the very code from the question, only replaced with a directory of mine (and "*.PNG"):
import cv2
import glob
import numpy as np
X_data = []
files = glob.glob ("C:/Users/xxx/Desktop/asdf/*.PNG")
for myFile in files:
print(myFile)
image = cv2.imread (myFile)
X_data.append (image)
print('X_data shape:', np.array(X_data).shape)
Appending images in a list and then converting it into a numpy array, is not working for me. I have a large dataset and RAM gets crashed every time I attempt it. Rather I append the numpy array, but this has its own cons. Appending into list and then converting into np array is space complex, but appending a numpy array is time complex. If you are patient enough, this will take care of RAM crasing problems.
def imagetensor(imagedir):
for i, im in tqdm(enumerate(os.listdir(imagedir))):
image= Image.open(im)
image= image.convert('HSV')
if i == 0:
images= np.expand_dims(np.array(image, dtype= float)/255, axis= 0)
else:
image= np.expand_dims(np.array(image, dtype= float)/255, axis= 0)
images= np.append(images, image, axis= 0)
return images
I am looking for better implementations that can take care of both space and time. Please comment if someone has a better idea.
Here is a solution for images that have certain special Unicode characters, or if we are working with PNGs with a transparency layer, which are two cases that I had to handle with my dataset. In addition, if there are any images that aren't of the desired resolution, they will not be added to the Numpy array. This uses the Pillow package instead of cv2.
resolution = 150
import glob
import numpy as np
from PIL import Image
X_data = []
files = glob.glob(r"D:\Pictures\*.png")
for my_file in files:
print(my_file)
image = Image.open(my_file).convert('RGB')
image = np.array(image)
if image is None or image.shape != (resolution, resolution, 3):
print(f'This image is bad: {myFile} {image.shape if image is not None else "None"}')
else:
X_data.append(image)
print('X_data shape:', np.array(X_data).shape)
# If you have 950 150x150 images, this would print 'X_data shape: (950, 150, 150, 3)'
If you aren't using Python 3.6+, you can replace the r-string with a regular string (except with \\ instead of \, if you're using Windows), and the f-string with regular string interpolation.
Your definition for the .JPG frame that will be put into a matrix of the same size should should be x, y, R, G, B, A. "A" is not used, but it does take up 8 bits at the end of each pixel.

Categories

Resources