From bd779759bde66c36614201f2f6dbaec9733d5084 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Mon, 5 Aug 2024 19:31:10 +0200 Subject: [PATCH 1/9] use git-bob 0.2.0 --- .github/workflows/comment-on-issue.yml | 13 ++++++++++--- .github/workflows/review-pull-request.yml | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index f3bbb91..d099ca2 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -21,6 +21,11 @@ jobs: echo "Organization - ${{ github.repository_owner }}" echo "Repository Name - ${{ github.repository }}" + - name: Print Job details + run: | + echo "Run ID - ${{ github.run_id }}" + echo "Job ID - ${{ github.job }}" + - name: Set up Python uses: actions/setup-python@v2 with: @@ -29,13 +34,15 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git-bob==0.1.1 + pip install git-bob==0.2.0 - name: Run Python env: - #ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}" - GIT_BOB_LLM_NAME: "gpt-4o-2024-05-13" + ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + GIT_BOB_LLM_NAME: "${{ secrets.GIT_BOB_LLM_NAME }}" OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" GITHUB_API_KEY: "${{ secrets.GITHUB_TOKEN }}" + GITHUB_RUN_ID: "${{ github.run_id }}" run: | git-bob comment-on-issue-action ${{ github.repository }} ${{ github.event.pull_request.number }} ${{ github.event.issue.number }} diff --git a/.github/workflows/review-pull-request.yml b/.github/workflows/review-pull-request.yml index 7b38c47..ae7dda2 100644 --- a/.github/workflows/review-pull-request.yml +++ b/.github/workflows/review-pull-request.yml @@ -20,6 +20,11 @@ jobs: echo "Organization - ${{ github.repository_owner }}" echo "Repository Name - ${{ github.repository }}" + - name: Print Job details + run: | + echo "Run ID - ${{ github.run_id }}" + echo "Job ID - ${{ github.job }}" + - name: Set up Python uses: actions/setup-python@v2 with: @@ -28,14 +33,16 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git-bob==0.1.1 + pip install git-bob==0.2.0 - name: Run Python env: - #ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}" - GIT_BOB_LLM_NAME: "gpt-4o-2024-05-13" + ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}" + GOOGLE_API_KEY: "${{ secrets.GOOGLE_API_KEY }}" + GIT_BOB_LLM_NAME: "${{ secrets.GIT_BOB_LLM_NAME }}" OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" GITHUB_API_KEY: "${{ secrets.GITHUB_TOKEN }}" + GITHUB_RUN_ID: "${{ github.run_id }}" run: | git-bob review-pull-request-action ${{ github.repository }} ${{ github.event.pull_request.number }} From 76c1e87a1d2b207fe02173deee29823c153caee5 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Sun, 18 Aug 2024 10:46:29 +0200 Subject: [PATCH 2/9] bugfix: animations of RGBA data didn't work --- stackview/_animate.py | 3 +++ stackview/_image_widget.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/stackview/_animate.py b/stackview/_animate.py index 538bdfd..fcfbc46 100644 --- a/stackview/_animate.py +++ b/stackview/_animate.py @@ -35,6 +35,9 @@ def animate(timelapse, filename:str=None, overwrite_file:bool=True, frame_delay_ from stackview._image_widget import _img_to_rgb from ._utilities import numpy_to_gif_bytestream, _gif_to_html + if isinstance(timelapse, list): + timelapse = np.asarray(timelapse) + if 0 <= timelapse.min() <= 1 and 0 <= timelapse.max() <= 1: warnings.warn("The timelapse has a small intensity range between 0 and 1. Consider normalizing it to the range between 0 and 255.") if timelapse.min() < 0 or timelapse.max() > 255: diff --git a/stackview/_image_widget.py b/stackview/_image_widget.py index a65f05c..5d911fd 100644 --- a/stackview/_image_widget.py +++ b/stackview/_image_widget.py @@ -72,7 +72,7 @@ def _img_to_rgb(image, display_max=None): from ._colormaps import _labels_lut, create_colormap - if len(image.shape) > 2 and image.shape[-1] == 3: + if len(image.shape) > 2 and (image.shape[-1] == 3 or image.shape[-1] == 4): return image if image.dtype == bool: From cfbd9f62f6acdb7e43b0347c19d967ccab17fd4e Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Sun, 18 Aug 2024 10:47:17 +0200 Subject: [PATCH 3/9] fix lists passed to animate_curtain --- stackview/_animate.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stackview/_animate.py b/stackview/_animate.py index fcfbc46..9dddb1a 100644 --- a/stackview/_animate.py +++ b/stackview/_animate.py @@ -118,6 +118,13 @@ def animate_curtain(timelapse, timelapse_curtain, import numpy as np from ._image_widget import _img_to_rgb + + if isinstance(timelapse, list): + timelapse = np.asarray(timelapse) + + if isinstance(timelapse_curtain, list): + timelapse_curtain = np.asarray(timelapse_curtain) + max_size = timelapse.shape[1] images = [] From 627504bec45b2eca635def67dcd460ac348efd1b Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Sun, 18 Aug 2024 10:47:40 +0200 Subject: [PATCH 4/9] bump version --- setup.py | 2 +- stackview/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f7ac314..d18135e 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="stackview", - version="0.8.0", + version="0.8.1", author="Robert Haase", author_email="robert.haase@uni-leipzig.de", description="Interactive image stack viewing in jupyter notebooks", diff --git a/stackview/__init__.py b/stackview/__init__.py index 2228465..8bed0a0 100644 --- a/stackview/__init__.py +++ b/stackview/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.8.0" +__version__ = "0.8.1" from ._static_view import jupyter_displayable_output, insight from ._utilities import merge_rgb From 1cafdc6d0bd24657b77e00cc93455a4425e8d63e Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Sun, 18 Aug 2024 16:35:54 +0200 Subject: [PATCH 5/9] Bug fix: clusterplot didn't work with 3d images --- stackview/_clusterplot.py | 5 ++++- stackview/_scatterplot.py | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/stackview/_clusterplot.py b/stackview/_clusterplot.py index d823524..885b06c 100644 --- a/stackview/_clusterplot.py +++ b/stackview/_clusterplot.py @@ -42,6 +42,8 @@ def clusterplot(df, labels, column_x:str="x", column_y:str="y", column_selection from ._scatterplot import scatterplot import functools + labels = np.asarray(labels) + if column_selection in df.columns: selection = df["selection"].tolist() @@ -72,6 +74,7 @@ def update(selection, label_image, selected_image, widget): return grid([[ image_display, - scatterplot + scatterplot, + ]]) diff --git a/stackview/_scatterplot.py b/stackview/_scatterplot.py index 8b9c6d5..8f30b62 100644 --- a/stackview/_scatterplot.py +++ b/stackview/_scatterplot.py @@ -120,7 +120,9 @@ def __init__(self, df, column_x, column_y, column_selection, figsize, selection_ manager = Gcf.get_active() Gcf.figs.pop(manager.num, None) - self.selector = Selector(self.fig, self.ax, self.plotted_points, callback=self.set_selection) + self.selector = None + #self.selector = Selector(self.fig, self.ax, self.plotted_points, callback=self.set_selection) + self.update() # show selection if defined if column_selection in df.columns: From 963de7ebb8b005dd9a99414b00aeb29524bdb032 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Sun, 18 Aug 2024 16:37:52 +0200 Subject: [PATCH 6/9] added example --- docs/clusterplot.ipynb | 306 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 docs/clusterplot.ipynb diff --git a/docs/clusterplot.ipynb b/docs/clusterplot.ipynb new file mode 100644 index 0000000..5067002 --- /dev/null +++ b/docs/clusterplot.ipynb @@ -0,0 +1,306 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "35d1c118-c57f-4ba4-9926-de78d5db451c", + "metadata": {}, + "outputs": [], + "source": [ + "import bia_bob\n", + "import pyclesperanto_prototype as cle\n", + "import stackview" + ] + }, + { + "cell_type": "raw", + "id": "39df4e10-bdf9-4e6f-8289-71554fa11ee8", + "metadata": {}, + "source": [ + "%bob load the cells3d dataset and extract the nuclei channel" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e9bbbd3b-ac3c-4171-a42f-ea07cadc7d63", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(60, 256, 256)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np # Already imported modules should not be imported again\n", + "from skimage.data import cells3d\n", + "\n", + "# Extract the nuclei channel (assuming channel 1 is the nuclei channel)\n", + "nuclei_channel = cells3d()[:, 1, :, :]\n", + "\n", + "nuclei_channel.shape # to verify the extraction" + ] + }, + { + "cell_type": "raw", + "id": "09769fd0-4606-437f-9fc5-f7f260f871a5", + "metadata": {}, + "source": [ + "%bob segment the nuclei_channel image using otsu thresholding and connected component labeling" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "72a47ee0-be9b-456c-91d3-f91a58c95dff", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
shape(60, 256, 256)
dtypeuint32
size15.0 MB
min0
max740
\n", + "\n", + "
" + ], + "text/plain": [ + "StackViewNDArray([[[0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " ...,\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0]],\n", + "\n", + " [[0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " ...,\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0]],\n", + "\n", + " [[0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " ...,\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0]],\n", + "\n", + " ...,\n", + "\n", + " [[0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " ...,\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0]],\n", + "\n", + " [[0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " ...,\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0]],\n", + "\n", + " [[0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " ...,\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0],\n", + " [0, 0, 0, ..., 0, 0, 0]]], dtype=uint32)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Apply Otsu's threshold to create a binary image\n", + "binary_nuclei = cle.threshold_otsu(nuclei_channel)\n", + "\n", + "# Perform connected component labeling\n", + "labeled_nuclei = cle.connected_components_labeling_box(binary_nuclei)\n", + "\n", + "# Display the segmented label map\n", + "stackview.insight(labeled_nuclei)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f94055dd-0aef-4de6-9d18-dc938bb0149e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
labelareamean_intensity
0127119.012862.000295
125.011143.800000
23168.011268.273810
3460.011758.683333
451.010527.000000
\n", + "
" + ], + "text/plain": [ + " label area mean_intensity\n", + "0 1 27119.0 12862.000295\n", + "1 2 5.0 11143.800000\n", + "2 3 168.0 11268.273810\n", + "3 4 60.0 11758.683333\n", + "4 5 1.0 10527.000000" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "from skimage.measure import regionprops_table\n", + "\n", + "# Define properties to extract\n", + "properties = ['label', 'area', 'mean_intensity']\n", + "\n", + "# Extract features\n", + "measurements = regionprops_table(np.asarray(labeled_nuclei), intensity_image=nuclei_channel, properties=properties)\n", + "\n", + "# Store results in a DataFrame\n", + "df = pd.DataFrame(measurements)\n", + "df.head() # To display the first few rows of the extracted features" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "1fba06ed-5ae5-4049-a656-d1d4ea18894d", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9e50d99d1f4b431787c2b422e15f6b49", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(HBox(children=(VBox(children=(HBox(children=(VBox(children=(ImageWidget(height=256, width=256),…" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import stackview\n", + "stackview.clusterplot(df, labeled_nuclei, column_x=\"area\", column_y=\"mean_intensity\", image=nuclei_channel)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c07c9fc-873b-490b-a2b3-b280a9557f13", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From f457f9ab1bdacbb91a9eb47b63e0c1ba417144c6 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Sun, 18 Aug 2024 16:38:46 +0200 Subject: [PATCH 7/9] bump version --- setup.py | 2 +- stackview/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index d18135e..4d2beb3 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="stackview", - version="0.8.1", + version="0.8.2", author="Robert Haase", author_email="robert.haase@uni-leipzig.de", description="Interactive image stack viewing in jupyter notebooks", diff --git a/stackview/__init__.py b/stackview/__init__.py index 8bed0a0..671d389 100644 --- a/stackview/__init__.py +++ b/stackview/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.8.1" +__version__ = "0.8.2" from ._static_view import jupyter_displayable_output, insight from ._utilities import merge_rgb From afe4fd24baa492397fb9a7ab8c5f71cdd2e08672 Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Wed, 28 Aug 2024 20:13:26 +0200 Subject: [PATCH 8/9] fix issue with numpy compatibility --- stackview/_static_view.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stackview/_static_view.py b/stackview/_static_view.py index 0e1ba06..f9f151a 100644 --- a/stackview/_static_view.py +++ b/stackview/_static_view.py @@ -46,6 +46,10 @@ def __array_finalize__(self, obj): if obj is None: return self.library_name = getattr(obj, 'library_name', None) self.help_url = getattr(obj, 'help_url', None) + self.obj = obj + + def __getitem__(self, index): + return self.obj.__getitem__(index) def _repr_html_(self): """HTML representation of the image object for IPython. From b13a5f23fddc310c20310b472c264e3796f104cd Mon Sep 17 00:00:00 2001 From: Robert Haase Date: Wed, 28 Aug 2024 20:18:53 +0200 Subject: [PATCH 9/9] bump version --- setup.py | 2 +- stackview/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 4d2beb3..a1b0828 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="stackview", - version="0.8.2", + version="0.9.0", author="Robert Haase", author_email="robert.haase@uni-leipzig.de", description="Interactive image stack viewing in jupyter notebooks", diff --git a/stackview/__init__.py b/stackview/__init__.py index 671d389..31381f1 100644 --- a/stackview/__init__.py +++ b/stackview/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.8.2" +__version__ = "0.9.0" from ._static_view import jupyter_displayable_output, insight from ._utilities import merge_rgb