Skip to content

Indexing

indexing

Functions

determine_IDs_to_labels(texture_array, all_discrete_texture_values=None, background_ID=None)

Return the mapping of unique IDs to labels, or None if values are continous floats

Parameters:

Name Type Description Default
texture_array ndarray

description

required
IDs_to_labels Union[None, dict]

description. Defaults to None.

required
all_discrete_texture_values Union[List, None]

description. Defaults to None.

None
background_ID int

description. Defaults to None.

None
Source code in geograypher/utils/indexing.py
def determine_IDs_to_labels(
    texture_array: np.ndarray,
    all_discrete_texture_values: typing.Union[typing.List, None] = None,
    background_ID: int = None,
):
    """Return the mapping of unique IDs to labels, or None if values are continous floats

    Args:
        texture_array (np.ndarray): _description_
        IDs_to_labels (typing.Union[None, dict], optional): _description_. Defaults to None.
        all_discrete_texture_values (typing.Union[typing.List, None], optional): _description_. Defaults to None.
        background_ID (int, optional): _description_. Defaults to None.
    """
    # Check to see if the data is a continious float representation
    if texture_array.dtype == float:
        finite_labels = texture_array[np.isfinite(texture_array)]

        # If the data does not approximate integer data, it truly represents a contious quantity. Therefore, return None
        if not np.allclose(finite_labels, finite_labels.astype(int)):
            return None

    # In this case, it's time to compute IDs to labels
    # If all the discrete texture values are provided, use that. Otherwise, just use
    # the texture array values.
    # TODO, it would be good to eliminate the concept of passing all_discrete_texture_values
    # and handle that solely with IDs_to_labels
    unique_values_source = (
        texture_array
        if all_discrete_texture_values is None
        else all_discrete_texture_values
    )
    unique_values = np.unique(unique_values_source)

    # Build the IDs to labels mapping

    IDs_to_label = {}
    # Iterate through and set the values that match to the integer label
    i = 0
    for unique_value in unique_values:
        # If it matches the background ID, increment to skip
        if i is not None and i == background_ID:
            i += 1

        # Set the value in the dict
        IDs_to_label[i] = unique_value

        # Increment normally
        i += 1

    return IDs_to_label

find_argmax_nonzero_value(array, keepdims=False, axis=1)

Find the argmax of an array, setting entires with zero sum or finite values to nan

Parameters:

Name Type Description Default
array ndarray

The input array

required
keepdims bool

Should the dimensions be kept. Defaults to False.

False
axis int

Which axis to perform the argmax along. Defaults to 1.

1

Returns:

Type Description
array

np.array: The argmax, with nans for invalid or all-zero entries

Source code in geograypher/utils/indexing.py
def find_argmax_nonzero_value(
    array: np.ndarray, keepdims: bool = False, axis: int = 1
) -> np.array:
    """Find the argmax of an array, setting entires with zero sum or finite values to nan

    Args:
        array (np.ndarray): The input array
        keepdims (bool, optional): Should the dimensions be kept. Defaults to False.
        axis (int, optional): Which axis to perform the argmax along. Defaults to 1.

    Returns:
        np.array: The argmax, with nans for invalid or all-zero entries
    """
    # Find the column with the highest value per row
    argmax = np.argmax(array, axis=axis, keepdims=keepdims).astype(float)

    # Find rows with zero sum or any infinite values
    zero_sum_mask = np.sum(array, axis=axis) == 0
    infinite_mask = np.any(~np.isfinite(array), axis=axis)

    # Set these rows in the argmax to nan
    argmax[np.logical_or(zero_sum_mask, infinite_mask)] = np.nan

    return argmax

inverse_map_interpolation(ijmap, downsample=1, fill=-1)

Inverts the type of map that can be fed to skimage.transform.warp.

The basic construction is the pixel position of these maps is the position in the destination image, and the value of the map is the position in the source image. Therefore if location [20, 30] has value [22.2, 28.4], it means that the destination image pixel [20, 30] will be sampled from the source image at pixel [22.2, 28.4] (the sampler can choose to snap to the closest integer value or interpolate nearby pixels).

By inverting, we seek to reverse this map through interpolation. In the example above, in the output we would now have the location [22, 28] have the value [20, 30] (or slightly different because it might interpolate with nearby values).

Parameters:

Name Type Description Default
ijmap (2, H, W) numpy array

a map of the structure discussed above

required
downsample int

Inverting (particularly griddata) can be expensive for high-res image maps. You can downsample in integer steps to save computation, which will downsample both the (i, j) axes. Using downsample=2 will therefore interpolate on 1/4 of the pixels.

1
fill int

Values outside of the interpolation convex hull will take this value.

-1

Returns:

Type Description
ndarray

(2, H, W) numpy array of the same shape as ijmap

Source code in geograypher/utils/indexing.py
def inverse_map_interpolation(
    ijmap: np.ndarray, downsample: int = 1, fill: int = -1
) -> np.ndarray:
    """
    Inverts the type of map that can be fed to skimage.transform.warp.

    The basic construction is the pixel position of these maps is the position
    in the destination image, and the value of the map is the position in the
    source image. Therefore if location [20, 30] has value [22.2, 28.4], it
    means that the destination image pixel [20, 30] will be sampled from the
    source image at pixel [22.2, 28.4] (the sampler can choose to snap to the
    closest integer value or interpolate nearby pixels).

    By inverting, we seek to reverse this map through interpolation. In the
    example above, in the output we would now have the location [22, 28]
    have the value [20, 30] (or slightly different because it might
    interpolate with nearby values).

    Arguments:
        ijmap ((2, H, W) numpy array): a map of the structure discussed above
        downsample (int): Inverting (particularly griddata) can be expensive
            for high-res image maps. You can downsample in integer steps to
            save computation, which will downsample both the (i, j) axes. Using
            downsample=2 will therefore interpolate on 1/4 of the pixels.
        fill (int): Values outside of the interpolation convex hull will take
            this value.

    Returns:
        (2, H, W) numpy array of the same shape as ijmap
    """

    # (row, col) grid of pixels coordinates
    H, W = ijmap.shape[1:]
    igrid, jgrid = np.meshgrid(np.arange(H), np.arange(W), indexing="ij")

    # Get an (N, 2) array of the grid coordinates
    grid_coords = np.stack([igrid.ravel(), jgrid.ravel()], axis=1)

    # Get (N, 2) arrays of the source and goal coordinates we have data for
    if downsample > 1:
        ds = slice(None, None, downsample)
        sample_y = np.stack([igrid[ds, ds].ravel(), jgrid[ds, ds].ravel()], axis=1)
        sample_x = np.stack(
            [ijmap[0][ds, ds].ravel(), ijmap[1][ds, ds].ravel()], axis=1
        )
    else:
        sample_y = grid_coords.copy()
        sample_x = np.stack([ijmap[0].ravel(), ijmap[1].ravel()], axis=1)

    # This is a little complicated, but griddata takes in
    # 1) The samples you have data for (x)
    # 2) The sample data (y)
    # 3) The new x at which you want to resample
    # In this case our sample x data is the mapped indices, the sample y data
    # is the grid that mapping came from, and the resample x is also the grid
    # because we are trying to invert.
    inv_i = griddata(
        sample_x, sample_y[:, 0], grid_coords, method="linear", fill_value=fill
    )
    inv_j = griddata(
        sample_x, sample_y[:, 1], grid_coords, method="linear", fill_value=fill
    )

    return np.stack([inv_i.reshape(H, W), inv_j.reshape(H, W)], axis=0)