labsyspharm / ashlar

ASHLAR: Alignment by Simultaneous Harmonization of Layer/Adjacency Registration
https://labsyspharm.github.io/ashlar/
MIT License
129 stars 42 forks source link

Slight tiling shift for subsequent rounds #134

Open ahnsws opened 2 years ago

ahnsws commented 2 years ago

Hello, great work on ashlar. I noticed a minor tiling shift for subsequent rounds, even if I used the same stack of images. Here is a gif of what I mean. The first image is the DAPI channel of the first round and is stitched pretty well, but the second image is the DAPI channel of the 'second' round (but still equal in values to the first).

original

I took a look through the code and I noticed that if I took out the call to this method, and I ran the code again, the results were as expected, with no shift (this is a gif!).

modified

Here is how I am generating the stitched images:

from pathlib import Path
from typing import List

from ashlar.scripts.ashlar import process_single

def register_cycles(paths: List[Path], dst_template: Path, verbose=True):
    aligner_args = dict(verbose=verbose, filter_sigma=2.0)
    mosaic_args = dict(verbose=verbose)

    process_single(
        [str(p) for p in paths],
        output_path_format=str(dst_template),
        flip_x=False,
        flip_y=False,
        ffp_paths=None,
        dfp_paths=None,
        aligner_args=aligner_args,
        mosaic_args=mosaic_args,
        pyramid=False,
        quiet=not verbose,
    )

def run():
    paths = [
        Path("images/test-stack.ome.tiff"),
        Path("images/test-stack1.ome.tiff"),
    ]
    # dst = Path("stitched") / "original-cycle-{cycle}-channel-{channel}.tif"
    dst = Path("stitched") / "modified-cycle-{cycle}-channel-{channel}.tif"
    dst.parent.mkdir(exist_ok=True, parents=True)
    register_cycles(paths, dst_template=dst)

if __name__ == "__main__":
    run()

I am using the master branch of ashlar, and changing filter_sigma to the default value did not change the result.

jmuhlich commented 2 years ago

Great test case! I can reproduce this locally and will investigate what's going on.

jmuhlich commented 2 years ago

When we align tiles across cycles, we find that pure-background tiles with no cells will align based on the camera dark current noise (phase correlation is that sensitive), which is not useful and throws off the statistics we use for replacing bad alignments with interpolated positions. Ashlar has logic to detect this and discard these dark current alignments, but I just realized this logic is also triggered when you try to align the same set of images to itself!

I've added an extra check that only triggers it when the corresponding tile in the first cycle is likely to be background. This fixes your same-images use case but we need to do more testing on images that actually required the original fix to make sure they're still handled OK.

ahnsws commented 2 years ago

Oh I see. This edge case only came up because we wanted to check what the stitching results were before committing to a second round of cycif.

Are you referring to hot/warm pixels? We've found that many of the brightest pixels are correlated, and so for our pipeline we take a few as-dark-as-possible images before a scan, take the median, find pixels above a certain percentile, and replace them with the median of their neighborhoods, although I just realized we didn't do this for the above test case. Do you think this would help?

jmuhlich commented 2 years ago

I think it's totally reasonable to try and register the same round of images to itself as a test, so I want to make sure that does give the expected results.

The issue is not just a few hot pixels but rather "fixed-pattern noise" (FPN) that produces low-level signal fluctuations at every pixel. Our FPN rejection logic should ignore hot pixels too since their positions are also fixed in the image, so you shouldn't need to filter them out before running Ashlar.

More on FPN: https://en.wikipedia.org/wiki/Fixed-pattern_noise http://isl.stanford.edu/~abbas/ee392b/lect07.pdf

Example image from https://www.researchgate.net/publication/341682797_CMOS_Fixed_Pattern_Noise_Removal_Based_on_Low_Rank_Sparse_Variational_Method : image

ahnsws commented 2 years ago

Good to know! I can see why that would be a problem for registration. Although, we still remove hot pixels anyway because we have long exposure times and they are very noticeable otherwise.

Please let me know when there is a branch to play with so I can check using our images. Thank you!

jmuhlich commented 2 years ago

This PR has the fix if you want to test it: https://github.com/labsyspharm/ashlar/pull/140

joaomamede commented 2 years ago

Thank you, this was a problem for me as well!