- remove center and shape from Transform

This commit is contained in:
w.pomp committed 2026-10-22 11:47:26 +02:00
1 parent e1caa1c0b0
commit f0404d0419
5 files changed
+67 -172

No files matched your search

@@ -116,24 +116,6 @@ class Transform:
@dparameters.setter
def dparameters(self, value: typing.Sequence[builtins.float]) -> None: ...
@property
def center(self) -> builtins.list[builtins.float]: ...
@center.setter
def center(self, value: typing.Sequence[builtins.float]) -> None: ...
@property
def origin(self) -> builtins.list[builtins.float]:
r"""
backwards compatibility
"""
@origin.setter
def origin(self, value: typing.Sequence[builtins.float]) -> None:
r"""
backwards compatibility
"""
@property
def shape(self) -> builtins.list[builtins.int]: ...
@shape.setter
def shape(self, value: typing.Sequence[builtins.int]) -> None: ...
@property
def ndim(self) -> builtins.int: ...
@property
def matrix(self) -> numpy.typing.NDArray[numpy.float64]: ...
@@ -149,18 +131,12 @@ class Transform:
def __new__(
cls,
parameters: typing.Optional[typing.Sequence[builtins.float]] = None,
shape: typing.Optional[typing.Sequence[builtins.int]] = None,
dparameters: typing.Optional[typing.Sequence[builtins.float]] = None,
center: typing.Optional[typing.Sequence[builtins.float]] = None,
) -> Transform: ...
def __getnewargs__(
self,
) -> tuple[
builtins.list[builtins.float],
builtins.list[builtins.int],
typing.Optional[builtins.list[builtins.float]],
]: ...
def __getstate__(self) -> builtins.list[builtins.float]: ...
def __setstate__(self, state: typing.Sequence[builtins.float]) -> None: ...
) -> tuple[builtins.list[builtins.float], builtins.list[builtins.float]]: ...
def __add__(self, other: Transform) -> Transform: ...
def __radd__(self, other: Transform) -> Transform: ...
def __sub__(self, other: Transform) -> Transform: ...
@@ -168,11 +144,6 @@ class Transform:
def __mul__(self, other: Transform | float) -> Transform: ...
def __rmul__(self, other: Transform | float) -> Transform: ...
def __truediv__(self, other: builtins.float) -> Transform: ...
def adapt(
self,
center: typing.Sequence[builtins.float],
shape: typing.Sequence[builtins.int],
) -> None: ...
@staticmethod
def from_scaling(scaling: typing.Sequence[builtins.float]) -> Transform: ...
@staticmethod
@@ -202,10 +173,10 @@ class Transform:
"""
def is_unity(self) -> builtins.bool: ...
def transform_point(
self, point: numpy.typing.ArrayLike
self, point: numpy.typing.ArrayLike, center: numpy.typing.ArrayLike
) -> numpy.typing.NDArray[numpy.float64]: ...
def transform_points(
self, points: numpy.typing.ArrayLike
self, points: numpy.typing.ArrayLike, center: numpy.typing.ArrayLike
) -> numpy.typing.NDArray[numpy.float64]: ...
@staticmethod
def register(
+32 -75
View File
@@ -1,6 +1,5 @@
use crate::error::Error;
use crate::py::imread::PyView;
use crate::readers::Reader;
use crate::transforms::Transforms;
use image_registration::metric::{SamplingArg, Sigma};
use image_registration::register::{Optimizer, RegistrationStep};
@@ -36,41 +35,31 @@ pub(crate) struct PyTransform {
#[pymethods]
impl PyTransform {
#[new]
#[pyo3(signature = (parameters = None, shape = None, center = None))]
#[pyo3(signature = (parameters = None, dparameters = None, center = None))]
fn new(
parameters: Option<Vec<f64>>,
shape: Option<Vec<usize>>,
dparameters: Option<Vec<f64>>,
center: Option<Vec<f64>>,
) -> Self {
let parameters = parameters.unwrap_or_else(|| vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
let shape = shape.unwrap_or_else(|| vec![512, 512]);
if let Some(center) = center {
Self {
inner: Transform::new_with_center(parameters, center, shape),
}
let mut transform = if let Some(center) = center {
Transform::new_around_center(parameters, &center)
} else {
Self {
inner: Transform::new(parameters, shape),
}
Transform::new(parameters)
};
if let Some(dparameters) = dparameters {
transform.dparameters = dparameters;
}
Self { inner: transform }
}
fn __getnewargs__(&self) -> (Vec<f64>, Vec<usize>, Option<Vec<f64>>) {
fn __getnewargs__(&self) -> (Vec<f64>, Vec<f64>) {
(
self.inner.parameters.clone(),
self.inner.shape.clone(),
Some(self.inner.center.clone()),
self.inner.dparameters.clone(),
)
}
fn __getstate__(&self) -> Vec<f64> {
self.inner.dparameters.clone()
}
fn __setstate__(&mut self, state: Vec<f64>) {
self.inner.dparameters = state;
}
fn __add__(&self, other: &PyTransform) -> PyTransform {
PyTransform {
inner: &self.inner + &other.inner,
@@ -159,38 +148,6 @@ impl PyTransform {
self.inner.dparameters = dparameters;
}
#[getter]
fn get_center(&self) -> Vec<f64> {
self.inner.center.clone()
}
#[setter]
fn set_center(&mut self, center: Vec<f64>) {
self.inner.center = center;
}
/// backwards compatibility
#[getter]
fn get_origin(&self) -> Vec<f64> {
self.inner.center.clone()
}
/// backwards compatibility
#[setter]
fn set_origin(&mut self, center: Vec<f64>) {
self.inner.center = center;
}
#[getter]
fn get_shape(&self) -> Vec<usize> {
self.inner.shape.clone()
}
#[setter]
fn set_shape(&mut self, shape: Vec<usize>) {
self.inner.shape = shape;
}
#[getter]
fn get_ndim(&self) -> usize {
self.inner.ndim()
@@ -233,10 +190,6 @@ impl PyTransform {
})
}
fn adapt(&mut self, center: Vec<f64>, shape: Vec<usize>) {
self.inner.adapt(center.as_slice(), shape.as_slice());
}
#[staticmethod]
fn from_scaling(scaling: Vec<f64>) -> PyTransform {
PyTransform {
@@ -436,13 +389,19 @@ impl PyTransform {
py: Python<'py>,
#[gen_stub(override_type(type_repr="numpy.typing.ArrayLike", imports=("numpy.typing")))]
point: PyArrayLike1<f64, AllowTypeChange>,
#[gen_stub(override_type(type_repr="numpy.typing.ArrayLike", imports=("numpy.typing")))]
center: PyArrayLike1<f64, AllowTypeChange>,
) -> Bound<'py, PyArray1<f64>> {
let point = point.as_array();
if let Some(slice) = point.as_slice() {
self.inner.transform_point(slice)
} else {
let point = point.to_vec();
self.inner.transform_point(&point)
let center = center.as_array();
match (point.as_slice(), center.as_slice()) {
(Some(point), Some(center)) => self.inner.transform_point(point, center),
(Some(point), None) => self.inner.transform_point(point, &center.to_vec()),
(None, Some(center)) => self.inner.transform_point(&point.to_vec(), center),
(None, None) => self
.inner
.transform_point(&point.to_vec(), &center.to_vec()),
}
.into_pyarray(py)
}
@@ -452,10 +411,19 @@ impl PyTransform {
py: Python<'py>,
#[gen_stub(override_type(type_repr="numpy.typing.ArrayLike", imports=("numpy.typing")))]
points: PyArrayLike2<f64, AllowTypeChange>,
#[gen_stub(override_type(type_repr="numpy.typing.ArrayLike", imports=("numpy.typing")))]
center: PyArrayLike1<f64, AllowTypeChange>,
) -> PyResult<Bound<'py, PyArray2<f64>>> {
let points = points.as_array();
let center = center.as_array();
Ok(py
.detach(|| self.inner.transform_points(points))
.detach(|| {
if let Some(center) = center.as_slice() {
self.inner.transform_points(points, center)
} else {
self.inner.transform_points(points, &center.to_vec())
}
})
.map_err(Error::from)?
.into_pyarray(py))
}
@@ -796,17 +764,6 @@ impl PyView {
main_channel,
default_transform,
)?;
let new_shape = vec![self.view.raw_shape().y, self.view.raw_shape().x];
for transform in new.view.transforms.channel.values_mut() {
for (c, (old_s, new_s)) in transform
.center
.iter_mut()
.zip(transform.shape.iter().zip(new_shape.iter()))
{
*c -= (*old_s as f64) / 2.0 - (*new_s as f64) / 2.0;
}
transform.shape = new_shape.clone();
}
Ok(new)
}
+27 -51
View File
@@ -33,15 +33,17 @@ impl TryFrom<HashMap<String, TransformOld>> for Transforms {
channel: t
.into_iter()
.map(|(n, i)| {
let center = i
.size
.iter()
.zip(i.center_of_rotation_point.iter())
.map(|(s, c)| c - s / 2.0 + 0.5)
.collect::<Vec<_>>();
Ok((
n,
Transform::new_with_center(
i.transform_parameters,
i.center_of_rotation_point,
i.size.into_iter().map(|j| j as usize).collect(),
)
.with_dparameters(i.dtransform_parameters)
.inverse()?,
Transform::new_around_center(i.transform_parameters, &center)
.with_dparameters(i.dtransform_parameters)
.inverse()?,
))
})
.collect::<Result<_, Error>>()?,
@@ -93,6 +95,7 @@ impl Serialize for Transforms {
#[derive(Serialize)]
struct Binary<'a> {
interpolation_order: Option<usize>,
channel: &'a HashMap<String, Transform<IxDyn>>,
drift: &'a Vec<Transform<IxDyn>>,
}
@@ -105,6 +108,7 @@ impl Serialize for Transforms {
.serialize(serializer)
} else {
Binary {
interpolation_order: self.interpolation_order,
channel: &self.channel,
drift: &self.drift,
}
@@ -164,8 +168,7 @@ impl Transforms {
if let Some(transform_file) = transform_file {
Self::load(transform_file)
} else {
let default_transform = default_transform
.map(|default_transform| Transform::new(default_transform, vec![512, 0]));
let default_transform = default_transform.map(Transform::new);
let mut transforms = Self::default();
if let Some(channels_files) = channels_files {
let main_channel = main_channel.unwrap_or(0);
@@ -303,8 +306,8 @@ impl Transforms {
match (self.channel.get(channel), self.drift.get(t)) {
(Some(channel), Some(drift)) => match (channel.ndim(), drift.ndim()) {
(2, 2) | (3, 3) => Ok(Some(Cow::Owned(drift * channel))),
(2, 3) => Ok(Some(Cow::Owned(drift.add_dimension(0, 1) * channel))),
(3, 2) => Ok(Some(Cow::Owned(drift * channel.add_dimension(0, 1)))),
(2, 3) => Ok(Some(Cow::Owned(drift.add_dimension(0) * channel))),
(3, 2) => Ok(Some(Cow::Owned(drift * channel.add_dimension(0)))),
(2 | 3, n) | (n, _) => Err(Error::TransformationNotImplemented(n)),
},
(Some(channel), None) => Ok(Some(Cow::Borrowed(channel))),
@@ -348,23 +351,13 @@ impl<D: Dimension, R: Reader> View<D, R> {
.max_proj(Axis::Z)?
.as_array::<f64>()?;
let mut transforms = HashMap::new();
let shape = vec![self.shape().y, self.shape().x];
let center = shape
.iter()
.map(|&i| (i as f64 - 1.0) / 2.0)
.collect::<Vec<_>>();
for (channel, metadata) in self.metadata()?.image[0].pixels.channel.iter().enumerate() {
let name = metadata
.name
.clone()
.unwrap_or_else(|| format!("channel{}", channel));
if channel == main_channel {
transforms.insert(
name,
Transform::default()
.with_shape(shape.clone())
.with_center(center.clone()),
);
transforms.insert(name, Transform::default());
} else {
let max = self
.slice_cztyx(s![channel, .., 0, .., ..])?
@@ -387,23 +380,13 @@ impl<D: Dimension, R: Reader> View<D, R> {
.slice_cztyx(s![main_channel, .., 0, .., ..])?
.as_array::<f64>()?;
let mut transforms = HashMap::new();
let shape = vec![self.shape().z, self.shape().y, self.shape().x];
let center = shape
.iter()
.map(|&i| (i as f64 - 1.0) / 2.0)
.collect::<Vec<_>>();
for (channel, metadata) in self.metadata()?.image[0].pixels.channel.iter().enumerate() {
let name = metadata
.name
.clone()
.unwrap_or_else(|| format!("channel{}", channel));
if channel == main_channel {
transforms.insert(
name,
Transform::default()
.with_shape(shape.clone())
.with_center(center.clone()),
);
transforms.insert(name, Transform::default());
} else {
let max = self
.slice_cztyx(s![channel, .., 0, .., ..])?
@@ -479,11 +462,12 @@ impl<D: Dimension, R: Reader> View<D, R> {
3 => {
let tr = transform.clone().into_dimensionality::<Ix3>()?;
let shape = self.shape();
let center = shape.iter().map(|&i| (i as f64) / 2.0).collect::<Vec<_>>();
let zf = z as f64;
let yf = shape.y as f64 - 1.0;
let xf = shape.x as f64 - 1.0;
let corners = array![[zf, 0.0, 0.0], [zf, 0.0, xf], [zf, yf, 0.0], [zf, yf, xf]];
let extent = tr.transform_points(&corners)?;
let extent = tr.transform_points(&corners, &center)?;
let z_extent = extent.slice(s![.., 2]);
let z_min = (z_extent
.iter()
@@ -652,26 +636,18 @@ mod tests {
let mut channel = HashMap::new();
channel.insert(
"TV1".to_string(),
Transform::new_with_center(
vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
vec![255.5, 255.5],
vec![512, 512],
),
Transform::new(vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0]),
);
channel.insert(
"TV2".to_string(),
Transform::new_with_center(
vec![
0.9995868322142533,
0.012140955373934502,
0.012592501668218145,
0.8571690967587099,
-5.270859902118885,
-5.81118547158953,
],
vec![255.5, 255.5],
vec![512, 512],
),
Transform::new(vec![
0.9995868322142533,
0.012140955373934502,
0.012592501668218145,
0.8571690967587099,
-5.270859902118885,
-5.81118547158953,
]),
);
let view_t = view.with_transforms(Transforms {
interpolation_order: None,
+3 -12
View File
@@ -1167,20 +1167,11 @@ impl<D: Dimension, R: Reader> View<D, R> {
for (ti, t) in slice_out[2].into_iter().zip(&slice[2]) {
if let Some(transform) = self.transforms.get_transform(channel_name, t as usize)? {
let mut transform = transform.into_owned();
if !defer_slice_xy {
if !defer_slice_xy && transpose {
if transform.ndim() == 2 {
transform.center[0] -= slice[3].start as f64;
transform.center[1] -= slice[4].start as f64;
if transpose {
transform = transform.swap_axes(0, 1)
}
transform = transform.swap_axes(0, 1)
} else {
transform.center[0] -= slice[1].start as f64;
transform.center[1] -= slice[3].start as f64;
transform.center[2] -= slice[4].start as f64;
if transpose {
transform = transform.swap_axes(1, 2);
}
transform = transform.swap_axes(1, 2);
}
}