杰瑞科技汇

Python PIL与skimage图像处理如何选?

Of course! This is a very common question for anyone working with images in Python. Pillow (PIL) and scikit-image (skimage) are two of the most popular libraries for image processing, but they have different philosophies and strengths.

Python PIL与skimage图像处理如何选?-图1
(图片来源网络,侵删)

Here’s a detailed comparison to help you understand when to use which one.


High-Level Summary

Feature Pillow (PIL Fork) scikit-image (skimage)
Primary Goal Image I/O and basic manipulation. It's the go-to library for opening, saving, resizing, cropping, and converting image formats. Scientific image analysis. It's designed for complex tasks like filtering, segmentation, feature detection, and measurement.
Analogy A digital photo editor (like a simplified Photoshop). A microscope with analysis tools (like ImageJ or CellProfiler).
Core Strength Simplicity, wide format support, and a user-friendly API for common tasks. A rich collection of algorithms built on NumPy for scientific and analytical tasks.
Data Structure PIL.Image object (wraps a NumPy array internally). NumPy ndarray. This is the fundamental data type.
Dependencies Minimal (only setuptools, Pillow itself). Heavier. Relies heavily on NumPy, SciPy, scikit-learn, and sometimes Matplotlib.
Best For - Loading/saving images
- Resizing, cropping, rotating
- Converting color spaces (RGB, RGBA, L)
- Creating thumbnails
- Applying filters (Gaussian, Sobel, etc.)
- Image segmentation (thresholding, watershed)
- Feature detection (SIFT, ORB)
- Measuring properties (area, perimeter) of objects

Detailed Breakdown

Philosophy and Design

  • Pillow (PIL):

    • Goal: To be a friendly, powerful, and easy-to-use library for "casual" image manipulation. It's the spiritual successor to the original Python Imaging Library (PIL).
    • Design: It provides a high-level Image object that abstracts away the underlying pixel data. You call methods on the image object (e.g., image.resize(), image.rotate()). This makes common operations very intuitive.
  • scikit-image (skimage):

    • Goal: To provide a collection of algorithms for image processing in a scientific context. It's part of the SciPy ecosystem.
    • Design: It's built around NumPy arrays. Every image you work with is essentially a NumPy array. This means you can use NumPy's powerful indexing, slicing, and mathematical functions directly on the image data. All functions are standalone functions that take a NumPy array as input and return a NumPy array as output (e.g., skimage.filters.gaussian(image_array)).

Data Structures: The Key Difference

This is the most important distinction to understand.

Python PIL与skimage图像处理如何选?-图2
(图片来源网络,侵删)

Pillow (PIL.Image)

A Pillow image is an object that contains a pixel mode ('RGB', 'L' for grayscale, 'RGBA', etc.) and a size. You can easily convert it to a NumPy array.

from PIL import Image
# Load an image
pil_image = Image.open('my_image.jpg')
# Get basic info
print(f"Mode: {pil_image.mode}, Size: {pil_image.size}")
# Convert to a NumPy array
import numpy as np
np_array = np.array(pil_image)
print(f"NumPy array shape: {np_array.shape}")
print(f"NumPy array dtype: {np_array.dtype}")

scikit-image (ndarray)

With scikit-image, you almost always start with a NumPy array. You often get this array by loading an image with another library (like Pillow!) or by creating it from scratch.

from skimage import io
import numpy as np
# Option 1: Load directly with skimage.io (uses Pillow under the hood)
skimage_array = io.imread('my_image.jpg')
print(f"skimage array shape: {skimage_array.shape}")
# Option 2: Load with Pillow and convert (very common workflow)
pil_image = Image.open('my_image.jpg')
skimage_array_from_pil = np.array(pil_image)

Feature Comparison with Code Examples

Let's see how you'd perform common tasks with both libraries.

Task 1: Loading and Saving an Image

  • Pillow: Very straightforward.

    Python PIL与skimage图像处理如何选?-图3
    (图片来源网络,侵删)
    from PIL import Image
    # Load
    img = Image.open('input.jpg')
    # Save in a different format
    img.save('output.png')
  • scikit-image: Also simple, but the function names are more explicit.

    from skimage import io
    # Load
    img = io.imread('input.jpg')
    # Save
    io.imsave('output.png', img)

Verdict: Both are easy. Pillow feels slightly more "native" to the object-oriented world.

Task 2: Resizing an Image

  • Pillow: The Image.resize() method is very intuitive.

    from PIL import Image
    img = Image.open('input.jpg')
    new_size = (300, 200)
    resized_img = img.resize(new_size, Image.LANCZOS) # LANCZOS is a high-quality resampling filter
    resized_img.save('resized_pil.jpg')
  • scikit-image: You use the transform.resize function.

    from skimage import io, transform
    import numpy as np
    img = io.imread('input.jpg')
    # Note: skimage uses (height, width) order
    output_shape = (200, 300)
    resized_img = transform.resize(img, output_shape, anti_aliasing=True)
    io.imsave('resized_skimage.jpg', resized_img)

Verdict: Pillow's method is arguably more readable for a simple resize. Skimage's function is more explicit about its parameters and is part of a larger transform module.

Task 3: Applying a Filter (e.g., Gaussian Blur)

This is where the libraries diverge significantly.

  • Pillow: Pillow has a few built-in filters like ImageFilter.

    from PIL import Image, ImageFilter
    img = Image.open('input.jpg')
    # Apply a predefined Gaussian filter
    blurred_img = img.filter(ImageFilter.GaussianBlur(radius=2))
    blurred_img.save('blurred_pil.jpg')
  • scikit-image: This is a core strength. You have access to a vast library of scientific filters.

    from skimage import io, filters
    import matplotlib.pyplot as plt # Often used for displaying results
    img = io.imread('input.jpg')
    # Apply a Gaussian filter with precise control over sigma
    blurred_img = filters.gaussian(img, sigma=2, channel_axis=-1)
    # Display the result
    # plt.imshow(blurred_img)
    # plt.show()
    io.imsave('blurred_skimage.jpg', blurred_img)

Verdict: For scientific filtering, scikit-image is the clear winner. Pillow's filters are basic conveniences.

Task 4: Converting to Grayscale

  • Pillow: Simple method call.

    from PIL import Image
    img = Image.open('input.jpg')
    gray_img = img.convert('L') # 'L' mode is 8-bit grayscale
    gray_img.save('gray_pil.jpg')
  • scikit-image: A dedicated function for this.

    from skimage import io, color
    img = io.imread('input.jpg')
    gray_img = color.rgb2gray(img) # Explicit function for RGB to grayscale
    # Note: rgb2gray returns a float array with values 0-1
    # You might want to convert it back to 0-255 uint8 to save
    gray_img_8bit = (gray_img * 255).astype(np.uint8)
    io.imsave('gray_skimage.jpg', gray_img_8bit)

Verdict: Pillow is more direct. Skimage is more explicit about the color space conversion and data types.


The Best of Both Worlds: A Common Workflow

In practice, many developers use both libraries together, leveraging the strengths of each.

Typical Workflow:

  1. Load and Prepare with Pillow: Use Pillow to load the image from a file, perform simple tasks like cropping or converting to a specific color mode.
  2. Convert to NumPy Array: Convert the Pillow Image object into a NumPy array. This is the bridge to the scientific world.
  3. Analyze with scikit-image: Pass the NumPy array to scikit-image functions for complex analysis, filtering, segmentation, etc.
  4. Visualize or Save: Use Matplotlib to display the results, or convert the final NumPy array back to a Pillow Image object to save it to a file.
from PIL import Image
from skimage import filters, io
import numpy as np
# 1. Load and prepare with Pillow
pil_img = Image.open('my_photo.jpg').convert('RGB') # Ensure it's RGB
# 2. Convert to a NumPy array (the bridge)
img_array = np.array(pil_img)
# 3. Analyze with scikit-image
# Let's detect edges using the Sobel filter
# Note: skimage.filters.sobel works on grayscale images
gray_img = filters.gaussian(img_array, channel_axis=-1) # Smooth first
edges = filters.sobel(gray_img)
# 4. Visualize the result
# Matplotlib is the standard for plotting in the SciPy ecosystem
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
axes[0].imshow(pil_img)
axes[0].set_title('Original Image')
axes[1].imshow(edges, cmap='gray')
axes[1].set_title('Edge Detection (skimage)')
for ax in axes:
    ax.axis('off')
plt.show()
# To save the result, convert back to a Pillow Image
# The edges array is float, so we need to scale and convert to uint8
edges_uint8 = (edges / edges.max() * 255).astype(np.uint8)
result_pil_image = Image.fromarray(edges_uint8)
result_pil_image.save('edges_result.png')

Final Recommendation

  • Use Pillow when:

    • Your primary task is I/O (reading from or writing to disk).
    • You need to perform basic geometric transformations (resize, crop, rotate).
    • You need to work with a wide variety of image file formats.
    • You want a simple, high-level API for common tasks.
  • Use scikit-image when:

    • You are performing scientific or analytical image processing.
    • You need to apply advanced filters or morphological operations.
    • Your goal is segmentation, object detection, or measurement.
    • You are already working within the NumPy/SciPy ecosystem.
  • Use both together when:

    You are doing almost any serious image analysis project. Use Pillow for the "plumbing" (loading/saving) and scikit-image for the "heavy lifting" (analysis).

分享:
扫描分享到社交APP
上一篇
下一篇