Skip to content

Commit

Permalink
Merge branch 'master' into galaxy
Browse files Browse the repository at this point in the history
  • Loading branch information
sunyi000 committed Aug 2, 2024
2 parents d228e8b + 5201261 commit cee98db
Show file tree
Hide file tree
Showing 111 changed files with 3,380 additions and 1,243 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-python-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
sudo apt-get install -yy mesa-utils libgl1-mesa-dev xvfb curl
- uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
auto-update-conda: false
auto-activate-base: true
activate-environment: ""
channel-priority: strict
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ _site*
.idea
Thumbs.db
~$*
*.ipynb
.ipynb_checkpoints
**/__pycache__/
Untitl*
32 changes: 12 additions & 20 deletions _includes/auto_threshold/auto_threshold_act1_skimage_napari.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# %% [markdown]
# ## Apply manual and automated thresholds
# %%
# Apply manual and automated thresholds

# %%
# initial imports
Expand All @@ -8,31 +8,23 @@
from OpenIJTIFF import open_ij_tiff

# %%
# Read the images
image1, axes1, scales1, units1 = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__nuclei_without_offset.tif')
image2, axes2, scales2, units2 = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__nuclei_with_offset.tif')
# Read two images
image1, *_ = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__nuclei_without_offset.tif')
image2, *_ = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__nuclei_with_offset.tif')

# %%
# Inspect image data type and values
print(image1.dtype, image1.shape, np.min(image1), np.max(image1))
print(image2.dtype, image2.shape, np.min(image2), np.max(image2))
# Inspect image data type and value range
print(image1.dtype, image1.shape, image1.min(), image1.min())
print(image2.dtype, image2.shape, image2.min(), image2.max())

# %%
# Instantiate the napari viewer and display the images
viewer = napari.Viewer()


# %%
# Add the images
viewer.add_image(image1, name='image1')
viewer.add_image(image2, name='image2')

# %%
# Explore the values of the image
info_type = np.iinfo(image1.dtype)
print('\n', info_type)
print('\n Min image1 %d max image1 %d' % (image1.min(), image1.max()))
print('\n Min image2 %d max image2 %d' % (image2.min(), image2.max()))
viewer.add_image(image1, name="image1")
viewer.add_image(image2, name="image2")

# %%
# Show the histogram
Expand Down Expand Up @@ -73,6 +65,6 @@
viewer.add_image(mean_thresholded1, name='mean_thresholded1', opacity = 0.4, colormap='magenta')
viewer.add_image(mean_thresholded2, name='mean_thresholded2', opacity = 0.4, colormap='magenta')

# %% [markdown]
# Explore other thresholding options \
# %%
# Explore other thresholding options
# Note that there exists a threshold_multiotsu function to handle cases with multi-peaks histograms
93 changes: 93 additions & 0 deletions _includes/batch_processing/batch_measure_nuclei_shape.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# %%
# Batch analysis of 2D nuclei shape measurements


# %%
# Import python modules
from OpenIJTIFF import open_ij_tiff, save_ij_tiff
from skimage.measure import label, regionprops_table
from skimage.filters import threshold_otsu
import pandas as pd
import pathlib
from pathlib import Path


# %%
# Create a function that analyses one image
# Below, this function will be called several times, for all images
def analyse(image_filepath, output_folder):

# This prints which image is currently analysed
print("Analyzing:", image_filepath)

# Convert the image_filepath String to a Path,
# which is more convenient to create the output files
image_filepath = pathlib.Path(image_filepath)

image, axes, scales, units = open_ij_tiff(image_filepath)

# Binarize the image using auto-thresholding
threshold = threshold_otsu(image)
print("Threshold:", threshold)
binary_image = image > threshold

# Perform connected components analysis (i.e create labels)
# Note that label returns 32 bit data which save_ij_tif below can't handle.
# We can safely convert to 16 bit as we know that we don't have too many objects
label_image = label(binary_image).astype('uint16')

# Save the labels
label_image_filepath = output_folder / f"{image_filepath.stem}_labels.tif"
save_ij_tiff(label_image_filepath, label_image, axes, scales, units)

# Measure calibrated (scaled) nuclei shapes
df = pd.DataFrame(regionprops_table(
label_image,
properties={'label', 'area', 'centroid'},
spacing=scales))

# Round all measurements to 2 decimal places.
# This increases the readability a lot,
# but depending on your scientific question,
# you may not want to round that much!
df = df.round(2)

# Add the image and label filepaths to the data-frame
df['image'] = image_filepath
df['labels'] = label_image_filepath

# Return the data-frame
return df


# %%
# Assign an output folder
# Note: This uses your current working directory; you may want to change this to another folder on your computer
output_dir = Path.cwd()


# %%
# Create a list of the paths to all data
image_paths = [output_dir / "xy_8bit__mitocheck_incenp_t1.tif",
output_dir / "xy_8bit__mitocheck_incenp_t70.tif"]
# Create an empty list for the measurement results
result_dfs = []


# %%
# The loop which performs the analysis
for image_path in image_paths:

# Computes the analysis and returns a data-frame with the resulting measurements
result_df = analyse(image_path, output_dir)

# Append the label image path to the list initialized before the loop
result_dfs.append(result_df)


# %%
# Concatenate the result data-frames to a single one which contains all results
final_df = pd.concat(result_dfs, ignore_index=True)
# Save the final results to disk
final_df.to_csv(output_dir / 'batch_processing_results.csv', sep='\t', index=False)

21 changes: 21 additions & 0 deletions _includes/batch_processing/batch_measure_nuclei_shapes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<h4 id="batchshape"><a href="#batchshape">Batch analysis of nuclear shapes</a></h4>

- Download the input.zip containing the input images from [here](https://github.com/NEUBIAS/training-resources/tree/master/image_data/batch_process) and unpack to your course directory
- In a previous module [there is a workflow to measure the shapes of nuclei in one image](https://neubias.github.io/training-resources/workflow_segment_2d_nuclei_measure_shape/index.html#2dnuclei)
- Adapt this workflow for automated batch analysis of many images
- Start by building the skeleton of the workflow without filling in the functionality;

Note that the pseudo-code below will run fine, but does not produce any results:

```
FUNCTION analyse(image_path, output_folder)
PRINT "Analyzing:", image_path
END FUNCTION
FOR each image_path in image_paths
CALL analyse(image_path, output_dir)
END FOR
```

- Make sure the loop with the (almost) empty analyse function runs without error before filling in the image analysis steps
- Inspect the analysis results in a suitable software
49 changes: 23 additions & 26 deletions _includes/binarization/binarization_act1_skimage_napari.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,45 @@
# %% [markdown]
# ## Thresholding bright and dim cell
# %%
# Thresholding bright and dim cells

# %%
# Instantiate the napari viewer
# Instantiate napari
import napari
from OpenIJTIFF import open_ij_tiff
viewer = napari.Viewer()

# %%
# Read the intensity image
image, axes, scales, units = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__two_cells.tif')
# Load the image
from OpenIJTIFF import open_ij_tiff
image, *_ = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__two_cells.tif')

# %%
# View the intensity image
viewer.add_image(image)

# Check the intensity image's datatype
# Check the datatype and view the image
print(image.dtype)

# %% [markdown]
# **Napari GUI** Inspect the intensity image values in order to identify a threshold that segments both cells \
# **Napari GUI** hover with mouse \
# **Napari GUI** make a line profile using the Napari plugin `napari-plot-profile` (`pip install napari-plot-profile`)
viewer.add_image(image)

# %%
# Threshold the image
binary_image_two_cells = image > 49
# Napari: Inspect the pixel values to identify a threshold that segments both cells

# %%
# Overlay the binary image
viewer.add_image(binary_image_two_cells, opacity=0.8)

# Inspect data type
print(binary_image_two_cells.dtype)
# Inspect the image histogram to confirm the above threshold
import matplotlib.pyplot as plt
import numpy as np
plt.hist(image.flatten(), bins=np.arange(image.min(), image.max() + 1));
plt.yscale('log') # the background peak is so dominat that without the log scale it is hard to see the threshold

# %%
# Inspect value content
# Threshold the image and inspect the resulting values and data type
binary_image_two_cells = image > 49
import numpy as np
print(np.unique(binary_image_two_cells))
print(binary_image_two_cells.dtype)

# %%
# Overlay the binary image
viewer.add_labels(binary_image_two_cells, opacity=0.8)

# %%
# Apply a higher threshold
# to only select the brighter cell
# and also add this to the viewer
binary_image_one_cell = image > 100
viewer.add_image(binary_image_one_cell, opacity=0.8)

# %%
viewer.add_labels(binary_image_one_cell, opacity=0.8)
11 changes: 6 additions & 5 deletions _includes/binarization/binarization_act2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<h4 id="spots"><a href="#spots">Spots and threshold interval</a></h4>
<h4 id="spots"><a href="#spots">Apply various thresholds to one image</a></h4>

- Open image [xy_8bit__PCNA.tif](https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__PCNA.tif)
- Find a threshold value so that there are 2 foreground nuclei
- Find a threshold value so that only the bright dots remain
- Find a threshold interval so that only the boundaries of the nuclei remain
- Open [xy_8bit__PCNA.tif](https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__PCNA.tif)
- Apply a threshold value such that there are two foreground nuclei
- Apply a higher threshold such that is only one foreground nucleus
- Apply an even higher threshold such that only the intra-nuclear speckles are foreground
- Optionally, also find a threshold interval so that only the boundaries of the nuclei remain
60 changes: 31 additions & 29 deletions _includes/binarization/binarization_act2_skimage_napari.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,56 @@
# %% [markdown]
# ## Spots and threshold interval
# %%
# Nuclei, spots and nuclei boundary segmentation

# %%
# Instantiate the napari viewer
# Import libraries and instantiate the viewer
import napari
import numpy as np
import matplotlib.pyplot as plt
viewer = napari.Viewer()

# %%
# Read the intensity image
from OpenIJTIFF import open_ij_tiff
image, axes, scales, units = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__PCNA.tif')

# View the intensity image
viewer.add_image(image)
image, *_ = open_ij_tiff('https://github.com/NEUBIAS/training-resources/raw/master/image_data/xy_8bit__PCNA.tif')

# %%
# Check the intensity image's datatype
# Check the image's data type and
# view the image
print(image.dtype)
viewer.add_image(image)

# %% [markdown]
# **Napari GUI** Inspect the intensity image values in order to identify a threshold \
# **Napari GUI** hover with mouse \
# **Napari GUI** make a line profile using the Napari plugin `napari-plot-profile` (`pip install napari-plot-profile`)
# %%
# Napari: Inspect the pixel values in order to identify a threshold

# %%
# Threshold the image
binary_image_two_nuclei = image > 5

# Overlay the binary image
viewer.add_image(binary_image_two_nuclei, opacity=0.8)
# Also check the image histogram for a threshold
# Observe that interestingly there are several local minima in the histogram
plt.hist(image.flatten(), bins=np.arange(image.min(), image.max() + 1));
plt.yscale('log')

# %%
# Inspect data type
# Threshold both nuclei
# check the resulting datatype and content
# and view the binary image
binary_image_two_nuclei = image > 5
print(binary_image_two_nuclei.dtype)

# %%
# Inspect value content
import numpy as np
print(np.unique(binary_image_two_nuclei))
viewer.add_image(binary_image_two_nuclei, opacity=0.8)

# %%
# Apply a higher threshold
# to only select the brighter dots
binary_image_bright_dots = image > 44
viewer.add_image(binary_image_bright_dots, opacity=0.8)
# to only segment the brighter nucleus
binary_image_one_nucleus = image > 15
viewer.add_image(binary_image_one_nucleus, opacity=0.8)

# %%
# Apply two thresholds
# to only select the boundary of cells
binary_image_boundary = (image < 5) * (image >= 4)
viewer.add_image(binary_image_boundary, opacity=0.8)
# Apply an even higher threshold
# to only select the intranuclear speckles
binary_image_speckles = image > 44
viewer.add_image(binary_image_speckles, opacity=0.8)

# %%
# Apply two thresholds (aka "gating")
# to only select the boundary of cells
binary_image_boundary = (image < 5) & (image >= 4)
viewer.add_image(binary_image_boundary, opacity=0.8)
8 changes: 1 addition & 7 deletions _includes/coding_with_llms/code_creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,10 @@ Activities that could be solved using a LLM:
- [Threshold an image](https://neubias.github.io/training-resources/binarization/index.html#brightdim)
- [Segment nuclei and measure their shape](https://neubias.github.io/training-resources/workflow_segment_2d_nuclei_measure_shape/index.html#2dnuclei)

Tips for creating the prompt:
- Often the instructions given in the activity can be a good starting point to create the LLM prompt.
- Tell the LLM in which programming language you would like the code to be implemented.
- Sometimes it may also be good to tell it which libraries to use. For example, chatGPT seems to like "openCV" while "skimage" is more what we may typically use in bioimage anlaysis.
- Tell the LLM where exactly the input data is stored on your computer such that it can write the code to open the data.

Fixing errors:
- If you get an error executing the code, create a new prompt with this error and ask the LLM to fix it.

Understanding the code:
- If you do not understand parts of the code ask the LLM to explain them to you.
- If you do not understand parts of the code, ask the LLM to explain them to you.


Loading

0 comments on commit cee98db

Please sign in to comment.