(The original source of this was under docs/source/examples
at main ipywidgets repo presently. I fixed it to work in full JupyterLab with a few noted edits herein.)
This example builds a simple UI for performing basic image manipulation with scikit-image.
# Imports for JupyterLite
%pip install -q ipywidgets scikit-learn numpy matplotlib scikit-image
Note: you may need to restart the kernel to use updated packages.
(Added scikit-image
to above list of installs beyond what original notebook had, based on https://stackoverflow.com/a/46380661/8508004.)
# Stdlib imports
from io import BytesIO
# Third-party libraries
from IPython.display import Image
from ipywidgets import interact, interactive, fixed
import matplotlib as mpl
from skimage import data, filters, io, img_as_float
import numpy as np
Let's load an image from scikit-image's collection, stored in the data
module. These come back as regular numpy arrays:
i = img_as_float(data.coffee())
i.shape
(400, 600, 3)
Let's make a little utility function for displaying Numpy arrays with the IPython display protocol:
import matplotlib.pyplot as plt # added to source notebook (https://github.com/jupyter-widgets/ipywidgets/blob/6f6156c7e2421f437614b0649a5a3f37c3b4a3ec/docs/source/examples/Image%20Processing.ipynb), based on https://stackoverflow.com/a/66771428/8508004
def arr2img(arr):
"""Display a 2- or 3-d numpy array as an image."""
if arr.ndim == 2:
format, cmap = 'png', mpl.cm.gray
elif arr.ndim == 3:
format, cmap = 'jpg', None
else:
raise ValueError("Only 2- or 3-d arrays can be displayed as images.")
# Don't let matplotlib autoscale the color range so we can control overall luminosity
vmax = 255 if arr.dtype == 'uint8' else 1.0
with BytesIO() as buffer:
mpl.image.imsave(buffer, arr, format=format, cmap=cmap, vmin=0, vmax=vmax)
out = buffer.getvalue()
return Image(out)
arr2img(i)
Now, let's create a simple "image editor" function, that allows us to blur the image or change its color balance:
def edit_image(image, sigma=0.1, R=1.0, G=1.0, B=1.0):
new_image = filters.gaussian(image, sigma=sigma, channel_axis=-1)
new_image[:,:,0] = R*new_image[:,:,0]
new_image[:,:,1] = G*new_image[:,:,1]
new_image[:,:,2] = B*new_image[:,:,2]
return arr2img(new_image)
We can call this function manually and get a new image. For example, let's do a little blurring and remove all the red from the image:
edit_image(i, sigma=5, R=0.1)
But it's a lot easier to explore what this function does by controlling each parameter interactively and getting immediate visual feedback. IPython's ipywidgets
package lets us do that with a minimal amount of code:
lims = (0.0,1.0,0.01)
interact(edit_image, image=fixed(i), sigma=(0.0,10.0,0.1), R=lims, G=lims, B=lims);
interactive(children=(FloatSlider(value=0.1, description='sigma', max=10.0), FloatSlider(value=1.0, descriptio…
The coffee cup isn't the only image that ships with scikit-image, the data
module has others. Let's make a quick interactive explorer for this:
def choose_img(name):
# Let's store the result in the global `img` that we can then use in our image editor below
global img
img = getattr(data, name)()
return arr2img(img)
# Skip 'load' and 'lena', two functions that don't actually return images
interact(choose_img, name=sorted(set(data.__all__)-{'lena', 'load'}));
interactive(children=(Dropdown(description='name', options=('astronaut', 'binary_blobs', 'brain', 'brick', 'ca…
And now, let's update our editor to cope correctly with grayscale and color images, since some images in the scikit-image collection are grayscale. For these, we ignore the red (R) and blue (B) channels, and treat 'G' as 'Grayscale':
lims = (0.0, 1.0, 0.01)
def edit_image(image, sigma, R, G, B):
new_image = filters.gaussian(image, sigma=sigma, channel_axis=-1)
if new_image.ndim == 3:
new_image[:,:,0] = R*new_image[:,:,0]
new_image[:,:,1] = G*new_image[:,:,1]
new_image[:,:,2] = B*new_image[:,:,2]
else:
new_image = G*new_image
return arr2img(new_image)
interact(edit_image, image=fixed(img), sigma=(0.0, 10.0, 0.1),
R=lims, G=lims, B=lims);
interactive(children=(FloatSlider(value=5.0, description='sigma', max=10.0), FloatSlider(value=0.5, descriptio…