Aligning array values - python

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.

Related

How do skimage.morphology.remove_small_holes and skimage.morphology.remove_small_objects differ?

Both of these methods are included in the Scikit-Image library for Python. I'm trying to extract certain objects from images and ran into these two methods in a senior dev's code, written for the same purpose.
I have read the documentation for both skimage.morphology.remove_small_holes and skimage.morphology.remove_small_objects. But I can't understand what difference these two methods pose when they are run on a ndarray containing an image.
One removes holes (value 0) within objects (any other single value), the other removes objects. Note that it acts on either binary images (ndarray of dtype bool) or segmentation masks (ndarray of dtype int, where each value represents one object). Hopefully this example clarifies their use:
import numpy as np
from skimage import morphology
objects = np.array([
[1, 1, 1, 0, 0],
[1, 0, 1, 0, 0],
[1, 1, 1, 0, 0],
[0, 0, 0, 0, 2],
])
You can see that this array has two objects, object "1" has 8 pixels, and a hole in it of size 1 pixel, while object "2" has only 1 pixel total. Now I do:
print(morphology.remove_small_objects(objects, 2))
This removes objects of size strictly less than 2 pixels, so "2" disappears:
[[1 1 1 0 0]
[1 0 1 0 0]
[1 1 1 0 0]
[0 0 0 0 0]]
Removing holes is a little more complicated, because that function only works with boolean arrays, but the same principle applies. We are going to:
convert the object image to binary/boolean
remove the holes
use segmentation.watershed to get objects back — preserving their original IDs.
from skimage import segmentation
binary_objects = objects.astype(bool)
binary_filled = morphology.remove_small_holes(binary_objects, 2)
objects_filled = segmentation.watershed(
binary_filled, objects, mask=binary_filled
)
print(objects_filled)
This removes from any object holes of size strictly less than 2, so the hole in the object "1" is removed:
[[1 1 1 0 0]
[1 1 1 0 0]
[1 1 1 0 0]
[0 0 0 0 2]]

Counting gaps for a tetris ai in python

I am trying to make a simple tetris ai in python(no genetic algorithms)
I want to count the gaps in the grid and make the best choice depending on it.
By gap I mean where you wont be able to place a piece without clearing some lines.
My grid is something like this:
[0, 0, 0, 0, 0]
['#ff0000', ....]
[...]
0 represents a blank space, while the hex code represents its covered by a block
I have tried to calculate gaps like this:
def grid_gaps(grid):
gaps = 0
for x in range(len(grid[0])):
for y in range(len(grid)):
if grid[y][x] == 0 and \
(y > 0 and grid[y - 1][x] != 0):
gaps += 1
return gaps
It works good when the grid is like this:
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[1, 1, 1, 0, 0],
[0, 0, 0, 1, 0]
1 is some color, it correctly tells me that there are 3 gaps but when the grid is someting like this:
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[1, 1, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 1, 0]
It again returns 3 but I want it to return 6.
I think the problem is that and grid[y - 1][x] != 0 is only looking at the cell directly above the current cell, so your bottom 3 cells in the second example aren't being counted.
One quick fix I can think of is to set a gap cell to some non-zero value once it's counted, that way the gap cells below will be counted too. (Then set them back to 0 after you're done, if you're using the same grid and not a copy for the rest of the game.)
The problem is that you're looking "up" to see whether there's a blocker, but you're only looking up one row. I think you want to reorganize this so you iterate over columns, and for each column, iterate down until you hit a 1, and then continue iterating and add to the gap count for each 0 that's encountered.

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)]

How to get an object bounding box given pixel label in python?

Say I have a scene parsing map for an image, each pixel in this scene parsing map indicates which object this pixel belongs to. Now I want to get bounding box of each object, how can I implement this in python?
For a detail example, say I have a scene parsing map like this:
0 0 0 0 0 0 0
0 1 1 0 0 0 0
1 1 1 1 0 0 0
0 0 1 1 1 0 0
0 0 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
So the bounding box is:
0 0 0 0 0 0 0
1 1 1 1 1 0 0
1 0 0 0 1 0 0
1 0 0 0 1 0 0
1 1 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
Actually, in my task, just know the width and height of this object is enough.
A basic idea is to search four edges in the scene parsing map, from top, bottom, left and right direction. But there might be a lot of small objects in the image, this way is not time efficient.
A second way is to calculate the coordinates of all non-zero elements and find the max/min x/y. Then calculate weight and height using these x and y.
Is there any other more efficient way to do this? Thx.
If you are processing images, you can use scipy's ndimage library.
If there is only one object in the image, you can get the measurements with scipy.ndimage.measurements.find_objects (http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.ndimage.measurements.find_objects.html):
import numpy as np
from scipy import ndimage
a = np.array([[0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]])
# Find the location of all objects
objs = ndimage.find_objects(a)
# Get the height and width
height = int(objs[0][0].stop - objs[0][0].start)
width = int(objs[0][1].stop - objs[0][1].start)
If there are many objects in the image, you first have to label each object and then get the measurements:
import numpy as np
from scipy import ndimage
a = np.array([[0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0]]) # Second object here
# Label objects
labeled_image, num_features = ndimage.label(a)
# Find the location of all objects
objs = ndimage.find_objects(labeled_image)
# Get the height and width
measurements = []
for ob in objs:
measurements.append((int(ob[0].stop - ob[0].start), int(ob[1].stop - ob[1].start)))
If you check ndimage.measurements, you can get more measurements: center of mass, area...
using numpy:
import numpy as np
ind = np.nonzero(arr.any(axis=0))[0] # indices of non empty columns
width = ind[-1] - ind[0] + 1
ind = np.nonzero(arr.any(axis=1))[0] # indices of non empty rows
height = ind[-1] - ind[0] + 1
a bit more explanation:
arr.any(axis=0) gives a boolean array telling you if the columns are empty (False) or not (True). np.nonzero(arr.any(axis=0))[0] then extract the non zero (i.e. True) indices from that array. ind[0] is the first element of that array, hence the left most column non empty column and ind[-1] is the last element, hence the right most non empty column. The difference then gives the width, give or take 1 depending on whether you include the borders or not.
Similar stuff for the height but on the other axis.

Filling numpy array upon iteration

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

Categories