- add option to skip autoscaling brightness when saving as mp4

- let coords_pandas also deal with polars dataframes
This commit is contained in:
w.pomp
2025-09-25 15:20:00 +02:00
parent 1fe3b3c824
commit 1b5febc35b
3 changed files with 48 additions and 12 deletions

View File

@@ -418,7 +418,7 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
)
return new
def __getstate__(self) -> dict[str:Any]:
def __getstate__(self) -> dict[str, Any]:
return {key: value for key, value in self.__dict__.items() if key not in self.do_not_pickle} | {
"cache_size": self.cache.maxlen
}
@@ -1100,6 +1100,7 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
t: str | int | Sequence[int] = None, # noqa
colors: tuple[str] = None,
brightnesses: tuple[float] = None,
no_scale_movie_brightnesses: bool = False,
scale: int = None,
bar: bool = True,
speed: float = None,
@@ -1133,9 +1134,20 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
frame = np.dstack([255 * frame * i for i in color])
return np.clip(np.round(frame), 0, 255).astype("uint8")
if no_scale_movie_brightnesses and self.dtype.kind != "f":
info = np.iinfo(self.dtype)
ab = list(zip(*[(info.min, info.max) for _ in range(self.shape["c"])]))
elif no_scale_movie_brightnesses:
ab = list(zip(*[(0, 1) for _ in range(self.shape["c"])]))
else:
ab = list(zip(*[get_ab(i) for i in self.transpose("cztyx")])) # type: ignore
colors = colors or ("r", "g", "b")[: self.shape["c"]] + max(0, self.shape["c"] - 3) * ("w",)
brightnesses = brightnesses or (1,) * self.shape["c"]
if brightnesses is None:
brightnesses = (1,) * self.shape["c"]
elif len(brightnesses) == 1:
brightnesses = brightnesses * self.shape["c"]
elif len(brightnesses) != self.shape["c"]:
raise ValueError("brightnesses must have same length as shape[c]")
scale = scale or 1
shape_x = 2 * ((self.shape["x"] * scale + 1) // 2)
shape_y = 2 * ((self.shape["y"] * scale + 1) // 2)
@@ -1538,6 +1550,13 @@ def main() -> None:
parser.add_argument("-C", "--movie-colors", help="colors for channels in movie", type=str, nargs="*")
parser.add_argument("-V", "--movie_speed", help="speed of move, default = 25 / 7", type=float, default=None)
parser.add_argument("-B", "--movie-brightnesses", help="scale brightness of each channel", type=float, nargs="*")
parser.add_argument(
"-N",
"--no-scale-movie-brightnesses",
help="do not scale brightness of each channel,"
" if image file has an integer data type it's range will be scaled to [0, 1]",
action="store_true",
)
parser.add_argument("-S", "--movie-scale", help="upscale movie xy size, int", type=float)
args = parser.parse_args()
@@ -1559,6 +1578,7 @@ def main() -> None:
args.time,
args.movie_colors,
args.movie_brightnesses,
args.no_scale_movie_brightnesses,
args.movie_scale,
bar=len(args.file) == 1,
speed=args.movie_speed,

View File

@@ -16,9 +16,14 @@ except ImportError:
sitk = None
try:
from pandas import DataFrame, Series, concat
import pandas as pd
except ImportError:
DataFrame, Series, concat = None, None, None
pd = None
try:
import polars as pl
except ImportError:
pl = None
if hasattr(yaml, "full_load"):
@@ -126,9 +131,13 @@ class Transforms(dict):
return inverse
def coords_pandas(self, array, channel_names, columns=None):
if isinstance(array, DataFrame):
return concat([self.coords_pandas(row, channel_names, columns) for _, row in array.iterrows()], axis=1).T
elif isinstance(array, Series):
if pd is None:
raise ImportError("pandas is not available")
if isinstance(array, pd.DataFrame):
return pd.concat(
[self.coords_pandas(row, channel_names, columns) for _, row in array.iterrows()], axis=1
).T
elif isinstance(array, pd.Series):
key = []
if "C" in array:
key.append(channel_names[int(array["C"])])
@@ -459,14 +468,21 @@ class Transform:
"""
if self.is_unity():
return array.copy()
elif DataFrame is not None and isinstance(array, (DataFrame, Series)):
elif pd is not None and isinstance(array, (pd.DataFrame, pd.Series)):
columns = columns or ["x", "y"]
array = array.copy()
if isinstance(array, DataFrame):
if isinstance(array, pd.DataFrame):
array[columns] = self.coords(np.atleast_2d(array[columns].to_numpy()))
elif isinstance(array, Series):
elif isinstance(array, pd.Series):
array[columns] = self.coords(np.atleast_2d(array[columns].to_numpy()))[0]
return array
elif pl is not None and isinstance(array, (pl.DataFrame, pl.LazyFrame)):
columns = columns or ["x", "y"]
if isinstance(array, pl.DataFrame):
xy = self.coords(np.atleast_2d(array.select(columns).to_numpy()))
elif isinstance(array, pl.LazyFrame):
xy = self.coords(np.atleast_2d(array.select(columns).collect().to_numpy()))
return array.with_columns(**{c: i for c, i in zip(columns, xy.T)})
else: # somehow we need to use the inverse here to get the same effect as when using self.frame
return np.array([self.inverse.transform.TransformPoint(i.tolist()) for i in np.asarray(array)])

View File

@@ -1,6 +1,6 @@
[project]
name = "ndbioimage"
version = "2025.8.0"
version = "2025.9.0"
description = "Bio image reading, metadata and some affine registration."
authors = [
{ name = "W. Pomp", email = "w.pomp@nki.nl" }