How to take n block of a 3d array in python? - python

I had 328 grayscale images with the size 128*128 and I convert all of them into a 3D array with the shape (128,128,328). Now I want to convert it into 5 separated 3d arrays with the shape of (128,128,64) without changing in sequence.
As you can see 328 is not divisible by 64 and using dsplit function is not working.
Is there any way to slice the 3d array on depth axes dynamically?

import numpy as np
arr = np.zeros(shape=(128,128,328))
# make aray divisible by 5 (trim it to the depth of 320)
arr = arr[:, :, :320]
# Split array
arrays = np.dsplit(arr, 5)
for array in arrays:
print(array.shape)
Output:
(128, 128, 64)
(128, 128, 64)
(128, 128, 64)
(128, 128, 64)
(128, 128, 64)
EDIT: Here is the same thing written in a dynamic way.
import numpy as np
num_subarrays = 5
subarray_depth = 64
# Initialize array
arr = np.zeros(shape=(128,128,328))
# make aray divisible by subarray_depth
arr = arr[:, :, :(arr.shape[2] // subarray_depth) * subarray_depth]
# Split array
arrays = np.dsplit(arr, num_subarrays)
for array in arrays:
print(array.shape)

Related

Subtract 2D array from each pixel of a 3D image and get a 4D array

I have a 2D array of shape (10, 3) and an image represented as a 3D array of shape (480, 640, 3). I'd like to perform a difference between each pixel and each element of the 2D array, to get a final result of shape (10, 480, 640, 3).
For now, my code looks like this:
arr_2d = np.random.rand(10, 3)
arr_3d = np.random.rand(480, 640, 3)
res = np.ones_like(arr_3d)
res = np.tile(res, (10, 1, 1, 1))
for i in range(10):
res[i] = arr_3d - arr_2d[i]
My question is if there's a way to do this without the for loop, only using numpy operations.
You can try broadcasting with np.array like this
arr_2d = arr_2d.reshape(-1,1,1,3)
arr_3d = arr_3d.reshape((-1,*arr_3d.shape))
res = arr_3d - arr_2d
This should give the same result as your original code

Select random arrays from a stack of arrays

I have a numpy array np_arr with a shape as such
(3787, 256, 256)
I want to sample 20 random arrays from the existing array such that
(20, 256, 256)
I tried
import random
new_array = random.sample(np_arr,20)
but that didn't work.
How do I go about it
You can use np.random.choiceto randomly choose indices of the first dimension, then index your array with it:
import numpy as np
# Generating random data
rand_arr = np.random.rand(3787, 256, 256)
rand_idx = np.random.choice(rand_arr.shape[0], 20)
rand_arr[rand_idx]
# > shape = (20, 256, 256)
# In one line:
rand_arr[np.random.choice(rand_arr.shape[0], 20)]

Stacking numpy arrays with padding

I have a list of 32 numpy arrays, each of which has shape (n, 108, 108, 2), where n is different in each array. I want to stack all of them to create a numpy array of shape (32, m, 108, 108, 2), where m is the maximum among the ns, and the shorter arrays are padded with zeros.
How do I do this?
I asked something similar yesterday, but the answers there seem to break when using deep arrays like in my case.
Concretely, I went with this solution in the end, which produced the cleanest code:
data = np.column_stack(zip_longest(*data, fillvalue=0))
But now it is throwing this error:
ValueError: setting an array element with a sequence.
I have found a godly answer in this webpage.
The pad_sequences function is exactly what I needed.
from tensorflow.python.keras.preprocessing.sequence import pad_sequences
result = pad_sequences(imgs, padding='post')
In my case I needed to stack images with different width and padded with zeros to the left side.
for me this works well:
np.random.seed(42)
image_batch = []
for i in np.random.randint(50,500,size=10):
image_batch.append(np.random.randn(32,i))
for im in image_batch:
print(im.shape)
output: (32, 152)
(32, 485)
(32, 398)
(32, 320)
(32, 156)
(32, 121)
(32, 238)
(32, 70)
(32, 152)
(32, 171)
def stack_images_rows_with_pad(list_of_images):
func = lambda x: np.array(list(zip_longest(*x, fillvalue=0))) # applied row wise
return np.array(list(map(func, zip(*list_of_images)))).transpose(2,0,1)
res = stack_images_rows_with_pad(image_batch)
for im in rez:
print(im.shape)
output: (32, 485)
(32, 485)
(32, 485)
(32, 485)
(32, 485)
(32, 485)
(32, 485)
(32, 485)
(32, 485)
(32, 485)
Try this:
# Create matrices with random first axis length.
depth = np.random.randint(3,20,size=32)
l = []
lmax = 0
for i in depth:
l.append(np.ones((i,10,10,2)))
lmax = i if i > lmax else lmax
# Join the matrices:
new_l = []
for m in l:
new_l.append(np.vstack([m, np.zeros((lmax-m.shape[0], 10, 10, 2))]))
master = np.stack(new_l, axis=0)
master.shape
>>> (32, 19, 10, 10, 2)
I find np.pad almost impossible to work with on higher dimensional matrix - luckily, what you asked was simple, where only one of the dimension will have to extended, such that it's easy to use np.vstack to stack a zeros array that make it conform to a new shape.
A = np.ones((4,3))
border_top_bottom = np.zeros((A.shape[1])).reshape(1,A.shape[1])
print(np.vstack([border_top_bottom,A,border_top_bottom]))
temp = np.vstack([border_top_bottom,A,border_top_bottom])
border_right_left = np.zeros((temp.shape[0])).reshape(temp.shape[0],1)
print(np.hstack([np.hstack([border_right_left,temp,border_right_left])]))

How to save 4d numpy array to images

I want to create an image date_set which includes 176 small images (128*128*3) from one big image (1408, 2048, 3).
I do the following thing:
step 1.
Load the big image and convert it to numpy array. (1408, 2048, 3) 3d array
step 2.
cut it into 176 pieces: (176, 128, 128, 3) 4d array
step 3.
I don't know how to save 176 images from 4d array in this step. Does anyone could help me to solve this problem?
Thanks very much!
from astropy.io import fits
from astropy.utils.data import download_file
image_file = download_file('https://data.sdss.org/sas/dr12/boss/photoObj/frames/301/1035/3/frame-irg-001035-3-0011.jpg', cache=True )
image = imread(image_file)
def blockshaped(arr, nrows, ncols, c):
"""
Return an array of shape (n, nrows, ncols) where
n * nrows * ncols = arr.size
If arr is a 2D array, the returned array should look like n subblocks with
each subblock preserving the "physical" layout of arr.
"""
h, w = arr.shape[:2]
return (arr.reshape(h//nrows, nrows, -1, ncols, c)
.swapaxes(1,2)
.reshape(-1, nrows, ncols, c))
a= image[:1408, :]
b= blockshaped(a, 128, 128, 3)
b.shape
b.shape = (176, 128, 128, 3)
Here's a possible way to do it.
import numpy as np
import scipy.misc
images = np.zeros((176,128,128,3))
for i in range(len(images)):
scipy.misc.imsave('date_set_' + str(i) + '.jpg', images[i,:,:,:])

python numpy array padding issue

I have a numpy array v with shape (1000, 68), v is supposed to padding to 100 dimension with 0s. As a result, the v's shape will be (1000, 100)
I tried to use the following approaches:
t = np.lib.pad(v, (16, 16), 'minimum') # numpy method
t = sequence.pad_sequences(v, maxlen = 100, padding = 'post') # Keras text processing method
Above two methods returned the t with correct shape (1000, 100), but each array t[n] (n from 0 to 99) is a zero vector [0, 0, 0, ....0]
Following numpy.pad documentation, I tried
np.pad(v, [(0,0), (16,16)], 'constant')
with the expected result: 16 columns of zeros added on the left, and 16 on the right.

Categories