Repository: spacetx/starfish

Issue:
Registration fails with latest starfish (and scikit-image)
#### Description
I ported an existing pipeline from `starfish` 0.2.2 to version 0.3.2 (also updating python and dependencies). The registration step used to work well. Without modifications to the code, the registration fails now. There is no error message, images are simply not registered.

#### Steps/Code to Reproduce
I boiled down the problem to the following reproducible example ([data uploaded for 30 days here](https://upload.disroot.org/r/LsMfrrzX#Xx8GT37w6ddzuQ2XgL1TibvWrnmoL2pEQhowOWqmPjk=)):
```python
from starfish import Experiment
from starfish.image import ApplyTransform, LearnTransform
from starfish.types import Axes
from starfish.util.plot import diagnose_registration
import matplotlib.pyplot as plt

exp = Experiment.from_json("experiment.json")
fov = exp.fovs()[0]
img = fov.get_image("primary")

learn_translation = LearnTransform.Translation(
    reference_stack=img.sel({Axes.ROUND: 0}), axes=Axes.ROUND
)
warp = ApplyTransform.Warp()

transforms_list = learn_translation.run(img)
print(transforms_list)

registered_img = warp.run(img, transforms_list=transforms_list)
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
diagnose_registration(img, {Axes.ROUND: 0}, {Axes.ROUND: 1}, ax=ax[0], title="before")
diagnose_registration(
    registered_img, {Axes.ROUND: 0}, {Axes.ROUND: 1}, ax=ax[1], title="after"
)
plt.savefig("reprex.png")
```

This example is simplified and uses a different strategy for registration (using nuclei rather than intensity projected spots) but I wanted to keep it simple and both approaches suffer from the same problem.

#### Expected Results
```
tile indices: {<Axes.ROUND: 'r'>: 0}
translation: y=0.0, x=0.0, rotation: 0.0, scale: 1.0
tile indices: {<Axes.ROUND: 'r'>: 1}
translation: y=-6.0, x=92.0, rotation: 0.0, scale: 1.0
```

![Image](https://github.com/user-attachments/assets/a30d7c6b-86ad-4e85-a830-6d3bcb8a61ca)

#### Actual Results
```
tile indices: {<Axes.ROUND: 'r'>: 0}
translation: y=0.0, x=0.0, rotation: 0.0, scale: 1.0
tile indices: {<Axes.ROUND: 'r'>: 1}
translation: y=0.0, x=0.0, rotation: 0.0, scale: 1.0
```

![Image](https://github.com/user-attachments/assets/326a095b-ba42-4e33-a8f9-9e642837c5f3)

#### Versions

##### Working as expected
```
Linux-6.8.0-57-generic-x86_64-with-glibc2.39
Python 3.9.16 | packaged by conda-forge | (main, Feb  1 2023, 21:39:03) 
[GCC 11.3.0]
NumPy 1.26.2
SciPy 1.11.3
scikit-image 0.18.3
pandas 1.3.2
sklearn 0.24.2
xarray 0.19.0
sympy 1.5.1
starfish 0.2.2
```

##### Not registering properly
```
Linux-6.1.0-32-amd64-x86_64-with-glibc2.36
Python 3.12.9 | packaged by conda-forge | (main, Mar  4 2025, 22:48:41) [GCC 13.3.0]
NumPy 1.26.4
SciPy 1.15.2
scikit-image 0.25.2
pandas 2.2.3
sklearn 1.6.1
xarray 2023.8.0
sympy 1.13.3
starfish 0.3.2
```

Discussion:
@iimog: This problem does not originate in `starfish`, it comes from a change in `scikit-image` from version 0.19.

From the [Release Notes of scikit-image 0.19](https://scikit-image.org/docs/stable/release_notes/release_0.19.html#scikit-image-0-19-0-2021-12-03):

> A bug fix to the phase normalization applied within skimage.register.phase_cross_correlation may result in a different result as compared to prior releases. The prior behavior of “unnormalized” cross correlation is still available by explicitly setting normalization=None. There is no change to the masked cross-correlation case, which uses a different algorithm.

Indeed, adjusting the `starfish` code to call `phase_cross_correlation` with `normalization=None` fixes the registration in my case. Currently, it is not possible to pass this parameter through `starfish`. I'm happy to prepare a pull request, that allows passing `**kwargs` from `LearnTransform.Translation.run()` to the internal call of `phase_cross_correlation`.

The documentation further states:
> The use of cross-correlation to estimate image translation has a long
    history dating back to at least [2]_. The "phase correlation"
    method (selected by ``normalization="phase"``) was first proposed in [3]_.
    Publications [1]_ and [2]_ use an unnormalized cross-correlation
    (``normalization=None``). Which form of normalization is better is
    application-dependent. For example, the phase correlation method works
    well in registering images under different illumination, but is not very
    robust to noise. In a high noise scenario, the unnormalized method may be
    preferable.

So depending on the experience from other users, `normalization=None` might be a sensible default in our domain.@iimog: With the changes in #2063 the old behavior can be restored by changing a single line above:
```python
transforms_list = learn_translation.run(img)
```
becomes
```python
transforms_list = learn_translation.run(img, normalization=None)
```

Solution patch:
diff --git a/examples/quick_start/plot_quick_start.py b/examples/quick_start/plot_quick_start.py
index 407ce906..12560c08 100644
--- a/examples/quick_start/plot_quick_start.py
+++ b/examples/quick_start/plot_quick_start.py
@@ -107,7 +107,8 @@ from starfish.types import Axes
 def register(imgs, dots, method = 'translation'):
     mip_imgs = imgs.reduce(dims = [Axes.CH, Axes.ZPLANE], func="max")
     mip_dots = dots.reduce(dims = [Axes.CH, Axes.ZPLANE], func="max")
-    learn_translation = LearnTransform.Translation(reference_stack=mip_dots, axes=Axes.ROUND, upsampling=1000)
+    learn_translation = LearnTransform.Translation(reference_stack=mip_dots, axes=Axes.ROUND,
+                                                   upsampling=1000, normalization=None)
     transforms_list = learn_translation.run(mip_imgs)
     warp = ApplyTransform.Warp()
     registered_imgs = warp.run(imgs, transforms_list=transforms_list, in_place=False, verbose=True)
diff --git a/examples/tutorials/plot_image_registration.py b/examples/tutorials/plot_image_registration.py
index 43991e54..515e5bc0 100644
--- a/examples/tutorials/plot_image_registration.py
+++ b/examples/tutorials/plot_image_registration.py
@@ -67,7 +67,7 @@ diagnose_registration(projected_imgs, {Axes.ROUND:0}, {Axes.ROUND:1}, {Axes.ROUN
 
 ###################################################################################################
 # Next we learn the translational transform using :py:class:`.LearnTransform.Translation`,
-# which wraps :py:class:`skimage.feature.register_translation` for efficient image translation
+# which wraps :py:func:`~skimage.registration.phase_cross_correlation` for efficient image translation
 # registration by cross-correlation. Running it will find the translational shift of every image
 # along a specified :py:class:`.Axes` of an :py:class:`.ImageStack` relative to the
 # `reference_image`.
@@ -75,10 +75,16 @@ diagnose_registration(projected_imgs, {Axes.ROUND:0}, {Axes.ROUND:1}, {Axes.ROUN
 # .. Note::
 #   There must be more landmarks than random noise. Otherwise the
 #   :py:class:`.LearnTransform.Translation` could align random noise.
+#   Depending on the noise level, there are two different normalization strategies provided by
+#   :py:func:`~skimage.registration.phase_cross_correlation`. The default strategy in
+#   :py:class:`.LearnTransform.Translation` is `normalization=None` which is suitable for high noise
+#   scenarios. The alternative is `normalization="phase"` which might be more suitable to cases where
+#   images have different illumination.
 
 from starfish.image import LearnTransform
 
-learn_translation = LearnTransform.Translation(reference_stack=dots, axes=Axes.ROUND, upsampling=1000)
+learn_translation = LearnTransform.Translation(reference_stack=dots, axes=Axes.ROUND,
+                                               upsampling=1000, normalization=None)
 transforms_list = learn_translation.run(projected_imgs)
 
 ###################################################################################################
diff --git a/requirements/REQUIREMENTS-JUPYTER.txt b/requirements/REQUIREMENTS-JUPYTER.txt
index f762f30c..0f77151a 100644
--- a/requirements/REQUIREMENTS-JUPYTER.txt
+++ b/requirements/REQUIREMENTS-JUPYTER.txt
@@ -9,8 +9,8 @@ attrs==25.3.0
 babel==2.17.0
 beautifulsoup4==4.13.3
 bleach==6.2.0
-boto3==1.37.17
-botocore==1.37.17
+boto3==1.38.9
+botocore==1.38.9
 certifi==2025.1.31
 cffi==1.17.1
 charset-normalizer==3.4.1
@@ -18,7 +18,7 @@ click==8.1.8
 comm==0.2.2
 contourpy==1.3.0
 cycler==0.12.1
-debugpy==1.8.13
+debugpy==1.8.14
 decorator==5.2.1
 defusedxml==0.7.1
 diskcache==5.6.3
@@ -27,11 +27,11 @@ entrypoints==0.4
 exceptiongroup==1.2.2
 executing==2.2.0
 fastjsonschema==2.21.1
-fonttools==4.56.0
+fonttools==4.57.0
 fqdn==1.5.1
-h11==0.14.0
+h11==0.16.0
 h5py==3.13.0
-httpcore==1.0.7
+httpcore==1.0.9
 httpx==0.28.1
 idna==3.10
 imageio==2.37.0
@@ -39,13 +39,13 @@ importlib_metadata==8.6.1
 importlib_resources==6.5.2
 ipykernel==6.29.5
 ipython==8.18.1
-ipywidgets==8.1.5
+ipywidgets==8.1.7
 isoduration==20.11.0
 jedi==0.19.2
 Jinja2==3.1.6
 jmespath==1.0.1
-joblib==1.4.2
-json5==0.10.0
+joblib==1.5.0
+json5==0.12.0
 jsonpointer==3.0.0
 jsonschema==4.17.3
 jupyter==1.1.1
@@ -59,7 +59,7 @@ jupyter_server_terminals==0.5.3
 jupyterlab==4.1.6
 jupyterlab_pygments==0.3.0
 jupyterlab_server==2.24.0
-jupyterlab_widgets==3.0.13
+jupyterlab_widgets==3.0.15
 kiwisolver==1.4.7
 lazy_loader==0.4
 looseversion==1.3.0
@@ -83,7 +83,7 @@ pandas==2.2.3
 pandocfilters==1.5.1
 parso==0.8.4
 pexpect==4.9.0
-pillow==11.1.0
+pillow==11.2.1
 platformdirs==4.3.7
 prometheus_client==0.21.1
 prompt_toolkit==3.0.50
@@ -92,11 +92,11 @@ ptyprocess==0.7.0
 pure_eval==0.2.3
 pycparser==2.22
 Pygments==2.19.1
-pyparsing==3.2.1
+pyparsing==3.2.3
 pyrsistent==0.20.0
 python-dateutil==2.9.0.post0
 python-json-logger==3.3.0
-pytz==2025.1
+pytz==2025.2
 PyYAML==6.0.2
 pyzmq==26.3.0
 read-roi==1.6.0
@@ -104,21 +104,21 @@ regional==1.1.2
 requests==2.32.3
 rfc3339-validator==0.1.4
 rfc3986-validator==0.1.1
-s3transfer==0.11.4
+s3transfer==0.12.0
 scikit-image==0.24.0
 scikit-learn==1.6.1
 scipy==1.13.1
 seaborn==0.13.2
 semantic-version==2.10.0
 Send2Trash==1.8.3
-setuptools==77.0.3
+setuptools==80.3.1
 showit==1.1.4
 six==1.17.0
 slicedimage==4.1.1
 sniffio==1.3.1
 soupsieve==2.6
 stack-data==0.6.3
-sympy==1.13.3
+sympy==1.14.0
 terminado==0.18.1
 threadpoolctl==3.6.0
 tifffile==2024.8.30
@@ -130,14 +130,14 @@ trackpy==0.6.4
 traitlets==5.14.3
 types-python-dateutil==2.9.0.20241206
 typing_extensions==4.12.2
-tzdata==2025.1
+tzdata==2025.2
 uri-template==1.3.0
 urllib3==1.26.20
-validators==0.34.0
+validators==0.35.0
 wcwidth==0.2.13
 webcolors==24.11.1
 webencodings==0.5.1
 websocket-client==1.8.0
-widgetsnbextension==4.0.13
+widgetsnbextension==4.0.14
 xarray==2023.8.0
 zipp==3.21.0
diff --git a/starfish/core/image/_registration/LearnTransform/translation.py b/starfish/core/image/_registration/LearnTransform/translation.py
index dc8058c5..42f2fd96 100644
--- a/starfish/core/image/_registration/LearnTransform/translation.py
+++ b/starfish/core/image/_registration/LearnTransform/translation.py
@@ -25,13 +25,21 @@ class Translation(LearnTransformAlgorithm):
         for an explanation of this parameter. In brief, this parameter determines the resolution of
         the registration. A value of 1 represents pixel resolution, a value of 10 is 1/10th of
         a pixel, a value of 300 is 1/300th of a pixel, and so on.
+    normalization : None | "phase"
+        normalization method (default=None). See
+        :py:func:`~skimage.registration.phase_cross_correlation` for an explanation of this
+        parameter.  In brief, this parameter toggles between unnormalized cross-correlation and
+        "phase correlation". Which form of normalization performs better depends on the application.
+        Under noise the unnormalized method might be preferred.
     """
 
-    def __init__(self, reference_stack: ImageStack, axes: Axes, upsampling: int = 1):
+    def __init__(self, reference_stack: ImageStack, axes: Axes, upsampling: int = 1,
+                 normalization=None):
 
         self.upsampling = upsampling
         self.axes = axes
         self.reference_stack = reference_stack
+        self.normalization = normalization
 
     def run(self, stack: ImageStack, verbose: bool = False, *args) -> TransformsList:
         """
@@ -66,7 +74,8 @@ class Translation(LearnTransformAlgorithm):
             shift, error, phasediff = phase_cross_correlation(
                 reference_image=target_image.data,
                 moving_image=reference_image.data,
-                upsample_factor=self.upsampling
+                upsample_factor=self.upsampling,
+                normalization=self.normalization
             )
 
             if verbose:


Test patch:
diff --git a/starfish/core/image/_registration/ApplyTransform/test/test_warp.py b/starfish/core/image/_registration/ApplyTransform/test/test_warp.py
index 6a75a9e3..db17d9a2 100644
--- a/starfish/core/image/_registration/ApplyTransform/test/test_warp.py
+++ b/starfish/core/image/_registration/ApplyTransform/test/test_warp.py
@@ -6,8 +6,29 @@ from starfish.core.image._registration.ApplyTransform.warp import Warp
 from starfish.core.image._registration.LearnTransform.translation import Translation
 from starfish.core.types import Axes
 
-
 expected_registered_values = np.array(
+    [[0.090654, 0.090593, 0.091554, 0.091661, 0.089967, 0.094072, 0.097398,
+      0.099046, 0.100969, 0.112108],
+     [0.09926, 0.096925, 0.096269, 0.097002, 0.095842, 0.097704, 0.09984,
+      0.101457, 0.105455, 0.106004],
+     [0.109834, 0.103609, 0.102693, 0.099931, 0.098222, 0.10074, 0.10251,
+      0.103838, 0.106874, 0.113451],
+     [0.12369, 0.112428, 0.111482, 0.10631, 0.106203, 0.104753, 0.106706,
+      0.105013, 0.10811, 0.11371],
+     [0.141802, 0.129946, 0.124285, 0.120928, 0.115908, 0.110735, 0.110735,
+      0.107454, 0.109468, 0.109255],
+     [0.147326, 0.14464, 0.141436, 0.132845, 0.124071, 0.121828, 0.118074,
+      0.112306, 0.109163, 0.109483],
+     [0.145296, 0.150362, 0.15082, 0.140337, 0.133806, 0.1299, 0.120592,
+      0.114046, 0.115496, 0.111666],
+     [0.131121, 0.145525, 0.150011, 0.146609, 0.137407, 0.129198, 0.127306,
+      0.118029, 0.116594, 0.111559],
+     [0.126482, 0.132372, 0.142596, 0.149538, 0.144701, 0.137469, 0.125353,
+      0.121996, 0.117342, 0.118273],
+     [0.122866, 0.126543, 0.133669, 0.145418, 0.150515, 0.140139, 0.129992,
+      0.124605, 0.120867, 0.121889]], dtype=np.float32)
+
+expected_registered_values_with_phase_normalization = np.array(
     [[0.085832, 0.085084, 0.086229, 0.08687, 0.089662, 0.092256,
       0.099474, 0.099489, 0.11017, 0.122408],
      [0.090654, 0.090593, 0.091554, 0.091661, 0.089967, 0.094072,
@@ -43,3 +64,17 @@ def test_calculate_translation_transforms_and_apply():
     assert np.allclose(
         expected_registered_values,
         warped_stack.xarray[2, 2, 0, 40:50, 40:50])
+
+def test_calculate_translation_transforms_and_apply_with_phase_normalization():
+    exp = data.ISS(use_test_data=True)
+    stack = exp.fov().get_image('primary')
+    reference_stack = exp.fov().get_image('dots')
+    translation = Translation(reference_stack=reference_stack, axes=Axes.ROUND,
+                              normalization="phase")
+    mp = Filter.Reduce((Axes.CH, Axes.ZPLANE)).run(stack)
+    transform_list = translation.run(mp)
+    apply_transform = Warp()
+    warped_stack = apply_transform.run(stack=stack, transforms_list=transform_list)
+    assert np.allclose(
+        expected_registered_values_with_phase_normalization,
+        warped_stack.xarray[2, 2, 0, 40:50, 40:50])
diff --git a/starfish/core/image/_registration/LearnTransform/test/test_translation.py b/starfish/core/image/_registration/LearnTransform/test/test_translation.py
index 76a9cc3f..b1ae918d 100644
--- a/starfish/core/image/_registration/LearnTransform/test/test_translation.py
+++ b/starfish/core/image/_registration/LearnTransform/test/test_translation.py
@@ -6,7 +6,8 @@ from starfish.core.image._registration.LearnTransform.translation import Transla
 from starfish.core.types import Axes
 
 
-ISS_SHIFTS = [[-23, 6], [-22, 2], [-22, -4], [-15, -4]]
+ISS_SHIFTS = [[-23, 6], [-22, 2], [-22, -3], [-15, -4]]
+ISS_SHIFTS_WITH_PHASE_NORMALIZATION = [[-23, 6], [-22, 2], [-22, -4], [-15, -4]]
 
 
 def test_learn_transforms_throws_error():
@@ -34,3 +35,20 @@ def test_learn_transforms_translation():
     for (_, _, transform), shift in zip(transform_list.transforms, ISS_SHIFTS):
         # assert that each TransformationObject has the correct translation shift
         assert np.array_equal(transform.translation, shift)
+
+
+def test_learn_transforms_translation_with_phase_normalization():
+    exp = data.ISS(use_test_data=True)
+    stack = exp.fov().get_image('primary')
+    reference_stack = exp.fov().get_image('dots')
+    translation = Translation(reference_stack=reference_stack, axes=Axes.ROUND,
+                              normalization="phase")
+    # Calculate max_proj accrss CH/Z
+    stack = Filter.Reduce((Axes.CH, Axes.ZPLANE)).run(stack)
+    transform_list = translation.run(stack)
+    # assert there's a transofrmation object for each round
+    assert len(transform_list.transforms) == stack.num_rounds
+    for (_, _, transform), shift in zip(transform_list.transforms,
+                                        ISS_SHIFTS_WITH_PHASE_NORMALIZATION):
+        # assert that each TransformationObject has the correct translation shift
+        assert np.array_equal(transform.translation, shift)
diff --git a/starfish/core/image/_registration/test/test_transforms_list.py b/starfish/core/image/_registration/test/test_transforms_list.py
index f645ce4a..652d4897 100644
--- a/starfish/core/image/_registration/test/test_transforms_list.py
+++ b/starfish/core/image/_registration/test/test_transforms_list.py
@@ -9,7 +9,7 @@ from starfish.core.image._registration.transforms_list import TransformsList
 from starfish.core.types import Axes, TransformType
 
 
-ISS_SHIFTS = [[-23, 6], [-22, 2], [-22, -4], [-15, -4]]
+ISS_SHIFTS = [[-23, 6], [-22, 2], [-22, -3], [-15, -4]]
 
 
 def test_export_import_transforms_object():
diff --git a/starfish/test/full_pipelines/api/test_iss_api.py b/starfish/test/full_pipelines/api/test_iss_api.py
index c2c1ed01..013a0a6e 100644
--- a/starfish/test/full_pipelines/api/test_iss_api.py
+++ b/starfish/test/full_pipelines/api/test_iss_api.py
@@ -58,26 +58,26 @@ def test_iss_pipeline_cropped_data(tmpdir):
     registered_image = iss.registered_imgs
 
     expected_registered_values = np.array(
-        [[8.182720e-03, 2.094890e-03, 1.921310e-03, 8.036800e-04, 0.000000e+00,
-          1.113000e-05, 2.362500e-04, 1.137100e-04, 2.881270e-03, 1.061417e-02],
-         [1.225884e-02, 7.359780e-03, 6.554780e-03, 3.967060e-03, 7.174400e-04,
-          1.377300e-04, 3.282700e-04, 1.963200e-04, 1.156900e-04, 2.786610e-03],
-         [1.817651e-02, 1.313646e-02, 1.080875e-02, 8.000580e-03, 2.264170e-03,
-          8.996000e-04, 1.071100e-03, 2.864700e-04, 4.592600e-04, 2.591370e-03],
-         [2.938030e-02, 2.027904e-02, 1.634731e-02, 1.067738e-02, 5.404920e-03,
-          3.325540e-03, 2.005550e-03, 3.105000e-05, 9.240000e-04, 5.751660e-03],
-         [4.422882e-02, 3.153970e-02, 2.650198e-02, 1.768988e-02, 1.198124e-02,
-          7.287380e-03, 4.359240e-03, 4.564000e-05, 1.156120e-03, 3.979250e-03],
-         [5.676676e-02, 4.604779e-02, 3.936250e-02, 2.943260e-02, 1.997995e-02,
-          1.306023e-02, 9.153120e-03, 1.940280e-03, 1.672590e-03, 1.607550e-03],
-         [5.948846e-02, 5.680262e-02, 5.028814e-02, 3.747543e-02, 2.800228e-02,
-          2.101545e-02, 1.274632e-02, 5.316650e-03, 4.062480e-03, 1.875220e-03],
-         [5.272433e-02, 5.822361e-02, 5.484088e-02, 4.344586e-02, 3.279146e-02,
-          2.484935e-02, 1.552278e-02, 8.065560e-03, 8.012830e-03, 2.026330e-03],
-         [4.264575e-02, 5.009904e-02, 5.163100e-02, 4.824880e-02, 3.619050e-02,
-          2.628749e-02, 1.851076e-02, 1.187091e-02, 8.305970e-03, 4.661620e-03],
-         [3.705096e-02, 4.012012e-02, 4.393976e-02, 4.851252e-02, 4.276346e-02,
-          3.062118e-02, 1.944861e-02, 1.515226e-02, 9.333680e-03, 9.347120e-03]],
+        [[9.956287e-03, 4.382827e-03, 3.377583e-03, 1.676941e-03, 1.843710e-04,
+          0.000000e+00, 1.060593e-04, 1.570793e-05, 1.083317e-03, 6.584799e-03],
+         [1.453807e-02, 9.621573e-03, 8.186622e-03, 5.916186e-03, 1.824629e-03,
+          3.533170e-04, 5.222907e-04, 3.772743e-04, 4.503722e-05, 1.102258e-03],
+         [2.307469e-02, 1.583596e-02, 1.238587e-02, 9.499015e-03, 3.546112e-03,
+          1.481345e-03, 1.324189e-03, 2.925306e-04, 5.596119e-04, 3.681781e-03],
+         [3.524983e-02, 2.441847e-02, 1.960656e-02, 1.255823e-02, 7.747118e-03,
+          4.900453e-03, 2.757738e-03, 3.212543e-04, 4.914876e-04, 5.271485e-03],
+         [5.139222e-02, 3.787590e-02, 3.136785e-02, 2.306333e-02, 1.551601e-02,
+          9.377318e-03, 6.117928e-03, 7.416179e-04, 1.231178e-03, 2.670616e-03],
+         [5.949953e-02, 5.164410e-02, 4.453927e-02, 3.411048e-02, 2.398818e-02,
+          1.655605e-02, 1.186486e-02, 4.347167e-03, 1.806095e-03, 1.728893e-03],
+         [5.874086e-02, 5.879546e-02, 5.402317e-02, 4.139205e-02, 3.177369e-02,
+          2.464539e-02, 1.448956e-02, 6.815665e-03, 6.004986e-03, 2.577132e-03],
+         [4.819431e-02, 5.582142e-02, 5.535191e-02, 4.698015e-02, 3.496249e-02,
+          2.583509e-02, 1.868034e-02, 1.033007e-02, 8.692421e-03, 2.931876e-03],
+         [4.109203e-02, 4.548026e-02, 4.914177e-02, 4.964774e-02, 4.018743e-02,
+          2.969893e-02, 1.954622e-02, 1.384825e-02, 8.802789e-03, 6.922520e-03],
+         [3.561261e-02, 3.782343e-02, 4.072055e-02, 4.671327e-02, 4.534628e-02,
+          3.361325e-02, 2.241083e-02, 1.681411e-02, 1.111553e-02, 1.011566e-02]],
         dtype=np.float32
     )
 
@@ -100,7 +100,7 @@ def test_iss_pipeline_cropped_data(tmpdir):
                                            'ETV4', 'GAPDH', 'GUS', 'HER2', 'RAC1',
                                            'TFRC', 'TP53', 'VEGF']))
 
-    assert np.array_equal(gene_counts, [19, 1, 5, 2, 1, 9, 1, 3, 2, 1, 1, 2])
+    assert np.array_equal(gene_counts, [19, 1, 5, 2, 1, 11, 1, 3, 2, 1, 1, 2])
     assert decoded.sizes[Features.AXIS] == 99
 
     masks = iss.masks
@@ -143,4 +143,4 @@ def test_iss_pipeline_cropped_data(tmpdir):
     # test that nans were properly removed from the expression matrix
     assert 'nan' not in expression_matrix.genes.data
     # test the number of spots that did not decode per cell
-    assert np.array_equal(expression_matrix.number_of_undecoded_spots.data, [14, 1, 37])
+    assert np.array_equal(expression_matrix.number_of_undecoded_spots.data, [13, 1, 36])