Skip to content
Merged

Dev #48

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/docmd-wiki.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Generate and Push Docs to Wiki
on:
release:
types: [created]

workflow_dispatch:
jobs:
generate-docs:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -45,6 +45,12 @@ jobs:
docmd PyPlaque.utils --out ../wiki
docmd PyPlaque.view --out ../wiki

- name: Fix markdown links to remove .md extension
run: |
cd wiki
# Use sed to replace .md) with ) in all markdown files
find . -name "*.md" -type f -exec sed -i 's/\.md)/)/g' {} +

- name: Check existing paths
run: |
pwd
Expand Down
29 changes: 22 additions & 7 deletions PyPlaque/experiment/crystal_violet.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,34 @@ class CrystalViolet:
Attributes:
plate_folder (str, required): The main directory containing subdirectories of the plates.

plate_mask_folder (str, required): The main directory containing subdirectories of the
plate masks.
plate_mask_folder (str, optional): The main directory containing subdirectories of the
plate masks. Gets created automatically in the same structure
as plate_folder if not given.

params (dict, optional): A dictionary of parameters for crystal violet plaques, thresholding,
etc. Default is an empty dictionary with default values set.
"""
def __init__(self, plate_folder, plate_mask_folder, params=None):
def __init__(self, plate_folder, plate_mask_folder=None, params=None):
#check data types
if not isinstance(plate_folder, str):
raise TypeError("Expected plate_folder argument to be str")
if not isinstance(plate_mask_folder, str):
raise TypeError("Expected plate_mask_folder argument to be str")
if plate_mask_folder:
if not isinstance(plate_mask_folder, str):
raise TypeError("Expected plate_folder argument to be str")
if params:
if not isinstance(params, dict):
raise TypeError("Expected params argument to be dict")

self.plate_folder = plate_folder
self.plate_mask_folder = plate_mask_folder
if plate_mask_folder:
self.plate_mask_folder = plate_mask_folder
self.plate_mask_folder_original = plate_mask_folder
else:
self.plate_mask_folder_original = None
plate_mask_folder_path = Path(plate_folder).parents[0] / 'masks'
plate_mask_folder_path.mkdir(parents=True, exist_ok=True)
self.plate_mask_folder = str(plate_mask_folder_path)


if not params:
params = {
Expand Down Expand Up @@ -93,7 +103,7 @@ def get_individual_plates(self, whole_plate=False,folder_pattern=None):

Args:
whole_plate (bool, optional): A flag to determine whether to consider that folders
have whole plate images or not (wells images of a plate
have whole plate images or not (well images of a plate
instead). Default is False.
folder_pattern (str, optional): A regular expression pattern used to filter directory names.

Expand Down Expand Up @@ -124,6 +134,11 @@ def get_individual_plates(self, whole_plate=False,folder_pattern=None):
if os.path.isdir(os.path.join(self.plate_folder, file))]
self.plate_mask_indiv_dir = [file for file in os.listdir(self.plate_mask_folder)
if os.path.isdir(os.path.join(self.plate_mask_folder, file))]

if self.plate_mask_folder_original == None:
_ = [os.mkdir(os.path.join(self.plate_mask_folder, file)) for file in self.plate_indiv_dir]
self.plate_mask_indiv_dir = [file for file in os.listdir(self.plate_mask_folder)
if os.path.isdir(os.path.join(self.plate_mask_folder, file))]

return self.plate_indiv_dir, self.plate_mask_indiv_dir

Expand Down
27 changes: 21 additions & 6 deletions PyPlaque/experiment/fluorescence_microscopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,23 @@ class FluorescenceMicroscopy:
Attributes:
plate_folder (str, required): The main directory containing subdirectories of the plates.

plate_mask_folder (str, required): The main directory containing subdirectories of the
plate masks.
plate_mask_folder (str, optional): The main directory containing subdirectories of the
plate masks. Gets created automatically in the same structure
as plate_folder if not given.

params (dict, optional): A dictionary of parameters for nuclei and virus channels to be used
for generating masks and readouts. Default is an empty dictionary.

Raises:
TypeError: If the provided arguments are not of the expected type.
"""
def __init__(self, plate_folder, plate_mask_folder, params=None):
def __init__(self, plate_folder, plate_mask_folder=None, params=None):
#check data types
if not isinstance(plate_folder, str):
raise TypeError("Expected plate_folder argument to be str")
if not isinstance(plate_mask_folder, str):
raise TypeError("Expected plate_mask_folder argument to be str")
if plate_mask_folder:
if not isinstance(plate_mask_folder, str):
raise TypeError("Expected plate_folder argument to be str")
if params:
if not isinstance(params, dict):
raise TypeError("Expected params argument to be dict")
Expand All @@ -59,7 +61,15 @@ def __init__(self, plate_folder, plate_mask_folder, params=None):
raise TypeError("Expected second nested object of params argument to be dict")

self.plate_folder = plate_folder
self.plate_mask_folder = plate_mask_folder
if plate_mask_folder:
self.plate_mask_folder = plate_mask_folder
self.plate_mask_folder_original = plate_mask_folder
else:
self.plate_mask_folder_original = None
plate_mask_folder_path = Path(plate_folder).parents[0] / 'masks'
plate_mask_folder_path.mkdir(parents=True, exist_ok=True)
self.plate_mask_folder = str(plate_mask_folder_path)

if not params:
params = {
'nuclei': {
Expand Down Expand Up @@ -150,6 +160,11 @@ def get_individual_plates(self, folder_pattern=None):
self.plate_mask_indiv_dir = [file for file in os.listdir(self.plate_mask_folder)
if os.path.isdir(os.path.join(self.plate_mask_folder, file))]

if self.plate_mask_folder_original == None:
_ = [os.mkdir(os.path.join(self.plate_mask_folder, file)) for file in self.plate_indiv_dir]
self.plate_mask_indiv_dir = [file for file in os.listdir(self.plate_mask_folder)
if os.path.isdir(os.path.join(self.plate_mask_folder, file))]

return self.plate_indiv_dir, self.plate_mask_indiv_dir


Expand Down
65 changes: 19 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ We introduce PyPlaque, an open-source Python package focusing on flexibility and

For further details please look at our paper: [https://www.biorxiv.org/content/10.1101/2024.08.07.603274v1]
___________
## Version
New release v0.2.0
## Documentation
The documentation has been added in our GitHub Wiki page [https://github.com/plaque2/PyPlaque/wiki](https://github.com/plaque2/PyPlaque/wiki). Please refer to it for links to our modules and subsequent links to classes and methods/functions for their descriptions.

___________
## Installation
The instructions apply for MacOS, Linux and Windows

See project's PyPi page [https://pypi.org/project/PyPlaque/](https://pypi.org/project/PyPlaque/)
You can also look at the project's PyPi page [https://pypi.org/project/PyPlaque/](https://pypi.org/project/PyPlaque/)

```
pip install PyPlaque
Expand All @@ -21,10 +22,21 @@ ___________
- run `pip install -e .`

___________
## Documentation
To be added in a separate webpage. Please refer to scripts in the repository now.
## Links to Google Colab notebooks for important workflows using the software

___________
- The ```load_data.ipynb``` notebook shows briefly steps to download programmatically the sample data used for analysis in the following notebooks. It specifically used ```curl``` to download the data from publicly published website links. This notebook can be opened in Colab here. <a href="https://colab.research.google.com/github/plaque2/PyPlaque/blob/master/notebooks/load_data.ipynb)" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

- The ```synth.ipynb``` notebook in PyPlaque generates synthetic shapes at random locations (that are seeded for reproducibility) to emulate segmentation masks. These simulated images are wrapped in standard PyPlaque classes like ```FluorescenceMicroscopy``` that treats each image of consisting of synthetic shapes as a fluorescence plate well, enabling seamless integration with the package's analysis and visualization pipelines. This allows us to compare image analysis algorithms' area and perimeter calculations on standard known shapes without relying on real irregular shapes where the true measurements could be ambiguous. This notebook can be tried in Colab here. <a href="https://colab.research.google.com/github/plaque2/PyPlaque/blob/master/notebooks/synthetic_data/synth.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

- The ```example_analysis.ipynb``` notebook demonstrates a systematic and comprehensive analysis workflow using real plaque assay data. It illustrates how to load fluorescence microscopy images of viral plaques using the ```FluorescenceMicroscopy``` class, generate plate level readouts (readouts for all wells of a plate) and filter them into viral and control groups based on well identifier metadata additionally stored in the class. We then use internal visualisation tools to validate our analysis by showcasing comparison barplots for readouts that indicate infectivity that should be different between viral and control wells. This notebook can be tried in Colab here. <a href="https://colab.research.google.com/github/plaque2/PyPlaque/blob/master/notebooks/visualization/example_analysis.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

- The ```cvp_detailed.ipynb``` notebook demonstrates functionality provided by PyPlaque for the analysis of the crystal violet stained plaques using the ```CrystalViolet``` class to load and store crystal violet stained, mobile photography images of viral plaques in 6-well plates available as individual wells. Using inbuilt classes and functions, steps are demonstrated to generate rudimentary binary detection masks for plaques if they are unavailable, stitching individual wells to create partial composite overviews of the plate, detection of plaques based on size criteria from available or generated detection masks, getting measures that describe the shape characteristics and spread of plaques in a well, visualise the spread of plaque measures in a well and finally generate individual well images and well and plaque detection masks from full plate images and full plate well and plaque detection masks in case they are unavailable. This notebook can be tried in Colab here. <a href="https://colab.research.google.com/https://github.com/plaque2/PyPlaque/blob/master/notebooks/cvp_detailed.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

- The ```fp_detailed.ipynb``` notebook demonstrates functionality provided by PyPlaque for the analysis of the fluorescence microscopy plaques using the ```FluorescenceMicroscopy``` class to load and store fluorescence microscopy images of viral plaques in 384-well plates available as individual wells. Using inbuilt classes and functions, steps are demonstrated to load virus and nuclei channel images for the wells, generate binary detection masks for both channels if unavailable, stitching individual wells to create partial composite overviews of the plate, and generation of plate level, well level and plaque level readouts at these 3 granularity levels. This notebook can be tried in Colab here. <a href="https://colab.research.google.com/https://github.com/plaque2/PyPlaque/blob/master/notebooks/fp_detailed.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>


Note : In notebooks, custom packages and additional data need to be loaded. Load your own data into the Colab environment by connecting via Google Drive or direct upload or using our sample analysis data using the code in the `load_data.ipynb` notebook above and then run the analysis.

## Usage
### Fluorescence Plaques

Expand Down Expand Up @@ -188,47 +200,8 @@ ___________
## Hierarchical Class Structure

![fig1](https://github.com/user-attachments/assets/65f4d2c5-2be0-44fd-8fdb-09a0507f16ea)
![fig2](https://github.com/user-attachments/assets/da0dda26-d7fb-4bed-85a6-1e4d0a4f5dac)

___________
## Class Types

### Experiment
1. CrystalViolet - This class is designed to contain metadata of multiple instances of a multititre plate of
Crystal Violet plaques.
2. FluorescenceMicroscopy - This class is designed to contain metadata of multiple instances of a multititre plate of Fluorescence
plaques.

### Specimen
1. PlaquesImageGray - This class is designed to hold grayscale image data containing multiple plaque phenotypes with a
respective binary mask. The class inherits from PlaquesMask.
2. PlaquesImageRGB - The class is designed to hold RGB image data containing multiple plaque phenotypes with a
respective binary mask. The class inherits from PlaquesMask.
3. PlaquesMask - This class is designed to hold a binary mask of multiple plaque instances in a well.
4. PlaquesWell - This class is designed to contain a full well of a multititre plate

### Phenotypes
1. CrystalVioletPlaque - This class contains a plaque obtained from crystal violet image. Class inherits from Plaque class
and is also designed to hold a single virological plaque phenotype.
2. FluorescencePlaque - This class contains a plaque obtained from fluorescence image. Class inherits from Plaque class
and is also designed to hold a single virological plaque phenotype.
3. Plaque - This class is designed to hold a single virological plaque phenotype as an object. It
encapsulates the properties and behaviors related to a specific plaque, including its mask,
centroid coordinates, bounding box, and usage preference for pick measurements.

### View
1. WellImageReadout - This class encapsulates metadata related to multiple instances of plaques
within a single well of a fluorescence plate.
2. PlaqueObjectReadout - This class encapsulate data related to a single instance of a
plaque from a fluorescence plaque well.
3. PlateReadout - This class contains readouts of multiple wells of a single plate
of a Fluorescence Plaque.
4. PlateImage - This class encapsulates a full multi-title plate image and
its corresponding binary mask. It provides methods to extract individual well images
from the plate based on specified criteria, visualize these wells annotated with their positions,
and more.

For more information about class attributes and functions please refer to scripts in the repository.
___________

## For further clarifications or queries, please contact:
1. Trina De (https://orcid.org/0000-0003-1111-9851)
Expand Down
Loading