MathOnco / valis

Virtual Alignment of pathoLogy Image Series
https://valis.readthedocs.io/en/latest/
MIT License
119 stars 29 forks source link

HE and H-DAB sections - alignment and ? merge #45

Open teacakedeadlift opened 1 year ago

teacakedeadlift commented 1 year ago

Hi @cdgatenbee,

Thanks for creating VALIS - proving incredibly useful in my large format slide analysis.

I have some questions I was hoping you could help with to see if I can improve outputs.

My samples comprises whole lesions over multiple blocks with up to 14 serial sections per block - a H&E at either end and DAB stains in the middle: 1-HE_2, 2-DAB_1, 3-DAB_2, ..... 13-DAB_12, 14-HE_2.

Alignment works well on the whole, but I have some small artifactual errors from the warping step - stretched nuclei etc. Currently running trials with max_non_rigid_registartion_dim_px=3000 to increase non-rigid registration resolution - I hope this is the correct variable to adjust?artefact

Does VALIS handle HE and DAB in a similar way? Currently I get it to align to the centre slide in the stack. Hopefully this is fine and the HE alignments wont be affected by being aligned to a DAB. So far they are ok but I was just wondering on best practice. Would it be better to align just the DABs alone?

Is there a way of merging brightfield slide images to create a pseudo-multiplex image? Presumably using the Haematoxylin across each to align and assigning each DAB stain (+/- Eosin) a channel in the combined image? Currently trying to achieve this as a post-processing step in QuPath but not having much success.

Thanks

Phil

cdgatenbee commented 1 year ago

Hi @teacakedeadlift,

Apologies for taking so long to get back to you, but glad to hear you're finding valis useful :) Unfortunately, occasional artifacts like stretched nuclei are hard to avoid when using non-rigid registration, as some parts of the tissue may need to be stretched/compressed to align the rest of it. One sure way to avoid any such stretching/compressing is to use only rigid transformations, although the alignments might not be as good overall. You can get an idea of what this would look like by checking the "rigid_overlap.png" image in the "overlaps" folder. If those look acceptable, you can skip the non-rigid transformations in one two ways: set non_rigid_registrar_cls=None when you initialize the Valis object, or set non_rigid=False when you call registrar.warp_and_save_slides. If you would still like to keep the non-rigid transforms, then I think the best way to minimize the artifacts may be to try increasing max_non_rigid_registartion_dim_px, as you are currently doing.

To answer your second question, yes, right now valis does handle HE and DAB in the same way, in that they both undergo the same preprocessing. The default preprocessor is the ColorfulStandardizer class, which essentially tries flatten the color in the image so that different regions in different images are not highlighted more than in another image (which can make the images look different). In other words, it tries to make the HE and DAB images look more similar, and so it should be OK include both stains in the registration. However, we are currently working on making it possible to specify a processor for each image, so that HE and DAB could be processed differently, something I hope to have in the next update. Currently though, if you would like them to be processed differently you could create a custom ImageProcesser that can determine if the image is HE or DAB (maybe based on the filename, i.e. src_f), and then process accordingly.

At the moment, there isn't exactly a built in way to process and merge brightfield images. However, it is certainly possible, and is an approach we've taken in our research, although it's all been in Python, and relatively simple. Essentially though, you would start by warping each image using the correspondingSlide.warp_slide to get warped_slide, which is a pyvips.Image. If your image is small enough, you could then convert it to a numpy array (warp_tools.vips2numpy) and process it to get a single channel image back (i.e. creating a stain mask, or measure stain intensity via stain deconvolution, or measure luminosity, etc...). If the images are large, you may want to process each in tiles, which can be accessed by warped_slide.extract_area (see documentation here), and then you build the complete image using arrayjoin or maybe by inserting each tile into an empty image (see insert), although I think arrayjoin is faster. Alternatively, you could process and save each original image using other software, and then warp that processed image by passing its file name as the src_f argument of Slide.warp_slide (see documentation, here). Or, you could process the saved warped image using other software and read that in using valis (see example, here). No matter how you do it, once you have all of the processed and aligned channels, you would next use bandjoin to combine the processed images to create a single multiplexed image, which can be saved using slide_io.save_ome_tiff. You would also need to create the ome-xml metadata, including the channel names, but that can be accomplished using slide_io.create_ome_xml. I'm sorry this is a bit complicated, but I may add in some functionality to make this easier in a future release.

Hopefully the above is helpful, but please don't hesitate to ask more questions if you'd like some extra advice and/or clarification.

Best, -Chandler

teacakedeadlift commented 1 year ago

Thanks @cdgatenbee.

Unfortunately, occasional artifacts like stretched nuclei are hard to avoid when using non-rigid registration, as some parts of the tissue may need to be stretched/compressed to align the rest of it.

We have decided that a little warping is acceptable so proceeding with the non-rigid registration and hopefully with increasing max_non_rigid_registartion_dim_px stretching might be less of an issue.

In other words, it tries to make the HE and DAB images look more similar, and so it should be OK include both stains in the registration.

Great. It doesn't seem to be having any negative effects so far so will continue as is until it becomes a feature to specify a processor for each image.

If your image is small enough, you could then convert it to a numpy array (warp_tools.vips2numpy) and process it to get a single channel image back (i.e. creating a stain mask, or measure stain intensity via stain deconvolution, or measure luminosity, etc...).

Not sure how small is small enough but my slides are massive - .ndpi input files 2-3Gb and the ome.tiff outputs 20-30Gb. But essentially this is what id like to do - extract the DAB stain from each slide and combine plus the Haematoxylin either as one channel combined from all slides or from the reference slide only (whatever is best). Not sure what the output would be like. I've had some success in QuPath using QUASI on the VALIS aligned slides but I end up with 3 channels for each slide (H, DAB, residual) stacked up: so for 10 slides, 30 channels of which 10 are repeats of marginally differing H, 10 are residuals, and 10 are the stains of interest. Resulting WSI are then either so large as to be impractical or so downsampled as to be unintelligible.

I'm sorry this is a bit complicated, but I may add in some functionality to make this easier in a future release.

I think it's probably beyond my skills at present but I'll have a go and see what happens. Thanks for all the suggestions.

Phil

ajr82 commented 1 year ago

Hi Phil, @teacakedeadlift

You can compress the ome.tiff files and this reduces the size.

Use compression='jpeg', when you are writing the ome.tiff files on valis. registrar.warp_and_save_slides(registered_slide_dst_dir, crop="all", compression="jpeg")