Skip to content

图像处理

SciPy 的 scipy.ndimage 模块提供了强大的多维图像处理功能,包括滤波、形态学操作、几何变换、特征检测等。虽然对于复杂的计算机视觉任务,专门的库如 OpenCV 可能更合适,但 scipy.ndimage 为基础的图像处理任务提供了简洁而高效的解决方案。本章将详细介绍如何使用 SciPy 进行各种图像处理操作。

scipy.ndimage 模块概述

scipy.ndimage 模块包含以下主要功能:

  • 图像滤波和卷积
  • 形态学操作
  • 几何变换和插值
  • 特征检测和测量
  • 分割和标记
  • 距离变换
python
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
from scipy.ndimage import (
    gaussian_filter, median_filter, uniform_filter,
    sobel, laplace, binary_erosion, binary_dilation,
    rotate, zoom, shift, affine_transform,
    label, center_of_mass, distance_transform_edt
)
import warnings
warnings.filterwarnings('ignore')

# 设置绘图样式
plt.style.use('seaborn-v0_8')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 查看 ndimage 模块的主要功能
print("scipy.ndimage 主要功能:")
functions = [attr for attr in dir(ndimage) if not attr.startswith('_')]
print(f"总共 {len(functions)} 个函数和类")
print("部分函数:", functions[:20])

图像创建和基本操作

1. 创建测试图像

python
# 创建各种测试图像
def create_test_images():
    """创建用于测试的图像"""
    # 1. 简单几何图形
    size = 200
    
    # 圆形
    y, x = np.ogrid[:size, :size]
    center_y, center_x = size // 2, size // 2
    radius = 60
    circle = (x - center_x)**2 + (y - center_y)**2 <= radius**2
    
    # 矩形
    rectangle = np.zeros((size, size), dtype=bool)
    rectangle[60:140, 80:120] = True
    
    # 棋盘图案
    checkerboard = np.zeros((size, size))
    square_size = 25
    for i in range(0, size, square_size):
        for j in range(0, size, square_size):
            if (i // square_size + j // square_size) % 2 == 0:
                checkerboard[i:i+square_size, j:j+square_size] = 1
    
    # 2. 合成图像
    # 添加噪声的圆形
    noisy_circle = circle.astype(float)
    noise = np.random.normal(0, 0.1, (size, size))
    noisy_circle = noisy_circle + noise
    noisy_circle = np.clip(noisy_circle, 0, 1)
    
    # 渐变图像
    gradient = np.linspace(0, 1, size)
    gradient_image = np.tile(gradient, (size, 1))
    
    # 3. 复杂图像
    # 多个对象
    multi_objects = np.zeros((size, size))
    
    # 添加多个圆形
    centers = [(50, 50), (150, 50), (100, 150), (50, 150), (150, 150)]
    radii = [20, 25, 30, 15, 22]
    
    for (cy, cx), r in zip(centers, radii):
        y, x = np.ogrid[:size, :size]
        mask = (x - cx)**2 + (y - cy)**2 <= r**2
        multi_objects[mask] = 1
    
    return {
        'circle': circle.astype(float),
        'rectangle': rectangle.astype(float),
        'checkerboard': checkerboard,
        'noisy_circle': noisy_circle,
        'gradient': gradient_image,
        'multi_objects': multi_objects
    }

# 创建测试图像
test_images = create_test_images()

# 可视化测试图像
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

image_names = list(test_images.keys())
for i, (name, image) in enumerate(test_images.items()):
    axes[i].imshow(image, cmap='gray')
    axes[i].set_title(f'{name.replace("_", " ").title()}')
    axes[i].axis('off')

plt.tight_layout()
plt.show()

print("\n测试图像信息:")
for name, image in test_images.items():
    print(f"{name:15s}: 形状={image.shape}, 数据类型={image.dtype}, 范围=[{image.min():.3f}, {image.max():.3f}]")

2. 图像基本属性和操作

python
# 选择一个图像进行详细分析
test_image = test_images['noisy_circle']

# 基本属性
print("图像基本属性:")
print(f"形状: {test_image.shape}")
print(f"数据类型: {test_image.dtype}")
print(f"像素值范围: [{test_image.min():.3f}, {test_image.max():.3f}]")
print(f"平均值: {test_image.mean():.3f}")
print(f"标准差: {test_image.std():.3f}")
print(f"总像素数: {test_image.size}")

# 直方图分析
fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# 原始图像
axes[0].imshow(test_image, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 直方图
axes[1].hist(test_image.flatten(), bins=50, alpha=0.7, edgecolor='black')
axes[1].set_title('像素值直方图')
axes[1].set_xlabel('像素值')
axes[1].set_ylabel('频次')
axes[1].grid(True, alpha=0.3)

# 累积分布
sorted_pixels = np.sort(test_image.flatten())
cumulative = np.arange(1, len(sorted_pixels) + 1) / len(sorted_pixels)
axes[2].plot(sorted_pixels, cumulative, 'b-', linewidth=2)
axes[2].set_title('累积分布函数')
axes[2].set_xlabel('像素值')
axes[2].set_ylabel('累积概率')
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

图像滤波

1. 线性滤波

python
# 使用含噪声的图像进行滤波演示
noisy_image = test_images['noisy_circle']

# 不同类型的线性滤波器
filters = {
    '高斯滤波': lambda img: gaussian_filter(img, sigma=2),
    '均值滤波': lambda img: uniform_filter(img, size=5),
    '高斯滤波(大核)': lambda img: gaussian_filter(img, sigma=4),
    '均值滤波(大核)': lambda img: uniform_filter(img, size=9)
}

# 应用滤波器
filtered_results = {}
for name, filter_func in filters.items():
    filtered_results[name] = filter_func(noisy_image)

# 可视化滤波结果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 原始图像
axes[0].imshow(noisy_image, cmap='gray')
axes[0].set_title('原始含噪图像')
axes[0].axis('off')

# 滤波结果
for i, (name, filtered_img) in enumerate(filtered_results.items(), 1):
    axes[i].imshow(filtered_img, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

# 误差分析
original_clean = test_images['circle']
axes[5].clear()

# 计算各滤波器的MSE
mse_values = []
filter_names = list(filtered_results.keys())

for name, filtered_img in filtered_results.items():
    mse = np.mean((original_clean - filtered_img)**2)
    mse_values.append(mse)

axes[5].bar(range(len(mse_values)), mse_values, color=['blue', 'green', 'red', 'orange'])
axes[5].set_title('滤波器性能比较 (MSE)')
axes[5].set_xlabel('滤波器')
axes[5].set_ylabel('均方误差')
axes[5].set_xticks(range(len(filter_names)))
axes[5].set_xticklabels(filter_names, rotation=45)
axes[5].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\n滤波器性能分析:")
for name, mse in zip(filter_names, mse_values):
    print(f"{name:15s}: MSE = {mse:.6f}")

# 最佳滤波器
best_filter = filter_names[np.argmin(mse_values)]
print(f"\n最佳滤波器: {best_filter} (最小MSE)")

2. 非线性滤波

python
# 非线性滤波器
nonlinear_filters = {
    '中值滤波': lambda img: median_filter(img, size=5),
    '最大值滤波': lambda img: ndimage.maximum_filter(img, size=5),
    '最小值滤波': lambda img: ndimage.minimum_filter(img, size=5),
    '百分位滤波': lambda img: ndimage.percentile_filter(img, percentile=50, size=5)
}

# 创建包含椒盐噪声的图像
salt_pepper_image = test_images['circle'].copy()
# 添加椒盐噪声
noise_mask = np.random.random(salt_pepper_image.shape) < 0.05
salt_pepper_image[noise_mask] = np.random.choice([0, 1], size=np.sum(noise_mask))

# 应用非线性滤波器
nonlinear_results = {}
for name, filter_func in nonlinear_filters.items():
    nonlinear_results[name] = filter_func(salt_pepper_image)

# 可视化非线性滤波结果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 含椒盐噪声的图像
axes[0].imshow(salt_pepper_image, cmap='gray')
axes[0].set_title('椒盐噪声图像')
axes[0].axis('off')

# 非线性滤波结果
for i, (name, filtered_img) in enumerate(nonlinear_results.items(), 1):
    axes[i].imshow(filtered_img, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

# 性能比较
axes[5].clear()
original_clean = test_images['circle']

# 计算PSNR
def calculate_psnr(original, processed):
    mse = np.mean((original - processed)**2)
    if mse == 0:
        return float('inf')
    max_pixel = 1.0
    psnr = 20 * np.log10(max_pixel / np.sqrt(mse))
    return psnr

psnr_values = []
nonlinear_names = list(nonlinear_results.keys())

for name, filtered_img in nonlinear_results.items():
    psnr = calculate_psnr(original_clean, filtered_img)
    psnr_values.append(psnr)

axes[5].bar(range(len(psnr_values)), psnr_values, color=['purple', 'brown', 'pink', 'gray'])
axes[5].set_title('非线性滤波器性能比较 (PSNR)')
axes[5].set_xlabel('滤波器')
axes[5].set_ylabel('PSNR (dB)')
axes[5].set_xticks(range(len(nonlinear_names)))
axes[5].set_xticklabels(nonlinear_names, rotation=45)
axes[5].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\n非线性滤波器性能分析:")
for name, psnr in zip(nonlinear_names, psnr_values):
    print(f"{name:15s}: PSNR = {psnr:.2f} dB")

# 最佳非线性滤波器
best_nonlinear = nonlinear_names[np.argmax(psnr_values)]
print(f"\n最佳非线性滤波器: {best_nonlinear} (最高PSNR)")

边缘检测

1. 梯度算子

python
# 使用棋盘图像进行边缘检测
edge_test_image = test_images['checkerboard']

# 不同的边缘检测算子
edge_operators = {
    'Sobel X': lambda img: sobel(img, axis=1),
    'Sobel Y': lambda img: sobel(img, axis=0),
    'Sobel 幅度': lambda img: np.sqrt(sobel(img, axis=0)**2 + sobel(img, axis=1)**2),
    'Laplacian': lambda img: laplace(img),
    'Prewitt X': lambda img: ndimage.prewitt(img, axis=1),
    'Prewitt Y': lambda img: ndimage.prewitt(img, axis=0)
}

# 应用边缘检测算子
edge_results = {}
for name, operator in edge_operators.items():
    edge_results[name] = operator(edge_test_image)

# 可视化边缘检测结果
fig, axes = plt.subplots(3, 3, figsize=(15, 15))
axes = axes.flatten()

# 原始图像
axes[0].imshow(edge_test_image, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 边缘检测结果
for i, (name, edge_img) in enumerate(edge_results.items(), 1):
    axes[i].imshow(edge_img, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

# 组合边缘检测
sobel_x = edge_results['Sobel X']
sobel_y = edge_results['Sobel Y']
sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
sobel_direction = np.arctan2(sobel_y, sobel_x)

axes[7].imshow(sobel_magnitude, cmap='gray')
axes[7].set_title('Sobel 幅度')
axes[7].axis('off')

axes[8].imshow(sobel_direction, cmap='hsv')
axes[8].set_title('Sobel 方向')
axes[8].axis('off')

plt.tight_layout()
plt.show()

# 边缘强度统计
print("\n边缘检测统计:")
for name, edge_img in edge_results.items():
    edge_strength = np.std(edge_img)
    max_edge = np.max(np.abs(edge_img))
    print(f"{name:15s}: 边缘强度标准差={edge_strength:.3f}, 最大边缘值={max_edge:.3f}")

2. 高级边缘检测

python
# Canny边缘检测的简化实现
def simple_canny_edge_detection(image, low_threshold=0.1, high_threshold=0.2):
    """简化的Canny边缘检测"""
    # 1. 高斯滤波降噪
    smoothed = gaussian_filter(image, sigma=1)
    
    # 2. 计算梯度
    grad_x = sobel(smoothed, axis=1)
    grad_y = sobel(smoothed, axis=0)
    gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
    gradient_direction = np.arctan2(grad_y, grad_x)
    
    # 3. 非最大值抑制(简化版)
    suppressed = gradient_magnitude.copy()
    
    # 4. 双阈值检测
    strong_edges = gradient_magnitude > high_threshold
    weak_edges = (gradient_magnitude > low_threshold) & (gradient_magnitude <= high_threshold)
    
    # 5. 边缘连接(简化版)
    edges = strong_edges.astype(float)
    edges[weak_edges] = 0.5
    
    return edges, gradient_magnitude, gradient_direction

# 应用Canny边缘检测
test_image_for_canny = test_images['multi_objects']
canny_edges, canny_magnitude, canny_direction = simple_canny_edge_detection(test_image_for_canny)

# 比较不同阈值的效果
thresholds = [(0.05, 0.1), (0.1, 0.2), (0.15, 0.3), (0.2, 0.4)]
canny_results = []

for low, high in thresholds:
    edges, _, _ = simple_canny_edge_detection(test_image_for_canny, low, high)
    canny_results.append((f'阈值({low}, {high})', edges))

# 可视化Canny边缘检测结果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 原始图像
axes[0].imshow(test_image_for_canny, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 梯度幅度
axes[1].imshow(canny_magnitude, cmap='gray')
axes[1].set_title('梯度幅度')
axes[1].axis('off')

# 不同阈值的结果
for i, (name, edges) in enumerate(canny_results[:4], 2):
    axes[i].imshow(edges, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

plt.tight_layout()
plt.show()

print("\nCanny边缘检测分析:")
for name, edges in canny_results:
    edge_pixels = np.sum(edges > 0.5)
    total_pixels = edges.size
    edge_ratio = edge_pixels / total_pixels
    print(f"{name:15s}: 边缘像素={edge_pixels}, 边缘比例={edge_ratio:.3f}")

形态学操作

1. 基本形态学操作

python
# 创建二值图像进行形态学操作
binary_image = test_images['multi_objects'] > 0.5

# 定义结构元素
structuring_elements = {
    '3x3方形': np.ones((3, 3)),
    '5x5方形': np.ones((5, 5)),
    '圆形(半径3)': ndimage.generate_binary_structure(2, 1),
    '十字形': np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])
}

# 基本形态学操作
morphological_ops = {
    '腐蚀': binary_erosion,
    '膨胀': binary_dilation,
    '开运算': lambda img, struct: binary_erosion(binary_dilation(img, struct), struct),
    '闭运算': lambda img, struct: binary_dilation(binary_erosion(img, struct), struct)
}

# 使用5x5方形结构元素进行操作
struct_elem = structuring_elements['5x5方形']
morphological_results = {}

for op_name, operation in morphological_ops.items():
    if op_name in ['腐蚀', '膨胀']:
        result = operation(binary_image, structure=struct_elem)
    else:
        result = operation(binary_image, struct_elem)
    morphological_results[op_name] = result

# 可视化形态学操作结果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 原始二值图像
axes[0].imshow(binary_image, cmap='gray')
axes[0].set_title('原始二值图像')
axes[0].axis('off')

# 形态学操作结果
for i, (name, result) in enumerate(morphological_results.items(), 1):
    axes[i].imshow(result, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

# 结构元素可视化
axes[5].imshow(struct_elem, cmap='gray')
axes[5].set_title('5x5方形结构元素')
axes[5].axis('off')

plt.tight_layout()
plt.show()

# 形态学操作统计
print("\n形态学操作统计:")
original_area = np.sum(binary_image)
print(f"原始图像面积: {original_area} 像素")

for name, result in morphological_results.items():
    result_area = np.sum(result)
    area_change = (result_area - original_area) / original_area * 100
    print(f"{name:8s}: 面积={result_area:5d} 像素, 变化={area_change:+6.1f}%")

2. 高级形态学操作

python
# 高级形态学操作
def morphological_gradient(image, structure):
    """形态学梯度"""
    dilated = binary_dilation(image, structure=structure)
    eroded = binary_erosion(image, structure=structure)
    return dilated.astype(int) - eroded.astype(int)

def top_hat_transform(image, structure):
    """顶帽变换"""
    opened = binary_erosion(binary_dilation(image, structure), structure)
    return image.astype(int) - opened.astype(int)

def black_hat_transform(image, structure):
    """黑帽变换"""
    closed = binary_dilation(binary_erosion(image, structure), structure)
    return closed.astype(int) - image.astype(int)

# 应用高级形态学操作
struct_elem = np.ones((7, 7))  # 使用较大的结构元素

advanced_morphological_results = {
    '形态学梯度': morphological_gradient(binary_image, struct_elem),
    '顶帽变换': top_hat_transform(binary_image, struct_elem),
    '黑帽变换': black_hat_transform(binary_image, struct_elem)
}

# 骨架提取
skeleton = ndimage.binary_erosion(binary_image)
for i in range(10):  # 迭代骨架化
    eroded = ndimage.binary_erosion(skeleton)
    if np.sum(eroded) == 0:
        break
    skeleton = eroded

advanced_morphological_results['骨架'] = skeleton

# 距离变换
distance_transform = distance_transform_edt(binary_image)
advanced_morphological_results['距离变换'] = distance_transform

# 可视化高级形态学操作
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 原始图像
axes[0].imshow(binary_image, cmap='gray')
axes[0].set_title('原始二值图像')
axes[0].axis('off')

# 高级形态学操作结果
for i, (name, result) in enumerate(advanced_morphological_results.items(), 1):
    if name == '距离变换':
        im = axes[i].imshow(result, cmap='viridis')
        plt.colorbar(im, ax=axes[i], fraction=0.046, pad=0.04)
    else:
        axes[i].imshow(result, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

plt.tight_layout()
plt.show()

print("\n高级形态学操作分析:")
for name, result in advanced_morphological_results.items():
    if name == '距离变换':
        max_distance = np.max(result)
        avg_distance = np.mean(result[result > 0])
        print(f"{name:12s}: 最大距离={max_distance:.2f}, 平均距离={avg_distance:.2f}")
    else:
        feature_pixels = np.sum(result > 0)
        print(f"{name:12s}: 特征像素={feature_pixels}")

几何变换

1. 基本几何变换

python
# 使用圆形图像进行几何变换演示
original_image = test_images['circle']

# 基本几何变换
geometric_transforms = {
    '旋转45度': lambda img: rotate(img, 45, reshape=False),
    '旋转90度': lambda img: rotate(img, 90, reshape=False),
    '缩放1.5倍': lambda img: zoom(img, 1.5),
    '缩放0.7倍': lambda img: zoom(img, 0.7),
    '水平平移': lambda img: shift(img, [0, 30]),
    '垂直平移': lambda img: shift(img, [30, 0])
}

# 应用几何变换
transform_results = {}
for name, transform in geometric_transforms.items():
    try:
        result = transform(original_image)
        # 确保结果尺寸一致(用于显示)
        if result.shape != original_image.shape:
            # 裁剪或填充到原始尺寸
            if result.shape[0] > original_image.shape[0] or result.shape[1] > original_image.shape[1]:
                # 裁剪
                start_y = (result.shape[0] - original_image.shape[0]) // 2
                start_x = (result.shape[1] - original_image.shape[1]) // 2
                result = result[start_y:start_y+original_image.shape[0], 
                              start_x:start_x+original_image.shape[1]]
            else:
                # 填充
                padded = np.zeros(original_image.shape)
                start_y = (original_image.shape[0] - result.shape[0]) // 2
                start_x = (original_image.shape[1] - result.shape[1]) // 2
                padded[start_y:start_y+result.shape[0], 
                      start_x:start_x+result.shape[1]] = result
                result = padded
        
        transform_results[name] = result
    except Exception as e:
        print(f"变换 {name} 失败: {e}")
        transform_results[name] = original_image

# 可视化几何变换结果
fig, axes = plt.subplots(3, 3, figsize=(15, 15))
axes = axes.flatten()

# 原始图像
axes[0].imshow(original_image, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 变换结果
for i, (name, result) in enumerate(transform_results.items(), 1):
    axes[i].imshow(result, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

# 组合变换示例
combined_transform = rotate(zoom(original_image, 0.8), 30, reshape=False)
axes[7].imshow(combined_transform, cmap='gray')
axes[7].set_title('组合变换(缩放+旋转)')
axes[7].axis('off')

# 插值方法比较
rotated_nearest = rotate(original_image, 45, order=0, reshape=False)  # 最近邻
rotated_bilinear = rotate(original_image, 45, order=1, reshape=False)  # 双线性

axes[8].imshow(np.abs(rotated_bilinear - rotated_nearest), cmap='hot')
axes[8].set_title('插值方法差异')
axes[8].axis('off')

plt.tight_layout()
plt.show()

print("\n几何变换分析:")
for name, result in transform_results.items():
    # 计算与原图的相似性
    correlation = np.corrcoef(original_image.flatten(), result.flatten())[0, 1]
    mse = np.mean((original_image - result)**2)
    print(f"{name:12s}: 相关系数={correlation:.3f}, MSE={mse:.6f}")

2. 仿射变换

python
# 仿射变换矩阵
def create_affine_matrix(rotation=0, scale_x=1, scale_y=1, shear_x=0, shear_y=0, 
                        translate_x=0, translate_y=0):
    """创建仿射变换矩阵"""
    # 旋转矩阵
    cos_r = np.cos(np.radians(rotation))
    sin_r = np.sin(np.radians(rotation))
    
    # 组合变换矩阵
    matrix = np.array([
        [scale_x * cos_r - shear_y * sin_r, -scale_x * sin_r - shear_y * cos_r, translate_x],
        [shear_x * cos_r + scale_y * sin_r, -shear_x * sin_r + scale_y * cos_r, translate_y],
        [0, 0, 1]
    ])
    
    return matrix[:2, :]  # 返回2x3矩阵

# 定义各种仿射变换
affine_transforms = {
    '剪切变换': create_affine_matrix(shear_x=0.3),
    '非均匀缩放': create_affine_matrix(scale_x=1.5, scale_y=0.7),
    '复合变换1': create_affine_matrix(rotation=30, scale_x=0.8, scale_y=0.8, translate_x=20),
    '复合变换2': create_affine_matrix(rotation=-15, shear_x=0.2, scale_x=1.2)
}

# 应用仿射变换
affine_results = {}
for name, matrix in affine_transforms.items():
    # 计算输出形状的中心
    center = np.array(original_image.shape) / 2
    
    # 应用仿射变换
    result = affine_transform(original_image, matrix, 
                            offset=center - np.dot(matrix[:, :2], center),
                            output_shape=original_image.shape,
                            order=1)  # 双线性插值
    
    affine_results[name] = result

# 可视化仿射变换结果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 原始图像
axes[0].imshow(original_image, cmap='gray')
axes[0].set_title('原始图像')
axes[0].axis('off')

# 仿射变换结果
for i, (name, result) in enumerate(affine_results.items(), 1):
    axes[i].imshow(result, cmap='gray')
    axes[i].set_title(name)
    axes[i].axis('off')

# 变换矩阵可视化
axes[5].clear()
matrix_names = list(affine_transforms.keys())
matrix_data = []

for name in matrix_names:
    matrix = affine_transforms[name]
    # 提取变换参数进行可视化
    det = np.linalg.det(matrix[:, :2])  # 行列式(面积缩放因子)
    matrix_data.append(det)

axes[5].bar(range(len(matrix_data)), matrix_data, color=['red', 'green', 'blue', 'orange'])
axes[5].set_title('变换矩阵行列式(面积缩放)')
axes[5].set_xlabel('变换类型')
axes[5].set_ylabel('行列式值')
axes[5].set_xticks(range(len(matrix_names)))
axes[5].set_xticklabels(matrix_names, rotation=45)
axes[5].grid(True, alpha=0.3)
axes[5].axhline(y=1, color='black', linestyle='--', alpha=0.7, label='无缩放')
axes[5].legend()

plt.tight_layout()
plt.show()

print("\n仿射变换分析:")
for name, result in affine_results.items():
    matrix = affine_transforms[name]
    det = np.linalg.det(matrix[:, :2])
    area_change = (det - 1) * 100
    
    # 计算图像质量
    correlation = np.corrcoef(original_image.flatten(), result.flatten())[0, 1]
    
    print(f"{name:12s}: 面积变化={area_change:+6.1f}%, 相关系数={correlation:.3f}")

图像分割和标记

1. 连通组件分析

python
# 使用多对象图像进行分割
segmentation_image = test_images['multi_objects'] > 0.5

# 连通组件标记
labeled_image, num_features = label(segmentation_image)

print(f"检测到 {num_features} 个连通组件")

# 计算每个组件的属性
component_properties = []
for i in range(1, num_features + 1):
    component_mask = labeled_image == i
    
    # 基本属性
    area = np.sum(component_mask)
    centroid = center_of_mass(component_mask)
    
    # 边界框
    coords = np.where(component_mask)
    min_row, max_row = np.min(coords[0]), np.max(coords[0])
    min_col, max_col = np.min(coords[1]), np.max(coords[1])
    bbox_area = (max_row - min_row + 1) * (max_col - min_col + 1)
    
    # 形状特征
    solidity = area / bbox_area if bbox_area > 0 else 0
    
    # 周长(简化计算)
    perimeter = np.sum(binary_erosion(component_mask) != component_mask)
    
    # 圆形度
    if perimeter > 0:
        circularity = 4 * np.pi * area / (perimeter ** 2)
    else:
        circularity = 0
    
    component_properties.append({
        'id': i,
        'area': area,
        'centroid': centroid,
        'bbox': (min_row, min_col, max_row, max_col),
        'solidity': solidity,
        'perimeter': perimeter,
        'circularity': circularity
    })

# 可视化分割结果
fig, axes = plt.subplots(2, 2, figsize=(12, 12))

# 原始图像
axes[0, 0].imshow(segmentation_image, cmap='gray')
axes[0, 0].set_title('原始二值图像')
axes[0, 0].axis('off')

# 标记图像
axes[0, 1].imshow(labeled_image, cmap='nipy_spectral')
axes[0, 1].set_title(f'连通组件标记 ({num_features}个组件)')
axes[0, 1].axis('off')

# 添加质心和边界框
axes[1, 0].imshow(segmentation_image, cmap='gray')
for prop in component_properties:
    centroid = prop['centroid']
    bbox = prop['bbox']
    
    # 绘制质心
    axes[1, 0].plot(centroid[1], centroid[0], 'ro', markersize=8)
    
    # 绘制边界框
    rect_height = bbox[2] - bbox[0]
    rect_width = bbox[3] - bbox[1]
    rect = plt.Rectangle((bbox[1], bbox[0]), rect_width, rect_height, 
                        fill=False, edgecolor='red', linewidth=2)
    axes[1, 0].add_patch(rect)
    
    # 添加标签
    axes[1, 0].text(centroid[1], centroid[0], str(prop['id']), 
                   color='yellow', fontsize=12, ha='center', va='center')

axes[1, 0].set_title('质心和边界框')
axes[1, 0].axis('off')

# 组件属性统计
axes[1, 1].clear()
areas = [prop['area'] for prop in component_properties]
circularities = [prop['circularity'] for prop in component_properties]

axes[1, 1].scatter(areas, circularities, c=range(len(areas)), cmap='viridis', s=100)
axes[1, 1].set_xlabel('面积 (像素)')
axes[1, 1].set_ylabel('圆形度')
axes[1, 1].set_title('组件特征分布')
axes[1, 1].grid(True, alpha=0.3)

# 添加组件ID标注
for i, prop in enumerate(component_properties):
    axes[1, 1].annotate(str(prop['id']), (prop['area'], prop['circularity']), 
                       xytext=(5, 5), textcoords='offset points')

plt.tight_layout()
plt.show()

# 打印组件属性
print("\n连通组件属性:")
print(f"{'ID':>3} {'面积':>6} {'质心':>15} {'圆形度':>8} {'实心度':>8}")
print("-" * 50)
for prop in component_properties:
    centroid_str = f"({prop['centroid'][0]:.1f}, {prop['centroid'][1]:.1f})"
    print(f"{prop['id']:>3} {prop['area']:>6} {centroid_str:>15} {prop['circularity']:>8.3f} {prop['solidity']:>8.3f}")

2. 区域生长分割

python
# 实现简单的区域生长算法
def region_growing(image, seed_points, threshold=0.1):
    """区域生长分割算法"""
    segmented = np.zeros_like(image, dtype=int)
    
    for region_id, seed in enumerate(seed_points, 1):
        # 初始化
        visited = np.zeros_like(image, dtype=bool)
        region_pixels = [seed]
        visited[seed] = True
        segmented[seed] = region_id
        
        # 区域生长
        while region_pixels:
            current_pixel = region_pixels.pop(0)
            current_value = image[current_pixel]
            
            # 检查8邻域
            for dy in [-1, 0, 1]:
                for dx in [-1, 0, 1]:
                    if dy == 0 and dx == 0:
                        continue
                    
                    ny, nx = current_pixel[0] + dy, current_pixel[1] + dx
                    
                    # 边界检查
                    if (0 <= ny < image.shape[0] and 0 <= nx < image.shape[1] and 
                        not visited[ny, nx]):
                        
                        neighbor_value = image[ny, nx]
                        
                        # 相似性检查
                        if abs(neighbor_value - current_value) < threshold:
                            visited[ny, nx] = True
                            segmented[ny, nx] = region_id
                            region_pixels.append((ny, nx))
    
    return segmented

# 使用梯度图像进行区域生长
gradient_image = test_images['gradient']

# 选择种子点
seed_points = [
    (50, 50),   # 左上角
    (50, 150),  # 右上角
    (150, 50),  # 左下角
    (150, 150)  # 右下角
]

# 应用区域生长
region_grown = region_growing(gradient_image, seed_points, threshold=0.15)

# 可视化区域生长结果
fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# 原始图像
axes[0].imshow(gradient_image, cmap='gray')
for i, seed in enumerate(seed_points):
    axes[0].plot(seed[1], seed[0], 'ro', markersize=10)
    axes[0].text(seed[1], seed[0], str(i+1), color='yellow', 
                fontsize=12, ha='center', va='center')
axes[0].set_title('原始图像和种子点')
axes[0].axis('off')

# 分割结果
axes[1].imshow(region_grown, cmap='nipy_spectral')
axes[1].set_title('区域生长分割结果')
axes[1].axis('off')

# 分割边界
boundaries = np.zeros_like(region_grown)
for i in range(1, region_grown.shape[0]-1):
    for j in range(1, region_grown.shape[1]-1):
        if (region_grown[i, j] != region_grown[i-1, j] or 
            region_grown[i, j] != region_grown[i+1, j] or
            region_grown[i, j] != region_grown[i, j-1] or 
            region_grown[i, j] != region_grown[i, j+1]):
            boundaries[i, j] = 1

axes[2].imshow(gradient_image, cmap='gray')
axes[2].imshow(boundaries, cmap='Reds', alpha=0.7)
axes[2].set_title('分割边界叠加')
axes[2].axis('off')

plt.tight_layout()
plt.show()

# 分析分割结果
print("\n区域生长分割分析:")
for region_id in range(1, len(seed_points) + 1):
    region_mask = region_grown == region_id
    region_area = np.sum(region_mask)
    region_mean = np.mean(gradient_image[region_mask])
    region_std = np.std(gradient_image[region_mask])
    
    print(f"区域 {region_id}: 面积={region_area:5d} 像素, 均值={region_mean:.3f}, 标准差={region_std:.3f}")

实际应用案例

1. 细胞计数应用

python
# 模拟细胞图像
def create_cell_image(size=300, num_cells=15):
    """创建模拟的细胞图像"""
    image = np.zeros((size, size))
    
    np.random.seed(42)
    for _ in range(num_cells):
        # 随机位置和大小
        center_y = np.random.randint(20, size-20)
        center_x = np.random.randint(20, size-20)
        radius = np.random.randint(8, 18)
        
        # 创建细胞
        y, x = np.ogrid[:size, :size]
        mask = (x - center_x)**2 + (y - center_y)**2 <= radius**2
        
        # 添加细胞强度
        intensity = np.random.uniform(0.6, 1.0)
        image[mask] = intensity
    
    # 添加背景噪声
    noise = np.random.normal(0, 0.05, (size, size))
    image = image + noise
    image = np.clip(image, 0, 1)
    
    return image

# 创建细胞图像
cell_image = create_cell_image()

# 细胞检测和计数流程
def cell_counting_pipeline(image):
    """细胞计数流程"""
    # 1. 预处理
    # 高斯滤波降噪
    smoothed = gaussian_filter(image, sigma=1)
    
    # 2. 阈值分割
    # 使用Otsu阈值
    threshold = 0.3  # 简化的阈值
    binary = smoothed > threshold
    
    # 3. 形态学操作
    # 去除小噪声
    cleaned = binary_erosion(binary, structure=np.ones((3, 3)))
    cleaned = binary_dilation(cleaned, structure=np.ones((3, 3)))
    
    # 分离粘连细胞
    distance = distance_transform_edt(cleaned)
    local_maxima = distance > 0.7 * np.max(distance)
    
    # 4. 连通组件分析
    labeled, num_cells = label(cleaned)
    
    return {
        'original': image,
        'smoothed': smoothed,
        'binary': binary,
        'cleaned': cleaned,
        'distance': distance,
        'labeled': labeled,
        'num_cells': num_cells
    }

# 执行细胞计数
cell_results = cell_counting_pipeline(cell_image)

# 可视化细胞计数结果
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()

# 处理步骤可视化
steps = [
    ('original', '原始图像', 'gray'),
    ('smoothed', '高斯滤波', 'gray'),
    ('binary', '阈值分割', 'gray'),
    ('cleaned', '形态学清理', 'gray'),
    ('distance', '距离变换', 'viridis'),
    ('labeled', '细胞标记', 'nipy_spectral')
]

for i, (key, title, cmap) in enumerate(steps):
    im = axes[i].imshow(cell_results[key], cmap=cmap)
    axes[i].set_title(f'{title}')
    axes[i].axis('off')
    
    if key == 'distance':
        plt.colorbar(im, ax=axes[i], fraction=0.046, pad=0.04)
    elif key == 'labeled':
        axes[i].set_title(f'{title} ({cell_results["num_cells"]} 个细胞)')

plt.tight_layout()
plt.show()

# 细胞特征分析
cell_features = []
for cell_id in range(1, cell_results['num_cells'] + 1):
    cell_mask = cell_results['labeled'] == cell_id
    
    # 基本特征
    area = np.sum(cell_mask)
    centroid = center_of_mass(cell_mask)
    
    # 强度特征
    mean_intensity = np.mean(cell_image[cell_mask])
    max_intensity = np.max(cell_image[cell_mask])
    
    # 形状特征
    coords = np.where(cell_mask)
    min_row, max_row = np.min(coords[0]), np.max(coords[0])
    min_col, max_col = np.min(coords[1]), np.max(coords[1])
    
    width = max_col - min_col + 1
    height = max_row - min_row + 1
    aspect_ratio = width / height if height > 0 else 0
    
    cell_features.append({
        'id': cell_id,
        'area': area,
        'centroid': centroid,
        'mean_intensity': mean_intensity,
        'max_intensity': max_intensity,
        'width': width,
        'height': height,
        'aspect_ratio': aspect_ratio
    })

print(f"\n细胞计数结果: 检测到 {cell_results['num_cells']} 个细胞")
print("\n细胞特征统计:")
print(f"{'ID':>3} {'面积':>6} {'平均强度':>10} {'宽度':>6} {'高度':>6} {'长宽比':>8}")
print("-" * 55)

for feature in cell_features:
    print(f"{feature['id']:>3} {feature['area']:>6} {feature['mean_intensity']:>10.3f} "
          f"{feature['width']:>6} {feature['height']:>6} {feature['aspect_ratio']:>8.2f}")

# 统计分析
areas = [f['area'] for f in cell_features]
intensities = [f['mean_intensity'] for f in cell_features]

print(f"\n统计摘要:")
print(f"平均细胞面积: {np.mean(areas):.1f} ± {np.std(areas):.1f} 像素")
print(f"平均细胞强度: {np.mean(intensities):.3f} ± {np.std(intensities):.3f}")
print(f"面积范围: {np.min(areas)} - {np.max(areas)} 像素")

2. 纹理分析应用

python
# 纹理特征提取
def extract_texture_features(image, window_size=16):
    """提取纹理特征"""
    features = {}
    
    # 1. 统计特征
    features['mean'] = np.mean(image)
    features['std'] = np.std(image)
    features['skewness'] = np.mean(((image - features['mean']) / features['std'])**3)
    features['kurtosis'] = np.mean(((image - features['mean']) / features['std'])**4)
    
    # 2. 梯度特征
    grad_x = sobel(image, axis=1)
    grad_y = sobel(image, axis=0)
    gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
    
    features['gradient_mean'] = np.mean(gradient_magnitude)
    features['gradient_std'] = np.std(gradient_magnitude)
    
    # 3. 局部二值模式 (简化版)
    def local_binary_pattern_simple(img):
        lbp = np.zeros_like(img)
        for i in range(1, img.shape[0]-1):
            for j in range(1, img.shape[1]-1):
                center = img[i, j]
                pattern = 0
                pattern += (img[i-1, j-1] >= center) << 7
                pattern += (img[i-1, j] >= center) << 6
                pattern += (img[i-1, j+1] >= center) << 5
                pattern += (img[i, j+1] >= center) << 4
                pattern += (img[i+1, j+1] >= center) << 3
                pattern += (img[i+1, j] >= center) << 2
                pattern += (img[i+1, j-1] >= center) << 1
                pattern += (img[i, j-1] >= center) << 0
                lbp[i, j] = pattern
        return lbp
    
    lbp = local_binary_pattern_simple(image)
    lbp_hist, _ = np.histogram(lbp.flatten(), bins=256, range=(0, 256))
    lbp_hist = lbp_hist / np.sum(lbp_hist)  # 归一化
    
    features['lbp_uniformity'] = np.sum(lbp_hist**2)
    features['lbp_entropy'] = -np.sum(lbp_hist * np.log2(lbp_hist + 1e-10))
    
    return features

# 创建不同纹理的图像
def create_texture_samples():
    """创建不同纹理样本"""
    size = 128
    textures = {}
    
    # 1. 随机纹理
    np.random.seed(42)
    textures['random'] = np.random.random((size, size))
    
    # 2. 正弦波纹理
    x, y = np.meshgrid(np.linspace(0, 4*np.pi, size), np.linspace(0, 4*np.pi, size))
    textures['sine_wave'] = 0.5 * (np.sin(x) + np.sin(y)) + 0.5
    
    # 3. 棋盘纹理
    checkerboard = np.zeros((size, size))
    square_size = 8
    for i in range(0, size, square_size):
        for j in range(0, size, square_size):
            if (i // square_size + j // square_size) % 2 == 0:
                checkerboard[i:i+square_size, j:j+square_size] = 1
    textures['checkerboard'] = checkerboard
    
    # 4. 分形纹理(简化版)
    fractal = np.zeros((size, size))
    for i in range(size):
        for j in range(size):
            fractal[i, j] = np.sin(0.1 * i) * np.cos(0.1 * j) + 0.2 * np.sin(0.3 * i) * np.cos(0.3 * j)
    textures['fractal'] = (fractal - fractal.min()) / (fractal.max() - fractal.min())
    
    return textures

# 创建纹理样本
texture_samples = create_texture_samples()

# 提取每个纹理的特征
texture_features = {}
for name, texture in texture_samples.items():
    texture_features[name] = extract_texture_features(texture)

# 可视化纹理样本和特征
fig, axes = plt.subplots(2, 4, figsize=(16, 8))

# 纹理样本
for i, (name, texture) in enumerate(texture_samples.items()):
    axes[0, i].imshow(texture, cmap='gray')
    axes[0, i].set_title(f'{name.replace("_", " ").title()}')
    axes[0, i].axis('off')

# 特征比较
feature_names = ['mean', 'std', 'gradient_mean', 'lbp_entropy']
colors = ['red', 'green', 'blue', 'orange']

for i, feature_name in enumerate(feature_names):
    values = [texture_features[texture_name][feature_name] for texture_name in texture_samples.keys()]
    axes[1, i].bar(range(len(values)), values, color=colors[i], alpha=0.7)
    axes[1, i].set_title(f'{feature_name.replace("_", " ").title()}')
    axes[1, i].set_xticks(range(len(texture_samples)))
    axes[1, i].set_xticklabels(list(texture_samples.keys()), rotation=45)
    axes[1, i].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\n纹理特征分析:")
print(f"{'纹理类型':>12} {'均值':>8} {'标准差':>8} {'梯度均值':>10} {'LBP熵':>8}")
print("-" * 55)

for name, features in texture_features.items():
    print(f"{name:>12} {features['mean']:>8.3f} {features['std']:>8.3f} "
          f"{features['gradient_mean']:>10.3f} {features['lbp_entropy']:>8.3f}")

# 纹理分类示例
def classify_texture_simple(features):
    """简单的纹理分类器"""
    if features['std'] < 0.2:
        return '均匀纹理'
    elif features['gradient_mean'] > 0.3:
        return '边缘丰富纹理'
    elif features['lbp_entropy'] > 6:
        return '复杂纹理'
    else:
        return '简单纹理'

print("\n纹理分类结果:")
for name, features in texture_features.items():
    classification = classify_texture_simple(features)
    print(f"{name:>12}: {classification}")

性能优化技巧

1. 算法选择和参数优化

python
import time

# 性能测试函数
def performance_test(func, *args, **kwargs):
    """测试函数性能"""
    start_time = time.time()
    result = func(*args, **kwargs)
    end_time = time.time()
    return result, end_time - start_time

# 创建大图像进行性能测试
large_image = np.random.random((1000, 1000))

# 比较不同滤波器的性能
filter_tests = {
    '高斯滤波(sigma=1)': lambda img: gaussian_filter(img, sigma=1),
    '高斯滤波(sigma=2)': lambda img: gaussian_filter(img, sigma=2),
    '均值滤波(5x5)': lambda img: uniform_filter(img, size=5),
    '均值滤波(9x9)': lambda img: uniform_filter(img, size=9),
    '中值滤波(5x5)': lambda img: median_filter(img, size=5)
}

print("滤波器性能比较:")
print(f"{'滤波器':>20} {'时间(秒)':>10} {'相对速度':>10}")
print("-" * 45)

performance_results = []
for name, filter_func in filter_tests.items():
    _, exec_time = performance_test(filter_func, large_image)
    performance_results.append((name, exec_time))

# 按执行时间排序
performance_results.sort(key=lambda x: x[1])
fastest_time = performance_results[0][1]

for name, exec_time in performance_results:
    relative_speed = exec_time / fastest_time
    print(f"{name:>20} {exec_time:>10.4f} {relative_speed:>10.2f}x")

# 内存使用优化
def memory_efficient_processing(image, chunk_size=256):
    """内存高效的图像处理"""
    result = np.zeros_like(image)
    
    for i in range(0, image.shape[0], chunk_size):
        for j in range(0, image.shape[1], chunk_size):
            # 处理图像块
            end_i = min(i + chunk_size, image.shape[0])
            end_j = min(j + chunk_size, image.shape[1])
            
            chunk = image[i:end_i, j:end_j]
            processed_chunk = gaussian_filter(chunk, sigma=1)
            result[i:end_i, j:end_j] = processed_chunk
    
    return result

# 测试内存效率
print("\n内存使用优化测试:")
_, time_normal = performance_test(gaussian_filter, large_image, sigma=1)
_, time_chunked = performance_test(memory_efficient_processing, large_image)

print(f"常规处理时间: {time_normal:.4f} 秒")
print(f"分块处理时间: {time_chunked:.4f} 秒")
print(f"时间开销: {time_chunked/time_normal:.2f}x")

2. 并行处理和缓存优化

python
from functools import lru_cache

# 缓存优化示例
@lru_cache(maxsize=128)
def cached_gaussian_kernel(sigma, size):
    """缓存高斯核"""
    kernel = np.zeros((size, size))
    center = size // 2
    
    for i in range(size):
        for j in range(size):
            x, y = i - center, j - center
            kernel[i, j] = np.exp(-(x**2 + y**2) / (2 * sigma**2))
    
    return kernel / np.sum(kernel)

# 优化的卷积函数
def optimized_convolution(image, kernel):
    """优化的卷积实现"""
    # 使用SciPy的优化卷积
    return ndimage.convolve(image, kernel, mode='reflect')

# 批处理优化
def batch_process_images(images, operation):
    """批处理图像"""
    results = []
    
    # 预分配内存
    if images:
        sample_result = operation(images[0])
        results = [np.zeros_like(sample_result) for _ in images]
        results[0] = sample_result
        
        # 处理剩余图像
        for i in range(1, len(images)):
            results[i] = operation(images[i])
    
    return results

# 创建测试图像集
test_images_batch = [np.random.random((200, 200)) for _ in range(5)]

# 测试批处理性能
print("\n批处理性能测试:")

# 单独处理
start_time = time.time()
individual_results = []
for img in test_images_batch:
    result = gaussian_filter(img, sigma=1)
    individual_results.append(result)
individual_time = time.time() - start_time

# 批处理
start_time = time.time()
batch_results = batch_process_images(test_images_batch, 
                                   lambda img: gaussian_filter(img, sigma=1))
batch_time = time.time() - start_time

print(f"单独处理时间: {individual_time:.4f} 秒")
print(f"批处理时间: {batch_time:.4f} 秒")
print(f"性能提升: {individual_time/batch_time:.2f}x")

# 参数调优建议
print("\n性能优化建议:")
print("1. 选择合适的滤波器大小和参数")
print("2. 对于大图像,考虑分块处理")
print("3. 使用缓存避免重复计算")
print("4. 批处理多个图像以提高效率")
print("5. 选择合适的插值方法平衡质量和速度")

小结

本章详细介绍了SciPy中的图像处理功能,主要内容包括:

核心概念

  • scipy.ndimage模块: SciPy的多维图像处理核心
  • 图像表示: 数组形式的图像数据结构
  • 滤波操作: 线性和非线性滤波技术
  • 形态学操作: 二值图像的结构化处理

主要功能

  1. 图像滤波: 高斯滤波、中值滤波、边缘检测
  2. 几何变换: 旋转、缩放、仿射变换
  3. 形态学处理: 腐蚀、膨胀、开闭运算
  4. 图像分割: 阈值分割、区域生长、连通组件分析
  5. 特征提取: 纹理分析、形状特征

实际应用

  • 生物医学图像: 细胞计数、组织分析
  • 工业检测: 缺陷检测、质量控制
  • 科学研究: 显微镜图像分析
  • 计算机视觉: 预处理和特征提取

性能优化

  • 选择合适的算法和参数
  • 内存高效的分块处理
  • 缓存和批处理优化
  • 并行处理技术

SciPy的图像处理功能为科学计算和研究提供了强大的工具,虽然不如专门的计算机视觉库功能丰富,但在基础图像处理任务中表现出色,特别适合科学数据的分析和处理。

练习题

  1. 基础滤波: 实现一个自定义的3x3均值滤波器,并与uniform_filter的结果进行比较。

  2. 边缘检测: 组合使用Sobel算子的X和Y方向结果,计算边缘的幅度和方向,并可视化结果。

  3. 形态学应用: 设计一个形态学操作序列来分离粘连的圆形对象。

  4. 几何变换: 实现一个图像配准函数,将一个旋转的图像恢复到原始方向。

  5. 分割算法: 改进区域生长算法,添加多个相似性准则(如颜色、纹理)。

  6. 特征提取: 实现灰度共生矩阵(GLCM)来提取纹理特征。

  7. 性能优化: 比较不同插值方法在图像缩放中的性能和质量差异。

  8. 综合应用: 开发一个完整的图像分析流程,包括预处理、分割、特征提取和分类。

本站内容仅供学习和研究使用。