How to change nearest neighbors function to not work diagonally - python

I've working nearest neighbors function but I don't know how to make it work only horizontally and vertically right now it works in all directions. Code below:
nnlst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
MAP_WIDTH = 3
MAP_HEIGHT = 3
def nearest_neighbors(map_x, map_y):
coordinates_list = []
for x_ in range(max(0, map_x - 1), min(MAP_WIDTH, map_x + 2)):
for y_ in range(max(0, map_y - 1), min(MAP_HEIGHT, map_y + 2)):
# we are ignoring result when x_ and y_ equals variable we ask for
if (map_x, map_y) == (x_, y_):
continue
coordinates_list.append([x_, y_])
return coordinates_list
print "function result"
print "nearest neighbors of", nnlst[0][1]
nearest_neighbor_coordinates_list = nearest_neighbors(0, 1)
for coordinates in nearest_neighbor_coordinates_list:
print coordinates, "=", nnlst[coordinates[0]][coordinates[1]]
As you can see right now it works in all directions.

You need to add one more condition to prevent inclusion of the diagonal ones:
def nearest_neighbors(map_x, map_y):
coordinates_list = []
for x_ in range(max(0, map_x - 1), min(MAP_WIDTH, map_x + 2)):
for y_ in range(max(0, map_y - 1), min(MAP_HEIGHT, map_y + 2)):
# we are ignoring result when x_ and y_ equals variable we ask for, also the diagonal neigbors that differ in both x & y coordinates
if (map_x, map_y) == (x_, y_) or (map_x != x_ and map_y != y_):
continue
coordinates_list.append([x_, y_])
return coordinates_list
to get the desired result:
function result
nearest neighbors of 2
[0, 0] = 1
[0, 2] = 3
[1, 1] = 5

alternatively, you could list all "admissible" displacements explicitly:
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
x_ = min(MAP_WIDTH, max(0, map_x + dx))
y_ = min(MAP_HEIGHT, max(0, map_y + dy))
if (map_x, map_y) == (x_, y_):
continue
...

For a problem with such as small number of possibilities, I would just spell them all out and pre-compute the function results for every position. That way the function can be eliminated and the problem reduced to doing simple table look-up operation.
Here's what I mean:
nnlist = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
MAP_WIDTH = len(nnlist[0])
MAP_HEIGHT = len(nnlist)
nearest_neighbors = {} # is now a dictionary
for x in range(MAP_WIDTH):
for y in range(MAP_HEIGHT):
neighbors = [[nx, ny] for nx, ny in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]
if -1 < nx < MAP_WIDTH and -1 < ny < MAP_HEIGHT]
nearest_neighbors[(x, y)] = neighbors
print "look-up result"
print "nearest neighbors of", nnlist[0][1]
nearest_neighbor_coordinates_list = nearest_neighbors[(0, 1)]
for coordinates in nearest_neighbor_coordinates_list:
print coordinates, "=", nnlist[coordinates[0]][coordinates[1]]

Related

Python shortest distance while escaping a binary maze using backtracking

Need help with finding shortest distance in a binary maze, which is a list of lists matrix, where 0 is an empty cell and 1 is a wall. The maze has x,y starting coordinates that default to 0,0 and it's end point is always bottom right corner. Shortest distance always includes the starting point (i.e., if there are four steps needed from the starting point, shortest distance will be 5)
I need to be using backtracking algorithm. So far I could come up with a function that determines if there is an escaping path at all. It works well:
def is_solution(maze, x=0, y=0):
n = len(maze)
m = len(maze[0])
if x == n - 1 and y == m - 1:
return True
maze[x][y] = 1
result = False
for a, b in [(x - 1, y), (x, y - 1), (x + 1, y), (x, y + 1)]:
if 0 <= a < n and 0 <= b < m and maze[a][b] == 0:
result = result or is_solution(maze, a, b)
maze[x][y] = 0
return result
maze = [
[0, 0, 1, 1],
[1, 0, 0, 0],
[1, 1, 1, 0]
]
is_solution(maze)
The above will result to True.
Now I am really struggling with finding the shortest distance. I think it should be relatively easy to update the code above so it showed distance if there is a path and inf if there isn't one. But I got stuck. In the example above shortest distance would be 6 (including the starting point)
I also need to add code to be able to get a list of all shortest distances and coordinates of each step in a list of lists format like [[(0, 0), (0, 1), (1, 1), (1, 2), (1, 3), (2, 3)]] . In this case there is only one path, but if there were two of distance six, that list would include also the list of second shortest path as well.
Thank you.
Simple change to your code to track shortest path
Shortest Path
def min_solution(maze, x = 0, y = 0, path = None):
def try_next(x, y):
' Next position we can try '
return [(a, b) for a, b in [(x - 1, y), (x, y - 1), (x + 1, y), (x, y + 1)] if 0 <= a < n and 0 <= b < m]
n = len(maze)
m = len(maze[0])
if path is None:
path = [(x, y)] # Init path to current position
# Reached destionation
if x == n - 1 and y == m - 1:
return path
maze[x][y] = 1 # Mark current position so we won't use this cell in recursion
# Recursively find shortest path
shortest_path = None
for a, b in try_next(x, y):
if not maze[a][b]:
last_path = min_solution(maze, a, b, path + [(a, b)]) # Solution going to a, b next
if not shortest_path:
shortest_path = last_path # Use since haven't found a path yet
elif last_path and len(last_path) < len(shortest_path):
shortest_path = last_path # Use since path is shorter
maze[x][y] = 0 # Unmark so we can use this cell
return shortest_path
maze = [
[0, 0, 1, 1],
[1, 0, 0, 0],
[1, 1, 1, 0]
]
t = min_solution(maze)
if t:
print(f'Shortest path {t} has length {len(t)}')
else:
print('Path not found')
Output:
Shortest path [(0, 0), (0, 1), (1, 1), (1, 2), (1, 3), (2, 3)] has length 6
All Paths
def all_paths(maze, x = 0, y = 0, path = None):
'''
All paths through Maze as a generator
'''
def try_next(x, y):
' Next position we can try '
return [(a, b) for a, b in [(x - 1, y), (x, y - 1), (x + 1, y), (x, y + 1)] if 0 <= a < n and 0 <= b < m]
n = len(maze)
m = len(maze[0])
if path is None:
path = [(x, y)]
# Reached destionation
if x == n - 1 and y == m - 1:
yield path
else:
maze[x][y] = 1 # Mark current position so we won't use this cell in recursion
# Recursively find pat
for a, b in try_next(x, y):
if not maze[a][b]:
yield from all_paths(maze, a, b, path + [(a, b)]) # Solution going to a, b next
maze[x][y] = 0 # Unmark so we can use this cell
maze = [[0, 0, 0],
[1, 0, 0],
[1, 1, 0]]
for t in all_paths(maze):
print(f'path {t} has length {len(t)}')
Output
path [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)] has length 5
path [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)] has length 5

Determining neighbours of cell as diamond shape in python

I have a matrix variable in size where 1 indicates the cell such as:
Cells = [[0,0,0,0,0],
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
]
I need to find neigbours in a parametric sized diamond shape. Not a box as answer given in here or not a fixed sized 1 diamond, answer given here. For example, N=2 I want to know the column, rows for below:
Mask = [[0,0,1,0,0],
[0,1,1,1,0],
[1,1,0,1,1],
[0,1,1,1,0],
[0,0,1,0,0],
]
The function should receive x and y for the requested column and row, (for above I will input 2,2) and N (input 2) the size of diamond. The function should return list of tuples (x,y) for the given diamond size.
I struggled at defining the shape as a function of x, y and k in for loops. I need to know both numpy (if there is anything that helps) and non-numpy solution.
For an iterative approach where you just construct the diamond:
def get_neighbors(center, n=1):
ret = []
for dx in range(-n, n + 1):
ydiff = n - abs(dx)
for dy in range(-ydiff, ydiff + 1):
ret.append((center[0] + dx, center[1] + dy))
return ret
Result of get_neighbors((2, 2), 2):
[(0, 2), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 1), (3, 2), (3, 3), (4, 2)]
Or, for a recursive approach:
dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)]
def add_tuples(a, b):
return tuple([x + y for (x, y) in zip(a, b)])
def get_neighbors(center, n=1, seen=set()):
seen.add(center)
if n <= 0:
return seen
for dir in dirs:
newpos = add_tuples(center, dir)
if newpos in seen:
continue
get_neighbors(newpos, n - 1, seen)
return seen
I would start by taking out a "sub-matrix" that is the smallest square that can contain your result cells. This is the part that numpy should be able to help with.
Then define a function that calculates the manhattan distance between two cells (abs(x - x_p) + abs(y - y_p)) and iterate through the cells of your sub-matrix and return the values with a manhattan distance of less than N from your origin.
Make mask with rotation
Convolute cell and mask
Fix the result
import numpy as np
from scipy.ndimage import rotate, convolve
import matplotlib.pyplot as plt
def diamond_filter(radius):
s = radius * 2 + 1
x = np.ones((s, s), dtype=int)
x[radius, radius] = 0
return rotate(x, angle=45)
def make_diamonds(x, radius):
filter = diamond_filter(radius)
out = convolve(x, filter)
out[out > 1] = 1
out -= x
out[out < 0] = 0
return out
def plot(x):
plt.imshow(x)
plt.show()
plt.close()
def main():
cell = np.random.choice([0, 1], size=(200, 200), p=[0.95, 0.05])
plot(diamond_filter(2))
plot(cell)
result = make_diamonds(cell, 2)
plot(result)
if __name__ == '__main__':
main()

pytorch (numpy) calculation about the closest pixels to points

I am trying to solve a complicated problem.
For example, I have a batch of 2D predicted images (softmax output, value between 0 and 1) with size: Batch x H x W and ground truth Batch x H x W
The light gray color pixels are the background with value 0, and the dark gray color pixels are the foreground with value 1. I try to compute the mass center coordinates using scipy.ndimage.center_of_mass on each ground truth image. Then I get the center location point C (red color) for each ground truth. The C points set is Batch x 1.
Now, for each pixel A (yellow color) in the predicted images, I want to get three pixels B1, B2, B3 (blue color) which are the closest to A on the line AC (here C is corresponding location of mass center in ground truth).
I used following code to get the three closest points B1, B2, B3.
def connect(ends, m=3):
d0, d1 = np.abs(np.diff(ends, axis=0))[0]
if d0 > d1:
return np.c_[np.linspace(ends[0, 0], ends[1, 0], m + 1, dtype=np.int32),
np.round(np.linspace(ends[0, 1], ends[1, 1], m + 1))
.astype(np.int32)]
else:
return np.c_[np.round(np.linspace(ends[0, 0], ends[1, 0], m + 1))
.astype(np.int32),
np.linspace(ends[0, 1], ends[1, 1], m + 1, dtype=np.int32)]
So the B points set is Batch x 3 x H x W.
Then, I want to compute like this: |Value(A)-Value(B1)|+|Value(A)-Value(B2)|+|Value(A)-Value(B3)|. The size of the result should be Batch x H x W.
Is there any numpy vectorization tricks that can be used to update the value of each pixel in predicted images? Or can this be solved using pytorch functions? I need to find a method to update the whole image. The predicted image is the softmax output. I cannot use for loop to compute each single value since it will become non-differentiable. Thanks a lot.
As suggested by #Matin, you could consider Bresenham's algorithm to get your points on the AC line.
A simplistic PyTorch implementation could be as follows (directly adapted from the pseudo-code here ; could be optimized):
import torch
def get_points_from_low(x0, y0, x1, y1, num_points=3):
dx = x1 - x0
dy = y1 - y0
xi = torch.sign(dx)
yi = torch.sign(dy)
dy = dy * yi
D = 2 * dy - dx
y = y0
x = x0
points = []
for n in range(num_points):
x = x + xi
is_D_gt_0 = (D > 0).long()
y = y + is_D_gt_0 * yi
D = D + 2 * dy - is_D_gt_0 * 2 * dx
points.append(torch.stack((x, y), dim=-1))
return torch.stack(points, dim=len(x0.shape))
def get_points_from_high(x0, y0, x1, y1, num_points=3):
dx = x1 - x0
dy = y1 - y0
xi = torch.sign(dx)
yi = torch.sign(dy)
dx = dx * xi
D = 2 * dx - dy
y = y0
x = x0
points = []
for n in range(num_points):
y = y + yi
is_D_gt_0 = (D > 0).long()
x = x + is_D_gt_0 * xi
D = D + 2 * dx - is_D_gt_0 * 2 * dy
points.append(torch.stack((x, y), dim=-1))
return torch.stack(points, dim=len(x0.shape))
def get_points_from(x0, y0, x1, y1, num_points=3):
is_dy_lt_dx = (torch.abs(y1 - y0) < torch.abs(x1 - x0)).long()
is_x0_gt_x1 = (x0 > x1).long()
is_y0_gt_y1 = (y0 > y1).long()
sign = 1 - 2 * is_x0_gt_x1
x0_comp, x1_comp, y0_comp, y1_comp = x0 * sign, x1 * sign, y0 * sign, y1 * sign
points_low = get_points_from_low(x0_comp, y0_comp, x1_comp, y1_comp, num_points=num_points)
points_low *= sign.view(-1, 1, 1).expand_as(points_low)
sign = 1 - 2 * is_y0_gt_y1
x0_comp, x1_comp, y0_comp, y1_comp = x0 * sign, x1 * sign, y0 * sign, y1 * sign
points_high = get_points_from_high(x0_comp, y0_comp, x1_comp, y1_comp, num_points=num_points) * sign
points_high *= sign.view(-1, 1, 1).expand_as(points_high)
is_dy_lt_dx = is_dy_lt_dx.view(-1, 1, 1).expand(-1, num_points, 2)
points = points_low * is_dy_lt_dx + points_high * (1 - is_dy_lt_dx)
return points
# Inputs:
# (#todo: extend A to cover all points in maps):
A = torch.LongTensor([[0, 1], [8, 6]])
C = torch.LongTensor([[6, 4], [2, 3]])
num_points = 3
# Getting points between A and C:
# (#todo: what if there's less than `num_points` between A-C?)
Bs = get_points_from(A[:, 0], A[:, 1], C[:, 0], C[:, 1], num_points=num_points)
print(Bs)
# tensor([[[1, 1],
# [2, 2],
# [3, 2]],
# [[7, 6],
# [6, 5],
# [5, 5]]])
Once you have your points, you could retrieve their "values" (Value(A), Value(B1), etc.) using torch.index_select() (note that as of now, this method only accept 1D indices, so you need to unravel your data). All things put together, this would look like something such as the following (extending A from shape (Batch, 2) to (Batch, H, W, 2) is left for exercise...)
# Inputs:
# (#todo: extend A to cover all points in maps):
A = torch.LongTensor([[0, 1], [8, 6]])
C = torch.LongTensor([[6, 4], [2, 3]])
batch_size = A.shape[0]
num_points = 3
map_size = (9, 9)
map_num_elements = map_size[0] * map_size[1]
map_values = torch.stack((torch.arange(0, map_num_elements).view(*map_size),
torch.arange(0, -map_num_elements, -1).view(*map_size)))
# Getting points between A and C:
# (#todo: what if there's less than `num_points` between A-C?)
Bs = get_points_from(A[:, 0], A[:, 1], C[:, 0], C[:, 1], num_points=num_points)
# Get map values in positions A:
A_unravel = torch.arange(0, batch_size) * map_num_elements
A_unravel = A_unravel + A[:, 0] * map_size[1] + A[:, 1]
values_A = torch.index_select(map_values.view(-1), dim=0, index=A_unravel)
print(values_A)
# tensor([ 1, -4])
# Get map values in positions A:
A_unravel = torch.arange(0, batch_size) * map_num_elements
A_unravel = A_unravel + A[:, 0] * map_size[1] + A[:, 1]
values_A = torch.index_select(map_values.view(-1), dim=0, index=A_unravel)
print(values_A)
# tensor([ 1, -78])
# Get map values in positions B:
Bs_flatten = Bs.view(-1, 2)
Bs_unravel = (torch.arange(0, batch_size)
.unsqueeze(1)
.repeat(1, num_points)
.view(num_points * batch_size) * map_num_elements)
Bs_unravel = Bs_unravel + Bs_flatten[:, 0] * map_size[1] + Bs_flatten[:, 1]
values_B = torch.index_select(map_values.view(-1), dim=0, index=Bs_unravel)
values_B = values_B.view(batch_size, num_points)
print(values_B)
# tensor([[ 10, 20, 29],
# [-69, -59, -50]])
# Compute result:
res = torch.abs(values_A.unsqueeze(-1).expand_as(values_B) - values_B)
print(res)
# tensor([[ 9, 19, 28],
# [ 9, 19, 28]])
res = torch.sum(res, dim=1)
print(res)
# tensor([56, 56])

Fitting a polynomial function for a vector field in python

At first, thank you everybody for the amazing work on stackoverflow... you guys are amazing and have helped me out quite some times already. Regarding my problem: I have a series of vectors in the format (VectorX, VectorY, StartingpointX, StartingpointY)
data = [(-0.15304757819399128, -0.034405679205349315, -5.42877197265625, 53.412933349609375), (-0.30532995491023485, -0.21523935094046465, -63.36669921875, 91.832427978515625), (-0.15872430479453215, -0.077999419482978283, -67.805389404296875, 81.001983642578125), (-0.36415549211687903, -0.33757147194808113, -59.015228271484375, 82.976226806640625), (0.0, 0.0, 0.0, 0.0), (-0.052973530805275004, 0.098212384392411423, 19.02667236328125, -13.72125244140625), (-0.34318724086483599, 0.17123742336019632, 80.0394287109375, 108.58499145507812), (0.19410169197834648, -0.17635303976555861, -55.603790283203125, -76.298828125), (-0.38774018337716143, -0.0824692384322816, -44.59942626953125, 68.402496337890625), (0.062202543524108478, -0.37219011831012949, -79.828826904296875, -10.764404296875), (-0.56582988168383963, 0.14872365390732512, 39.67657470703125, 97.303192138671875), (0.12496832467900276, -0.12216653754859408, 24.65948486328125, -30.92584228515625)]
When I plot the vectorfield it looks like this:
import numpy as np
import matplotlib.pyplot as plt
def main():
# Format Data...
numdata = len(data)
x = np.zeros(numdata)
y = np.zeros(numdata)
u = np.zeros(numdata)
v = np.zeros(numdata)
for i,el in enumerate(data):
x[i] = el[2]
y[i] = el[3]
# length of vector
z[i] = math.sqrt(el[0]**2+el[1]**2)
u[i] = el[0]
v[i] = el[1]
# Plot
plt.quiver(x,y,u,v )
# showing the length with color
plt.scatter(x, y, c=z)
plt.show()
main()
I want to create a polynomial function to fit a continous vector field for the whole area. After some research I found the following functions for fitting polynoms in two dimensions. The problem is, that it only accepts one value for the value that is fitted.
def polyfit2d(x, y, z, order=3):
ncols = (order + 1)**2
G = np.zeros((x.size, ncols))
ij = itertools.product(range(order+1), range(order+1))
for k, (i,j) in enumerate(ij):
G[:,k] = x**i * y**j
m, _, _, _ = np.linalg.lstsq(G, z)
return m
def polyval2d(x, y, m):
order = int(np.sqrt(len(m))) - 1
ij = itertools.product(range(order+1), range(order+1))
z = np.zeros_like(x)
for a, (i,j) in zip(m, ij):
z += a * x**i * y**j
return z
Also when I tried to fit the one dimensional length of the vectors, the values returned from the polyval2d were completely off. Does anybody know a method to get a fitted function that will return a vector (x,y) for any point in the grid?
Thank you!
A polynomial to fit a 2-d vector field will be two bivariate polynomials - one for the x-component and one for the y-component. In other words, your final polynomial fitting will look something like:
P(x,y) = ( x + x*y, 1 + x + y )
So you will have to call polyfit2d twice. Here is an example:
import numpy as np
import itertools
def polyfit2d(x, y, z, order=3):
ncols = (order + 1)**2
G = np.zeros((x.size, ncols))
ij = itertools.product(range(order+1), range(order+1))
for k, (i,j) in enumerate(ij):
G[:,k] = x**i * y**j
m, _, _, _ = np.linalg.lstsq(G, z)
return m
def fmt1(x,i):
if i == 0:
return ""
elif i == 1:
return x
else:
return x + '^' + str(i)
def fmt2(i,j):
if i == 0:
return fmt1('y',j)
elif j == 0:
return fmt1('x',i)
else:
return fmt1('x',i) + fmt1('y',j)
def fmtpoly2(m, order):
for (i,j), c in zip(itertools.product(range(order+1), range(order+1)), m):
yield ("%f %s" % (c, fmt2(i,j)))
xs = np.array([ 0, 1, 2, 3] )
ys = np.array([ 0, 1, 2, 3] )
zx = np.array([ 0, 2, 6, 12])
zy = np.array([ 1, 3, 5, 7])
mx = polyfit2d(xs, ys, zx, 2)
print "x-component(x,y) = ", ' + '.join(fmtpoly2(mx,2))
my = polyfit2d(xs, ys, zy, 2)
print "y-component(x,y) = ", ' + '.join(fmtpoly2(my,2))
In this example our vector field is:
at (0,0): (0,1)
at (1,1): (2,3)
at (2,2): (6,5)
at (3,3): (12,7)
Also, I think I found a bug in polyval2d - this version gives more accurate results:
def polyval2d(x, y, m):
order = int(np.sqrt(len(m))) - 1
ij = itertools.product(range(order+1), range(order+1))
z = np.zeros_like(x)
for a, (i,j) in zip(m, ij):
z = z + a * x**i * y**j
return z

Animate flood fill in python

I have just finished with my food fill algorithm in python. It runs on an N*N matrix filled with integers. I would like to somehow animate it's workings. Is it somehow possible on the console? I think of something like updateing the nodes with a wait() inbetween the updates.
You could use something like this:
#! /usr/bin/python3
import time
m = [ [c for c in line] for line in '''............................
..XXXXXXXXXX...........XXX..
..X........X...........X.X..
..XXXXXX...X....XXXXXXXX.X..
.......X...X....X........X..
....XXXX...XXXXXX........X..
....X....................X..
....X.................XXXX..
....XXXXXXXXXXXXXXXXXXX.....'''.split ('\n') ]
def flood (matrix, start):
maxX = len (matrix [0] )
maxY = len (matrix)
tbf = [start]
while tbf:
x, y = tbf [0]
tbf = tbf [1:]
if x < 0 or x >= maxX or y < 0 or y >= maxY: continue
if matrix [y] [x] == 'X': continue
matrix [y] [x] = 'X'
tbf += [ (x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1) ]
print ('\x1b[0J\x1b[1;1H') #Clear screen and position cursor top left
for line in matrix: print (''.join (line) )
time.sleep (.2)
#flood (m, (0, 0) )
flood (m, (4, 2) )
This should work on any console that supports ANSI escape sequences (CSI). You can use the same CSI codes for outputting colours (Wiki).

Categories

Resources