Online Scikit-Image Compiler

Run scikit-image code in your browser. Load sample images, filter and threshold them, count objects, resize and rotate them.

Python
# Basic image processing using Scikit-Image

from skimage import io, color, filters
import requests
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
import numpy as np

# Fetch the image from the URL using requests
url = 'https://images.unsplash.com/photo-1444464666168-49d633b86797?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8YmlyZHxlbnwwfHwwfHx8Mg%3D%3D'
response = requests.get(url)
image = Image.open(BytesIO(response.content))

# Convert the image to a NumPy array
image_np = np.array(image)

# Convert the image to grayscale
gray_image = color.rgb2gray(image_np)

# Apply a Gaussian filter to the image
blurred_image = filters.gaussian(gray_image, sigma=1)

# Detect edges using the Sobel filter
edges = filters.sobel(blurred_image)

# Display the original and processed images
plt.figure(figsize=(10, 7))

plt.subplot(2, 2, 1)
plt.title('Original Image')
plt.imshow(image_np)
plt.axis('off')

plt.subplot(2, 2, 2)
plt.title('Grayscale Image')
plt.imshow(gray_image, cmap='gray')
plt.axis('off')

plt.subplot(2, 2, 3)
plt.title('Blurred Image')
plt.imshow(blurred_image, cmap='gray')
plt.axis('off')

plt.subplot(2, 2, 4)
plt.title('Edge Detection')
plt.imshow(edges, cmap='gray')
plt.axis('off')

plt.tight_layout()
plt.show()

scikit-image, imported as skimage, is a collection of image processing algorithms for Python. An image is a plain NumPy array, and the functions sit in submodules such as filters, transform, measure and segmentation. People use it to clean up photos, find edges and measure objects such as cells or particles in scientific images. This page is an online scikit-image compiler: the code runs in your browser, so you can try it without installing anything. Run the example first, then paste any snippet below into a new cell to try it.

What the example does

requests.get() downloads a photo of a kingfisher, Pillow's Image.open() reads the JPEG bytes, and np.array() turns the picture into a (534, 800, 3) array of uint8 values: 534 rows, 800 columns and three colour channels. color.rgb2gray() combines the channels into one 2-D array of floats between 0 and 1. filters.gaussian(gray_image, sigma=1) blurs it, and a larger sigma blurs more. filters.sobel() measures how fast the brightness changes at each pixel, so edges come out bright and flat areas black. matplotlib shows the four images in a 2 by 2 grid, with cmap='gray' for the single-channel ones.

Load a sample image

skimage.data has test images that ship with the package, so they load without a download. Greyscale images are 2-D arrays, and colour images add a third axis for red, green and blue:

from skimage import data
import matplotlib.pyplot as plt

images = {"camera": data.camera(), "coins": data.coins(), "astronaut": data.astronaut()}

fig, axes = plt.subplots(1, 3, figsize=(10, 4))
for ax, (name, img) in zip(axes, images.items()):
    print(name, img.shape, img.dtype)
    ax.imshow(img, cmap="gray")    # cmap only affects 2-D images
    ax.set_title(name)
    ax.axis("off")
plt.show()

Count objects in an image

A single threshold fails on data.coins() because the light is uneven: with filters.threshold_otsu(), the bright background at the top counts as one more object. Instead, mark the pixels that are surely background or surely coin, and let segmentation.watershed() grow those markers across the edge map from filters.sobel(). measure.label() then gives each coin its own number:

import numpy as np
from scipy import ndimage as ndi
from skimage import data, filters, measure, morphology, segmentation, color
import matplotlib.pyplot as plt

coins = data.coins()
markers = np.zeros_like(coins)
markers[coins < 30] = 1                  # surely background
markers[coins > 150] = 2                 # surely coin
regions = segmentation.watershed(filters.sobel(coins), markers)
mask = ndi.binary_fill_holes(regions == 2)
mask = morphology.remove_small_objects(mask, min_size=100)
labels = measure.label(mask)             # number each separate region
areas = [int(r.area) for r in measure.regionprops(labels)]
print(labels.max(), "coins, from", min(areas), "to", max(areas), "pixels")

plt.imshow(color.label2rgb(labels, image=coins, bg_label=0))
plt.axis("off")
plt.show()

measure.regionprops() also gives each region's centroid, bounding box and perimeter.

Resize, rescale and rotate

transform.resize() takes the output shape as (rows, columns), and transform.rescale() takes a factor. transform.rotate() turns the image counterclockwise, and resize=True enlarges the frame so no corner is cut off. On a uint8 image, all three return floats between 0 and 1, and util.img_as_ubyte() converts back:

from skimage import data, transform, util
import matplotlib.pyplot as plt

camera = data.camera()                                # (512, 512) uint8
small = transform.resize(camera, (128, 256))          # (rows, columns)
half = transform.rescale(camera, 0.5)
turned = transform.rotate(camera, 30, resize=True)    # grow the frame to fit
print(small.shape, half.shape, turned.shape)
print(small.dtype, small.min().round(3), small.max().round(3))

back = util.img_as_ubyte(small)                       # 0-1 floats to 0-255
print(back.dtype, back.min(), back.max())

plt.imshow(turned, cmap="gray")
plt.axis("off")
plt.show()

Improve contrast

Most of data.moon() is a narrow band of grey: 96% of its pixels lie between 78 and 129. exposure.rescale_intensity() stretches that band to the full 0 to 255 range, and exposure.equalize_hist() spreads the values out across the whole range:

import numpy as np
from skimage import data, exposure
import matplotlib.pyplot as plt

moon = data.moon()
low, high = np.percentile(moon, (2, 98))
print("2nd and 98th percentiles:", low, high)
stretched = exposure.rescale_intensity(moon, in_range=(low, high))
equalized = exposure.equalize_hist(moon)

fig, axes = plt.subplots(1, 3, figsize=(10, 4))
for ax, img, title in zip(axes, [moon, stretched, equalized], ["original", "stretched", "equalized"]):
    ax.imshow(img, cmap="gray")
    ax.set_title(title)
    ax.axis("off")
plt.show()

Good to know

  • Many functions, including rgb2gray(), gaussian(), resize() and rotate(), return float64 images between 0 and 1, even for uint8 input. Check image.dtype before you compare pixels with a threshold such as 128.
  • Shapes and coordinates are (row, column), while OpenCV's cv2.resize() takes (width, height).
  • Some skimage.data images, such as cells3d(), kidney() and brain(), are not in the package. They need the optional pooch package to download their files, and raise ModuleNotFoundError here.