5. HCS plates¶
Navigate a whole high-content screening plate.
An HCS plate is a grid of wells, each holding one or more images, possibly from different
acquisitions. The OmeZarrPlate class gives you the rows, columns and
acquisitions of the plate, and the images inside each well.
Open an OmeZarrPlate object.
from pathlib import Path
from ngio import open_ome_zarr_plate
from ngio.utils import download_ome_zarr_dataset
download_dir = Path("./data").absolute()
hcs_path = download_ome_zarr_dataset("CardiomyocyteSmallMip", download_dir=download_dir)
ome_zarr_plate = open_ome_zarr_plate(hcs_path)
print(ome_zarr_plate)
This example plate is very small and contains only a single well.
Plate overview¶
The OmeZarrPlate object gives you a high-level overview of the plate through three properties:
Retrieving the path to the images¶
The OmeZarrPlate object provides multiple methods to retrieve the path to the images in the plate.
This will return the paths to all images in the plate:
Getting the images¶
get_well_images takes the row and column of a well and returns a dictionary mapping each image path to its OmeZarrContainer.
Get all images in the plate:
OmeZarrContainer object.
Get all images in a well:
OmeZarrContainer object.
Get a specific image in a well:
OmeZarrContainer object for the image in the well.
In these methods, you can also filter the images by acquisition. When available, the acquisition parameter can be used to filter the images by acquisition id.
acquisition is optional: omit it and every image in the well is returned. Pass an acquisition id that the plate does not define — as on this example plate, which carries no acquisition metadata — and you get an empty dictionary back.
Creating a plate¶
ngio provides a utility function to create a plate.
The first step is to create a list of ImageInWellPath objects. Each ImageInWellPath object contains the path to the image and the corresponding well.
from ngio import ImageInWellPath
list_of_images = [
ImageInWellPath(path="0", row="A", column=0),
ImageInWellPath(path="0", row="B", column=1),
ImageInWellPath(path="0", row="C", column=1),
ImageInWellPath(
path="1",
row="A",
column=0,
acquisition_id=1,
acquisition_name="acquisition_1",
),
]
Note
The order in which the images are added is not important. The rows and columns attributes of the plate will be sorted in alphabetical/numerical order.
Then, you can create the plate using the create_empty_plate function.
from ngio import create_empty_plate
plate = create_empty_plate(
store="data/new_plate.zarr",
name="test_plate",
images=list_of_images,
overwrite=True,
)
print(plate)
This has created a new empty plate with the metadata correctly set. But no images have been added yet.
Modifying the plate¶
You can add or remove images.
To add images to the plate, use the add_image method. It takes the row and column of the well and the path to the image within it.
print(f"Before adding images: {plate.rows} rows, {plate.columns} columns")
plate.add_image(row="D", column=0, image_path="0")
print(f"After adding images: {plate.rows} rows, {plate.columns} columns")
Note
The order in which the images are added is not important. The rows and columns attributes of the plate will be sorted in alphabetical/numerical order.
Warning
This function is not multiprocessing safe. If you are using multiprocessing, you should use the atomic_add_image method instead.
atomic_add_image serialises the update behind an OS file lock, so it holds across threads and processes on one machine. It requires a local store — on a remote store there is no lock to take and it raises NgioValueError — and on a shared network filesystem it is only as reliable as the mount's flock support. On Windows the lock is best-effort and warns, because filelock can hand the same lock to two workers at once: a single writer is safe, but concurrent ones can still lose an update, so run those on Linux or macOS.
To remove images from the plate, use the remove_image method. It takes the same arguments as add_image.
print(f"Before removing images: {plate.wells_paths()} wells")
plate.remove_image(row="D", column=0, image_path="0")
print(f"After removing images: {plate.wells_paths()} wells")
Warning
No data will be removed from the store. If an image is saved in the store it will remain there.
Also the metadata will only be removed from the plate.well metadata. The number of columns and rows will not be updated.
This function is not multiprocessing safe. If you are using multiprocessing, you should use the atomic_remove_image method instead, under the same store and platform limits as atomic_add_image above.
Next steps¶
- Iterators — build pipelines that scale across a plate.
- HCS exploration tutorial — a worked example on real plate data.
- HCS API reference —
OmeZarrPlateandOmeZarrWell.