Skip to content

Commit

Permalink
Doc: added tutorial on macro/plugins/...
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreRaybaut committed Jan 21, 2024
1 parent 0a9c928 commit 4f88309
Show file tree
Hide file tree
Showing 20 changed files with 980 additions and 7 deletions.
Binary file added doc/images/tutorials/custom_func/01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/09.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/tutorials/custom_func/nb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions doc/intro/tutorials/blobs.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _tutorial_blobs:

Detecting blobs on an image
===========================

Expand Down
76 changes: 76 additions & 0 deletions doc/intro/tutorials/cdl_custom_func.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-

"""
Custom denoising filter plugin
==============================
This is a simple example of a DataLab image processing plugin.
It is part of the DataLab custom function tutorial.
.. note::
This plugin is not installed by default. To install it, copy this file to
your DataLab plugins directory (see `DataLab documentation
<https://codra-ingenierie-informatique.github.io/DataLab/en/features/general/plugins.html>`_).
"""

import numpy as np
import scipy.ndimage as spi

import cdl.core.computation.image as cpi
import cdl.obj
import cdl.param
import cdl.plugins


def weighted_average_denoise(values: np.ndarray) -> float:
"""Apply a custom denoising filter to an image.
This filter averages the pixels in a 5x5 neighborhood, but gives less weight
to pixels that significantly differ from the central pixel.
"""
central_pixel = values[len(values) // 2]
differences = np.abs(values - central_pixel)
weights = np.exp(-differences / np.mean(differences))
return np.average(values, weights=weights)


def compute_weighted_average_denoise(src: cdl.obj.ImageObj) -> cdl.obj.ImageObj:
"""Compute Weighted average denoise
This function is a wrapper around the ``weighted_average_denoise`` function,
allowing to process an image object (instead of a NumPy array).
It is required to use the function in DataLab.
Args:
src (ImageObj): input image object
Returns:
ImageObj: output image object
"""
dst = cpi.dst_11(src, weighted_average_denoise.__name__)
dst.data = spi.generic_filter(src.data, weighted_average_denoise, size=5)
return dst


class CustomFilters(cdl.plugins.PluginBase):
"""DataLab Custom Filters Plugin"""

PLUGIN_INFO = cdl.plugins.PluginInfo(
name="My custom filters",
version="1.0.0",
description="This is an example plugin",
)

def apply_func(self, func, title) -> None:
"""Apply custom function"""
self.imagepanel.processor.compute_11(func, title=title)

def create_actions(self) -> None:
"""Create actions"""
acth = self.imagepanel.acthandler
with acth.new_menu(self.PLUGIN_INFO.name):
for name, func in (
("Weighted average denoise", compute_weighted_average_denoise),
):
acth.new_action(name, triggered=lambda: self.apply_func(func, name))
150 changes: 150 additions & 0 deletions doc/intro/tutorials/custom_func.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "76b9af91-1233-485b-a616-826a8ccfcbc8",
"metadata": {},
"source": [
"DataLab custom function example\n",
"===============================\n",
"\n",
"This is part of the DataLab's custom function tutorial which aims at illustrating the extensibility of DataLab (macros, plugins, and control from an external IDE or a Jupyter notebook). \n",
"\n",
"The only requirement is to install the *DataLab Simple Client* package (using `pip install cdlclient`, for example)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "47c889ba-ca07-46dd-bec0-b08d67bd1eb2",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import scipy.ndimage as spi\n",
"from cdlclient import SimpleRemoteProxy\n",
"\n",
"# Define our custom processing function\n",
"def weighted_average_denoise(values: np.ndarray) -> float:\n",
" \"\"\"Apply a custom denoising filter to an image.\n",
"\n",
" This filter averages the pixels in a 5x5 neighborhood, but gives less weight\n",
" to pixels that significantly differ from the central pixel.\n",
" \"\"\"\n",
" central_pixel = values[len(values) // 2]\n",
" differences = np.abs(values - central_pixel)\n",
" weights = np.exp(-differences / np.mean(differences))\n",
" return np.average(values, weights=weights)"
]
},
{
"cell_type": "markdown",
"id": "721d52f6-2df8-4559-89d2-f7088a5e2ee2",
"metadata": {},
"source": [
"Connecting to DataLab current session:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "1de8616a-619c-4d5b-869f-fb41cf72b7cf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Connecting to DataLab XML-RPC server...OK (port: 61330)\n"
]
}
],
"source": [
"proxy = SimpleRemoteProxy()\n",
"proxy.connect()"
]
},
{
"cell_type": "markdown",
"id": "9246a105-d73d-4ba3-81e1-4fadd042a4c6",
"metadata": {},
"source": [
"Switch to the \"Image panel\" and get the current image"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "ccdddb07-cc54-42dd-b744-751516ae73d9",
"metadata": {},
"outputs": [],
"source": [
"proxy.set_current_panel(\"image\")\n",
"image = proxy.get_object()\n",
"if image is None:\n",
" # We raise an explicit error if there is no image to process\n",
" raise RuntimeError(\"No image to process!\")"
]
},
{
"cell_type": "markdown",
"id": "0b717a9d-f92f-4269-8c47-150beeed1075",
"metadata": {},
"source": [
"Get a copy of the image data, apply the function to it, and add new image to the panel"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7216fc12-2675-48ff-a62a-64b5516c2e09",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data = np.array(image.data, copy=True)\n",
"data = spi.generic_filter(data, weighted_average_denoise, size=5)\n",
"proxy.add_image(\"Filtered using a Jupyter notebook\", data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0ca0385b-bb43-4c14-b4ab-2768d44f04cc",
"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.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
48 changes: 48 additions & 0 deletions doc/intro/tutorials/custom_func.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
"""
Example of remote control of DataLab current session,
from a Python script running outside DataLab (e.g. in Spyder)
Created on Fri May 12 12:28:56 2023
@author: p.raybaut
"""

# %% Importing necessary modules

import numpy as np
import scipy.ndimage as spi
from cdlclient import SimpleRemoteProxy

# %% Connecting to DataLab current session

proxy = SimpleRemoteProxy()
proxy.connect()

# %% Executing commands in DataLab (...)

# Define our custom processing function
def weighted_average_denoise(values: np.ndarray) -> float:
"""Apply a custom denoising filter to an image.
This filter averages the pixels in a 5x5 neighborhood, but gives less weight
to pixels that significantly differ from the central pixel.
"""
central_pixel = values[len(values) // 2]
differences = np.abs(values - central_pixel)
weights = np.exp(-differences / np.mean(differences))
return np.average(values, weights=weights)

# Switch to the "Image panel" and get the current image
proxy.set_current_panel("image")
image = proxy.get_object()
if image is None:
# We raise an explicit error if there is no image to process
raise RuntimeError("No image to process!")

# Get a copy of the image data, and apply the function to it
data = np.array(image.data, copy=True)
data = spi.generic_filter(data, weighted_average_denoise, size=5)

# Add new image to the panel
proxy.add_image("Filtered using Spyder", data)
Loading

0 comments on commit 4f88309

Please sign in to comment.