Skip to content

Commit

Permalink
optional relative vs absolute positioning for neighborhoods, cleaner …
Browse files Browse the repository at this point in the history
…neighborhoods impl, new variable-distance band fn
  • Loading branch information
wpbonelli committed Mar 5, 2022
1 parent e0d5cb3 commit 2b52223
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 52 deletions.
37 changes: 37 additions & 0 deletions cactice/bands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from typing import Tuple, Dict

import numpy as np


def get_band(
grid: np.ndarray,
i: int,
j: int,
start: int,
exclude_zero: bool = False,
absolute_coords: bool = False) -> Dict[Tuple[int, int], int]:
"""
:param grid: The grid
:param i: The central cell's row index
:param j: The central cell's column index
:param start: The distance from the central cell to start the band
:param exclude_zero: Whether to exclude zero-valued cells
:param absolute_coords: Use absolute coordinates rather than location relative to the central cell (the default)
:return: A dictionary mapping cell locations to their respective values
"""

if start < 1 or start > min(grid.shape):
raise ValueError(f"Band starting distance must be greater than 0 and less than min(grid length, grid width)")

band = {}
for ii in range(max(i - start, 0), min(i + 2, grid.shape[0])):
for jj in range(max(j - 1, 0), min(j + 2, grid.shape[1])):
coords = (ii, jj) if absolute_coords else (ii - i, jj - j)
band[coords] = grid[ii, jj]

# optionally exclude zeros (missing)
if exclude_zero:
band = {k: v for k, v in band.items() if (k == (0, 0) or (k != (0, 0) and v != 0))}

return band
2 changes: 1 addition & 1 deletion cactice/knn.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def predict(self, grids: List[np.ndarray] = None) -> List[np.ndarray]:
i=i,
j=j,
neighbors=self.__neighbors,
layers=self.__layers,
width=self.__layers,
exclude_zero=True)

# ignore central cell
Expand Down
2 changes: 1 addition & 1 deletion cactice/mrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def neighborhood_H(
i=i,
j=j,
neighbors=neighbors,
layers=layers,
width=layers,
exclude_zero=True)

# ignore central cell
Expand Down
70 changes: 21 additions & 49 deletions cactice/neighbors.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,68 +15,40 @@ def get_neighborhood(
i: int,
j: int,
neighbors: Neighbors = Neighbors.CARDINAL,
layers: int = 1,
exclude_zero: bool = False) -> Dict[Tuple[int, int], int]:
exclude_zero: bool = False,
absolute_coords: bool = False) -> Dict[Tuple[int, int], int]:
"""
Computes the neighborhood around the given grid cell.
Gets the neighborhood around the given grid cell.
:param grid: The grid
:param i: The cell's row index
:param j: The cell's column index
:param neighbors: The cells to consider neighbors
:param layers: The width of the neighborhood
:param exclude_zero: Whether to exclude zero-valued neighbors
:return: A dictionary mapping relative locations from the central cell to neighboring cell values
:param absolute_coords: Use absolute coordinates rather than location relative to the central cell (the default)
:return: A dictionary mapping cell locations to their respective values
"""

# set the center of the neighborhood
neighborhood = {(0, 0): grid[i, j]}

for layer in range(1, layers + 1):
# check if we're up against any boundaries
bt = (i - layer < 0) # top
bb = i >= (grid.shape[0] - layer) # bottom
bl = (j - layer < 0) # left
br = j >= (grid.shape[1] - layer) # right

# compute cardinal neighbors (set to None if on or beyond boundary)
top = None if bt else grid[i - layer, j]
bottom = None if bb else grid[i + layer, j]
left = None if bl else grid[i, j - layer]
right = None if br else grid[i, j + layer]

# compute diagonal neighbors (set to None if on or beyond boundary)
topleft = None if (bt or bl) else grid[i - layer, j - layer]
topright = None if (bt or br) else grid[i - layer, j + layer]
bottomleft = None if (bb or bl) else grid[i + layer, j - layer]
bottomright = None if (bb or br) else grid[i + layer, j + layer]

# TODO: compute off-cardinal/-diagonal neighbors

# store cardinal neighbors
if neighbors == Neighbors.CARDINAL or neighbors == Neighbors.COMPLETE:
neighborhood[(-1, 0)] = top
neighborhood[(1, 0)] = bottom
neighborhood[(0, -1)] = left
neighborhood[(0, 1)] = right

# store diagonal neighbors
elif neighbors == Neighbors.DIAGONAL or neighbors == Neighbors.COMPLETE:
neighborhood[(-1, -1)] = topleft
neighborhood[(-1, 1)] = topright
neighborhood[(1, -1)] = bottomleft
neighborhood[(1, 1)] = bottomright

if neighbors == Neighbors.COMPLETE:
# TODO: store off-cardinal/-diagonal neighbors
pass

# optionally exclude zero-valued neighbors
# neighborhood = {(0, 0): grid[i, j]}

neighborhood = {}
for ii in range(max(i - 1, 0), min(i + 2, grid.shape[0])):
for jj in range(max(j - 1, 0), min(j + 2, grid.shape[1])):
if not (i == ii and j == jj): continue # ignore the center
coords = (ii, jj) if absolute_coords else (ii - i, jj - j)
if (neighbors == Neighbors.CARDINAL or neighbors == Neighbors.COMPLETE) and \
(i == ii and j != jj) or (i != ii and j == jj):
neighborhood[coords] = grid[ii, jj]
elif (neighbors == Neighbors.DIAGONAL or neighbors == Neighbors.COMPLETE) and \
(i != ii and j != jj):
neighborhood[coords] = grid[ii, jj]

# optionally exclude zeros (missing)
if exclude_zero:
neighborhood = {k: v for k, v in neighborhood.items() if (k == (0, 0) or (k != (0, 0) and v != 0))}

# return only non-None neighbors
return {k: v for k, v in neighborhood.items() if v is not None}
return neighborhood


def get_neighborhoods(
Expand Down
2 changes: 1 addition & 1 deletion cactice/rns.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def predict(self, grids: List[np.ndarray] = None) -> List[np.ndarray]:
i=i,
j=j,
neighbors=self.__neighbors,
layers=self.__layers,
width=self.__layers,
exclude_zero=True)

# ignore central cell
Expand Down

0 comments on commit 2b52223

Please sign in to comment.