Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slight tiling shift for subsequent rounds #134

Open
ahnsws opened this issue May 22, 2022 · 7 comments
Open

Slight tiling shift for subsequent rounds #134

ahnsws opened this issue May 22, 2022 · 7 comments

Comments

@ahnsws
Copy link

ahnsws commented May 22, 2022

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
Copy link
Collaborator

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

@jmuhlich
Copy link
Collaborator

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
Copy link
Author

ahnsws commented May 25, 2022

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
Copy link
Collaborator

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
Copy link
Author

ahnsws commented May 26, 2022

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
Copy link
Collaborator

This PR has the fix if you want to test it:
#140

@joaomamede
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants