# 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.
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.
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()
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.
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()
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()
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.cv2.resize() takes (width, height).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.