czbiohub-sf / iohub

Pythonic and parallelizable I/O for N-dimensional imaging data with OME metadata
https://czbiohub-sf.github.io/iohub/
BSD 3-Clause "New" or "Revised" License
29 stars 6 forks source link

Save grayscale data and binary masks in the OME-zarr store #156

Open nbathreya opened 1 year ago

nbathreya commented 1 year ago

If the user calculates Max Intensity Projection on every channel for every FOV, along with maybe some segmentation results, what would be the best way to store them in the new version of zarr store?

The raw microscopy data TCZYX dimensions are not the same for the max proj and binary mask data.

ziw-liu commented 1 year ago

Assuming you are talking about HCS stores, there are three ways possible:

  1. Store as extra Zarr arrays. The resulting array will not be part of the OME-NGFF metadata, thus cannot be opened by third-party tools such as napari-ome-zarr. To do this:
    position: iohub.ngff.Position
    array: np.array
    # write
    position.zgroup["name_of_array"] = array
    # read
    array = position.zgroup["name_of_array"]

    Where NGFFNode.zgroup is the underlying Zarr group for the NGFF node (plate, position, etc.). Refer to Zarr docs for API details.

  2. Store as extra channels as you would store any other channel. If you chunk by XY-plane, no data will be written for the Z-indices that do not exist (all but one), and they will be zero-filled/black if read/visualized. The main downside is that binary (boolean) values have to be stored in the same dtype as other channels, but this should be quite insignificant, especially if multiple mask channels are combined into integer-valued labels (i.e. back ground is 0 and each object is 1, 2, 3,...).
  3. Masks can also be stored as NGFF labels arrays. There is currently no specific API for it, but can be achieved by:

    from iohub.ngff_meta import LabelsMeta, LabelsColorMeta, ImageLabelMeta
    
    label_group = position.zgroup.group("labels")
    label_group["name_matching_image/0"] = label_array
    # initialize label metadata
    labels_meta = ...
    image_label_meta = ...
    label_group.attrs["labels"] = labels_meta
    label_group["name_matching_image"].attrs["image-label"] = image_label_meta

    If you find this preferable we can think about implementing this explicitly in iohub.

mattersoflight commented 2 months ago

Thanks for alerting me to this issue @ziw-liu. Yes, It will be useful to have an API to add the label group and link it to the matching image for our work in virtual staining and segmentation of nuclei and membrane. Let's do this during hackathon and document it.