scverse / scverse/spatialdata

Transformation on large image results in very large chunks

Open
#1,161 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

needs: triage priority: medium
Dominant language
Python
Stars
394
Forks
95
Avg merge
4d 3h
Merged PRs (30d)
7

Description

I am analyzing the 10x Genomics Xenium example datasets.

https://cf.10xgenomics.com/samples/xenium/4.0.0/Human_Breast_Biomarkers_S1_Top/Human_Breast_Biomarkers_S1_Top_outs.zip
https://cf.10xgenomics.com/samples/xenium/4.0.0/Human_Breast_Biomarkers_S1_Top/Human_Breast_Biomarkers_S1_Top_he_image.ome.tif
https://cf.10xgenomics.com/samples/xenium/4.0.0/Human_Breast_Biomarkers_S1_Top/Human_Breast_Biomarkers_S1_Top_he_imagealignment.csv
sdata = spatialdata_io.xenium(
    data_dir,
    nucleus_boundaries=False,
    cells_labels=False,
    nucleus_labels=False,
    transcripts=False,
    aligned_images=True,
)
print(sdata)
print(sd.transformations.get_transformation(sdata.images["he_image"], get_all=True))

The dataset contains an additional H&E image with an Affine to the global coordinate system.

SpatialData object
├── Images
│     ├── 'he_image': DataTree[cyx] (3, 43995, 20515), (3, 21997, 10257), (3, 10998, 5128), (3, 5499, 2564), (3, 2749, 1282)
│     └── 'morphology_focus': DataTree[cyx] (4, 27420, 53994), (4, 13710, 26997), (4, 6855, 13498), (4, 3427, 6749), (4, 1713, 3374)
├── Shapes
│     └── 'cell_boundaries': GeoDataFrame shape: (209467, 1) (2D shapes)
└── Tables
      └── 'table': AnnData (209467, 280)
with coordinate systems:
    ▸ 'global', with elements:
        he_image (Images), morphology_focus (Images), cell_boundaries (Shapes)

{'global': Affine (x, y -> x, y)
     [ 9.88824121e-03  1.28956723e+00 -1.10808655e+03]
     [-1.28956723e+00  9.88824121e-03  2.67224245e+04]
     [0. 0. 1.]}

After transforming the image to the global coordinate system, the image contains only 3 large chunks.

image = sdata_full.images["he_image"]["scale0"].image
print(image)

image_trans = sd.transform(sdata.images["he_image"], to_coordinate_system="global")["scale0"].image
print(image_trans)

Before transform:

Array Chunk
Bytes 2.52 GiB 16.00 MiB
Shape (3, 43995, 20515) (1, 4096, 4096)
Dask graph 198 chunks in 5 graph layers
Data type uint8 numpy.ndarray

After transform:

Array Chunk
Bytes 4.28 GiB 1.54 GiB
Shape (3, 26890, 56937) (3, 26890, 20515)
Dask graph 3 chunks in 9 graph layers
Data type uint8 numpy.ndarray
for k, v in image.data[:, :1024, :1024].dask.layers.items():
    print(k, type(v), len(v))

for k, v in image_trans.data[:, :1024, :1024].dask.layers.items():
    print(k, type(v), len(v))

Before transform:

array-1f6906e306bdd2a15af122a29f1cc176 <class 'dask.highlevelgraph.MaterializedLayer'> 1
_map_read_frame-e4934b9d23dc66be228a602e126e7d51 <class 'dask.blockwise.Blockwise'> 1
getitem-2e03aac7dab88134e57e5b5893266c0c <class 'dask.highlevelgraph.MaterializedLayer'> 1
transpose-557e428061a6933b28b8db44ad0ed7d8 <class 'dask.blockwise.Blockwise'> 1
rechunk-merge-7640fe91c4d24108ec25cbaaedaeb750 <class 'dask.highlevelgraph.MaterializedLayer'> 396
getitem-9694c8fd0ff4f4b05c07751859e8b88a <class 'dask.highlevelgraph.MaterializedLayer'> 3

After transform:

affine_transform-e20839f41827529f6fcd15c7d7795e6b <class 'dask.highlevelgraph.MaterializedLayer'> 3
array-1f6906e306bdd2a15af122a29f1cc176 <class 'dask.highlevelgraph.MaterializedLayer'> 1
_map_read_frame-e4934b9d23dc66be228a602e126e7d51 <class 'dask.blockwise.Blockwise'> 1
getitem-2e03aac7dab88134e57e5b5893266c0c <class 'dask.highlevelgraph.MaterializedLayer'> 1
transpose-557e428061a6933b28b8db44ad0ed7d8 <class 'dask.blockwise.Blockwise'> 1
rechunk-merge-7640fe91c4d24108ec25cbaaedaeb750 <class 'dask.highlevelgraph.MaterializedLayer'> 396
getitem-bb69ef301749c714cbc84798ebeaeca8 <class 'dask.highlevelgraph.MaterializedLayer'> 72
getitem-f9415ec8602e80f5ee1d861ed3a40acc <class 'dask.highlevelgraph.MaterializedLayer'> 90
getitem-b95f72c34293e0492a54f7416d743a1a <class 'dask.highlevelgraph.MaterializedLayer'> 72
getitem-8f49fea7021e419e7c77ceea26ee6933 <class 'dask.highlevelgraph.MaterializedLayer'> 1

It seems that affine_transform will cause the reading of large blocks of the image.

Is any solutions of slicing the transformed image data that minimizes the reading?

Desktop:

  • OS: Windows 10 22H2 19045.2604
  • Version: spatialdata 0.7.3, spatialdata_io 0.7.0

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The relevant entry points are spatialdata_io.xenium(...), sd.transform(...), and the affine_transform Dask layer shown in the issue. Start by reproducing the before-and-after chunk metadata on the linked Xenium dataset and inspect how slicing the transformed image maps to source reads. Done means identifying a transformation path that avoids unnecessary large-block reads.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.