技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
scikit-image【数据与研究】A collection of algorithms for image processing in Python. Built on NumPy, SciPy, and Cython. It focuses on scientific image analysis including segmentation, geometric transformations, color space manipulation, analysis, and filtering.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
scikit-image treats images as NumPy arrays. It provides a comprehensive suite of algorithms for filtering, feature detection, and object measurement, making it the standard for research-grade image analysis.
Official docs: https://scikit-image.org/
User Guide: https://scikit-image.org/docs/stable/user_guide.html
Search patterns: skimage.filters, skimage.segmentation, skimage.feature, skimage.morphology
A grayscale image is a 2D array (M, N). A color image is a 3D array (M, N, 3). A multichannel 3D volume is (P, M, N, C).
The origin (0, 0) is at the top-left corner. Coordinates are always represented as (row, column).
scikit-image handles various dtypes with specific ranges:
uint8: 0 to 255uint16: 0 to 65535float: -1 to 1 or 0 to 1pip install scikit-image
import numpy as np
import matplotlib.pyplot as plt
from skimage import io, filters, segmentation, feature, measure, morphology, color, util
from skimage import io, filters, color
# Load image
image = io.imread('data.png')
# Convert to grayscale if needed
gray_image = color.rgb2gray(image)
# Apply a filter (e.g., Gaussian blur)
blurred = filters.gaussian(gray_image, sigma=2.0)
# Display
io.imshow(blurred)
io.show()
util.img_as_float, util.img_as_ubyte to handle rescaling automatically when changing dtypes.image.shape and image.dtype before processing.measure.label is the standard way to identify connected components.image / 255.0; use util.img_as_float.# ❌ BAD: Manual dtype conversion (doesn't handle scaling)
image_float = image.astype(float) # Values stay 0-255 but as floats
# ✅ GOOD: Safe utility conversion
from skimage import util
image_float = util.img_as_float(image) # Values scaled to 0.0-1.0
# ❌ BAD: Looping over pixels
for r in range(rows):
for c in range(cols):
if image[r, c] > 128:
image[r, c] = 255
# ✅ GOOD: Vectorized thresholding
image[image > 0.5] = 1.0
# ❌ BAD: Plotting multiple images without a shared axis
plt.imshow(img1); plt.show()
plt.imshow(img2); plt.show()
# ✅ GOOD: Using subplots for comparison
fig, ax = plt.subplots(1, 2)
ax[0].imshow(img1, cmap='gray')
ax[1].imshow(img2, cmap='gray')
from skimage import filters
# Edge detection
edges_sobel = filters.sobel(image)
edges_canny = feature.canny(image, sigma=3)
# Denoising
denoised = filters.median(image, morphology.disk(3))
# Thresholding (Automatic)
val = filters.threshold_otsu(image)
binary = image > val
from skimage import morphology
# Binary morphology
struct_element = morphology.disk(5)
eroded = morphology.erosion(binary, struct_element)
dilated = morphology.dilation(binary, struct_element)
opened = morphology.opening(binary, struct_element) # Erosion then Dilation
# Skeletonization (Reducing objects to 1-pixel width)
skeleton = morphology.skeletonize(binary)
# Removing small objects
clean_binary = morphology.remove_small_objects(binary, min_size=64)
from skimage import segmentation, color
# Watershed Segmentation (Marker-based)
from scipy import ndimage as ndi
distance = ndi.distance_transform_edt(binary)
coords = feature.peak_local_max(distance, footprint=np.ones((3, 3)), labels=binary)
mask = np.zeros(distance.shape, dtype=bool)
mask[tuple(coords.T)] = True
markers, _ = ndi.label(mask)
labels = segmentation.watershed(-distance, markers, mask=binary)
# SLIC (Superpixels)
segments = segmentation.slic(image, n_segments=100, compactness=10)
out = color.label2rgb(segments, image, kind='avg')
from skimage import feature
# Local Binary Patterns (Texture analysis)
lbp = feature.local_binary_pattern(image, P=8, R=1)
# Corner detection (Harris)
coords = feature.corner_peaks(feature.corner_harris(image), min_distance=5)
# Blob detection (Difference of Gaussian)
blobs = feature.blob_dog(image, max_sigma=30, threshold=.1)
# blobs: array of [row, col, sigma]
from skimage import measure
# Label connected components
labels = measure.label(binary)
# Calculate properties
props = measure.regionprops(labels)
for prop in props:
print(f"Label: {prop.label}")
print(f"Area: {prop.area}")
print(f"Centroid: {prop.centroid}")
print(f"Eccentricity: {prop.eccentricity}")
# Find contours
contours = measure.find_contours(binary, 0.8)
def count_particles(image_path):
# 1. Load and grayscale
img = color.rgb2gray(io.imread(image_path))
# 2. Denoise
img_denoised = filters.gaussian(img, sigma=1)
# 3. Threshold
thresh = filters.threshold_otsu(img_denoised)
binary = img_denoised < thresh # Assuming dark particles
# 4. Morphological cleaning
binary = morphology.remove_small_objects(binary, 50)
binary = morphology.closing(binary, morphology.disk(3))
# 5. Label and measure
labels = measure.label(binary)
return measure.regionprops(labels), labels
# properties, label_img = count_particles('samples.tif')
def segment_nuclei(dna_image):
"""Identify nuclei in a DAPI/Hoechst stained image."""
# Local thresholding for uneven illumination
local_thresh = filters.threshold_local(dna_image, block_size=35)
binary = dna_image > local_thresh
# Fill holes
filled = ndi.binary_fill_holes(binary)
# Separate touching nuclei
distance = ndi.distance_transform_edt(filled)
local_maxi = feature.peak_local_max(distance, indices=False, footprint=np.ones((15, 15)), labels=filled)
markers = measure.label(local_maxi)
labels = segmentation.watershed(-distance, markers, mask=filled)
return labels
def detect_change(img_before, img_after, threshold=0.1):
# Ensure float
im1 = util.img_as_float(color.rgb2gray(img_before))
im2 = util.img_as_float(color.rgb2gray(img_after))
# Difference
diff = np.abs(im1 - im2)
# Filter noise in difference
diff_clean = filters.median(diff, morphology.disk(2))
return diff_clean > threshold
from skimage.util import view_as_windows
# Create overlapping patches without copying data (using strides)
# Fast for local neighborhood calculations
patches = view_as_windows(image, (64, 64), step=32)
# Shape: (n_patches_r, n_patches_c, 64, 64)
# Many skimage functions are already Cython-optimized.
# For batch processing, use standard joblib or multiprocessing.
from joblib import Parallel, delayed
results = Parallel(n_jobs=-1)(delayed(filters.gaussian)(img) for img in image_list)
# ❌ Problem: Filters failing on RGB images
# result = filters.median(rgb_image) # Raises error
# ✅ Solution: Filter per channel or convert
# filters.median(color.rgb2gray(rgb_image))
# OR apply to each channel
# result = np.stack([filters.median(rgb_image[..., i]) for i in range(3)], axis=-1)
# ❌ Problem: Large noisy images produce millions of 1-pixel labels
# ✅ Solution: Clear small noise before labeling
binary = morphology.remove_small_objects(binary, min_size=10)
labels = measure.label(binary)
# ❌ Problem: Points appear swapped on plot
# Matplotlib: plt.plot(x, y)
# scikit-image: image[row, col]
# ✅ Solution: Explicitly swap for visualization
coords = feature.peak_local_max(img) # Returns [row, col]
plt.plot(coords[:, 1], coords[:, 0], 'r.') # Plot as [x, y]
scikit-image provides the mathematical rigor needed for scientific discovery. By building on the NumPy ecosystem, it allows for a seamless workflow from raw sensor data to quantifiable insights.