|
2 | 2 |
|
3 | 3 | import itertools
|
4 | 4 | from functools import singledispatch
|
5 |
| -from typing import TYPE_CHECKING, Any, Optional, Union |
| 5 | +from typing import TYPE_CHECKING, Any, Optional |
6 | 6 |
|
7 | 7 | import dask.array as da
|
8 | 8 | import dask_image.ndinterp
|
|
11 | 11 | from dask.dataframe.core import DataFrame as DaskDataFrame
|
12 | 12 | from geopandas import GeoDataFrame
|
13 | 13 | from multiscale_spatial_image import MultiscaleSpatialImage
|
14 |
| -from skimage.transform import estimate_transform |
15 | 14 | from spatial_image import SpatialImage
|
16 | 15 | from xarray import DataArray
|
17 | 16 |
|
|
21 | 20 | from spatialdata.models import SpatialElement, get_axis_names, get_model
|
22 | 21 | from spatialdata.models._utils import DEFAULT_COORDINATE_SYSTEM
|
23 | 22 | from spatialdata.transformations._utils import _get_scale, compute_coordinates
|
24 |
| -from spatialdata.transformations.operations import ( |
25 |
| - get_transformation, |
26 |
| - set_transformation, |
27 |
| -) |
| 23 | +from spatialdata.transformations.operations import set_transformation |
28 | 24 |
|
29 | 25 | if TYPE_CHECKING:
|
30 | 26 | from spatialdata.transformations.transformations import (
|
31 |
| - Affine, |
32 | 27 | BaseTransformation,
|
33 | 28 | Translation,
|
34 | 29 | )
|
@@ -384,156 +379,3 @@ def _(data: GeoDataFrame, transformation: BaseTransformation, maintain_positioni
|
384 | 379 | )
|
385 | 380 | ShapesModel.validate(transformed_data)
|
386 | 381 | return transformed_data
|
387 |
| - |
388 |
| - |
389 |
| -def get_transformation_between_landmarks( |
390 |
| - references_coords: Union[GeoDataFrame, DaskDataFrame], |
391 |
| - moving_coords: Union[GeoDataFrame, DaskDataFrame], |
392 |
| -) -> Affine: |
393 |
| - """ |
394 |
| - Get a similarity transformation between two lists of (n >= 3) landmarks. Landmarks are assumed to be in the same space. |
395 |
| -
|
396 |
| - Parameters |
397 |
| - ---------- |
398 |
| - references_coords |
399 |
| - landmarks annotating the reference element. Must be a valid element describing points or circles. |
400 |
| - moving_coords |
401 |
| - landmarks annotating the moving element. Must be a valid element describing points or circles. |
402 |
| -
|
403 |
| - Returns |
404 |
| - ------- |
405 |
| - The Affine transformation that maps the moving element to the reference element. |
406 |
| -
|
407 |
| - Examples |
408 |
| - -------- |
409 |
| - If you save the landmark points using napari_spatialdata, they will be alredy saved as circles. Here is an |
410 |
| - example on how to call this function on two sets of numpy arrays describing x, y coordinates. |
411 |
| - >>> import numpy as np |
412 |
| - >>> from spatialdata.models import PointsModel |
413 |
| - >>> from spatialdata.transform import get_transformation_between_landmarks |
414 |
| - >>> points_moving = np.array([[0, 0], [1, 1], [2, 2]]) |
415 |
| - >>> points_reference = np.array([[0, 0], [10, 10], [20, 20]]) |
416 |
| - >>> moving_coords = PointsModel(points_moving) |
417 |
| - >>> references_coords = PointsModel(points_reference) |
418 |
| - >>> transformation = get_transformation_between_landmarks(references_coords, moving_coords) |
419 |
| - """ |
420 |
| - from spatialdata.transformations.transformations import ( |
421 |
| - Affine, |
422 |
| - BaseTransformation, |
423 |
| - Sequence, |
424 |
| - ) |
425 |
| - |
426 |
| - assert get_axis_names(references_coords) == ("x", "y") |
427 |
| - assert get_axis_names(moving_coords) == ("x", "y") |
428 |
| - |
429 |
| - if isinstance(references_coords, GeoDataFrame): |
430 |
| - references_xy = np.stack([references_coords.geometry.x, references_coords.geometry.y], axis=1) |
431 |
| - moving_xy = np.stack([moving_coords.geometry.x, moving_coords.geometry.y], axis=1) |
432 |
| - elif isinstance(references_coords, DaskDataFrame): |
433 |
| - references_xy = references_coords[["x", "y"]].to_dask_array().compute() |
434 |
| - moving_xy = moving_coords[["x", "y"]].to_dask_array().compute() |
435 |
| - else: |
436 |
| - raise TypeError("references_coords must be either an GeoDataFrame or a DaskDataFrame") |
437 |
| - |
438 |
| - model = estimate_transform("affine", src=moving_xy, dst=references_xy) |
439 |
| - transform_matrix = model.params |
440 |
| - a = transform_matrix[:2, :2] |
441 |
| - d = np.linalg.det(a) |
442 |
| - final: BaseTransformation |
443 |
| - if d < 0: |
444 |
| - m = (moving_xy[:, 0].max() - moving_xy[:, 0].min()) / 2 |
445 |
| - flip = Affine( |
446 |
| - np.array( |
447 |
| - [ |
448 |
| - [-1, 0, 2 * m], |
449 |
| - [0, 1, 0], |
450 |
| - [0, 0, 1], |
451 |
| - ] |
452 |
| - ), |
453 |
| - input_axes=("x", "y"), |
454 |
| - output_axes=("x", "y"), |
455 |
| - ) |
456 |
| - flipped_moving = transform(moving_coords, flip, maintain_positioning=False) |
457 |
| - if isinstance(flipped_moving, GeoDataFrame): |
458 |
| - flipped_moving_xy = np.stack([flipped_moving.geometry.x, flipped_moving.geometry.y], axis=1) |
459 |
| - elif isinstance(flipped_moving, DaskDataFrame): |
460 |
| - flipped_moving_xy = flipped_moving[["x", "y"]].to_dask_array().compute() |
461 |
| - else: |
462 |
| - raise TypeError("flipped_moving must be either an GeoDataFrame or a DaskDataFrame") |
463 |
| - model = estimate_transform("similarity", src=flipped_moving_xy, dst=references_xy) |
464 |
| - final = Sequence([flip, Affine(model.params, input_axes=("x", "y"), output_axes=("x", "y"))]) |
465 |
| - else: |
466 |
| - model = estimate_transform("similarity", src=moving_xy, dst=references_xy) |
467 |
| - final = Affine(model.params, input_axes=("x", "y"), output_axes=("x", "y")) |
468 |
| - |
469 |
| - affine = Affine( |
470 |
| - final.to_affine_matrix(input_axes=("x", "y"), output_axes=("x", "y")), |
471 |
| - input_axes=("x", "y"), |
472 |
| - output_axes=("x", "y"), |
473 |
| - ) |
474 |
| - return affine |
475 |
| - |
476 |
| - |
477 |
| -def align_elements_using_landmarks( |
478 |
| - references_coords: Union[GeoDataFrame | DaskDataFrame], |
479 |
| - moving_coords: Union[GeoDataFrame | DaskDataFrame], |
480 |
| - reference_element: SpatialElement, |
481 |
| - moving_element: SpatialElement, |
482 |
| - reference_coordinate_system: str = "global", |
483 |
| - moving_coordinate_system: str = "global", |
484 |
| - new_coordinate_system: Optional[str] = None, |
485 |
| - write_to_sdata: Optional[SpatialData] = None, |
486 |
| -) -> BaseTransformation: |
487 |
| - """ |
488 |
| - Maps a moving object into a reference object using two lists of (n >= 3) landmarks; returns the transformations that enable this |
489 |
| - mapping and optinally saves them, to map to a new shared coordinate system. |
490 |
| -
|
491 |
| - Parameters |
492 |
| - ---------- |
493 |
| - references_coords |
494 |
| - landmarks annotating the reference element. Must be a valid element describing points or circles. |
495 |
| - moving_coords |
496 |
| - landmarks annotating the moving element. Must be a valid element describing points or circles. |
497 |
| - reference_element |
498 |
| - the reference element. |
499 |
| - moving_element |
500 |
| - the moving element. |
501 |
| - reference_coordinate_system |
502 |
| - the coordinate system of the reference element that have been used to annotate the landmarks. |
503 |
| - moving_coordinate_system |
504 |
| - the coordinate system of the moving element that have been used to annotate the landmarks. |
505 |
| - new_coordinate_system |
506 |
| - If provided, both elements will be mapped to this new coordinate system with the new transformations just |
507 |
| - computed. |
508 |
| - write_to_sdata |
509 |
| - If provided, the transformations will be saved to disk in the specified SpatialData object. The SpatialData |
510 |
| - object must be backed and must contain both the reference and moving elements. |
511 |
| -
|
512 |
| - Returns |
513 |
| - ------- |
514 |
| - A similarity transformation that maps the moving element to the same coordinate of reference element in the |
515 |
| - coordinate system specified by reference_coordinate_system. |
516 |
| - """ |
517 |
| - from spatialdata.transformations.transformations import BaseTransformation, Sequence |
518 |
| - |
519 |
| - affine = get_transformation_between_landmarks(references_coords, moving_coords) |
520 |
| - |
521 |
| - # get the old transformations of the visium and xenium data |
522 |
| - old_moving_transformation = get_transformation(moving_element, moving_coordinate_system) |
523 |
| - old_reference_transformation = get_transformation(reference_element, reference_coordinate_system) |
524 |
| - assert isinstance(old_moving_transformation, BaseTransformation) |
525 |
| - assert isinstance(old_reference_transformation, BaseTransformation) |
526 |
| - |
527 |
| - # compute the new transformations |
528 |
| - new_moving_transformation = Sequence([old_moving_transformation, affine]) |
529 |
| - new_reference_transformation = old_reference_transformation |
530 |
| - |
531 |
| - if new_coordinate_system is not None: |
532 |
| - # this allows to work on singleton objects, not embedded in a SpatialData object |
533 |
| - set_transformation( |
534 |
| - moving_element, new_moving_transformation, new_coordinate_system, write_to_sdata=write_to_sdata |
535 |
| - ) |
536 |
| - set_transformation( |
537 |
| - reference_element, new_reference_transformation, new_coordinate_system, write_to_sdata=write_to_sdata |
538 |
| - ) |
539 |
| - return new_moving_transformation |
0 commit comments