Filling numpy array upon iteration - python

I never understood why this is not working
import numpy as np
cube = np.empty((10, 100, 100), dtype=np.float32)
for plane in cube:
plane = np.random.random(10000).reshape(100, 100)
With this the cube is still empty (just zeros). I have to do it like that to make it work:
for idx in range(10):
cube[idx] = np.random.random(10000).reshape(100, 100)
Why is that?
thanks 😊

Because each iteration of the loop you first assign an element of cube to plane then in the loop suite you assign a different thing to plane and you never change anything in cube.
Python is cool because you can play around in the shell and figure out how things work:
>>> a = [0,0,0,0]
>>> for thing in a:
print(thing),
thing = 2
print(thing),
print(a)
0 2 [0, 0, 0, 0]
0 2 [0, 0, 0, 0]
0 2 [0, 0, 0, 0]
0 2 [0, 0, 0, 0]
>>>
Iterating Over Arrays

Related

Python3 initializing 2D Array ignores index specification range(start,end,step)

I want to initialize an array in python3 with an index NOT starting at zero and 2 dimensions.
x_length=16
y_length=4
x_start=-4
y_start=-400
# Later passing those variables with:
level_matrix=generate_area(x_length,y_length,x_start,y_start)
Part of the function:
def generate_area(xlen,ylen,xstart,ystart):
matrix=[[0 for y in range(ystart, ystart+ylen)] for x in range(xstart, xstart+xlen)]
for index, value in enumerate(matrix):
print(f"S1_vec('{index}')<= {value}")
for index, value in enumerate(matrix[0]):
print(f"S1_vec('{index}')<= {value}")
for x in range(xstart, xstart+xlen):
for y in range(ystart, ystart+ylen):
print("Y:"+str(y))
print("X:"+str(x))
Output:
S1_vec('0')<= [0, 0, 0, 0]
S1_vec('1')<= [0, 0, 0, 0]
S1_vec('2')<= [0, 0, 0, 0]
S1_vec('3')<= [0, 0, 0, 0]
S1_vec('4')<= [0, 0, 0, 0]
S1_vec('5')<= [0, 0, 0, 0]
S1_vec('6')<= [0, 0, 0, 0]
S1_vec('7')<= [0, 0, 0, 0]
S1_vec('8')<= [0, 0, 0, 0]
S1_vec('9')<= [0, 0, 0, 0]
S1_vec('10')<= [0, 0, 0, 0]
S1_vec('11')<= [0, 0, 0, 0]
S1_vec('12')<= [0, 0, 0, 0]
S1_vec('13')<= [0, 0, 0, 0]
S1_vec('14')<= [0, 0, 0, 0]
S1_vec('15')<= [0, 0, 0, 0]
S1_vec('0')<= 0
S1_vec('1')<= 0
S1_vec('2')<= 0
S1_vec('3')<= 0
Y:-400
X:-4
IndexError: list assignment index out of range
Well, as you can clearly see, there is no negative indexes in the array. This also does not properly work with positive offset as well. The loops want to access offset index values and the script obviously fails, since the index only gets created from 0 to the var1 in case of in range(var1, var2). This makes no sense, since it should work like: range(starting_point, end_point, steps_if_needed). And the copy paste for loop syntax gets successfully used later in the script in multiple instances.
What causes such weird behavior and how to fix this without changing anything else except the initialization of the array in the code? I need 2D arrays to exactly work within the specified region. Is there a simple solution?
Edit:
Just to clarify the goal:
I need a 2D array with negative index capabilities. The range is known, but each entry needs to be defined. Append is useless, because it will not add a specific negative index for me.
If I for example need to define matrix[-4][-120]="Jeff", this needs to work. I do not even care at all, if there is a solution like in Bash, where you have to write matrix["-4,-120"]. Yet I need a reasonable way to address such entries.
You can use a virtual indexing strategy to do the same. Here is my approach:
offset = 5
size = 4
a = [x for x in range(offset,offset+size)]
print(a) # [5, 6, 7, 8]
def get_loc(idx):
return a[idx-offset]
def set_loc(idx, val):
a[idx-offset] = val
set_loc(6,15)
print(a) # [5, 15, 7, 8]
set_loc(8, 112)
print(a) # [5, 15, 7, 112]
This code was just to understand the what i mean by virtual indexing strategy, you can simply use below to get and set values:
# to get a[n] use a[n-offset]
print(a[8-offset]) # 112
# to set a[n] = x | Use a[n-offset] = x
a[7-offset] = 21
print(a) # [5, 15, 21, 112]

PIL's Image.fromarray function() confused me

I want to create a small image with PIL, my idea is first creating an ndarray object with numpy, then transforming it into a Image object, but it doesn't work!
small = np.array([[0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 1, 0]])
small = Image.fromarray(small, 'L')
print(small.size)
these codes print (6, 2), so why it transposes my original input?
What made my even more confused is that when I try to print all the pixels:
for i in range(6):
for j in range(2):
print(small.getpixel((i, j)), end='')
it prints out : 0 0 0 0 0 1 0 0 0 0 0 0
I had no idea about what have happened ........
Image.fromarray(small,'L')
expects an 8 bit Input? converting the Input Array to np.int8 works for me..
small = np.array([[0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 1, 0]],dtype=np.int8)
Just removing the 'L' will also work

How should I compare elements at every position in two lists using numpy?

Suppose there are two lists with the same dimension, let's say [0,1,2,2] and [0,1,2,2]. How should I compare two elements at every position and return a matrix which, in this case, is [[1,0,0,0],[0,1,0,0],[0,0,1,1],[0,0,1,1]]?
i.e. f(x,y)=1 if x=y else 0, and x,y are come from the above two lists respectively.
Already tried the for-loop block with python, which is rather slow when dealing with large lists.
Is there any efficient way of handling this problem with numpy instead of using codes below:
for i in list_1:
for j in list_2:
1 if i==j else 0
You can use np.equal to compare the elements and use np.where to convert it into 1 or 0.:
import numpy as np
a = np.array([0,1,2,2])
b = np.array([0,1,2,2])
z = np.where(np.equal(a, b[:,np.newaxis]), 1, 0)
Output:
array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 1],
[0, 0, 1, 1]])
Or as #Onyambu suggested, you can do
z = (a==b[:,None]).astype(int)
Try the below code, I hope this would help.
print([1 if i==j else 0 for i,j in zip([0,1,2,2],[0,1,0,2])])
Ouput :
[1, 1, 0, 1]

What is the most pythonic way to find all coordinate pairs in a numpy array that match a specific condition?

So given a 2d numpy array consisting of ones and zeros, I want to find every index where it is a value of one and where either to its top, left, right, or bottom consists of a zero. For example in this array
0 0 0 0 0
0 0 1 0 0
0 1 1 1 0
0 0 1 0 0
0 0 0 0 0
I only want coordinates for (1,2), (2,1), (2,3) and (3,2) but not for (2,2).
I have created code that works and creates two lists of coordinates, similar to the numpy nonzero method, however I don't think its very "pythonic" and I was hoping there was a better and more efficient way to solve this problem. (*Note this only works on arrays padded by zeros)
from numpy import nonzero
...
array= ... # A numpy array consistent of zeros and ones
non_zeros_pairs=nonzero(array)
coordinate_pairs=[[],[]]
for x, y in zip(temp[0],temp[1]):
if array[x][y+1]==0 or array[x][y-1]==0 or array[x+1][y]==0 or array[x-1][y]==0:
coordinate_pairs[0].append(x)
coordinate_pairs[1].append(y)
...
If there exist methods in numpy that can handle this for me, that would be awesome. If this question has already been asked/answered on stackoverflow before, I will gladly remove this, I just struggled to find anything. Thank You.
Setup
import scipy.signal
import numpy as np
a = np.array([[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]])
Create a window which matches the four directions from each value, and convolve. Then, you can check if elements are 1, and if their convolution is less than 4, since a value ==4 means that the value was surrounded by 1s
window = np.array([[0, 1, 0],
[1, 0, 1],
[0, 1, 0]])
m = scipy.signal.convolve2d(a, window, mode='same', fillvalue=1)
v = np.where(a & (m < 4))
list(zip(*v))
[(1, 2), (2, 1), (2, 3), (3, 2)]

Aligning array values

Lets say I have two arrays, both with values representing a brightness of the sun. The first array has values measured in the morning and second one has values measured in the evening. In the real case I have around 80 arrays. I'm going to plot the pictures using matplotlib. The plotted circle will (in both cases) be the same size. However the position of the image will change a bit because of the Earth's motion and this should be avoided.
>>> array1
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 1, 3, 1, 0]
[0, 0, 1, 1, 2, 0]
[0, 0, 1, 1, 1, 0]
[0, 0, 0, 0, 0, 0]
>>> array2
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 1, 2, 1, 0]
[0, 0, 1, 1, 4, 0]
[0, 0, 1, 1, 1, 0]
In the example above larger values mean brighter spots and zero values are plotted as black space. The arrays are always the same size. How do I align the significant values (not zero) in array2 with the ones in array1? So the outcome should be like this.
>>> array2(aligned)
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 1, 2, 1, 0]
[0, 0, 1, 1, 4, 0]
[0, 0, 1, 1, 1, 0]
[0, 0, 0, 0, 0, 0]
This must be done in order to post-process arrays in a meaningful way e.q. calculating average or sum etc. Note! Finding a mass center point and aligning accordingly doesn't work because of possible high values on the edges that change during a day.
One thing that may cause problems with this kind of data is that the images are not nicely aligned with the pixels. I try to illustrate my point with two arrays with a square in them:
array1:
0 0 0 0 0
0 2 2 2 0
0 2 2 2 0
0 2 2 2 0
0 0 0 0 0
array2:
0 0 0 0 0
0 1 2 2 1
0 1 2 2 1
0 1 2 2 1
0 0 0 0 0
As you see, the limited resolution is a challenge, as the image has moved 0.5 pixels.
Of course, it is easy to calculate the COG of both of these, and see that it is (row,column=2,2) for the first array and (2, 2.5) for the second array. But if we move the second array by .5 to the left, we get:
array2_shifted:
0 0 0 0 0
0.5 1.5 2.0 1.5 0.5
0.5 1.5 2.0 1.5 0.5
0.5 1.5 2.0 1.5 0.5
0 0 0 0 0
So that things start to spread out.
Of course, it may be that your arrays are large enough so that you can work without worrying about subpixels, but if you only have a few or a few dozen pixels in each direction, this may become a nuisance.
One way out of this is to first increase the image size by suitable extrapolation (such as done with an image processing program; the cv2 module is full of possibilities with this). Then the images can be fitted together with single-pixel precision and downsampled back.
In any case you'll need a method to find out where the fit between the images is the best. There are a lot of choices to make. One important thing to notice is that you may not want to align the images with the first image, you may want to alignt all images with a reference. The reference could in this case be a perfect circle in the center of the image. Then you will just need to move all images to match this reference.
Once you have chosen your reference, you need to choose the method which gives you some metrics about the alignment of the images. There are several possibilities, but you may start with these:
Calculate the center of gravity of the image.
Calculate the correlation between an image and the reference. The highest point(s) of the resulting correlation array give you the best match.
Do either of the above but only after doing some processing for the image (typically limiting the dynamic range at each or both ends).
I would start by something like this:
possibly upsample the image (if the resolution is low)
limit the high end of the dynamic range (e.g. clipped=np.clip(image,0,max_intensity))
calculate the center of gravity (e.g. scipy.ndimage.center_of_mass(clipped))
translate the image by the offset of the center of gravity
Translation of a 2D array requires a bit of code but should not be excessively difficult. If you are sure you have black all around, you can use:
translated = np.roll(np.roll(original, deltar, axis=0), deltac, axis=1)
This rolls the leftmost pixels to the right (or vice versa). If that is bad, then you'll need to zero them out. (Or have a look at: python numpy roll with padding).
A word of warning about the alignment procedures: The simples (COG, correlation) fail, if you have an intensity gradient across the image. Due to this you may want to look for edges and then correlate. The intensity limiting also helps here, if your background is really black.

Categories