from typing import Union
import numpy as np
def _fix_no_contrast(frames: np.ndarray, value: Union[float, int] = 1):
"""
Ensures that frames with no contrast (i.e., containing only a single unique value) are adjusted.
This function modifies frames that lack contrast by adding a small value to the first pixel in
the affected frame. This prevents downstream issues in image processing pipelines that require
a minimum level of contrast.
Parameters
----------
frames : ndarray
A 3D array of shape `(H, W, N)`, where:
- `H` is the height of the frame,
- `W` is the width of the frame,
- `N` is the number of frames or channels.
Each frame (or channel) is independently checked for contrast.
value : int or float, optional
The value to add to the first pixel (`frames[0, 0, k]`) of any frame that lacks contrast.
Default is `1`.
Returns
-------
ndarray
The modified `frames` array, where frames with no contrast have been adjusted.
Integer inputs are returned as float so the added value cannot overflow.
Notes
-----
- A frame is determined to have "no contrast" if all its finite pixel values are identical.
Frames that are entirely NaN are left untouched.
- Only the first pixel (`[0, 0, k]`) of a no-contrast frame is modified, leaving the rest
of the frame unchanged.
- This is meant for consumers that cannot handle uniform input, such as deep-learning
segmentation models. Do not apply it to data that is measured or written to disk.
"""
if not np.issubdtype(frames.dtype, np.floating):
frames = frames.astype(float)
for k in range(frames.shape[2]):
channel = frames[:, :, k]
if not np.any(np.isfinite(channel)):
continue
if np.nanmin(channel) == np.nanmax(channel):
frames[0, 0, k] = np.nanmin(channel) + value
return frames
[docs]
def interpolate_nan_multichannel(frames: np.ndarray) -> np.ndarray:
"""
Interpolate NaNs in a multichannel image.
Parameters
----------
frames : ndarray
Multichannel image (H, W, C).
Returns
-------
ndarray
Image with NaNs interpolated.
"""
frames = np.moveaxis(
[interpolate_nan(frames[:, :, c].copy()) for c in range(frames.shape[-1])],
0,
-1,
)
return frames
[docs]
def interpolate_nan(img: np.ndarray, method: str = "nearest") -> np.ndarray:
"""
Interpolate NaN on single channel array 2D
Parameters
----------
img : ndarray
Input image.
method : str, optional
Interpolation method. Default is 'nearest'.
Returns
-------
ndarray
Image with NaNs interpolated.
"""
from scipy.interpolate import griddata
if np.all(img == 0):
return img
if np.any(img.flatten() != img.flatten()):
# then need to interpolate
x_grid, y_grid = np.meshgrid(np.arange(img.shape[1]), np.arange(img.shape[0]))
mask = [~np.isnan(img)][0]
x = x_grid[mask].reshape(-1)
y = y_grid[mask].reshape(-1)
points = np.array([x, y]).T
values = img[mask].reshape(-1)
interp_grid = griddata(points, values, (x_grid, y_grid), method=method)
return interp_grid
else:
return img