scverse / squidpy

Spatial Single Cell Analysis in Python
https://squidpy.readthedocs.io/en/stable/
BSD 3-Clause "New" or "Revised" License
439 stars 79 forks source link

get centroids (or segmentation mask id) for spots #253

Closed giovp closed 3 years ago

giovp commented 3 years ago

am checking out this really nice method for deconvolution/mapping: https://github.com/broadinstitute/Tangram

using this with our image container makes it very easy to run, and we should probably work more on interfacing functionalities.

One really nice feature this method has is the ability to map cell types to segmentation masks under each spots: see this image: image

and notebook: https://github.com/broadinstitute/Tangram/blob/master/example/mapping-visium-example.ipynb

I got 95% there but am missing a crucial information, that is the centroid coordinates of each segmentation mask that is assigned to each spot.

It's easy to get the centroid of each segmentation mask, just need to do:

from skimage.measure import regionprops
props = regionprops(img["segmented_watershed"].data)

where props is a list of <skimage.measure._regionprops.RegionProperties at 0x7f93e2791670>. to get the coordinates then

props[0].centroid
>>> (1.3333333333333333, 3325.1111111111113, 0.0)

problem is that with the visium fluorescent crop data, I get ~18k segmentation masks, but only ~3k are assigned to the spots (had to filter etc so proportion is ok).

I'm now gonna try by getting crops, getting segmentation ids, and filter by that. It would be nice however to get this info straight out of sq.im.calculate_image_features.

I'd say this is a fairly urgent feature request and wwould potentially included in #239 ? It seems relatively straightforward to me, what do you guys think @hspitzer @michalk8 ?

I'll try to present at tuesday gm about this, so you'll get the idea on how this could become essentially a universal mapping between any spatial data and any sc data. Tangram is still very very (very) under development, but let's say if we can do something to speed up the process there as well.

giovp commented 3 years ago

ok, played around a bit and not really able to make it to work.

the problem is that I am not able to filter the segmentation objects asthey were assigned to the spots. So like,if I plot the segmentaion objects remapped by tangram, I get this: image

which is wrong because I should only see segmentation objects under selected spots, that are these: image

could be an error from my side, but maybe this clarifies why having that info would be important :)

michalk8 commented 3 years ago

@giovp not sure if #239 will move fast enough, there are still a few things to address.

could be an error from my side, but maybe this clarifies why having that info would be important :)

Do I understand correctly that you want to store some sort of a mapping in the form seg_obj (i.e. from watershed or blob) -> spot id? In #239 should be doable by storing the the seg. objects in the metadata in each crop as a local coordinates (i.e. withing the crop) + combining these with the crop coordinates (position of the crop in the image) should have enough info to create the mapping

w.r.t. to the API sq.im.segment_img would need to be passed the spot ids (or the whole adata object), which is not really nice

hspitzer commented 3 years ago

Just some general thoughts:

what did you do now? Why did it not work.

calculate_image_features also uses some of the region props to calculate segmentation features, but as of now we calculate the mean / std of all objects per spot to get a value per spot. You need to have a value per segmentation object. This should be a new function. I think it might be useful, we just have to think about how to best store it. I'm not sure if I would store it in the image - locations per spot are not pure image information but also contains information of the adata, right?

giovp commented 3 years ago

basically what didn't work is that the if I count the unique integers under each img.generate_spot_crops I get more hits than the counts I get in segmentation_label (also figure from the paper, where it shows the number of unique segm object under each spot). The former are much more than the latter.

For instance, let's take this df image

cell_n is what I get out of


# define image layer to use for segmentation
features_kwargs = {"segmentation": {"label_img_id": "segmented_watershed"}}
# calculate segmentation features
sq.im.calculate_image_features(
    adata_st,
    img,
    key_added="features",
    features_kwargs=features_kwargs,
    features="segmentation",
    size=1,
    scale=1.0,
    mask_circle=True,
)
adata_st.obsm["features"].fillna(value=0, inplace=True)
adata_st.obs["cell_count"] = adata_st.obsm["features"]["segmentation_label"]

the centroids series instead contains all unique segmentation idx that relates to a list of centroids I computed.

The problem is that sometime, for a given spot, the number of cell_count and the number of unique segmentation idx is different. I thought there is some type of filtering when the segmentation labels are calcualted. Nevertheless, having such array as centroids as well as the centroids_coordinates saved as arrays in the adata.obsm["image_features"] would be very useful.

Do I understand correctly that you want to store some sort of a mapping in the form seg_obj (i.e. from watershed or blob) -> spot id?

exactly, both an id mapping but also the (x,y) coordinates of all seg_obj assigned to a given spot_id

In #239 should be doable by storing the the seg. objects in the metadata in each crop as a local coordinates (i.e. withing the crop) + combining these with the crop coordinates (position of the crop in the image) should have enough info to create the mapping

exactly something like that

w.r.t. to the API sq.im.segment_img would need to be passed the spot ids (or the whole adata object), which is not really nice

I'm not sure what you mean here, but this is already done right?

# calculate segmentation features
sq.im.calculate_image_features(
    adata_st,
    img,...)
giovp commented 3 years ago

just tried with #239 branch: image

πŸŽ‰ πŸŽ‰ πŸŽ‰ so cool! I'll clean up the notebook and push it in external!

michalk8 commented 3 years ago

closed via #239 , notebook in question is done in https://github.com/theislab/squidpy_notebooks/pull/30