diff --git a/binder/apt.txt b/binder/apt.txt index 1d0df38..882c207 100644 --- a/binder/apt.txt +++ b/binder/apt.txt @@ -1,11 +1,13 @@ dbus-x11 -firefox xfce4 xfce4-panel xfce4-session xfce4-settings xorg xubuntu-icon-theme +tigervnc-standalone-server +tigervnc-xorg-extension +libegl1 libx11-xcb-dev libglu1-mesa-dev libxrender-dev diff --git a/binder/environment.yml b/binder/environment.yml index d9b19af..90480c4 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -2,11 +2,14 @@ name: binder channels: - conda-forge dependencies: - - python=3.9 - - jupyter-server-proxy>=1.4 + - python=3.11 + - jupyterlab - jupytext - - pip + - jupyterlab-myst + - napari + - pyqt + - jupyter-book - websockify - - libxcb + - pip - pip: - - jupyter-desktop-server \ No newline at end of file + - jupyter-remote-desktop-proxy \ No newline at end of file diff --git a/binder/postBuild b/binder/postBuild deleted file mode 100644 index 485be76..0000000 --- a/binder/postBuild +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -pip install -r requirements.txt \ No newline at end of file diff --git a/napari-workshops/_config.yml b/napari-workshops/_config.yml index 892133e..dfd4288 100644 --- a/napari-workshops/_config.yml +++ b/napari-workshops/_config.yml @@ -9,7 +9,7 @@ logo: logo.ico only_build_toc_files: true launch_buttons: - notebook_interface : classic # The interface interactive links will activate ["classic", "jupyterlab"] + notebook_interface : "jupyterlab" # The interface interactive links will activate ["classic", "jupyterlab"] binderhub_url : https://mybinder.org # The URL of the BinderHub (e.g., https://mybinder.org) # Force re-execution of notebooks on each build. diff --git a/napari-workshops/launching_binder.md b/napari-workshops/launching_binder.md index 1a8fd7b..4e482ec 100644 --- a/napari-workshops/launching_binder.md +++ b/napari-workshops/launching_binder.md @@ -12,17 +12,15 @@ To open a notebook in Binder, click on the rocketship badge at the top of a note ![Binder badge shown in the "Bioimage visualization in Python" notebook](./resources/binder_button.png) -Just opening the notebook is not sufficient. Because napari is a desktop application, we need a "Desktop" tab to see the napari GUI interface. To do this we need to return to the Jupyter dashboard, by clicking the Jupyter logo (marked in red below) in the upper left. - -![Jupyter logo tool-tip showing `dashboard`](./resources/jupyter_logo_dashboard.png) +This will open the notebook in Markdown format, but it will not be runnable. Further, because napari is a desktop application, we need a "Desktop" tab to see the napari GUI interface. To do this, close the Markdown notebook and return to the Jupyter launcher. ## Open desktop tab -In the Jupyter launcher tab, click on "Desktop". +In the Jupyter launcher tab, click on the "Desktop" tile. ![Desktop tab button in Jupyter launcher tab](./resources/desktop_tab.png) -After this, you should see a new tab open up in your browser window called "noVNC", with a basic desktop interface. Note if at first it doesn't connect, just close the tab and click the `D` tile a second time. +After this, you should see a new tab open up in your browser window called "Jupyter Remote Desktop Proxy", with a basic Linux desktop interface. Note if at first it doesn't connect, just close the tab and click the `D` tile a second time. ![Desktop interface shown in browser tab](./resources/desktop.png) diff --git a/napari-workshops/notebooks/spot_detection.md b/napari-workshops/notebooks/spot_detection.md index 5cf606c..1fae879 100644 --- a/napari-workshops/notebooks/spot_detection.md +++ b/napari-workshops/notebooks/spot_detection.md @@ -120,7 +120,7 @@ nbscreenshot(viewer) ## Create an image filter -If you look carefull at the `spots` layer, you will notice that it contains background and +If you look carefully at the `spots` layer, you will notice that it contains background and autofluorescence from the cells. It may help to just look at the single channel. ```{code-cell} ipython3 @@ -130,7 +130,8 @@ viewer.layers['nuclei'].visible = False To improve spot detection, we will apply a high pass filter to improve the contrast of the spots. A simple way to achieve this is to: -1. blur the image with a Gaussian, which removes high-frequency information—this is why we use it for denoising. +1. blur the image with a Gaussian, which removes high-frequency information (small features, including +our spots)—this is why we use it for denoising. 2. subtract the blurred image from the original. Lets try this using the `gaussian_filter` from `scipy` with a sigma of 2 px and lets clip any negative values: @@ -164,7 +165,7 @@ nbscreenshot(viewer) ## Detect spots -Next, we will use one of the blob detection algorithms from sci-kit image to +Next, we will use one of the blob detection algorithms from scikit-image to perform the blob detection. ```{tip} @@ -177,11 +178,9 @@ from skimage.feature import blob_log # detect the spots on the filtered image blobs_log = blob_log( - high_passed_spots, - max_sigma=3, - threshold=None, # use a relative threshold instead - threshold_rel=0.2 -) + high_passed_spots, max_sigma=3, + threshold=None, # use a relative threshold instead + threshold_rel=0.2) # convert the output of the blob detector to the # desired points_coords and sizes arrays @@ -216,21 +215,20 @@ viewer.camera.zoom = 8 nbscreenshot(viewer) ``` -## Optional: using Points layer `properties` +## Optional: using Points layer `features` + +`features` is a table associated with a Points layer that can store additional data associated with +a data point. It has one row per data element (Point) and one column per feature. This would enable you +to add other attributes like `volume` or `maximum-intensity` should you calculate those for each cell. +Importantly, napari can not only display the values of associated features in the status bar, but +also use them for styling, e.g. for `face_color`. For more information, see the +[Points layer guide](https://napari.org/stable/howtos/layers/points.html#using-the-points-features-table), +[the Points annotation tutorial](https://napari.org/stable/tutorials/annotation/annotate_points.html) +or the ["Add points with features" Gallery example](https://napari.org/stable/gallery/add_points_with_features.html#sphx-glr-gallery-add-points-with-features-py). -`properties` is a table associated with a Points layer that can store additional data associated with -a data point. It has one row per data element (Point) and one column per feature. Importantly, -napari can not only display the values of associated features in the status bar, but also use them for -styling, e.g. for `face_color`. For more information, see the [Points layer guide](https://napari.org/stable/howtos/layers/points.html#setting-point-edge-and-face-color-with-properties). -```{note} -napari plans to deprecate `properties` in favor of `features` in a future release, but for the time being -the documentation for `properties` is superior, so we use that here. Note that some examples in the Gallery -have been updated to use `features`, but for the purposes of these simple examples you can replace -`features` with `properties` or vice versa. -``` -Let's use the `properties` dictionary to store the intensity value of the image at the coordinate of point +Let's use the `features` dictionary to store the intensity value of the image at the coordinate of point and then we can encode the color of the point marker using that value. First let's get an array of the pixel values of the original data array at the coordinates of our detected @@ -242,10 +240,10 @@ tuple_of_spots_as_indexes = tuple(np.round(spot_coords).astype(int).T) intensities = viewer.layers['spots'].data[tuple_of_spots_as_indexes] ``` -Now we will set the `properties` table of our Points layer—we could have done this when we constructed the layer. +Now we will set the `features` table of our Points layer—we could have done this when we constructed the layer. ```{code-cell} ipython3 -viewer.layers['spot_coords'].properties = {'intensity': intensities} +viewer.layers['spot_coords'].features = {'intensity': intensities} ``` Now when you mouseover a point, you should see the corresponding intensity value in the status bar. diff --git a/napari-workshops/notebooks/spot_detection_functions.md b/napari-workshops/notebooks/spot_detection_functions.md index 7927ccb..2491ef6 100644 --- a/napari-workshops/notebooks/spot_detection_functions.md +++ b/napari-workshops/notebooks/spot_detection_functions.md @@ -19,6 +19,19 @@ There are a number of ways to go about creating your own widgets, you can see [a In this module, we will implement elements of our previous workflow as functions and then use [`magicgui.magicgui`](https://pyapp-kit.github.io/magicgui/api/magicgui/#magicguimagicgui) decorator on those functions to return us compound widgets that we can use to make exploring the parameters easier in the GUI. For a nice overview of the `magicgui` decorators, see [the official documentation](https://pyapp-kit.github.io/magicgui/decorators/). +## `binder` setup + +```{code-cell} ipython3 +:tags: [remove-output] + +# this cell is required to run these notebooks on Binder. Make sure that you also have a desktop tab open. +import os +if 'BINDER_SERVICE_HOST' in os.environ: + os.environ['DISPLAY'] = ':1.0' +``` + +## Loading data + Let's get everything set up, based on the previous notebook: ```{code-cell} ipython3 @@ -45,6 +58,8 @@ viewer.add_image(nuclei, colormap = 'I Forest', blending = 'minimum') viewer.add_image(spots, colormap = 'I Orange', blending='minimum') ``` +## A basic filtering function + Now let's write a function that takes an array and a `sigma` value and performs the high-pass operation. @@ -96,7 +111,9 @@ For more information, see the official Python documentation for: from magicgui import magicgui @magicgui -def gaussian_high_pass(image: "napari.types.ImageData", sigma: float = 2)->"napari.types.ImageData": +def gaussian_high_pass( + image: "napari.types.ImageData", sigma: float = 2 + ) -> "napari.types.ImageData": """Apply a gaussian high pass filter to an image. Parameters @@ -194,10 +211,13 @@ viewer.window.remove_dock_widget("all") ``` ```{code-cell} ipython3 -@magicgui(auto_call=True, - sigma={"widget_type": "FloatSlider", "min": 0, "max": 20} - ) -def gaussian_high_pass(image: "napari.types.ImageData", sigma: float = 2)->"napari.types.ImageData": +@magicgui( + auto_call=True, + sigma={"widget_type": "FloatSlider", "min": 0, "max": 20} + ) +def gaussian_high_pass( + image: "napari.types.ImageData", sigma: float = 2 + ) -> "napari.types.ImageData": """Apply a gaussian high pass filter to an image. Parameters @@ -276,11 +296,11 @@ from skimage.feature import blob_log @magicgui def detect_spots( - image: "napari.layers.Image", - high_pass_sigma: float = 2, - spot_threshold: float = 0.2, - blob_sigma: float = 2 - )->"napari.types.LayerDataTuple": + image: "napari.layers.Image", + high_pass_sigma: float = 2, + spot_threshold: float = 0.2, + blob_sigma: float = 2 + ) -> "napari.types.LayerDataTuple": """Apply a gaussian high pass filter to an image. Parameters